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