8a36835ca23fcf631ebc416f92fb11c9b274808b
[libfirm] / ir / ir / irgopt.c
1 /* Copyright (C) 1998 - 2000 by Universitaet Karlsruhe
2 ** All rights reserved.
3 **
4 ** Author: Christian Schaefer
5 **
6 ** Optimizations for a whole ir graph, i.e., a procedure.
7 */
8
9 /* $Id$ */
10
11 #ifdef HAVE_CONFIG_H
12 # include <config.h>
13 #endif
14
15 # include <assert.h>
16
17 # include "irgopt.h"
18 # include "irnode_t.h"
19 # include "irgraph_t.h"
20 # include "iropt.h"
21 # include "irgwalk.h"
22 # include "ircons.h"
23 # include "misc.h"
24 # include "irgmod.h"
25
26 # include "pset.h"
27
28 /* Defined in iropt.c */
29 pset *new_identities (void);
30 void  del_identities (pset *value_table);
31 void  add_identities   (pset *value_table, ir_node *node);
32
33 #if 0  /* Warum ist das hier nochmal definiert?
34           Hat's nicht gelinkt wegen typeO tities - tity ?? */
35 /* To fill the hash table */
36 void
37 add_identity (pset *value_table, ir_node *n) {
38   /* identify_remember (value_table, n);*/
39 }
40 #endif
41
42 /********************************************************************/
43 /* apply optimizations of iropt to all nodes.                       */
44 /********************************************************************/
45
46 void init_link (ir_node *n, void *env) {
47   set_irn_link(n, NULL);
48 }
49
50 void
51 optimize_in_place_wrapper (ir_node *n, void *env) {
52   int i;
53   ir_node *optimized;
54
55   /* optimize all sons after recursion, i.e., the sons' sons are
56      optimized already. */
57   for (i = -1; i < get_irn_arity(n); i++) {
58     optimized = optimize_in_place(get_irn_n(n, i));
59     set_irn_n(n, i, optimized);
60   }
61 }
62
63 void
64 local_optimize_graph (ir_graph *irg) {
65   ir_graph *rem = current_ir_graph;
66   current_ir_graph = irg;
67
68   /* Should we clean the value_table in irg for the cse? Better do so... */
69   del_identities(irg->value_table);
70   irg->value_table = new_identities();
71
72   /* walk over the graph */
73   irg_walk(irg->end, init_link, optimize_in_place_wrapper, NULL);
74
75   current_ir_graph = rem;
76 }
77
78 /********************************************************************/
79 /* Routines for dead node elimination / copying garbage collection  */
80 /* of the obstack.                                                  */
81 /********************************************************************/
82
83 /* Remeber the new node in the old node by using a field all nodes have. */
84 inline void
85 set_new_node (ir_node *old, ir_node *new)
86 {
87   old->link = new;
88 }
89
90 /* Get this new node, before the old node is forgotton.*/
91 inline ir_node *
92 get_new_node (ir_node * n)
93 {
94   return n->link;
95 }
96
97
98 /* We use the block_visited flag to mark that we have computed the
99    number of useful predecessors for this block.
100    Further we encode the new arity in this flag in the old blocks.
101    Remembering the arity is useful, as it saves a lot of pointer
102    accesses.  This function is called for all Phi and Block nodes
103    in a Block. */
104 inline int
105 compute_new_arity(ir_node *b) {
106   int i, res;
107   int irg_v, block_v;
108
109   irg_v = get_irg_block_visited(current_ir_graph);
110   block_v = get_Block_block_visited(b);
111   if (block_v >= irg_v) {
112     /* we computed the number of preds for this block and saved it in the
113        block_v flag */
114     return block_v - irg_v;
115   } else {
116     /* compute the number of good predecessors */
117     res = get_irn_arity(b);
118     for (i = 0; i < get_irn_arity(b); i++)
119       if (get_irn_opcode(get_irn_n(b, i)) == iro_Bad) res--;
120     /* save it in the flag. */
121     set_Block_block_visited(b, irg_v + res);
122     return res;
123   }
124 }
125
126 /* Copies the node to the new obstack. The Ins of the new node point to
127    the predecessors on the old obstack.  For block/phi nodes not all
128    predecessors might be copied.  n->link points to the new node.
129    For Phi and Block nodes the function allocates in-arrays with an arity
130    only for useful predecessors.  The arity is determined by counting
131    the non-bad predecessors of the block. */
132 void
133 copy_node (ir_node *n, void *env) {
134   ir_node *nn, *block;
135   int new_arity;
136
137   if (get_irn_opcode(n) == iro_Block) {
138     block = NULL;
139     new_arity = compute_new_arity(n);
140   } else {
141     block = get_nodes_Block(n);
142     if (get_irn_opcode(n) == iro_Phi) {
143       new_arity = compute_new_arity(block);
144     } else {
145       new_arity = get_irn_arity(n);
146     }
147   }
148   nn = new_ir_node(current_ir_graph,
149                    block,
150                    get_irn_op(n),
151                    get_irn_mode(n),
152                    new_arity,
153                    get_irn_in(n));
154   /* Copy the attributes.  These might point to additional data.  If this
155      was allocated on the old obstack the pointers now are dangling.  This
156      frees e.g. the memory of the graph_arr allocated in new_immBlock. */
157   copy_attrs(n, nn);
158   set_new_node(n, nn);
159
160   /*  printf("\n old node: "); DDMSG2(n);
161       printf(" new node: "); DDMSG2(nn); */
162
163 }
164
165 /* Copies new predecessors of old node to new node remembered in link.
166    Spare the Bad predecessors of Phi and Block nodes. */
167 void
168 copy_preds (ir_node *n, void *env) {
169   ir_node *nn, *block, *on;
170   int i, j;
171
172   nn = get_new_node(n);
173
174   /* printf("\n old node: "); DDMSG2(n);
175      printf(" new node: "); DDMSG2(nn);
176      printf(" arities: old: %d, new: %d\n", get_irn_arity(n), get_irn_arity(nn)); */
177
178   if (get_irn_opcode(n) == iro_Block) {
179     /* Don't copy Bad nodes. */
180     j = 0;
181     for (i = 0; i < get_irn_arity(n); i++)
182       if (get_irn_opcode(get_irn_n(n, i)) != iro_Bad) {
183         set_irn_n (nn, j, get_new_node(get_irn_n(n, i)));
184         j++;
185       }
186     /* repair the block visited flag from above misuse. Repair it in both
187        graphs so that the old one can still be used. */
188     set_Block_block_visited(nn, 0);
189     set_Block_block_visited(n, 0);
190     /* Local optimization could not merge two subsequent blocks if
191        in array contained Bads.  Now it's possible.
192        @@@ I removed the call to optimize_in_place as it requires
193        that the fields in ir_graph are set properly. */
194     if (get_Block_n_cfgpreds(nn) == 1
195         && get_irn_op(get_Block_cfgpred(nn, 0)) == op_Jmp)
196       exchange(nn, get_nodes_Block(get_Block_cfgpred(nn, 0)));
197   } else if (get_irn_opcode(n) == iro_Phi) {
198     /* Don't copy node if corresponding predecessor in block is Bad.
199        The Block itself should not be Bad. */
200     block = get_nodes_Block(n);
201     set_irn_n (nn, -1, get_new_node(block));
202     j = 0;
203     for (i = 0; i < get_irn_arity(n); i++)
204       if (get_irn_opcode(get_irn_n(block, i)) != iro_Bad) {
205         set_irn_n (nn, j, get_new_node(get_irn_n(n, i)));
206         j++;
207       }
208     /* If the pre walker reached this Phi after the post walker visited the
209        block block_visited is > 0. */
210     set_Block_block_visited(get_nodes_Block(n), 0);
211     /* Compacting the Phi's ins might generate Phis with only one
212        predecessor. */
213     if (get_irn_arity(n) == 1)
214       exchange(n, get_irn_n(n, 0));
215   } else {
216     for (i = -1; i < get_irn_arity(n); i++)
217       set_irn_n (nn, i, get_new_node(get_irn_n(n, i)));
218   }
219   /* Now the new node is complete.  We can add it to the hash table for cse. */
220   /* add_identity (current_ir_graph->value_table, nn); */
221   add_identities (current_ir_graph->value_table, nn);
222 }
223
224 /* Copies the graph reachable from current_ir_graph->end to the obstack
225    in current_ir_graph.
226    Then fixes the fields in current_ir_graph containing nodes of the
227    graph.  */
228 void
229 copy_graph () {
230   /* Not all nodes remembered in current_ir_graph might be reachable
231      from the end node.  Assure their link is set to NULL, so that
232      we can test whether new nodes have been computed. */
233   set_irn_link(get_irg_frame  (current_ir_graph), NULL);
234   set_irn_link(get_irg_globals(current_ir_graph), NULL);
235   set_irn_link(get_irg_args   (current_ir_graph), NULL);
236
237   /* we use the block walk flag for removing Bads from Blocks ins. */
238   inc_irg_block_visited(current_ir_graph);
239
240   /* copy the graph */
241   irg_walk(get_irg_end(current_ir_graph), copy_node, copy_preds, NULL);
242
243   /* fix the fields in current_ir_graph */
244   set_irg_end        (current_ir_graph, get_new_node(get_irg_end(current_ir_graph)));
245   set_irg_end_block  (current_ir_graph, get_new_node(get_irg_end_block(current_ir_graph)));
246   if (get_irn_link(get_irg_frame(current_ir_graph)) == NULL) {
247     copy_node (get_irg_frame(current_ir_graph), NULL);
248     copy_preds(get_irg_frame(current_ir_graph), NULL);
249   }
250   if (get_irn_link(get_irg_globals(current_ir_graph)) == NULL) {
251     copy_node (get_irg_globals(current_ir_graph), NULL);
252     copy_preds(get_irg_globals(current_ir_graph), NULL);
253   }
254   if (get_irn_link(get_irg_args(current_ir_graph)) == NULL) {
255     copy_node (get_irg_args(current_ir_graph), NULL);
256     copy_preds(get_irg_args(current_ir_graph), NULL);
257   }
258   set_irg_start  (current_ir_graph, get_new_node(get_irg_start(current_ir_graph)));
259
260   set_irg_start_block(current_ir_graph,
261                       get_new_node(get_irg_start_block(current_ir_graph)));
262   set_irg_frame  (current_ir_graph, get_new_node(get_irg_frame(current_ir_graph)));
263   set_irg_globals(current_ir_graph, get_new_node(get_irg_globals(current_ir_graph)));
264   set_irg_args   (current_ir_graph, get_new_node(get_irg_args(current_ir_graph)));
265   if (get_irn_link(get_irg_bad(current_ir_graph)) == NULL) {
266     copy_node(get_irg_bad(current_ir_graph), NULL);
267     copy_preds(get_irg_bad(current_ir_graph), NULL);
268   }
269   set_irg_bad(current_ir_graph, get_new_node(get_irg_bad(current_ir_graph)));
270 }
271
272 /* Copies all reachable nodes to a new obstack.  Removes bad inputs
273    from block nodes and the corresponding inputs from Phi nodes.
274    Merges single exit blocks with single entry blocks and removes
275    1-input Phis.
276    Adds all new nodes to a new hash table for cse.  Does not
277    perform cse, so the hash table might contain common subexpressions. */
278 /* Amroq call this emigrate() */
279 void
280 dead_node_elimination(ir_graph *irg) {
281   ir_graph *rem;
282   struct obstack *graveyard_obst = NULL;
283   struct obstack *rebirth_obst   = NULL;
284
285   /* Remember external state of current_ir_graph. */
286   rem = current_ir_graph;
287   current_ir_graph = irg;
288
289   if (get_optimize() && get_opt_dead_node_elimination()) {
290
291     /* A quiet place, where the old obstack can rest in peace,
292        until it will be cremated. */
293     graveyard_obst = irg->obst;
294
295     /* A new obstack, where the reachable nodes will be copied to. */
296     rebirth_obst = (struct obstack *) xmalloc (sizeof (struct obstack));
297     current_ir_graph->obst = rebirth_obst;
298     obstack_init (current_ir_graph->obst);
299
300     /* We also need a new hash table for cse */
301     del_identities (irg->value_table);
302     irg->value_table = new_identities ();
303
304     /* Copy the graph from the old to the new obstack */
305     copy_graph();
306
307     /* Free memory from old unoptimized obstack */
308     obstack_free(graveyard_obst, 0);  /* First empty the obstack ... */
309     xfree (graveyard_obst);           /* ... then free it.           */
310   }
311
312   current_ir_graph = rem;
313 }
314
315 /**********************************************************************/
316 /*  Funcionality for inlining                                         */
317 /**********************************************************************/
318
319 /* Copy node for inlineing.  Copies the node by calling copy_node and
320    then updates the entity if it's a local one.  env must be a pointer
321    to the frame type of the procedure. The new entities must be in
322    the link field of the entities. */
323 void
324 copy_node_inline (ir_node *n, void *env) {
325   ir_node *new;
326   type *frame_tp = (type *)env;
327
328   copy_node(n, NULL);
329   if (get_irn_op(n) == op_Sel) {
330     new = get_new_node (n);
331     assert(get_irn_op(new) == op_Sel);
332     if (get_entity_owner(get_Sel_entity(n)) == frame_tp) {
333       set_Sel_entity(new, get_entity_link(get_Sel_entity(n)));
334     }
335   }
336 }
337
338 void inline_method(ir_node *call, ir_graph *called_graph) {
339   ir_node *pre_call;
340   ir_node *post_call, *post_bl;
341   ir_node *in[5];
342   ir_node *end, *end_bl;
343   ir_node **res_pred;
344   ir_node **cf_pred;
345   ir_node *ret, *phi;
346   ir_node *cf_op, *bl;
347   int arity, n_ret, n_exc, n_res, i, j;
348   type *called_frame, *caller_frame;
349
350   if (!get_opt_inline()) return;
351
352   /** Check preconditions **/
353   assert(get_irn_op(call) == op_Call);
354   assert(get_Call_type(call) == get_entity_type(get_irg_ent(called_graph)));
355   assert(get_type_tpop(get_Call_type(call)) == type_method);
356   if (called_graph == current_ir_graph) return;
357
358   /** Part the Call node into two nodes.  Pre_call collects the parameters of
359   the procedure and later replaces the Start node of the called graph.
360   Post_call is the old Call node and collects the results of the called
361   graph. Both will end up being a tuple.  **/
362   post_bl = get_nodes_Block(call);
363   set_irg_current_block(current_ir_graph, post_bl);
364   /* XxMxPxP von Start + Parameter von Call */
365   in[0] = new_Jmp();
366   in[1] = get_Call_mem(call);
367   in[2] = get_irg_frame(current_ir_graph);
368   in[3] = get_irg_globals(current_ir_graph);
369   in[4] = new_Tuple (get_Call_n_params(call),
370                      get_Call_param_arr(call));
371   pre_call = new_Tuple(5, in);
372   post_call = call;
373
374   /** Part the block of the Call node into two blocks.
375       The new block gets the ins of the old block, pre_call and all its
376       predecessors and all Phi nodes. **/
377   part_block(pre_call);
378
379   /** Prepare state for dead node elimination **/
380   /* Visited flags in calling irg must be >= flag in called irg.
381      Else walker and arity computation will not work. */
382   if (get_irg_visited(current_ir_graph) <= get_irg_visited(called_graph))
383     set_irg_visited(current_ir_graph, get_irg_visited(called_graph)+1);   /***/
384   if (get_irg_block_visited(current_ir_graph)< get_irg_block_visited(called_graph))
385     set_irg_block_visited(current_ir_graph, get_irg_block_visited(called_graph));
386   /* Set pre_call as new Start node in link field of the start node of
387       calling graph and pre_calls block as new block for the start block
388       of calling graph.
389       Further mark these nodes so that they are not visited by the
390       copying. */
391   set_irn_link(get_irg_start(called_graph), pre_call);
392   set_irn_visited(get_irg_start(called_graph),
393                   get_irg_visited(current_ir_graph));/***/
394   set_irn_link(get_irg_start_block(called_graph),
395                get_nodes_Block(pre_call));
396   set_irn_visited(get_irg_start_block(called_graph),
397                   get_irg_visited(current_ir_graph));  /***/
398
399   /* Initialize for compaction of in arrays */
400   inc_irg_block_visited(current_ir_graph);
401   /*
402   set_Block_block_visited(get_irg_start_block(called_graph),
403                         get_irg_block_visited(current_ir_graph) +1 +1); /* count for self edge */
404
405   /*** Replicate local entities of the called_graph ***/
406   /* copy the entities. */
407   called_frame = get_irg_frame_type(called_graph);
408   for (i = 0; i < get_class_n_member(called_frame); i++) {
409     entity *new_ent, *old_ent;
410     old_ent = get_class_member(called_frame, i);
411     new_ent = copy_entity_own(old_ent, get_cur_frame_type());
412     set_entity_link(old_ent, new_ent);
413   }
414
415   /* visited is > than that of called graph.  With this trick visited will
416      remain unchanged so that an outer walker calling this inline will
417      not visit the inlined nodes. */
418   set_irg_visited(current_ir_graph, get_irg_visited(current_ir_graph)-1);
419
420   /** Performing dead node elimination inlines the graph **/
421   /* Copies the nodes to the obstack of current_ir_graph. Updates links to new
422      entities. */
423   /* @@@ endless loops are not copied!! */
424   irg_walk(get_irg_end(called_graph), copy_node_inline, copy_preds,
425            get_irg_frame_type(called_graph));
426
427   /* Repair called_graph */
428   set_irg_visited(called_graph, get_irg_visited(current_ir_graph));
429   set_irg_block_visited(called_graph, get_irg_block_visited(current_ir_graph));
430   set_Block_block_visited(get_irg_start_block(called_graph), 0);
431
432   /*** Merge the end of the inlined procedure with the call site ***/
433   /* We will turn the old Call node into a Tuple with the following
434      predecessors:
435      -1:  Block of Tuple.
436      0: Phi of all Memories of Return statements.
437      1: Jmp from new Block that merges the control flow from all exception
438         predecessors of the old end block.
439      2: Tuple of all arguments.
440      3: Phi of Exception memories.
441   */
442
443   /** Precompute some values **/
444   end_bl = get_new_node(get_irg_end_block(called_graph));
445   end = get_new_node(get_irg_end(called_graph));
446   arity = get_irn_arity(end_bl);    /* arity = n_exc + n_ret  */
447   n_res = get_method_n_res(get_Call_type(call));
448
449   res_pred = (ir_node **) malloc ((n_res) * sizeof (ir_node *));
450   cf_pred = (ir_node **) malloc (arity * sizeof (ir_node *));
451
452   set_irg_current_block(current_ir_graph, post_bl); /* just to make sure */
453
454
455   /** Collect control flow from Return blocks to post_calls block. Replace
456       Return nodes by Jump nodes. **/
457   n_ret = 0;
458   for (i = 0; i < arity; i++) {
459     ir_node *ret;
460     ret = get_irn_n(end_bl, i);
461     if (get_irn_op(ret) == op_Return) {
462       cf_pred[n_ret] = new_r_Jmp(current_ir_graph, get_nodes_Block(ret));
463       n_ret++;
464     }
465   }
466   set_irn_in(post_bl, n_ret, cf_pred);
467
468   /** Collect results from Return nodes to post_call. Post_call is
469       turned into a tuple. **/
470   turn_into_tuple(post_call, 4);
471   /* First the Memory-Phi */
472   n_ret = 0;
473   for (i = 0; i < arity; i++) {
474     ret = get_irn_n(end_bl, i);
475     if (get_irn_op(ret) == op_Return) {
476       cf_pred[n_ret] = get_Return_mem(ret);
477       n_ret++;
478     }
479   }
480   phi = new_Phi(n_ret, cf_pred, mode_M);
481   set_Tuple_pred(call, 0, phi);
482   set_irn_link(phi, get_irn_link(post_bl));  /* Conserve Phi-list for further inlinings */
483   set_irn_link(post_bl, phi);
484   /* Now the real results */
485   if (n_res > 0) {
486     for (j = 0; j < n_res; j++) {
487       n_ret = 0;
488       for (i = 0; i < arity; i++) {
489         ret = get_irn_n(end_bl, i);
490         if (get_irn_op(ret) == op_Return) {
491           cf_pred[n_ret] = get_Return_res(ret, j);
492           n_ret++;
493         }
494       }
495       phi = new_Phi(n_ret, cf_pred, get_irn_mode(cf_pred[0]));
496       res_pred[j] = phi;
497       set_irn_link(phi, get_irn_link(post_bl));  /* Conserve Phi-list for further inlinings */
498       set_irn_link(post_bl, phi);
499     }
500     set_Tuple_pred(call, 2, new_Tuple(n_res, res_pred));
501   } else {
502     set_Tuple_pred(call, 2, new_Bad());
503   }
504   /* Finally the exception control flow.  We need to add a Phi node to
505      collect the memory containing the exception objects.  Further we need
506      to add another block to get a correct representation of this Phi.  To
507      this block we add a Jmp that resolves into the X output of the Call
508      when the Call is turned into a tuple. */
509   n_exc = 0;
510   for (i = 0; i < arity; i++) {
511     ir_node *ret;
512     ret = get_irn_n(end_bl, i);
513     if (is_fragile_op(skip_Proj(ret)) || (get_irn_op(skip_Proj(ret)) == op_Raise)) {
514       cf_pred[n_exc] = ret;
515       n_exc++;
516     }
517   }
518   if (n_exc > 0) {
519     new_Block(n_exc, cf_pred);      /* whatch it: current_block is changed! */
520     set_Tuple_pred(call, 1, new_Jmp());
521     /* The Phi for the memories with the exception objects */
522     n_exc = 0;
523     for (i = 0; i < arity; i++) {
524       ir_node *ret;
525       ret = skip_Proj(get_irn_n(end_bl, i));
526       if (get_irn_op(ret) == op_Call) {
527         cf_pred[n_exc] = new_r_Proj(current_ir_graph, get_nodes_Block(ret), ret, mode_M, 3);
528         n_exc++;
529       } else if (is_fragile_op(ret)) {
530         /* We rely that all cfops have the memory output at the same position. */
531         cf_pred[n_exc] = new_r_Proj(current_ir_graph, get_nodes_Block(ret), ret, mode_M, 0);
532         n_exc++;
533       } else if (get_irn_op(ret) == op_Raise) {
534         cf_pred[n_exc] = new_r_Proj(current_ir_graph, get_nodes_Block(ret), ret, mode_M, 1);
535         n_exc++;
536       }
537     }
538     set_Tuple_pred(call, 3, new_Phi(n_exc, cf_pred, mode_M));
539   } else {
540     set_Tuple_pred(call, 1, new_Bad());
541     set_Tuple_pred(call, 3, new_Bad());
542   }
543   free(res_pred);
544   free(cf_pred);
545
546   /*** Correct the control flow to the end node.
547        If the exception control flow from the Call directly branched to the
548        end block we now have the following control flow predecessor pattern:
549        ProjX -> Tuple -> Jmp.
550        We must remove the Jmp along with it's empty block and add Jmp's
551        predecessors as predecessors of this end block. ***/
552   /* find the problematic predecessor of the end block. */
553   end_bl = get_irg_end_block(current_ir_graph);
554   for (i = 0; i < get_Block_n_cfgpreds(end_bl); i++) {
555     cf_op = get_Block_cfgpred(end_bl, i);
556     if (get_irn_op(cf_op) == op_Proj) {
557       cf_op = get_Proj_pred(cf_op);
558       if (get_irn_op(cf_op) == op_Tuple) {
559         cf_op = get_Tuple_pred(cf_op, 1);
560         assert(get_irn_op(cf_op) == op_Jmp);
561         break;
562       }
563     }
564   }
565   /* repair */
566   if (i < get_Block_n_cfgpreds(end_bl)) {
567     bl = get_nodes_Block(cf_op);
568     arity = get_Block_n_cfgpreds(end_bl) + get_Block_n_cfgpreds(bl) - 1;
569     cf_pred = (ir_node **) malloc (arity * sizeof (ir_node *));
570     for (j = 0; j < i; j++)
571       cf_pred[j] = get_Block_cfgpred(end_bl, j);
572     for (j = j; j < i + get_Block_n_cfgpreds(bl); j++)
573       cf_pred[j] = get_Block_cfgpred(bl, j-i);
574     for (j = j; j < arity; j++)
575       cf_pred[j] = get_Block_cfgpred(end_bl, j-get_Block_n_cfgpreds(bl) +1);
576     set_irn_in(end_bl, arity, cf_pred);
577     free(cf_pred);
578   }
579 }