a86c4a517c597a00e421f642ac79ce5c65f05890
[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_t.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 /**
229  * Walker, clear Block mark and Phi list
230  */
231 static void clear_block_infos(ir_node *node, void *env)
232 {
233         (void) env;
234
235         /* we visit blocks before any other nodes (from the block) */
236         if (!is_Block(node))
237                 return;
238
239         /* clear the PHI list */
240         set_Block_phis(node, NULL);
241         set_Block_mark(node, 0);
242 }
243
244 /**
245  * Walker: collect Phi nodes and update the
246  */
247 static void collect_phis(ir_node *node, void *env)
248 {
249         (void) env;
250
251         if (is_Phi(node)) {
252                 ir_node *block = get_nodes_block(node);
253                 add_Block_phi(block, node);
254                 return;
255         }
256
257         /* Ignore control flow nodes, these will be removed. */
258         if (get_irn_pinned(node) == op_pin_state_pinned &&
259                         !is_Block(node) && !is_cfop(node)) {
260                 ir_node *block = get_nodes_block(node);
261                 set_Block_mark(block, 1);
262         }
263 }
264
265 ir_node *skip_empty_block(ir_node *node)
266 {
267         ir_node      *block;
268
269         if(!is_Jmp(node))
270                 return node;
271
272         block = get_nodes_block(node);
273         if(get_Block_n_cfgpreds(block) != 1)
274                 return node;
275
276         if(get_Block_mark(block))
277                 return node;
278
279         return get_Block_cfgpred(block, 0);
280 }
281
282 static void find_cf_and_or_walker(ir_node *block, void *env)
283 {
284         int i, i2;
285         int n_cfgpreds = get_Block_n_cfgpreds(block);
286         (void) env;
287
288         if(n_cfgpreds < 2)
289                 return;
290
291         /* Find the following structure:
292          *
293          *        upper_block
294          *         /       |
295          *        /        |
296          *   lower_block   |
297          *     /  \        |
298          *   ...   \       |
299          *           block
300          */
301
302 restart:
303         for(i = 0; i < n_cfgpreds; ++i) {
304                 ir_node      *lower_block;
305                 ir_node      *lower_cf;
306                 ir_node      *cond;
307                 ir_node      *cond_selector;
308                 ir_node      *lower_pred;
309
310                 lower_cf = get_Block_cfgpred(block, i);
311                 lower_cf = skip_empty_block(lower_cf);
312                 if(!is_Proj(lower_cf))
313                         continue;
314
315                 lower_block = get_nodes_block(lower_cf);
316                 if(get_Block_n_cfgpreds(lower_block) != 1)
317                         continue;
318
319                 cond = get_Proj_pred(lower_cf);
320                 if(!is_Cond(cond))
321                         continue;
322
323                 cond_selector = get_Cond_selector(cond);
324                 if(get_irn_mode(cond_selector) != mode_b)
325                         continue;
326
327                 /* the block must not produce any side-effects */
328                 if(get_Block_mark(lower_block))
329                         continue;
330
331                 lower_pred = get_Block_cfgpred_block(lower_block, 0);
332
333                 for(i2 = 0; i2 < n_cfgpreds; ++i2) {
334                         ir_node   *upper_block;
335                         ir_node   *upper_cf;
336                         ir_node   *upper_cond;
337                         ir_node   *upper_cond_selector;
338                         ir_node   *replacement;
339                         ir_graph  *irg;
340                         cond_pair  cpair;
341
342                         upper_cf    = get_Block_cfgpred(block, i2);
343                         upper_cf    = skip_empty_block(upper_cf);
344                         if(is_Bad(upper_cf))
345                                 continue;
346                         upper_block = get_nodes_block(upper_cf);
347                         if(upper_block != lower_pred)
348                                 continue;
349
350                         assert(is_Proj(upper_cf));
351                         upper_cond = get_Proj_pred(upper_cf);
352                         assert(is_Cond(upper_cond));
353                         upper_cond_selector = get_Cond_selector(upper_cond);
354                         if(get_irn_mode(upper_cond_selector) != mode_b)
355                                 continue;
356
357                         /* we have found the structure */
358                         /* TODO: check phis */
359                         if(!find_cond_pair(cond_selector, upper_cond_selector, &cpair))
360                                 continue;
361
362                         /* normalize pncs: we need the true case to jump into the
363                          * common block */
364                         irg = current_ir_graph;
365                         if(get_Proj_proj(lower_cf) == pn_Cond_false) {
366                                 if(cpair.proj_lo == cond_selector) {
367                                         ir_mode *mode = get_tarval_mode(cpair.tv_lo);
368                                         cpair.pnc_lo  = get_negated_pnc(cpair.pnc_lo, mode);
369                                         cpair.proj_lo = new_r_Proj(irg, lower_block,
370                                                         get_Proj_pred(cpair.proj_lo), mode_b, cpair.pnc_lo);
371                                 } else {
372                                         ir_mode *mode = get_tarval_mode(cpair.tv_hi);
373                                         assert(cpair.proj_hi == cond_selector);
374                                         cpair.pnc_hi  = get_negated_pnc(cpair.pnc_hi, mode);
375                                         cpair.proj_hi = new_r_Proj(irg, lower_block,
376                                                         get_Proj_pred(cpair.proj_hi), mode_b, cpair.pnc_hi);
377                                 }
378                         }
379                         if(get_Proj_proj(upper_cf) == pn_Cond_false) {
380                                 if(cpair.proj_lo == upper_cond_selector) {
381                                         ir_mode *mode = get_tarval_mode(cpair.tv_lo);
382                                         cpair.pnc_lo  = get_negated_pnc(cpair.pnc_lo, mode);
383                                         cpair.proj_lo = new_r_Proj(irg, upper_block,
384                                                         get_Proj_pred(cpair.proj_lo), mode_b, cpair.pnc_lo);
385                                 } else {
386                                         ir_mode *mode = get_tarval_mode(cpair.tv_hi);
387                                         assert(cpair.proj_hi == upper_cond_selector);
388                                         cpair.pnc_hi  = get_negated_pnc(cpair.pnc_hi, mode);
389                                         cpair.proj_hi = new_r_Proj(irg, upper_block,
390                                                         get_Proj_pred(cpair.proj_hi), mode_b, cpair.pnc_hi);
391                                 }
392                         }
393
394                         /* can we optimize the case? */
395                         replacement = bool_or(&cpair);
396                         if(replacement == NULL)
397                                 continue;
398
399                         /* move all nodes from lower block to upper block */
400                         exchange(lower_block, upper_block);
401
402                         set_Block_cfgpred(block, i2, new_Bad());
403
404                         /* the optimisations expected the true case to jump */
405                         if(get_Proj_proj(lower_cf) == pn_Cond_false) {
406                                 ir_node *block = get_nodes_block(replacement);
407                                 replacement    = new_rd_Not(NULL, current_ir_graph, block,
408                                                             replacement, mode_b);
409                         }
410                         set_Cond_selector(cond, replacement);
411
412                         ir_fprintf(stderr, "replaced (ub %+F)\n", upper_block);
413                         goto restart;
414                 }
415         }
416 }
417
418 void opt_bool(ir_graph *const irg)
419 {
420         irg_walk_graph(irg, NULL, bool_walk, NULL);
421
422         set_using_block_mark(irg);
423
424         irg_walk_graph(irg, clear_block_infos, collect_phis, NULL);
425
426         irg_block_walk_graph(irg, NULL, find_cf_and_or_walker, NULL);
427
428         set_irg_outs_inconsistent(irg);
429         set_irg_doms_inconsistent(irg);
430         set_irg_extblk_inconsistent(irg);
431         set_irg_loopinfo_inconsistent(irg);
432
433         clear_using_block_mark(irg);
434 }