removed wrong INLINE
[libfirm] / ir / opt / strength_red.c
1 /**
2  *
3  * @file strength_red.c
4  *
5  * Project:     libFIRM
6  * File name:   ir/opt/strength_red.c
7  * Purpose:     Make strength reduction .
8  * Author:      Beyhan Veliev
9  * Modified by:
10  * Created:     22.8.2004
11  * CVS-ID:      $Id$
12  * Copyright:   (c) 2004 Universität Karlsruhe
13  * Licence:     This file protected by GPL -  GNU GENERAL PUBLIC LICENSE.
14  */
15
16
17 /*
18
19 reducible(o)
20    while (reducible)
21      o = reduce(o)
22
23 reduce_itervar(induct_var_info *iv)
24   for each (out o of iv) {
25     if (o is reducible) {
26        if (o is strong (Mul))
27          iv_new = reduce(o), remember_pattern(o)
28        else     // o is not strong (Add ...)
29          if (o is the only user)
30            iv_new = reducible(o)
31     }
32   }
33
34 */
35
36 # include "strength_red.h"
37
38 # include "irouts.h"
39 # include "irnode_t.h"
40 # include "irgwalk.h"
41 # include "irloop_t.h"
42 # include "ircons.h"
43 # include "irgmod.h"
44 # include "irdump_t.h"
45 # include "firmstat.h"
46
47
48 /* The information needed for an induction variable */
49 typedef struct _induct_var_info {
50   ir_op   *operation_code;              /**< the opcode of the induction variable, either op_Add or op_Sub */
51   ir_node *increment;                   /**< the increment/decrement expression of the induction vriable */
52   ir_node *init;                        /**< the init expression */
53   ir_node *op;                          /**< the modify expression of the induction variable, ie the Add/Sub */
54   ir_node *itervar_phi;                 /**< the Phi operation of the induction variable */
55   ir_node *c, *new_phi, *new_increment, *new_init;
56   ir_node *new_op, *new_add, *reducible_node;
57   ir_node *old_ind, *symconst, *new_cmp;
58   ir_node *cmp_const;                   /**< the (loop invariant) expression that compared with the induction variable */
59   ir_node *cmp_init_block;
60   ir_node *cmp;                         /**< if set, the cmp at the end of the loop using the induction variable */
61   ir_loop *l_itervar_phi;               /**< the loop of the induction variable */
62   int      strong_reduced;
63   int      init_pred_pos;               /**< the position of the init expression in the inductions Phi */
64   int      op_pred_pos;                 /**< the position of the induction operation in the inductions Phi */
65   int      out_loop_res;
66   int      phi_pred;                    /**< number of users of the induction variable's phi */
67   int      reducible;                   /**< set if reducible */
68 } induct_var_info;
69
70
71 /** Counter for verbose information about optimization. */
72 static int n_reduced_expressions;
73 static int n_made_new_phis;
74
75 /** Detect basic iteration variables.
76  *
77  * The variable ir represented by a subgraph as this:
78  *
79  *       init
80  *       /|\
81  *        |
82  *   |-- Phi
83  *   |   /|\
84  *   |    |
85  *   |-->op
86  *
87  * Where op is a Add or Sub, and init is loop invariant.
88  * @@@ So far we only accept Phi nodes with two predecessors.
89  * We could expand this to Phi nodes where all preds are either
90  * op or loop invariant.
91  *
92  * @param n     A phi node.
93  * @param info  After call contains the induction variable information
94  */
95 static induct_var_info *is_induction_variable (induct_var_info *info) {
96
97   int i;
98   int op_pred, Store_in_op, Store_in_phi, cmp_in_phi;
99
100   info->c                = NULL;
101   info->cmp              = NULL;
102   info->cmp_const        = NULL;
103   info->cmp_init_block   = NULL;
104   info->increment        = NULL;
105   info->init             = NULL;
106   info->l_itervar_phi    = NULL;
107   info->new_add          = NULL;
108   info->new_cmp          = NULL;
109   info->new_increment    = NULL;
110   info->new_init         = NULL;
111   info->new_op           = NULL;
112   info->new_phi          = NULL;
113   info->operation_code   = NULL;
114   info->op               = NULL;
115   info->old_ind          = NULL;
116   info->reducible_node   = NULL;
117   info->out_loop_res     = 1;
118   info->reducible        = 0;
119   info->phi_pred         = 0;
120   info->strong_reduced   = 0;
121   info->init_pred_pos    = -1;
122   info->op_pred_pos      = -1;
123
124   assert(get_irn_op(info->itervar_phi) == op_Phi);
125
126   /* The necessary conditions for the phi node. */
127   if (get_irn_arity(info->itervar_phi) != 2             ||
128       !has_backedges(get_nodes_block(info->itervar_phi))  )
129     return NULL;
130
131   for (i = 0; i < 2; ++i) {
132     ir_node *pred = get_Phi_pred(info->itervar_phi, i);
133     ir_op *op = get_irn_op(pred);
134
135     /* Compute if the induction variable is added or substracted wiht a constant . */
136     if (op == op_Add || op == op_Sub) {
137       ir_node *n_l = get_binop_left(pred);
138       ir_node *n_r = get_binop_right(pred);
139
140       if (n_l == info->itervar_phi) {
141         info->operation_code = op;
142         info->increment      = n_r;
143         info->op_pred_pos    = i;
144         info->init_pred_pos  = i ^ 1;
145         break;
146       }
147       else if (n_r == info->itervar_phi) {
148         info->operation_code = op;
149         info->increment      = n_l;
150         info->op_pred_pos    = i;
151         info->init_pred_pos  = i ^ 1;
152         break;
153       }
154     }
155   }
156   /* check if we found something */
157   if (! info->operation_code)
158     return NULL;
159
160   /* Compute the position of the backedge. */
161   if (is_backedge(get_nodes_block(info->itervar_phi), info->op_pred_pos)){
162     info->op     = get_Phi_pred(info->itervar_phi, info->op_pred_pos);
163     info->init   = get_Phi_pred(info->itervar_phi, info->init_pred_pos);
164   }
165   else {
166     /* irregular control flow detected. */
167     return NULL;
168   }
169
170   if (get_Block_dom_depth(get_nodes_block(info->init))  >=
171       get_Block_dom_depth(get_nodes_block(info->itervar_phi))) {
172     return NULL;
173   }
174
175   /* This "for" marks if the iteration operation have a Store successor .*/
176   op_pred      = get_irn_n_outs(info->op);
177   Store_in_op  = 0;
178   Store_in_phi = 0;
179   cmp_in_phi   = 0;
180   for (i = 0; i < op_pred; ++i){
181     ir_node *out  = get_irn_out(info->op, i);
182     ir_op *out_op = get_irn_op(out);
183     if (out_op == op_Store)
184       Store_in_op++;
185   }
186
187   /* Information about loop of itervar_phi. */
188   info->l_itervar_phi = get_irn_loop(get_nodes_block(info->itervar_phi));
189
190   /* This "for" searchs for the Cmp successor of the
191      iter_var to reduce and marks if the iter_var have a Store
192      successor or a successor out of loop.*/
193   info->phi_pred = get_irn_n_outs(info->itervar_phi);
194   for (i = 0; i < info->phi_pred; ++i) {
195     ir_node *out = get_irn_out(info->itervar_phi, i);
196     ir_op   *out_op = get_irn_op(out);
197
198     if ((get_irn_loop(get_nodes_block(out)) != info->l_itervar_phi) &&
199         ( get_Block_dom_depth(get_nodes_block(out))  >
200           get_Block_dom_depth(get_nodes_block(info->itervar_phi))))
201       info->out_loop_res = 0;
202
203     if (out_op == op_Store)
204       Store_in_phi++;
205     else if (out_op == op_Cmp){
206       info->cmp = out;
207       cmp_in_phi++;
208     }
209   }
210
211   if((info->phi_pred == 3 && op_pred == 1 && Store_in_phi == 0 && cmp_in_phi == 1)  ||
212      (info->phi_pred == 2 && op_pred == 2 && Store_in_op == 0 && info->cmp != NULL )  ||
213      (info->phi_pred == 1 && Store_in_op == 0))
214     info->reducible = 1;
215
216   // Search for loop invariant of Cmp.
217   if (info->cmp != NULL) {
218     ir_node *cmp_const_block;
219
220     if (get_Cmp_left(info->cmp) == info->itervar_phi)
221       info->cmp_const = get_Cmp_right(info->cmp);
222     else
223       info->cmp_const = get_Cmp_left(info->cmp);
224
225     cmp_const_block = get_nodes_block(info->cmp_const);
226     if (get_Block_dom_depth(get_nodes_block(info->init)) >=
227         get_Block_dom_depth(cmp_const_block))
228       info->cmp_init_block = get_nodes_block(info->init);
229     else
230       info->cmp_init_block = cmp_const_block;
231   }
232   return info;
233 }
234
235 /**
236  * Creates a new Add node from operands.
237  */
238 static INLINE ir_node *
239 my_new_r_Add (ir_graph *irg, ir_node *b, ir_node *op1, ir_node *op2) {
240   ir_mode *m  = get_irn_mode(op1);
241   ir_mode *m2 = get_irn_mode(op2);
242
243   if (mode_is_reference(m2))
244     m = m2;
245   return new_r_Add(irg, b, op1, op2, m);
246 }
247
248 /**
249  * Creates a new Sub node from operands.
250  */
251 static INLINE ir_node *
252 my_new_r_Sub (ir_graph *irg, ir_node *b, ir_node *op1, ir_node *op2) {
253   ir_mode *m  = get_irn_mode(op1);
254   ir_mode *m2 = get_irn_mode(op2);
255
256   if (mode_is_reference(m) && mode_is_reference(m2))
257     m = mode_Is;        /* FIXME: may be other mode! */
258   else if (mode_is_reference(m2))
259     m = m2;
260   return new_r_Sub(irg, b, op1, op2, m);
261 }
262
263 /* Reduce a Add, Sub or Mul node
264  *
265  * @param *reduce_var  The node to reduce.
266  * @param *ivi         Contains the induction variable information.
267  */
268 static int reduce(ir_node *reduce_var, induct_var_info *ivi)
269 {
270   // Essential conditions for a reducable node.
271   if (get_irn_loop(get_nodes_block(reduce_var)) != ivi->l_itervar_phi)
272     return 0;
273
274   if (get_irn_op(reduce_var) == op_Mul) {
275     ir_node *mul_init  = NULL;
276     ir_node *mul_const = NULL;
277
278     // Search for constant and init of strong.
279     ir_node  *mul_right = get_Mul_right(reduce_var);
280     ir_node  *mul_left  = get_Mul_left(reduce_var);
281     ir_op *mul_right_op = get_irn_op(mul_right);
282     ir_op  *mul_left_op = get_irn_op(mul_left);
283
284     ir_node *in[2], *block_init;
285     ir_node *block_inc;
286
287     ir_node *init_block;
288     ir_node *increment_block;
289     ir_node *c_block;
290
291     n_reduced_expressions++;
292
293     if (mul_right_op == op_Const) {
294       mul_const = mul_right;
295       mul_init  = mul_left;
296     }
297     else if (mul_left_op == op_Const) {
298       mul_const = mul_left;
299       mul_init  = mul_right;
300     }
301
302     if (mul_const == NULL || mul_init == NULL)
303       return 0;
304
305     init_block      = get_nodes_block(mul_init);
306     increment_block = get_nodes_block(ivi->increment);
307     c_block         = get_nodes_block(mul_const);
308
309     if (get_Block_dom_depth(increment_block) >= get_Block_dom_depth(c_block))
310       block_inc = increment_block;
311     else
312       block_inc = c_block;
313
314     if (get_Block_dom_depth(init_block) >= get_Block_dom_depth(c_block))
315       block_init = init_block;
316     else
317       block_init = c_block;
318
319     if (! ivi->reducible){
320       int reduce_var_pred;
321
322       // Essential condition for the constant of strong.
323       if (get_Block_dom_depth(get_nodes_block(mul_const))  >=
324           get_Block_dom_depth(get_nodes_block(ivi->itervar_phi)))
325         return 0;
326
327       n_made_new_phis++;
328       if (get_opt_strength_red_verbose() && get_firm_verbosity() > 1) {
329         printf("The new Phi node is : "); DDMN(ivi->itervar_phi);
330         printf("reducing operation is : "); DDMN(reduce_var);
331         printf("in graph : "); DDMG(current_ir_graph);
332       }
333
334       ivi->new_increment  = new_r_Mul (current_ir_graph, block_inc, ivi->increment, mul_const,
335                                        get_irn_mode(mul_const));
336       if (!(get_irn_op(mul_init) == op_Phi)){
337         ivi->new_init = new_r_Mul (current_ir_graph, block_init, ivi->init, mul_const,
338                                    get_irn_mode(mul_const));
339         ivi->new_init = my_new_r_Add(current_ir_graph, block_init, ivi->new_init,
340                                     ivi->new_increment);
341       } else
342         ivi->new_init = new_r_Mul (current_ir_graph, block_init, ivi->init, mul_const,
343                                    get_irn_mode(mul_const));
344
345       /* Generate a new basic induction variable. Break the data flow loop
346          initially by using an Unknown node. */
347
348       in[ivi->op_pred_pos]   = new_Unknown(get_irn_mode(ivi->new_init));
349
350       in[ivi->init_pred_pos] = ivi->new_init;
351       ivi->new_phi = new_r_Phi(current_ir_graph, get_nodes_block(ivi->itervar_phi), 2, in,
352                                get_irn_mode(mul_const));
353       mark_irn_visited(ivi->new_phi);
354
355       if (ivi->operation_code == op_Add)
356         ivi->new_op = my_new_r_Add(current_ir_graph, get_nodes_block(ivi->op),
357                                   ivi->new_increment,ivi-> new_phi);
358       else if (ivi->operation_code == op_Sub)
359         ivi->new_op = my_new_r_Sub(current_ir_graph, get_nodes_block(ivi->op),ivi-> new_phi,
360                                    ivi->new_increment);
361
362       set_Phi_pred(ivi->new_phi, ivi->op_pred_pos, ivi->new_op);
363
364
365
366
367
368       // This for search for a reducible successor of reduc_var.
369       reduce_var_pred =  get_irn_n_outs(reduce_var);
370       if (reduce_var_pred == 1) {
371         ir_node *old_ind =get_irn_out(reduce_var, 0);
372         if(get_irn_op(old_ind) == op_Add || get_irn_op(old_ind) == op_Sub ||
373            get_irn_op(old_ind) == op_Mul){
374           ivi->reducible = 1;
375           ivi->reducible_node = old_ind;
376         }
377       }
378       /* Replace the use of the strength reduced value. */
379       exchange(reduce_var, ivi->new_phi);
380       return 1;
381     }
382     else { /* ivi->reducible */
383       if(ivi->new_phi == NULL){
384         ivi->init = new_r_Mul (current_ir_graph, get_nodes_block(ivi->init),
385                                mul_const, ivi->init,
386                                get_irn_mode(mul_const));
387         if(ivi->cmp != NULL)
388           ivi->cmp_const = new_r_Mul (current_ir_graph, ivi->cmp_init_block,
389                                       ivi->cmp_const, mul_const, get_irn_mode(mul_const));
390         ivi->increment = new_r_Mul (current_ir_graph, block_init,
391                                     ivi->increment, mul_const, get_irn_mode(mul_const));
392       }else {
393         ivi->new_init = new_r_Mul (current_ir_graph, get_nodes_block(ivi->init),
394                                    mul_const, ivi->new_init,
395                                    get_irn_mode(mul_const));
396         ivi->new_increment = new_r_Mul (current_ir_graph, block_init,
397                                         ivi->new_increment, mul_const,
398                                         get_irn_mode(mul_const));
399       }
400       if (get_opt_strength_red_verbose() && get_firm_verbosity() > 1) {
401         printf("\nReducing operation is : "); DDMN(reduce_var);
402         printf("in graph : "); DDMG(current_ir_graph);
403       }
404       return 1;
405     }
406
407   }else if (get_irn_op (reduce_var) == op_Add){
408     ir_node *add_init  = NULL;
409     ir_node *add_const = NULL;
410
411     // Search for constant of add.
412     ir_node  *add_right = get_Add_right(reduce_var);
413     ir_node  *add_left  = get_Add_left(reduce_var);
414     ir_op *add_right_op = get_irn_op(add_right);
415     ir_op  *add_left_op = get_irn_op(add_left);
416
417     n_reduced_expressions++;
418
419     if (add_right_op != op_Const)
420       add_init = add_right;
421     else if (add_left_op != op_Const)
422       add_init = add_left;
423     if (add_right_op == op_Const || add_right_op == op_SymConst)
424       add_const = add_right;
425     else if (add_left_op == op_Const || add_left_op == op_SymConst)
426       add_const = add_left;
427     if (add_const == NULL) return 0;
428     if (ivi->new_phi == NULL){
429       ivi->init = my_new_r_Add (current_ir_graph, get_nodes_block(ivi->init),
430                                 add_const, ivi->init);
431       if(ivi->cmp != NULL)
432         ivi->cmp_const = my_new_r_Add (current_ir_graph, ivi->cmp_init_block,
433                                        add_const, ivi->cmp_const);
434     } else{
435       ivi->new_init = my_new_r_Add (current_ir_graph, get_nodes_block(ivi->init),
436                                     add_const, ivi->new_init);
437     }
438     if (get_opt_strength_red_verbose() && get_firm_verbosity() > 1) {
439       printf("\nReducing operation is : "); DDMN(reduce_var);
440       printf("in graph : "); DDMG(current_ir_graph);
441     }
442     return 1;
443   } else if(get_irn_op(reduce_var) == op_Sub ){
444     ir_node *sub_init  = NULL;
445     ir_node *sub_const = NULL;
446     // Search for constant of sub.
447     ir_node  *sub_right = get_Sub_right(reduce_var);
448     ir_node  *sub_left  = get_Sub_left(reduce_var);
449     ir_op *sub_right_op = get_irn_op(sub_right);
450     ir_op  *sub_left_op = get_irn_op(sub_left);
451
452     n_reduced_expressions++;
453
454     if (sub_right_op != op_Const)
455       sub_init = sub_right;
456     else if (sub_left_op != op_Const)
457       sub_init = sub_left;
458     if (sub_right_op == op_Const)
459       sub_const = sub_right;
460     else if (sub_left_op == op_Const)
461       sub_const = sub_left;
462
463     if (sub_const == NULL) return 0;
464
465     if (ivi->new_phi == NULL) {
466       ivi->init = my_new_r_Sub (current_ir_graph, get_nodes_block(ivi->init),
467                                 ivi->init, sub_const);
468       if (ivi->cmp != NULL)
469         ivi->cmp_const =my_new_r_Sub (current_ir_graph, get_nodes_block(ivi->init),
470                                       ivi->cmp_const,sub_const);
471     } else
472       ivi->new_init = my_new_r_Sub (current_ir_graph, get_nodes_block(ivi->init),
473                                     ivi->new_init, sub_const);
474     if (get_opt_strength_red_verbose() && get_firm_verbosity() > 1) {
475       printf("\nReducing operation is : "); DDMN(reduce_var);
476       printf("in graph : "); DDMG(current_ir_graph);
477     }
478     return 1;
479   }
480   return 0;
481 }
482
483 static ir_node *reducible(ir_node *out, induct_var_info *ivi)
484 {
485   ir_node *reduced = NULL;
486   int pred;
487
488   for (pred = 1; pred == 1; pred = get_irn_n_outs(out)) {
489     if (reduce(out, ivi))
490       reduced = out;
491     else
492       return reduced;
493     out = get_irn_out(out, 0);
494   }
495   return reduced;
496 }
497
498 /**
499  * Reduce a node.
500  *
501  * @param *itervar_phi   The iteration variable of a loop.
502  * @param *env           Free environment pointer.
503  */
504 static void reduce_itervar(ir_node *itervar_phi, void *env)
505 {
506   induct_var_info ivi;
507
508   if (get_irn_op(itervar_phi) != op_Phi)
509     return;
510
511   ivi.itervar_phi = itervar_phi;
512
513   /* This "if" finds the iteration variable. */
514   if (is_induction_variable(&ivi)) {
515     int i, op_out;
516
517     for (i = 0; i < ivi.phi_pred; i++) {
518       ir_node *out = get_irn_out(ivi.itervar_phi, i);
519       ir_op   *out_op = get_irn_op(out);
520       if(ivi.reducible){
521         if(ivi.phi_pred == 3 && out != ivi.op && out !=ivi.cmp){
522           ir_node *reduced = reducible(out, &ivi);
523           if (reduced != NULL)
524             exchange( reduced, ivi.itervar_phi);
525         }
526       } else if (out_op == op_Mul)
527         if(reduce(out, &ivi) && ivi.reducible){
528           ir_node *reduced = reducible(ivi.reducible_node, &ivi);
529           if(reduced != NULL)
530             exchange(reduced, ivi.new_phi);
531           ivi.reducible = 0;
532           set_Phi_pred(ivi.new_phi, ivi.init_pred_pos, ivi.new_init);
533           set_irn_mode(ivi.new_phi,get_irn_mode(ivi.new_init));
534           set_irn_mode(ivi.new_op,get_irn_mode(ivi.new_phi));
535         }
536     }
537
538     op_out = get_irn_n_outs(ivi.op);
539     for (i = 0; i < op_out; i++){
540       ir_node *out = get_irn_out(ivi.op, i);
541       ir_op   *out_op = get_irn_op(out);
542       if(op_out == 2 && out != ivi.itervar_phi){
543         ir_node *reduced = reducible(out, &ivi);
544         if(reduced != NULL)
545           exchange( reduced, ivi.op);
546       }else if (out_op == op_Mul)
547         if(reduce(out, &ivi) && ivi.reducible){
548           ir_node *reduced = reducible(ivi.reducible_node, &ivi);
549           if(reduced != NULL)
550             exchange(reduced, ivi.new_phi);
551           ivi.reducible = 0;
552           set_Phi_pred(ivi.new_phi, ivi.init_pred_pos, ivi.new_init);
553           set_irn_mode(ivi.new_phi,get_irn_mode(ivi.new_init));
554           set_irn_mode(ivi.new_op,get_irn_mode(ivi.new_phi));
555         }
556     }
557
558     if(ivi.reducible){
559       if(get_irn_op(ivi.op) == op_Add)
560         if(get_Add_left(ivi.op) == ivi.itervar_phi)
561           set_Add_right(ivi.op, ivi.increment);
562         else
563           set_Add_left(ivi.op, ivi.increment);
564       else if(get_Sub_left(ivi.op) == ivi.itervar_phi)
565         set_Sub_right(ivi.op, ivi.increment);
566       else
567         set_Sub_right(ivi.op, ivi.increment);
568       set_Phi_pred(ivi.itervar_phi, ivi.init_pred_pos, ivi.init);
569       set_irn_mode(ivi.itervar_phi, get_irn_mode(ivi.init));
570       set_irn_mode(ivi.op, get_irn_mode(ivi.itervar_phi));
571       if (ivi.cmp != NULL){
572         set_irn_mode(ivi.cmp_const, get_irn_mode(ivi.itervar_phi));
573         if(get_Cmp_left(ivi.cmp) == ivi.itervar_phi)
574           set_Cmp_right(ivi.cmp, ivi.cmp_const);
575         else
576           set_Cmp_left(ivi.cmp, ivi.cmp_const);
577       }
578     }
579   }
580 }
581
582 /* Performs strength reduction for the passed graph. */
583 void reduce_strength(ir_graph *irg) {
584   ir_graph *rem = current_ir_graph;
585
586   if (!get_optimize() || !get_opt_strength_red()) return;
587
588   current_ir_graph = irg;
589
590   n_reduced_expressions = 0;
591   n_made_new_phis = 0;
592   /* -- Precompute some information -- */
593   /* Call algorithm that computes the backedges */
594   construct_cf_backedges(irg);
595   /* Call algorithm that computes the dominator trees. */
596   compute_doms(irg);
597   /* Call algorithm that computes the out edges */
598   compute_outs(irg);
599
600   /* -- Search expressions that can be optimized -- */
601   irg_walk_graph(irg, NULL, reduce_itervar, NULL);
602
603   if (get_opt_strength_red_verbose()) {
604     printf ("\n %d made new_phis und  ", n_made_new_phis);
605     printf("reduced %d iteration variables "
606            "in \n graph %s.%s.\n", n_reduced_expressions,
607        get_type_name(get_entity_owner(get_irg_entity(irg))),
608        get_entity_name(get_irg_entity(irg)));
609   }
610
611   current_ir_graph = rem;
612 }