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