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