commit the beginning of advanced boolopt (had that laying around for months now ...
[libfirm] / ir / opt / boolopt.c
1 #include <assert.h>
2 #include <string.h>
3
4 #include "adt/obst.h"
5 #include "ircons.h"
6 #include "irgmod.h"
7 #include "irgwalk.h"
8 #include "irnode.h"
9 #include "tv.h"
10
11 typedef struct cond_pair {
12         ir_node *cmp_lo;
13         ir_node *cmp_hi;
14         pn_Cmp   pnc_lo;
15         pn_Cmp   pnc_hi;
16         ir_node *proj_lo;
17         ir_node *proj_hi;
18         tarval  *tv_lo;
19         tarval  *tv_hi;
20 } cond_pair;
21
22 static int find_cond_pair(ir_node *const l, ir_node *const r, cond_pair *const res)
23 {
24         if (is_Proj(l) && is_Proj(r)) {
25                 ir_node *const lo = get_Proj_pred(l);
26                 ir_node *const ro = get_Proj_pred(r);
27
28                 if (is_Cmp(lo) && is_Cmp(ro)) {
29                         ir_node *const lol = get_Cmp_left(lo);
30                         ir_node *const lor = get_Cmp_right(lo);
31                         ir_node *const rol = get_Cmp_left(ro);
32                         ir_node *const ror = get_Cmp_right(ro);
33
34                         if(is_Const(lor) && is_Const_null(lor) && is_Const(ror) && is_Const_null(ror) && get_Proj_proj(l) == pn_Cmp_Lg && get_Proj_proj(r) == pn_Cmp_Lg) {
35                                 ir_fprintf(stderr, "found zero zero\n");
36                         }
37
38                         /* TODO float */
39                         /* The constants shall be unequal.  Local optimisations handle the
40                          * equal case */
41                         if (lol == rol && mode_is_int(get_irn_mode(lol)) && lor != ror && is_Const(lor) && is_Const(ror)) {
42                                 tarval *const tv_l  = get_Const_tarval(lor);
43                                 tarval *const tv_r  = get_Const_tarval(ror);
44                                 pn_Cmp  const pnc_l = get_Proj_proj(l);
45                                 pn_Cmp  const pnc_r = get_Proj_proj(r);
46                                 pn_Cmp  const rel   = tarval_cmp(tv_l, tv_r);
47
48                                 assert(rel != pn_Cmp_Eq);
49
50                                 if (rel == pn_Cmp_Lt) {
51                                         res->cmp_lo  = lo;
52                                         res->cmp_hi  = ro;
53                                         res->pnc_lo  = pnc_l;
54                                         res->pnc_hi  = pnc_r;
55                                         res->proj_lo = l;
56                                         res->proj_hi = r;
57                                         res->tv_lo   = tv_l;
58                                         res->tv_hi   = tv_r;
59                                 } else {
60                                         assert(rel == pn_Cmp_Gt);
61                                         res->cmp_lo  = ro;
62                                         res->cmp_hi  = lo;
63                                         res->pnc_lo  = pnc_r;
64                                         res->pnc_hi  = pnc_l;
65                                         res->proj_lo = r;
66                                         res->proj_hi = l;
67                                         res->tv_lo   = tv_r;
68                                         res->tv_hi   = tv_l;
69                                 }
70                                 return 1;
71                         }
72                 }
73         }
74         return 0;
75 }
76
77 static ir_node *bool_and(cond_pair* const cpair)
78 {
79         ir_node *const cmp_lo  = cpair->cmp_lo;
80         ir_node *const cmp_hi  = cpair->cmp_hi;
81         pn_Cmp   const pnc_lo  = cpair->pnc_lo;
82         pn_Cmp   const pnc_hi  = cpair->pnc_hi;
83         ir_node *const proj_lo = cpair->proj_lo;
84         ir_node *const proj_hi = cpair->proj_hi;
85         tarval  *const tv_lo   = cpair->tv_lo;
86         tarval  *const tv_hi   = cpair->tv_hi;
87
88         if ((pnc_lo == pn_Cmp_Lt || pnc_lo == pn_Cmp_Le || pnc_lo == pn_Cmp_Eq) &&
89                         (pnc_hi == pn_Cmp_Eq || pnc_hi == pn_Cmp_Ge || pnc_hi == pn_Cmp_Gt)) {
90                 /* x <|<=|== lo | x ==|>=|> hi -> false */
91                 ir_node *const t = new_Const(mode_b, tarval_b_false);
92                 return t;
93         } else if ((pnc_lo == pn_Cmp_Lt || pnc_lo == pn_Cmp_Le || pnc_lo == pn_Cmp_Eq) &&
94                                                  (pnc_hi == pn_Cmp_Lt || pnc_hi == pn_Cmp_Le || pnc_hi == pn_Cmp_Ne)) {
95                 /* x <|<=|== lo && x <|<=|!= hi -> x <|<=|== lo */
96                 return proj_lo;
97         } else if ((pnc_lo == pn_Cmp_Ge || pnc_lo == pn_Cmp_Gt || pnc_lo == pn_Cmp_Ne) &&
98                                                  (pnc_hi == pn_Cmp_Eq || pnc_hi == pn_Cmp_Ge || pnc_hi == pn_Cmp_Gt)) {
99                 /* x >=|>|!= lo || x ==|>=|> hi -> x ==|>=|> hi */
100                 return proj_hi;
101         } else if (tarval_is_one(tarval_sub(tv_hi, tv_lo))) { /* lo + 1 == hi */
102                 if (pnc_lo == pn_Cmp_Ge && pnc_hi == pn_Cmp_Lt) {
103                         /* x >= c || x < c + 1 -> x == c */
104                         ir_graph *const irg   = current_ir_graph;
105                         ir_node  *const block = get_nodes_block(cmp_lo);
106                         ir_node  *const p = new_r_Proj(irg, block, cmp_lo, mode_b, pn_Cmp_Eq);
107                         return p;
108                 } else if (pnc_lo == pn_Cmp_Gt) {
109                         if (pnc_hi == pn_Cmp_Ne) {
110                                 /* x > c || x != c + 1 -> x > c + 1 */
111                                 ir_graph *const irg   = current_ir_graph;
112                                 ir_node  *const block = get_nodes_block(cmp_hi);
113                                 ir_node  *const p = new_r_Proj(irg, block, cmp_hi, mode_b, pn_Cmp_Gt);
114                                 return p;
115                         } else if (pnc_hi == pn_Cmp_Lt) {
116                                 /* x > c || x < c + 1 -> false */
117                                 ir_node *const t = new_Const(mode_b, tarval_b_false);
118                                 return t;
119                         } else if (pnc_hi == pn_Cmp_Le) {
120                                 /* x > c || x <= c + 1 -> x != c + 1 */
121                                 ir_graph *const irg   = current_ir_graph;
122                                 ir_node  *const block = get_nodes_block(cmp_hi);
123                                 ir_node  *const p = new_r_Proj(irg, block, cmp_hi, mode_b, pn_Cmp_Eq);
124                                 return p;
125                         }
126                 } else if (pnc_lo == pn_Cmp_Ne && pnc_hi == pn_Cmp_Lt) {
127                         /* x != c || c < c + 1 -> x < c */
128                         ir_graph *const irg   = current_ir_graph;
129                         ir_node  *const block = get_nodes_block(cmp_lo);
130                         ir_node  *const p     = new_r_Proj(irg, block, cmp_lo, mode_b, pn_Cmp_Lt);
131                         return p;
132                 }
133         }
134         return NULL;
135 }
136
137 static ir_node *bool_or(cond_pair *const cpair)
138 {
139         ir_node *const cmp_lo  = cpair->cmp_lo;
140         ir_node *const cmp_hi  = cpair->cmp_hi;
141         pn_Cmp   const pnc_lo  = cpair->pnc_lo;
142         pn_Cmp   const pnc_hi  = cpair->pnc_hi;
143         ir_node *const proj_lo = cpair->proj_lo;
144         ir_node *const proj_hi = cpair->proj_hi;
145         tarval  *const tv_lo   = cpair->tv_lo;
146         tarval  *const tv_hi   = cpair->tv_hi;
147
148         if ((pnc_lo == pn_Cmp_Ge || pnc_lo == pn_Cmp_Gt || pnc_lo == pn_Cmp_Ne) &&
149                         (pnc_hi == pn_Cmp_Lt || pnc_hi == pn_Cmp_Le || pnc_hi == pn_Cmp_Ne)) {
150                 /* x >=|>|!= lo | x <|<=|!= hi -> true */
151                 ir_node *const t = new_Const(mode_b, tarval_b_true);
152                 return t;
153         } else if ((pnc_lo == pn_Cmp_Lt || pnc_lo == pn_Cmp_Le || pnc_lo == pn_Cmp_Eq) &&
154                                                  (pnc_hi == pn_Cmp_Lt || pnc_hi == pn_Cmp_Le || pnc_hi == pn_Cmp_Ne)) {
155                 /* x <|<=|== lo || x <|<=|!= hi -> x <|<=|!= hi */
156                 return proj_hi;
157         } else if ((pnc_lo == pn_Cmp_Ge || pnc_lo == pn_Cmp_Gt || pnc_lo == pn_Cmp_Ne) &&
158                                                  (pnc_hi == pn_Cmp_Eq || pnc_hi == pn_Cmp_Ge || pnc_hi == pn_Cmp_Gt)) {
159                 /* x >=|>|!= lo || x ==|>=|> hi -> x >=|>|!= lo */
160                 return proj_lo;
161         } else if (tarval_is_one(tarval_sub(tv_hi, tv_lo))) { /* lo + 1 == hi */
162                 if (pnc_lo == pn_Cmp_Lt && pnc_hi == pn_Cmp_Ge) {
163                         /* x < c || x >= c + 1 -> x != c */
164                         ir_graph *const irg   = current_ir_graph;
165                         ir_node  *const block = get_nodes_block(cmp_lo);
166                         ir_node  *const p = new_r_Proj(irg, block, cmp_lo, mode_b, pn_Cmp_Ne);
167                         return p;
168                 } else if (pnc_lo == pn_Cmp_Le) {
169                         if (pnc_hi == pn_Cmp_Eq) {
170                                 /* x <= c || x == c + 1 -> x <= c + 1 */
171                                 ir_graph *const irg   = current_ir_graph;
172                                 ir_node  *const block = get_nodes_block(cmp_hi);
173                                 ir_node  *const p = new_r_Proj(irg, block, cmp_hi, mode_b, pn_Cmp_Le);
174                                 return p;
175                         } else if (pnc_hi == pn_Cmp_Ge) {
176                                 /* x <= c || x >= c + 1 -> true */
177                                 ir_node *const t = new_Const(mode_b, tarval_b_true);
178                                 return t;
179                         } else if (pnc_hi == pn_Cmp_Gt) {
180                                 /* x <= c || x > c + 1 -> x != c + 1 */
181                                 ir_graph *const irg   = current_ir_graph;
182                                 ir_node  *const block = get_nodes_block(cmp_hi);
183                                 ir_node  *const p = new_r_Proj(irg, block, cmp_hi, mode_b, pn_Cmp_Ne);
184                                 return p;
185                         }
186                 } else if (pnc_lo == pn_Cmp_Eq && pnc_hi == pn_Cmp_Ge) {
187                         /* x == c || c >= c + 1 -> x >= c */
188                         ir_graph *const irg   = current_ir_graph;
189                         ir_node  *const block = get_nodes_block(cmp_lo);
190                         ir_node  *const p     = new_r_Proj(irg, block, cmp_lo, mode_b, pn_Cmp_Ge);
191                         return p;
192                 }
193         }
194         return NULL;
195 }
196
197 static void bool_walk(ir_node *n, void *env)
198 {
199         (void)env;
200
201         if (get_irn_mode(n) != mode_b)
202                 return;
203
204         if (is_And(n)) {
205                 ir_node *const l = get_And_left(n);
206                 ir_node *const r = get_And_right(n);
207                 ir_node *      replacement;
208                 cond_pair      cpair;
209                 if (!find_cond_pair(l, r, &cpair))
210                         return;
211                 replacement = bool_and(&cpair);
212                 if (replacement)
213                         exchange(n, replacement);
214         } else if (is_Or(n)) {
215                 ir_node *const l = get_Or_left(n);
216                 ir_node *const r = get_Or_right(n);
217                 ir_node *      replacement;
218                 cond_pair      cpair;
219                 if (!find_cond_pair(l, r, &cpair))
220                         return;
221                 replacement = bool_or(&cpair);
222                 if (replacement)
223                         exchange(n, replacement);
224         }
225 }
226
227 static struct obstack obst;
228
229 typedef struct block_info_t {
230         ir_node *phi;   /**< head of the Phi list */
231         int has_pinned; /**< set if the block contains instructions that cannot be
232                              moved */
233 } block_info_t;
234
235 static block_info_t* get_block_blockinfo(ir_node* block)
236 {
237         return get_irn_link(block);
238 }
239
240 static void create_blockinfos(ir_node *node, void *env)
241 {
242         block_info_t *block_info;
243         (void) env;
244
245         /* we visit blocks before any other nodes (from the block) */
246         if (!is_Block(node))
247                 return;
248
249         block_info = obstack_alloc(&obst, sizeof(block_info[0]));
250         memset(block_info, 0, sizeof(block_info[0]));
251         set_irn_link(node, block_info);
252 }
253
254 static void collect_phis(ir_node *node, void *env)
255 {
256         (void) env;
257
258         if (is_Phi(node)) {
259                 ir_node      *block = get_nodes_block(node);
260                 block_info_t *bi    = get_block_blockinfo(block);
261
262                 set_irn_link(node, bi->phi);
263                 bi->phi = node;
264                 return;
265         }
266
267         /* Ignore control flow nodes, these will be removed. */
268         if (get_irn_pinned(node) == op_pin_state_pinned &&
269                         !is_Block(node) && !is_cfop(node)) {
270                 ir_node      *block = get_nodes_block(node);
271                 block_info_t *bi    = get_block_blockinfo(block);
272
273                 bi->has_pinned = 1;
274         }
275 }
276
277 ir_node *skip_empty_block(ir_node *node)
278 {
279         ir_node      *block;
280         block_info_t *block_info;
281
282         if(!is_Jmp(node))
283                 return node;
284
285         block = get_nodes_block(node);
286         if(get_Block_n_cfgpreds(block) != 1)
287                 return node;
288
289         block_info = get_block_blockinfo(block);
290         if(block_info->has_pinned)
291                 return node;
292
293         return get_Block_cfgpred(block, 0);
294 }
295
296 static void find_cf_and_or_walker(ir_node *block, void *env)
297 {
298         int i, i2;
299         int n_cfgpreds = get_Block_n_cfgpreds(block);
300         (void) env;
301
302         if(n_cfgpreds < 2)
303                 return;
304
305         /* Find the following structure:
306          *
307          *        upper_block
308          *         /       |
309          *        /        |
310          *   lower_block   |
311          *     /  \        |
312          *   ...   \       |
313          *           block
314          */
315
316 restart:
317         for(i = 0; i < n_cfgpreds; ++i) {
318                 ir_node      *lower_block;
319                 ir_node      *lower_cf;
320                 ir_node      *cond;
321                 ir_node      *cond_selector;
322                 block_info_t *block_info;
323                 ir_node      *lower_pred;
324
325                 lower_cf = get_Block_cfgpred(block, i);
326                 lower_cf = skip_empty_block(lower_cf);
327                 if(!is_Proj(lower_cf))
328                         continue;
329
330                 lower_block = get_nodes_block(lower_cf);
331                 if(get_Block_n_cfgpreds(lower_block) != 1)
332                         continue;
333
334                 cond = get_Proj_pred(lower_cf);
335                 if(!is_Cond(cond))
336                         continue;
337
338                 cond_selector = get_Cond_selector(cond);
339                 if(get_irn_mode(cond_selector) != mode_b)
340                         continue;
341
342                 /* the block must not produce any side-effects */
343                 block_info = get_block_blockinfo(lower_block);
344                 if(block_info->has_pinned)
345                         continue;
346
347                 lower_pred = get_Block_cfgpred_block(lower_block, 0);
348
349                 for(i2 = 0; i2 < n_cfgpreds; ++i2) {
350                         ir_node   *upper_block;
351                         ir_node   *upper_cf;
352                         ir_node   *upper_cond;
353                         ir_node   *upper_cond_selector;
354                         ir_node   *replacement;
355                         ir_graph  *irg;
356                         cond_pair  cpair;
357
358                         upper_cf    = get_Block_cfgpred(block, i2);
359                         upper_cf    = skip_empty_block(upper_cf);
360                         if(is_Bad(upper_cf))
361                                 continue;
362                         upper_block = get_nodes_block(upper_cf);
363                         if(upper_block != lower_pred)
364                                 continue;
365
366                         assert(is_Proj(upper_cf));
367                         upper_cond = get_Proj_pred(upper_cf);
368                         assert(is_Cond(upper_cond));
369                         upper_cond_selector = get_Cond_selector(upper_cond);
370                         if(get_irn_mode(upper_cond_selector) != mode_b)
371                                 continue;
372
373                         /* we have found the structure */
374                         /* TODO: check phis */
375                         if(!find_cond_pair(cond_selector, upper_cond_selector, &cpair))
376                                 continue;
377
378                         /* normalize pncs: we need the true case to jump into the
379                          * common block */
380                         irg = current_ir_graph;
381                         if(get_Proj_proj(lower_cf) == pn_Cond_false) {
382                                 if(cpair.proj_lo == cond_selector) {
383                                         ir_mode *mode = get_tarval_mode(cpair.tv_lo);
384                                         cpair.pnc_lo  = get_negated_pnc(cpair.pnc_lo, mode);
385                                         cpair.proj_lo = new_r_Proj(irg, lower_block,
386                                                         get_Proj_pred(cpair.proj_lo), mode_b, cpair.pnc_lo);
387                                 } else {
388                                         assert(cpair.proj_hi == cond_selector);
389                                         ir_mode *mode = get_tarval_mode(cpair.tv_hi);
390                                         cpair.pnc_hi  = get_negated_pnc(cpair.pnc_hi, mode);
391                                         cpair.proj_hi = new_r_Proj(irg, lower_block,
392                                                         get_Proj_pred(cpair.proj_hi), mode_b, cpair.pnc_hi);
393                                 }
394                         }
395                         if(get_Proj_proj(upper_cf) == pn_Cond_false) {
396                                 if(cpair.proj_lo == upper_cond_selector) {
397                                         ir_mode *mode = get_tarval_mode(cpair.tv_lo);
398                                         cpair.pnc_lo  = get_negated_pnc(cpair.pnc_lo, mode);
399                                         cpair.proj_lo = new_r_Proj(irg, upper_block,
400                                                         get_Proj_pred(cpair.proj_lo), mode_b, cpair.pnc_lo);
401                                 } else {
402                                         assert(cpair.proj_hi == upper_cond_selector);
403                                         ir_mode *mode = get_tarval_mode(cpair.tv_hi);
404                                         cpair.pnc_hi  = get_negated_pnc(cpair.pnc_hi, mode);
405                                         cpair.proj_hi = new_r_Proj(irg, upper_block,
406                                                         get_Proj_pred(cpair.proj_hi), mode_b, cpair.pnc_hi);
407                                 }
408                         }
409
410                         /* can we optimize the case? */
411                         replacement = bool_or(&cpair);
412                         if(replacement == NULL)
413                                 continue;
414
415                         /* move all nodes from lower block to upper block */
416                         exchange(lower_block, upper_block);
417
418                         set_Block_cfgpred(block, i2, new_Bad());
419
420                         /* the optimisations expected the true case to jump */
421                         if(get_Proj_proj(lower_cf) == pn_Cond_false) {
422                                 ir_node *block = get_nodes_block(replacement);
423                                 replacement    = new_rd_Not(NULL, current_ir_graph, block,
424                                                             replacement, mode_b);
425                         }
426                         set_Cond_selector(cond, replacement);
427
428                         ir_fprintf(stderr, "replaced (ub %+F)\n", upper_block);
429                         goto restart;
430                 }
431         }
432 }
433
434 void opt_bool(ir_graph *const irg)
435 {
436         irg_walk_graph(irg, NULL, bool_walk, NULL);
437
438         set_using_irn_link(irg);
439
440         obstack_init(&obst);
441
442         irg_walk_graph(irg, create_blockinfos, collect_phis, NULL);
443
444         irg_block_walk_graph(irg, NULL, find_cf_and_or_walker, NULL);
445
446         obstack_free(&obst, NULL);
447
448         set_irg_outs_inconsistent(irg);
449         set_irg_doms_inconsistent(irg);
450         set_irg_extblk_inconsistent(irg);
451         set_irg_loopinfo_inconsistent(irg);
452
453         clear_using_irn_link(irg);
454 }