irgopt: inline_small_irgs implemented
[libfirm] / ir / ir / irgopt.c
1 /* Coyright (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 "irprog.h"
18 # include "irgopt.h"
19 # include "irnode_t.h"
20 # include "irgraph_t.h"
21 # include "iropt_t.h"
22 # include "irgwalk.h"
23 # include "ircons.h"
24 # include "misc.h"
25 # include "irgmod.h"
26 # include "array.h"
27 # include "pset.h"
28
29 /* Defined in iropt.c */
30 pset *new_identities (void);
31 void  del_identities (pset *value_table);
32 void  add_identities   (pset *value_table, ir_node *node);
33
34 #if 0  /* Warum ist das hier nochmal definiert?
35           Hat's nicht gelinkt wegen typeO tities - tity ?? */
36 /* To fill the hash table */
37 void
38 add_identity (pset *value_table, ir_node *n) {
39   /* identify_remember (value_table, n);*/
40 }
41 #endif
42
43 /********************************************************************/
44 /* apply optimizations of iropt to all nodes.                       */
45 /********************************************************************/
46
47 void init_link (ir_node *n, void *env) {
48   set_irn_link(n, NULL);
49 }
50
51 void
52 optimize_in_place_wrapper (ir_node *n, void *env) {
53   int start, i;
54   ir_node *optimized;
55
56   if (get_irn_op(n) == op_Block)
57     start = 0;
58   else
59     start = -1;
60
61   /* optimize all sons after recursion, i.e., the sons' sons are
62      optimized already. */
63   for (i = start; i < get_irn_arity(n); i++) {
64     optimized = optimize_in_place_2(get_irn_n(n, i));
65     set_irn_n(n, i, optimized);
66     assert(get_irn_op(optimized) != op_Id);
67   }
68 }
69
70 void
71 local_optimize_graph (ir_graph *irg) {
72   ir_graph *rem = current_ir_graph;
73   current_ir_graph = irg;
74
75   /* Handle graph state */
76   assert(get_irg_phase_state(irg) != phase_building);
77   if (get_opt_global_cse())
78     set_irg_pinned(current_ir_graph, floats);
79   if (get_irg_outs_state(current_ir_graph) == outs_consistent)
80     set_irg_outs_inconsistent(current_ir_graph);
81
82   /* Clean the value_table in irg for the cse. */
83   del_identities(irg->value_table);
84   irg->value_table = new_identities();
85
86   /* walk over the graph */
87   irg_walk(irg->end, init_link, optimize_in_place_wrapper, NULL);
88
89   current_ir_graph = rem;
90 }
91
92 /********************************************************************/
93 /* Routines for dead node elimination / copying garbage collection  */
94 /* of the obstack.                                                  */
95 /********************************************************************/
96
97 /* Remeber the new node in the old node by using a field all nodes have. */
98 inline void
99 set_new_node (ir_node *old, ir_node *new)
100 {
101   old->link = new;
102 }
103
104 /* Get this new node, before the old node is forgotton.*/
105 inline ir_node *
106 get_new_node (ir_node * n)
107 {
108   return n->link;
109 }
110
111 /* We use the block_visited flag to mark that we have computed the
112    number of useful predecessors for this block.
113    Further we encode the new arity in this flag in the old blocks.
114    Remembering the arity is useful, as it saves a lot of pointer
115    accesses.  This function is called for all Phi and Block nodes
116    in a Block. */
117 inline int
118 compute_new_arity(ir_node *b) {
119   int i, res;
120   int irg_v, block_v;
121
122   irg_v = get_irg_block_visited(current_ir_graph);
123   block_v = get_Block_block_visited(b);
124   if (block_v >= irg_v) {
125     /* we computed the number of preds for this block and saved it in the
126        block_v flag */
127     return block_v - irg_v;
128   } else {
129     /* compute the number of good predecessors */
130     res = get_irn_arity(b);
131     for (i = 0; i < get_irn_arity(b); i++)
132       if (get_irn_opcode(get_irn_n(b, i)) == iro_Bad) res--;
133     /* save it in the flag. */
134     set_Block_block_visited(b, irg_v + res);
135     return res;
136   }
137 }
138
139 /* Copies the node to the new obstack. The Ins of the new node point to
140    the predecessors on the old obstack.  For block/phi nodes not all
141    predecessors might be copied.  n->link points to the new node.
142    For Phi and Block nodes the function allocates in-arrays with an arity
143    only for useful predecessors.  The arity is determined by counting
144    the non-bad predecessors of the block. */
145 void
146 copy_node (ir_node *n, void *env) {
147   ir_node *nn, *block;
148   int new_arity;
149
150   if (get_irn_opcode(n) == iro_Block) {
151     block = NULL;
152     new_arity = compute_new_arity(n);
153   } else {
154     block = get_nodes_Block(n);
155     if (get_irn_opcode(n) == iro_Phi) {
156       new_arity = compute_new_arity(block);
157     } else {
158       new_arity = get_irn_arity(n);
159     }
160   }
161   nn = new_ir_node(current_ir_graph,
162                    block,
163                    get_irn_op(n),
164                    get_irn_mode(n),
165                    new_arity,
166                    get_irn_in(n));
167   /* Copy the attributes.  These might point to additional data.  If this
168      was allocated on the old obstack the pointers now are dangling.  This
169      frees e.g. the memory of the graph_arr allocated in new_immBlock. */
170   copy_attrs(n, nn);
171   set_new_node(n, nn);
172
173   /*  printf("\n old node: "); DDMSG2(n);
174       printf(" new node: "); DDMSG2(nn); */
175
176 }
177
178 /* Copies new predecessors of old node to new node remembered in link.
179    Spare the Bad predecessors of Phi and Block nodes. */
180 void
181 copy_preds (ir_node *n, void *env) {
182   ir_node *nn, *block, *on;
183   int i, j;
184
185   nn = get_new_node(n);
186
187   /* printf("\n old node: "); DDMSG2(n);
188      printf(" new node: "); DDMSG2(nn);
189      printf(" arities: old: %d, new: %d\n", get_irn_arity(n), get_irn_arity(nn)); */
190
191   if (get_irn_opcode(n) == iro_Block) {
192     /* Don't copy Bad nodes. */
193     j = 0;
194     for (i = 0; i < get_irn_arity(n); i++)
195       if (get_irn_opcode(get_irn_n(n, i)) != iro_Bad) {
196         set_irn_n (nn, j, get_new_node(get_irn_n(n, i)));
197         j++;
198       }
199     /* repair the block visited flag from above misuse. Repair it in both
200        graphs so that the old one can still be used. */
201     set_Block_block_visited(nn, 0);
202     set_Block_block_visited(n, 0);
203     /* Local optimization could not merge two subsequent blocks if
204        in array contained Bads.  Now it's possible.
205        We don't call optimize_in_place as it requires
206        that the fields in ir_graph are set properly. */
207     if (get_Block_n_cfgpreds(nn) == 1
208         && get_irn_op(get_Block_cfgpred(nn, 0)) == op_Jmp)
209       exchange(nn, get_nodes_Block(get_Block_cfgpred(nn, 0)));
210   } else if (get_irn_opcode(n) == iro_Phi) {
211     /* Don't copy node if corresponding predecessor in block is Bad.
212        The Block itself should not be Bad. */
213     block = get_nodes_Block(n);
214     set_irn_n (nn, -1, get_new_node(block));
215     j = 0;
216     for (i = 0; i < get_irn_arity(n); i++)
217       if (get_irn_opcode(get_irn_n(block, i)) != iro_Bad) {
218         set_irn_n (nn, j, get_new_node(get_irn_n(n, i)));
219         j++;
220       }
221     /* If the pre walker reached this Phi after the post walker visited the
222        block block_visited is > 0. */
223     set_Block_block_visited(get_nodes_Block(n), 0);
224     /* Compacting the Phi's ins might generate Phis with only one
225        predecessor. */
226     if (get_irn_arity(n) == 1)
227       exchange(n, get_irn_n(n, 0));
228   } else {
229     for (i = -1; i < get_irn_arity(n); i++)
230       set_irn_n (nn, i, get_new_node(get_irn_n(n, i)));
231   }
232   /* Now the new node is complete.  We can add it to the hash table for cse.
233      @@@ inlinening aborts if we identify End. Why? */
234   if(get_irn_op(nn) != op_End)
235     add_identities (current_ir_graph->value_table, nn);
236 }
237
238 /* Copies the graph recursively, compacts the keepalive of the end node. */
239 void
240 copy_graph () {
241   ir_node *oe, *ne; /* old end, new end */
242   ir_node *ka; /* keep alive */
243   int i;
244
245   oe = get_irg_end(current_ir_graph);
246   /* copy the end node by hand, allocate dynamic in array! */
247   ne = new_ir_node(current_ir_graph,
248                    NULL,
249                    op_End,
250                    mode_X,
251                    -1,
252                    NULL);
253   /* Copy the attributes.  Well, there might be some in the future... */
254   copy_attrs(oe, ne);
255   set_new_node(oe, ne);
256
257   /* copy the live nodes */
258   irg_walk(get_nodes_Block(oe), copy_node, copy_preds, NULL);
259   /* copy_preds for the end node ... */
260   set_nodes_Block(ne, get_new_node(get_nodes_Block(oe)));
261
262   /** ... and now the keep alives. **/
263   /* First pick the not marked block nodes and walk them.  We must pick these
264      first as else we will oversee blocks reachable from Phis. */
265   for (i = 0; i < get_irn_arity(oe); i++) {
266     ka = get_irn_n(oe, i);
267     if ((get_irn_op(ka) == op_Block) &&
268         (get_irn_visited(ka) < get_irg_visited(current_ir_graph))) {
269       /* We must keep the block alive and copy everything reachable */
270       set_irg_visited(current_ir_graph, get_irg_visited(current_ir_graph)-1);
271       irg_walk(ka, copy_node, copy_preds, NULL);
272       add_End_keepalive(ne, get_new_node(ka));
273     }
274   }
275
276   /* Now pick the Phis.  Here we will keep all! */
277   for (i = 0; i < get_irn_arity(oe); i++) {
278     ka = get_irn_n(oe, i);
279     if ((get_irn_op(ka) == op_Phi)) {
280       if (get_irn_visited(ka) < get_irg_visited(current_ir_graph)) {
281         /* We didn't copy the Phi yet.  */
282         set_irg_visited(current_ir_graph, get_irg_visited(current_ir_graph)-1);
283         irg_walk(ka, copy_node, copy_preds, NULL);
284       }
285       add_End_keepalive(ne, get_new_node(ka));
286     }
287   }
288 }
289
290 /* Copies the graph reachable from current_ir_graph->end to the obstack
291    in current_ir_graph and fixes the environment.
292    Then fixes the fields in current_ir_graph containing nodes of the
293    graph.  */
294 void
295 copy_graph_env () {
296   /* Not all nodes remembered in current_ir_graph might be reachable
297      from the end node.  Assure their link is set to NULL, so that
298      we can test whether new nodes have been computed. */
299   set_irn_link(get_irg_frame  (current_ir_graph), NULL);
300   set_irn_link(get_irg_globals(current_ir_graph), NULL);
301   set_irn_link(get_irg_args   (current_ir_graph), NULL);
302
303   /* we use the block walk flag for removing Bads from Blocks ins. */
304   inc_irg_block_visited(current_ir_graph);
305
306   /* copy the graph */
307   copy_graph();
308
309   /* fix the fields in current_ir_graph */
310   free_End(get_irg_end(current_ir_graph));
311   set_irg_end        (current_ir_graph, get_new_node(get_irg_end(current_ir_graph)));
312   set_irg_end_block  (current_ir_graph, get_new_node(get_irg_end_block(current_ir_graph)));
313   if (get_irn_link(get_irg_frame(current_ir_graph)) == NULL) {
314     copy_node (get_irg_frame(current_ir_graph), NULL);
315     copy_preds(get_irg_frame(current_ir_graph), NULL);
316   }
317   if (get_irn_link(get_irg_globals(current_ir_graph)) == NULL) {
318     copy_node (get_irg_globals(current_ir_graph), NULL);
319     copy_preds(get_irg_globals(current_ir_graph), NULL);
320   }
321   if (get_irn_link(get_irg_args(current_ir_graph)) == NULL) {
322     copy_node (get_irg_args(current_ir_graph), NULL);
323     copy_preds(get_irg_args(current_ir_graph), NULL);
324   }
325   set_irg_start  (current_ir_graph, get_new_node(get_irg_start(current_ir_graph)));
326
327   set_irg_start_block(current_ir_graph,
328                       get_new_node(get_irg_start_block(current_ir_graph)));
329   set_irg_frame  (current_ir_graph, get_new_node(get_irg_frame(current_ir_graph)));
330   set_irg_globals(current_ir_graph, get_new_node(get_irg_globals(current_ir_graph)));
331   set_irg_args   (current_ir_graph, get_new_node(get_irg_args(current_ir_graph)));
332   if (get_irn_link(get_irg_bad(current_ir_graph)) == NULL) {
333     copy_node(get_irg_bad(current_ir_graph), NULL);
334     copy_preds(get_irg_bad(current_ir_graph), NULL);
335   }
336   set_irg_bad(current_ir_graph, get_new_node(get_irg_bad(current_ir_graph)));
337 }
338
339 /* Copies all reachable nodes to a new obstack.  Removes bad inputs
340    from block nodes and the corresponding inputs from Phi nodes.
341    Merges single exit blocks with single entry blocks and removes
342    1-input Phis.
343    Adds all new nodes to a new hash table for cse.  Does not
344    perform cse, so the hash table might contain common subexpressions. */
345 /* Amroq call this emigrate() */
346 void
347 dead_node_elimination(ir_graph *irg) {
348   ir_graph *rem;
349   struct obstack *graveyard_obst = NULL;
350   struct obstack *rebirth_obst   = NULL;
351
352   /* Remember external state of current_ir_graph. */
353   rem = current_ir_graph;
354   current_ir_graph = irg;
355
356   /* Handle graph state */
357   assert(get_irg_phase_state(current_ir_graph) != phase_building);
358   free_outs(current_ir_graph);
359
360   if (get_optimize() && get_opt_dead_node_elimination()) {
361
362     /* A quiet place, where the old obstack can rest in peace,
363        until it will be cremated. */
364     graveyard_obst = irg->obst;
365
366     /* A new obstack, where the reachable nodes will be copied to. */
367     rebirth_obst = (struct obstack *) xmalloc (sizeof (struct obstack));
368     current_ir_graph->obst = rebirth_obst;
369     obstack_init (current_ir_graph->obst);
370
371     /* We also need a new hash table for cse */
372     del_identities (irg->value_table);
373     irg->value_table = new_identities ();
374
375     /* Copy the graph from the old to the new obstack */
376     copy_graph_env();
377
378     /* Free memory from old unoptimized obstack */
379     obstack_free(graveyard_obst, 0);  /* First empty the obstack ... */
380     xfree (graveyard_obst);           /* ... then free it.           */
381   }
382
383   current_ir_graph = rem;
384 }
385
386 /**********************************************************************/
387 /*  Funcionality for inlining                                         */
388 /**********************************************************************/
389
390 /* Copy node for inlineing.  Copies the node by calling copy_node and
391    then updates the entity if it's a local one.  env must be a pointer
392    to the frame type of the procedure. The new entities must be in
393    the link field of the entities. */
394 void
395 copy_node_inline (ir_node *n, void *env) {
396   ir_node *new;
397   type *frame_tp = (type *)env;
398
399   copy_node(n, NULL);
400   if (get_irn_op(n) == op_Sel) {
401     new = get_new_node (n);
402     assert(get_irn_op(new) == op_Sel);
403     if (get_entity_owner(get_Sel_entity(n)) == frame_tp) {
404       set_Sel_entity(new, get_entity_link(get_Sel_entity(n)));
405     }
406   }
407 }
408
409 void inline_method(ir_node *call, ir_graph *called_graph) {
410   ir_node *pre_call;
411   ir_node *post_call, *post_bl;
412   ir_node *in[5];
413   ir_node *end, *end_bl;
414   ir_node **res_pred;
415   ir_node **cf_pred;
416   ir_node *ret, *phi;
417   ir_node *cf_op, *bl;
418   int arity, n_ret, n_exc, n_res, i, j;
419   type *called_frame, *caller_frame;
420
421   if (!get_opt_inline()) return;
422
423   /* Handle graph state */
424   assert(get_irg_phase_state(current_ir_graph) != phase_building);
425   if (get_irg_outs_state(current_ir_graph) == outs_consistent)
426     set_irg_outs_inconsistent(current_ir_graph);
427
428   /** Check preconditions **/
429   assert(get_irn_op(call) == op_Call);
430   assert(get_Call_type(call) == get_entity_type(get_irg_ent(called_graph)));
431   assert(get_type_tpop(get_Call_type(call)) == type_method);
432   if (called_graph == current_ir_graph) return;
433
434   /** Part the Call node into two nodes.  Pre_call collects the parameters of
435           the procedure and later replaces the Start node of the called graph.
436           Post_call is the old Call node and collects the results of the called
437           graph. Both will end up being a tuple.  **/
438   post_bl = get_nodes_Block(call);
439   set_irg_current_block(current_ir_graph, post_bl);
440   /* XxMxPxP of Start + parameter of Call */
441   in[0] = new_Jmp();
442   in[1] = get_Call_mem(call);
443   in[2] = get_irg_frame(current_ir_graph);
444   in[3] = get_irg_globals(current_ir_graph);
445   in[4] = new_Tuple (get_Call_n_params(call),
446                                          get_Call_param_arr(call));
447   pre_call = new_Tuple(5, in);
448   post_call = call;
449
450   /** Part the block of the Call node into two blocks.
451       The new block gets the ins of the old block, pre_call and all its
452       predecessors and all Phi nodes. **/
453   part_block(pre_call);
454
455   /** Prepare state for dead node elimination **/
456   /* Visited flags in calling irg must be >= flag in called irg.
457      Else walker and arity computation will not work. */
458   if (get_irg_visited(current_ir_graph) <= get_irg_visited(called_graph))
459     set_irg_visited(current_ir_graph, get_irg_visited(called_graph)+1);   /***/
460   if (get_irg_block_visited(current_ir_graph)< get_irg_block_visited(called_graph))
461     set_irg_block_visited(current_ir_graph, get_irg_block_visited(called_graph));
462   /* Set pre_call as new Start node in link field of the start node of
463          calling graph and pre_calls block as new block for the start block
464          of calling graph.
465          Further mark these nodes so that they are not visited by the
466          copying. */
467   set_irn_link(get_irg_start(called_graph), pre_call);
468   set_irn_visited(get_irg_start(called_graph),
469                                   get_irg_visited(current_ir_graph));/***/
470   set_irn_link(get_irg_start_block(called_graph),
471                            get_nodes_Block(pre_call));
472   set_irn_visited(get_irg_start_block(called_graph),
473                                   get_irg_visited(current_ir_graph));  /***/
474
475   /* Initialize for compaction of in arrays */
476   inc_irg_block_visited(current_ir_graph);
477   /*
478           set_Block_block_visited(get_irg_start_block(called_graph),
479           get_irg_block_visited(current_ir_graph) +1 +1); /* count for self edge */
480
481   /*** Replicate local entities of the called_graph ***/
482   /* copy the entities. */
483   called_frame = get_irg_frame_type(called_graph);
484   for (i = 0; i < get_class_n_member(called_frame); i++) {
485     entity *new_ent, *old_ent;
486     old_ent = get_class_member(called_frame, i);
487     new_ent = copy_entity_own(old_ent, get_cur_frame_type());
488     set_entity_link(old_ent, new_ent);
489   }
490
491   /* visited is > than that of called graph.  With this trick visited will
492      remain unchanged so that an outer walker, e.g., searching the call nodes
493      to inline, calling this inline will not visit the inlined nodes. */
494   set_irg_visited(current_ir_graph, get_irg_visited(current_ir_graph)-1);
495
496   /** Performing dead node elimination inlines the graph **/
497   /* Copies the nodes to the obstack of current_ir_graph. Updates links to new
498      entities. */
499   /* @@@ endless loops are not copied!! */
500   irg_walk(get_irg_end(called_graph), copy_node_inline, copy_preds,
501                    get_irg_frame_type(called_graph));
502
503   /* Repair called_graph */
504   set_irg_visited(called_graph, get_irg_visited(current_ir_graph));
505   set_irg_block_visited(called_graph, get_irg_block_visited(current_ir_graph));
506   set_Block_block_visited(get_irg_start_block(called_graph), 0);
507
508   /*** Merge the end of the inlined procedure with the call site ***/
509   /* We will turn the old Call node into a Tuple with the following
510      predecessors:
511      -1:  Block of Tuple.
512      0: Phi of all Memories of Return statements.
513      1: Jmp from new Block that merges the control flow from all exception
514          predecessors of the old end block.
515      2: Tuple of all arguments.
516      3: Phi of Exception memories.
517   */
518
519   /** Precompute some values **/
520   end_bl = get_new_node(get_irg_end_block(called_graph));
521   end = get_new_node(get_irg_end(called_graph));
522   arity = get_irn_arity(end_bl);    /* arity = n_exc + n_ret  */
523   n_res = get_method_n_res(get_Call_type(call));
524
525   res_pred = (ir_node **) malloc ((n_res) * sizeof (ir_node *));
526   cf_pred = (ir_node **) malloc (arity * sizeof (ir_node *));
527
528   set_irg_current_block(current_ir_graph, post_bl); /* just to make sure */
529
530   /** archive keepalives **/
531   for (i = 0; i < get_irn_arity(end); i++)
532     add_End_keepalive(get_irg_end(current_ir_graph), get_irn_n(end, i));
533   /* The new end node will die, but the in array is not on the obstack ... */
534   free_End(end);
535
536   /** Collect control flow from Return blocks to post_calls block. Replace
537       Return nodes by Jump nodes. **/
538   n_ret = 0;
539   for (i = 0; i < arity; i++) {
540     ir_node *ret;
541     ret = get_irn_n(end_bl, i);
542     if (get_irn_op(ret) == op_Return) {
543       cf_pred[n_ret] = new_r_Jmp(current_ir_graph, get_nodes_Block(ret));
544       n_ret++;
545     }
546   }
547   set_irn_in(post_bl, n_ret, cf_pred);
548
549   /** Collect results from Return nodes to post_call. Post_call is
550       turned into a tuple. **/
551   turn_into_tuple(post_call, 4);
552   /* First the Memory-Phi */
553   n_ret = 0;
554   for (i = 0; i < arity; i++) {
555     ret = get_irn_n(end_bl, i);
556     if (get_irn_op(ret) == op_Return) {
557       cf_pred[n_ret] = get_Return_mem(ret);
558       n_ret++;
559     }
560   }
561   phi = new_Phi(n_ret, cf_pred, mode_M);
562   set_Tuple_pred(call, 0, phi);
563   set_irn_link(phi, get_irn_link(post_bl));  /* Conserve Phi-list for further inlinings */
564   set_irn_link(post_bl, phi);
565   /* Now the real results */
566   if (n_res > 0) {
567     for (j = 0; j < n_res; j++) {
568       n_ret = 0;
569       for (i = 0; i < arity; i++) {
570                 ret = get_irn_n(end_bl, i);
571                 if (get_irn_op(ret) == op_Return) {
572                   cf_pred[n_ret] = get_Return_res(ret, j);
573                   n_ret++;
574                 }
575       }
576       phi = new_Phi(n_ret, cf_pred, get_irn_mode(cf_pred[0]));
577       res_pred[j] = phi;
578       set_irn_link(phi, get_irn_link(post_bl));  /* Conserve Phi-list for further inlinings */
579       set_irn_link(post_bl, phi);
580     }
581     set_Tuple_pred(call, 2, new_Tuple(n_res, res_pred));
582   } else {
583     set_Tuple_pred(call, 2, new_Bad());
584   }
585   /* Finally the exception control flow.  We need to add a Phi node to
586      collect the memory containing the exception objects.  Further we need
587      to add another block to get a correct representation of this Phi.  To
588      this block we add a Jmp that resolves into the X output of the Call
589      when the Call is turned into a tuple. */
590   n_exc = 0;
591   for (i = 0; i < arity; i++) {
592     ir_node *ret;
593     ret = get_irn_n(end_bl, i);
594     if (is_fragile_op(skip_Proj(ret)) || (get_irn_op(skip_Proj(ret)) == op_Raise)) {
595       cf_pred[n_exc] = ret;
596       n_exc++;
597     }
598   }
599   if (n_exc > 0) {
600     new_Block(n_exc, cf_pred);      /* whatch it: current_block is changed! */
601     set_Tuple_pred(call, 1, new_Jmp());
602     /* The Phi for the memories with the exception objects */
603     n_exc = 0;
604     for (i = 0; i < arity; i++) {
605       ir_node *ret;
606       ret = skip_Proj(get_irn_n(end_bl, i));
607       if (get_irn_op(ret) == op_Call) {
608                 cf_pred[n_exc] = new_r_Proj(current_ir_graph, get_nodes_Block(ret), ret, mode_M, 3);
609                 n_exc++;
610       } else if (is_fragile_op(ret)) {
611                 /* We rely that all cfops have the memory output at the same position. */
612                 cf_pred[n_exc] = new_r_Proj(current_ir_graph, get_nodes_Block(ret), ret, mode_M, 0);
613                 n_exc++;
614       } else if (get_irn_op(ret) == op_Raise) {
615                 cf_pred[n_exc] = new_r_Proj(current_ir_graph, get_nodes_Block(ret), ret, mode_M, 1);
616                 n_exc++;
617       }
618     }
619     set_Tuple_pred(call, 3, new_Phi(n_exc, cf_pred, mode_M));
620   } else {
621     set_Tuple_pred(call, 1, new_Bad());
622     set_Tuple_pred(call, 3, new_Bad());
623   }
624   free(res_pred);
625   free(cf_pred);
626
627   /*** Correct the control flow to the end node.
628        If the exception control flow from the Call directly branched to the
629        end block we now have the following control flow predecessor pattern:
630        ProjX -> Tuple -> Jmp.
631        We must remove the Jmp along with it's empty block and add Jmp's
632        predecessors as predecessors of this end block. ***/
633   /* find the problematic predecessor of the end block. */
634   end_bl = get_irg_end_block(current_ir_graph);
635   for (i = 0; i < get_Block_n_cfgpreds(end_bl); i++) {
636     cf_op = get_Block_cfgpred(end_bl, i);
637     if (get_irn_op(cf_op) == op_Proj) {
638       cf_op = get_Proj_pred(cf_op);
639       if (get_irn_op(cf_op) == op_Tuple) {
640                 cf_op = get_Tuple_pred(cf_op, 1);
641                 assert(get_irn_op(cf_op) == op_Jmp);
642                 break;
643       }
644     }
645   }
646   /* repair */
647   if (i < get_Block_n_cfgpreds(end_bl)) {
648     bl = get_nodes_Block(cf_op);
649     arity = get_Block_n_cfgpreds(end_bl) + get_Block_n_cfgpreds(bl) - 1;
650     cf_pred = (ir_node **) malloc (arity * sizeof (ir_node *));
651     for (j = 0; j < i; j++)
652       cf_pred[j] = get_Block_cfgpred(end_bl, j);
653     for (j = j; j < i + get_Block_n_cfgpreds(bl); j++)
654       cf_pred[j] = get_Block_cfgpred(bl, j-i);
655     for (j = j; j < arity; j++)
656       cf_pred[j] = get_Block_cfgpred(end_bl, j-get_Block_n_cfgpreds(bl) +1);
657     set_irn_in(end_bl, arity, cf_pred);
658     free(cf_pred);
659   }
660 }
661
662 /********************************************************************/
663 /* Apply inlineing to small methods.                                */
664 /********************************************************************/
665
666 static int pos;
667
668 /* It makes no sense to inline too many calls in one procedure. Anyways,
669    I didn't get a version with NEW_ARR_F to run. */
670 #define MAX_INLINE 1024
671
672 static void collect_calls(ir_node *call, void *env) {
673   ir_node **calls = (ir_node **)env;
674   ir_node *addr;
675   tarval *tv;
676   ir_graph *called_irg;
677
678   if (get_irn_op(call) != op_Call) return;
679
680   addr = get_Call_ptr(call);
681   if (get_irn_op(addr) == op_Const) {
682     /* Check whether the constant is the pointer to a compiled entity. */
683     tv = get_Const_tarval(addr);
684     if (tv->u.p.ent) {
685       called_irg = get_entity_irg(tv->u.p.ent);
686       if (called_irg && pos < MAX_INLINE) {
687         /* The Call node calls a locally defined method.  Remember to inline. */
688         calls[pos] = call;
689         pos++;
690       }
691     }
692   }
693 }
694
695
696 /* Inlines all small methods at call sites where the called address comes
697    from a Const node that references the entity representing the called
698    method.
699    The size argument is a rough measure for the code size of the method:
700    Methods where the obstack containing the firm graph is smaller than
701    size are inlined. */
702 void inline_small_irgs(ir_graph *irg, int size) {
703   int i;
704   ir_node *calls[MAX_INLINE];
705   ir_graph *rem = current_ir_graph;
706
707   if (!(get_optimize() && get_opt_inline())) return;
708
709   /*DDME(get_irg_ent(current_ir_graph));*/
710
711   current_ir_graph = irg;
712   /* Handle graph state */
713   assert(get_irg_phase_state(current_ir_graph) != phase_building);
714
715   /* Find Call nodes to inline.
716      (We can not inline during a walk of the graph, as inlineing the same
717      method several times changes the visited flag of the walked graph:
718      after the first inlineing visited of the callee equals visited of
719      the caller.  With the next inlineing both are increased.) */
720   pos = 0;
721   irg_walk(get_irg_end(irg), NULL, collect_calls, (void *) calls);
722
723   if ((pos > 0) && (pos < MAX_INLINE)) {
724     /* There are calls to inline */
725     collect_phiprojs(irg);
726     for (i = 0; i < pos; i++) {
727       tarval *tv;
728       ir_graph *callee;
729       tv = get_Const_tarval(get_Call_ptr(calls[i]));
730       callee = get_entity_irg(tv->u.p.ent);
731       if ((_obstack_memory_used(callee->obst) - obstack_room(callee->obst)) < size) {
732         /*printf(" inlineing "); DDME(tv->u.p.ent);*/
733         inline_method(calls[i], callee);
734       }
735     }
736   }
737
738   current_ir_graph = rem;
739 }