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