38e5028cefccf611da8b731962c18b4e35d11460
[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-2004 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 *list;                  /**< The list of all Calls that will be changed. */
29   ir_node *proj_list;             /**< list of all potential Proj nones that must be fixed.*/
30 } env_t;
31
32 /**
33  * remove memory from const function calls by rerouting
34  * it's ProjM and connection the call with a NoMem node.
35  *
36  * Note: By "const function" we understand a function that did neither
37  * read nor write memory. Hence its result depends solely on its
38  * arguments.
39  */
40 static void rem_mem_from_const_fkt_calls(ir_node *node, void *env)
41 {
42   env_t *ctx = env;
43   ir_node *call, *ptr;
44   entity *ent;
45
46   if (get_irn_op(node) == op_Call) {
47     call = node;
48
49     set_irn_link(call, NULL);
50     ptr = get_Call_ptr(call);
51     if (get_irn_op(ptr) == op_SymConst && get_SymConst_kind(ptr) == symconst_addr_ent) {
52       ent = get_SymConst_entity(ptr);
53
54       if ((get_entity_additional_properties(ent) & mtp_property_const) == 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 real 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       for (i = 0; i < n_callees; ++i) {
68         ent = get_Call_callee(call, i);
69         if (ent == unknown_entity) {
70           /* we don't know which entity is called here */
71           return;
72         }
73         if ((get_entity_additional_properties(ent) & mtp_property_const) == 0)
74           return;
75       }
76       ++ctx->n_calls_removed_Sel;
77     }
78     else
79       return;
80
81     /* ok, if we get here we found a call to a const function */
82     set_irn_link(call, ctx->list);
83     ctx->list = call;
84   }
85   else if (get_irn_op(node) == op_Proj) {
86     /*
87      * Collect all memory and exception Proj's from
88      * calls.
89      */
90     call = get_Proj_pred(node);
91     if (get_irn_op(call) != op_Call)
92       return;
93
94     switch (get_Proj_proj(node)) {
95     case pn_Call_M_regular:
96     case pn_Call_X_except:
97     case pn_Call_M_except:
98       set_irn_link(node, ctx->proj_list);
99       ctx->proj_list = node;
100       break;
101     default:
102       ;
103     }
104   }
105 }  /* rem_mem_from_const_fkt_calls */
106
107 /**
108  * Fix the list of collected Calls.
109  *
110  * @param irg        the graph that contained calls to pure functions
111  * @param call_list  the list of all call sites of pure functions
112  * @param proj_list  the list of all memory/exception Projs of this call sites
113  */
114 static void fix_call_list(ir_graph *irg, ir_node *call_list, ir_node *proj_list) {
115   ir_node *call, *next, *mem, *proj;
116   int exc_changed = 0;
117
118   /* fix all calls by removing it's memory input */
119   for (call = call_list; call; call = next) {
120     next = get_irn_link(call);
121     mem  = get_Call_mem(call);
122
123     set_irn_link(call, mem);
124     set_Call_mem(call, get_irg_no_mem(irg));
125
126     /*
127      * Sorrily we cannot simply set the node to 'float'.
128      * There is a reason for that:
129      *
130      * - The call might be inside a loop/if that is NOT entered
131      *   and calls a endless function. Setting the call to float
132      *   would allow to move it out from the loop/if causing this
133      *   function be called even if the loop/if is not entered ...
134      *
135      * This could be fixed using post-dominators for calls and Pin nodes
136      * but need some more analyses to ensure that a call that potential
137      * never returns is not executed before some code that generates
138      * observable states...
139      */
140
141     /* finally, this call can float
142     set_irn_pinned(call, op_pin_state_floats); */
143     hook_func_call(irg, call);
144   }
145
146   /* finally fix all Proj's */
147   for (proj = proj_list; proj; proj = next) {
148     next = get_irn_link(proj);
149     call = get_Proj_pred(proj);
150     mem  = get_irn_link(call);
151     if (! mem)
152       continue;
153
154     switch (get_Proj_proj(proj)) {
155     case pn_Call_M_regular: {
156       exchange(proj, mem);
157     } break;
158     case pn_Call_X_except:
159     case pn_Call_M_except:
160       exc_changed = 1;
161       exchange(proj, get_irg_bad(irg));
162       break;
163     default:
164       ;
165     }
166   }
167
168   /* changes were done ... */
169   set_irg_outs_inconsistent(irg);
170   set_irg_loopinfo_state(current_ir_graph, loopinfo_cf_inconsistent);
171
172   if (exc_changed) {
173     /* ... including exception edges */
174     set_irg_doms_inconsistent(irg);
175   }
176 }  /* fix_call_list */
177
178 /*
179  * optimize function calls by handling const functions
180  */
181 void optimize_funccalls(int force_run)
182 {
183   int i, j;
184   int change;
185   unsigned num_pure = 0;
186
187   if (! get_opt_real_function_call())
188     return;
189
190   /* first step: detect, which functions are const, i.e. do NOT touch any memory */
191   for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
192     ir_graph *irg  = get_irp_irg(i);
193     ir_node *end   = get_irg_end(irg);
194     ir_node *endbl = get_nodes_block(end);
195
196     change = 0;
197
198     if (get_irg_additional_properties(irg) & mtp_property_const) {
199       /* already marked as a const function */
200       ++num_pure;
201     }
202     else {
203       /* visit every Return */
204       for (j = get_Block_n_cfgpreds(endbl) - 1; j >= 0; --j) {
205         ir_node *node = get_Block_cfgpred(endbl, j);
206         ir_op   *op   = get_irn_op(node);
207         ir_node *mem;
208
209         /* Bad nodes usually do NOT produce anything, so it's ok */
210         if (op == op_Bad)
211           continue;
212
213         if (op == op_Return) {
214           mem = get_Return_mem(node);
215
216           /* Bad nodes usually do NOT produce anything, so it's ok */
217           if (is_Bad(mem))
218             continue;
219
220           change = mem != get_irg_initial_mem(irg);
221           if (change)
222             break;
223         }
224         else {
225           /* exception found */
226           change = 1;
227           break;
228         }
229       }
230
231       if (! change) {
232         /* check, if a keep-alive exists */
233         for (j = get_End_n_keepalives(end) - 1; j >= 0; --j) {
234           ir_node *mem = get_End_keepalive(end, j);
235
236           if (mode_M != get_irn_mode(mem))
237             continue;
238
239           change = mem != get_irg_initial_mem(irg);
240           if (change)
241             break;
242         }
243       }
244
245       if (! change) {
246         /* no memory changes found, it's a const function */
247         set_irg_additional_property(irg, mtp_property_const);
248         ++num_pure;
249       }
250     }
251   }
252
253   if (force_run || num_pure > 0) {
254     env_t ctx;
255
256     ctx.n_calls_removed_SymConst = 0;
257     ctx.n_calls_removed_Sel = 0;
258
259     /* all calls of pure functions can be transformed into FuncCalls */
260     for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
261       ir_graph *irg  = get_irp_irg(i);
262
263       /* no need to do this on const functions */
264       if ((get_irg_additional_properties(irg) & mtp_property_const) == 0) {
265         ctx.list      = NULL;
266         ctx.proj_list = NULL;
267         irg_walk_graph(irg, NULL, rem_mem_from_const_fkt_calls, &ctx);
268
269         if (ctx.list)
270           fix_call_list(irg, ctx.list, ctx.proj_list);
271       }
272     }
273
274     if (get_firm_verbosity()) {
275       printf("Detected %d graphs without side effects.\n", num_pure);
276       printf("Optimizes %d(SymConst) + %d(Sel) calls to const functions.\n",
277              ctx.n_calls_removed_SymConst, ctx.n_calls_removed_Sel);
278     }
279   }
280   else {
281     if (get_firm_verbosity()) {
282       printf("No graphs without side effects detected\n");
283     }
284   }
285 }  /* optimize_funccalls */