Removed ANNOUNCE macro
[libfirm] / ir / opt / funccall.c
1 /*
2  * Project:     libFIRM
3  * File name:   ir/opt/funccall.c
4  * Purpose:     optimization of function calls
5  * Author:      Michael Beck
6  * Created:
7  * CVS-ID:      $Id$
8  * Copyright:   (c) 1998-2006 Universität Karlsruhe
9  * Licence:     This file protected by GPL -  GNU GENERAL PUBLIC LICENSE.
10  */
11 #ifdef HAVE_CONFIG_H
12 #include "config.h"
13 #endif
14
15 #include "irnode_t.h"
16 #include "irgraph_t.h"
17 #include "irgmod.h"
18 #include "irgwalk.h"
19 #include "irvrfy.h"
20 #include "dbginfo_t.h"
21 #include "irflag_t.h"
22 #include "ircons.h"
23 #include "funccall.h"
24 #include "irhooks.h"
25
26 /**
27  * The walker environment for rem_mem_from_const_fkt_calls
28  */
29 typedef struct _env_t {
30   int  n_calls_removed_SymConst;
31   int  n_calls_removed_Sel;
32   ir_node *const_call_list;       /**< The list of all const function calls that will be changed. */
33   ir_node *pure_call_list;        /**< The list of all pure function calls that will be changed. */
34   ir_node *proj_list;             /**< The list of all potential Proj nodes that must be fixed. */
35 } env_t;
36
37 /**
38  * Collect all calls to const and pure functions
39  * to lists. Collect all Proj(Call) nodes into a Proj list.
40  */
41 static void collect_calls(ir_node *node, void *env)
42 {
43   env_t *ctx = env;
44   ir_node *call, *ptr;
45   ir_entity *ent;
46   unsigned mode;
47
48   if (is_Call(node)) {
49     call = node;
50
51     /* set the link to NULL for all non-const/pure calls */
52     set_irn_link(call, NULL);
53     ptr = get_Call_ptr(call);
54     if (is_SymConst(ptr) && get_SymConst_kind(ptr) == symconst_addr_ent) {
55       ent = get_SymConst_entity(ptr);
56
57       mode = get_entity_additional_properties(ent);
58       if ((mode & (mtp_property_const|mtp_property_pure)) == 0)
59         return;
60       ++ctx->n_calls_removed_SymConst;
61     } else if (get_opt_closed_world() &&
62               is_Sel(ptr) &&
63               get_irg_callee_info_state(current_ir_graph) == irg_callee_info_consistent) {
64       /* If all possible callees are const functions, we can remove the memory edge. */
65       int i, n_callees = get_Call_n_callees(call);
66       if (n_callees == 0)
67         /* This is kind of strange:  dying code or a Call that will raise an exception
68            when executed as there is no implementation to call.  So better not
69            optimize. */
70         return;
71
72       /* note that const function are a subset of pure ones */
73       mode = mtp_property_const | mtp_property_pure;
74       for (i = 0; i < n_callees; ++i) {
75         ent = get_Call_callee(call, i);
76         if (ent == unknown_entity) {
77           /* we don't know which entity is called here */
78           return;
79         }
80         mode &= get_entity_additional_properties(ent);
81         if (mode == 0)
82           return;
83       }
84       ++ctx->n_calls_removed_Sel;
85     } else
86       return;
87
88     /* ok, if we get here we found a call to a const or a pure function */
89     if (mode & mtp_property_pure) {
90       set_irn_link(call, ctx->pure_call_list);
91       ctx->pure_call_list = call;
92     } else {
93       set_irn_link(call, ctx->const_call_list);
94       ctx->const_call_list = call;
95     }
96   } else if (is_Proj(node)) {
97     /*
98      * Collect all memory and exception Proj's from
99      * calls.
100      */
101     call = get_Proj_pred(node);
102     if (! is_Call(call))
103       return;
104
105     /* collect the Proj's in the Proj list */
106     switch (get_Proj_proj(node)) {
107     case pn_Call_M_regular:
108     case pn_Call_X_except:
109     case pn_Call_M_except:
110       set_irn_link(node, ctx->proj_list);
111       ctx->proj_list = node;
112       break;
113     default:
114       break;
115     }
116   }
117 }  /* collect_calls */
118
119 /**
120  * Fix the list of collected Calls.
121  *
122  * @param irg        the graph that contained calls to pure functions
123  * @param call_list  the list of all call sites of const functions
124  * @param proj_list  the list of all memory/exception Proj's of this call sites
125  */
126 static void fix_const_call_list(ir_graph *irg, ir_node *call_list, ir_node *proj_list) {
127   ir_node *call, *next, *mem, *proj;
128   int exc_changed = 0;
129   ir_graph *rem = current_ir_graph;
130
131   current_ir_graph = irg;
132
133   /* First step: fix all calls by removing it's memory input.
134      It's original memory input is preserved in their link fields. */
135   for (call = call_list; call; call = next) {
136     next = get_irn_link(call);
137     mem  = get_Call_mem(call);
138
139     set_irn_link(call, mem);
140     set_Call_mem(call, get_irg_no_mem(irg));
141
142     /*
143      * Sorrily we cannot simply set the node to 'float'.
144      * There is a reason for that:
145      *
146      * - The call might be inside a loop/if that is NOT entered
147      *   and calls a endless function. Setting the call to float
148      *   would allow to move it out from the loop/if causing this
149      *   function be called even if the loop/if is not entered ...
150      *
151      * This could be fixed using post-dominators for calls and Pin nodes
152      * but need some more analyzes to ensure that a call that potential
153      * never returns is not executed before some code that generates
154      * observable states...
155      */
156
157     /* finally, this call can float
158     set_irn_pinned(call, op_pin_state_floats); */
159     hook_func_call(irg, call);
160   }
161
162   /* Second step: fix all Proj's */
163   for (proj = proj_list; proj; proj = next) {
164     next = get_irn_link(proj);
165     call = get_Proj_pred(proj);
166     mem  = get_irn_link(call);
167
168     /* beware of calls in the pure call list */
169     if (! mem || get_irn_op(mem) == op_Call)
170       continue;
171     assert(get_irn_mode(mem) == mode_M);
172
173     switch (get_Proj_proj(proj)) {
174     case pn_Call_M_regular: {
175       /* in dead code there might be cycles where proj == mem */
176       if (proj != mem)
177         exchange(proj, mem);
178     } break;
179     case pn_Call_X_except:
180     case pn_Call_M_except:
181       exc_changed = 1;
182       exchange(proj, get_irg_bad(irg));
183       break;
184     default:
185       ;
186     }
187   }
188
189   /* changes were done ... */
190   set_irg_outs_inconsistent(irg);
191   set_irg_loopinfo_state(irg, loopinfo_cf_inconsistent);
192
193   if (exc_changed) {
194     /* ... including exception edges */
195     set_irg_doms_inconsistent(irg);
196   }
197   current_ir_graph = rem;
198 }  /* fix_call_list */
199
200 #if 0
201 /**
202  * Check if a graph represents a const function.
203  *
204  * @param irg  the graph
205  */
206 static int is_const_function(ir_graph *irg)
207 {
208   ir_node *end, *endbl;
209   int j, change;
210
211   if (get_irg_additional_properties(irg) & mtp_property_const) {
212     /* already marked as a const function */
213     return 0;
214   }
215
216   end   = get_irg_end(irg);
217   endbl = get_nodes_block(end);
218   change = 0;
219
220   /* visit every Return */
221   for (j = get_Block_n_cfgpreds(endbl) - 1; j >= 0; --j) {
222     ir_node *node = get_Block_cfgpred(endbl, j);
223     ir_op   *op   = get_irn_op(node);
224     ir_node *mem;
225
226     /* Bad nodes usually do NOT produce anything, so it's ok */
227     if (op == op_Bad)
228       continue;
229
230     if (op == op_Return) {
231       mem = get_Return_mem(node);
232
233       /* Bad nodes usually do NOT produce anything, so it's ok */
234       if (is_Bad(mem))
235         continue;
236
237       change = mem != get_irg_initial_mem(irg);
238       if (change)
239         break;
240     }
241     else {
242       /* exception found */
243       change = 1;
244       break;
245     }
246   }
247
248   if (! change) {
249     /* check, if a keep-alive exists */
250     for (j = get_End_n_keepalives(end) - 1; j >= 0; --j) {
251       ir_node *mem = get_End_keepalive(end, j);
252
253       if (mode_M != get_irn_mode(mem))
254         continue;
255
256       change = mem != get_irg_initial_mem(irg);
257       if (change)
258         break;
259     }
260   }
261
262   if (! change) {
263     /* no memory changes found, it's a const function */
264     set_irg_additional_property(irg, mtp_property_const);
265     return 1;
266   }
267   return 0;
268 }  /* is_const_function */
269 #endif
270
271 /* a marker */
272 static char _mark;
273 #define MARK &_mark
274
275 #define UNMARK_IRG(irg)     set_irg_link((irg), NULL)
276 #define MARK_IRG(irg)       set_irg_link((irg), MARK)
277 #define IS_IRG_MARKED(irg)  (get_irg_link(irg) == MARK)
278
279 /* forward */
280 static int is_pure_function(ir_graph *irg);
281
282 #define UMAX(a,b) (a) > (b) ? (a) : (b)
283
284 /**
285  * Follow the memory chain starting at node and determine
286  * the mtp_property.
287  *
288  * @return mtp_property_const if only calls of const functions are detected
289  *         mtp_property_pure if only Loads and const/pure
290  *         calls detected
291  *         bad_property else
292  */
293 static unsigned _follow_mem(ir_node *node) {
294   unsigned m, mode = mtp_property_const;
295   ir_node  *ptr;
296   int i;
297
298   for (;;) {
299     if (irn_visited(node))
300       return mode;
301
302     mark_irn_visited(node);
303
304     switch (get_irn_opcode(node)) {
305     case iro_Proj:
306       node = get_Proj_pred(node);
307       break;
308
309     case iro_NoMem:
310       /* finish here */
311       return mode;
312
313     case iro_Phi:
314     case iro_Sync:
315       for (i = get_irn_arity(node) - 1; i >= 0; --i) {
316         mode &= _follow_mem(get_irn_n(node, i));
317       }
318       break;
319
320     case iro_Load:
321       /* Beware volatile Loads are NOT allowed in pure functions */
322       if (get_Load_volatility(node) == volatility_is_volatile)
323         return 0;
324       mode = mtp_property_pure;
325       node = get_Load_mem(node);
326       break;
327
328     case iro_Call:
329       /* a call is only tolerable if its either constant or pure */
330       ptr = get_Call_ptr(node);
331       if (get_irn_op(ptr) == op_SymConst &&
332           get_SymConst_kind(ptr) == symconst_addr_ent) {
333         ir_entity *ent = get_SymConst_entity(ptr);
334         ir_graph  *irg = get_entity_irg(ent);
335
336         if (irg == current_ir_graph) {
337           /* A recursive call. The did not mode depend on this call */
338         }
339         else if (irg == NULL) {
340           m = get_entity_additional_properties(ent) & (mtp_property_const|mtp_property_pure);
341           if (! m)
342             return 0;
343           mode = UMAX(mode, m);
344         }
345         else if (irg != NULL) {
346           /* we have a graph. Check if it is already analyzed */
347           if (IS_IRG_MARKED(irg))
348             (void)is_pure_function(irg);
349
350           m = get_irg_additional_properties(irg) & (mtp_property_const|mtp_property_pure);
351           if (! m)
352             return 0;
353           mode = UMAX(mode, m);
354         }
355       }
356       else
357         return 0;
358       node = get_Call_mem(node);
359       break;
360
361     default:
362       return 0;
363     }
364   }
365 }  /* follow_mem */
366
367 /**
368  * Follow the memory chain starting at node and determine
369  * the mtp_property.
370  *
371  * @return mtp_property_const if only calls of const functions are detected
372  *         mtp_property_pure if only Loads and const/pure
373  *         calls detected
374  *         0 else
375  */
376 static unsigned follow_mem(ir_graph *irg, ir_node *node, unsigned mode) {
377   unsigned m;
378
379   inc_irg_visited(irg);
380   /* mark the initial mem: recursion stops here */
381   mark_irn_visited(get_irg_initial_mem(irg));
382   m = _follow_mem(node);
383   if (! m)
384     return 0;
385   return UMAX(mode, m);
386 }  /* follow_mwm */
387
388 /*
389  * Check if a graph represents a pure function.
390  *
391  * @param irg  the graph
392  */
393 static int is_pure_function(ir_graph *irg) {
394   ir_node *end, *endbl;
395   int j;
396   unsigned mode = get_irg_additional_properties(irg);
397   ir_graph *rem = current_ir_graph;
398
399   if (mode & mtp_property_const) {
400     /* already marked as a const function */
401     return mtp_property_const;
402   }
403   if (mode & mtp_property_pure) {
404     /* already marked as a pure function */
405     return mtp_property_const;
406   }
407
408   if (! IS_IRG_MARKED(irg))
409     return 0;
410   UNMARK_IRG(irg);
411
412   end   = get_irg_end(irg);
413   endbl = get_nodes_block(end);
414   mode  = mtp_property_const;
415
416   current_ir_graph = irg;
417
418   /* visit every Return */
419   for (j = get_Block_n_cfgpreds(endbl) - 1; j >= 0; --j) {
420     ir_node *node = get_Block_cfgpred(endbl, j);
421     ir_op   *op   = get_irn_op(node);
422     ir_node *mem;
423
424     /* Bad nodes usually do NOT produce anything, so it's ok */
425     if (op == op_Bad)
426       continue;
427
428     if (op == op_Return) {
429       mem = get_Return_mem(node);
430
431       /* Bad nodes usually do NOT produce anything, so it's ok */
432       if (is_Bad(mem))
433         continue;
434
435       if (mem != get_irg_initial_mem(irg))
436         mode = follow_mem(irg, mem, mode);
437     }
438     else {
439       /* exception found. */
440       mode = follow_mem(irg, node, mode);
441       break;
442     }
443     if (mode == 0)
444       break;
445   }
446
447   if (mode != 0) {
448     /* check, if a keep-alive exists */
449     for (j = get_End_n_keepalives(end) - 1; j >= 0; --j) {
450       ir_node *mem = get_End_keepalive(end, j);
451
452       if (mode_M != get_irn_mode(mem))
453         continue;
454
455       mode = follow_mem(irg, mem, mode);
456       if (mode == 0)
457         break;
458     }
459   }
460
461   if (mode)
462     set_irg_additional_property(irg, mode);
463   current_ir_graph = rem;
464   return mode;
465 }  /* is_pure_function */
466
467 /**
468  * Handle calls to const functions.
469  */
470 static void handle_const_Calls(env_t *ctx)
471 {
472   int i;
473
474   ctx->n_calls_removed_SymConst = 0;
475   ctx->n_calls_removed_Sel      = 0;
476
477   /* all calls of const functions can be transformed */
478   for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
479     ir_graph *irg  = get_irp_irg(i);
480
481     ctx->const_call_list = NULL;
482     ctx->pure_call_list  = NULL;
483     ctx->proj_list = NULL;
484     irg_walk_graph(irg, NULL, collect_calls, ctx);
485
486     if (ctx->const_call_list)
487       fix_const_call_list(irg, ctx->const_call_list, ctx->proj_list);
488   }
489 }  /* handle_const_Calls */
490
491 /*
492  * optimize function calls by handling const functions
493  */
494 void optimize_funccalls(int force_run)
495 {
496   int i, n;
497   unsigned num_const = 0;
498   unsigned num_pure  = 0;
499
500   if (! get_opt_function_call())
501     return;
502
503   /* prepare: mark all graphs as not analyzed */
504   n = get_irp_n_irgs();
505   for (i = n - 1; i >= 0; --i)
506     MARK_IRG(get_irp_irg(i));
507
508   /* first step: detect, which functions are const, i.e. do NOT touch any memory */
509   for (i = n - 1; i >= 0; --i) {
510     ir_graph *irg = get_irp_irg(i);
511     unsigned mode = is_pure_function(irg);
512
513     if (mode & mtp_property_const)
514       ++num_const;
515     else if (mode & mtp_property_pure)
516       ++num_pure;
517   }
518
519   if (force_run || num_const > 0) {
520     env_t ctx;
521
522     handle_const_Calls(&ctx);
523     if (get_firm_verbosity()) {
524       printf("Detected %d graphs without side effects.\n", num_const);
525       printf("Optimizes %d(SymConst) + %d(Sel) calls to const/pure functions.\n",
526                ctx.n_calls_removed_SymConst, ctx.n_calls_removed_Sel);
527     }
528   }
529   else {
530     if (get_firm_verbosity()) {
531       printf("No graphs without side effects detected\n");
532     }
533   }
534 }  /* optimize_funccalls */