Added phases
[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  changed;                   /**< flag, is set if a graph was changed */
27   int  n_calls_removed_SymConst;
28   int  n_calls_removed_Sel;
29 } env_t;
30
31 /**
32  * remove memory from const function calls by rerouting
33  * it's ProjM and connection the call with a NoMem node.
34  *
35  * Note: By "const function" we understand a function that did neither
36  * read nor write memory. Hence its result depends solely on its
37  * arguments.
38  */
39 static void rem_mem_from_const_fkt_calls(ir_node *node, void *env)
40 {
41   env_t *ctx = env;
42   ir_node *call, *ptr, *mem;
43   entity *ent;
44
45   if (get_irn_op(node) == op_Call) {
46     call = node;
47
48     set_irn_link(call, NULL);
49
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      * route the NoMem node to the call */
83     mem   = get_Call_mem(call);
84
85     set_irn_link(call, mem);
86     set_Call_mem(call, new_r_NoMem(current_ir_graph));
87
88     /* finally, this call can float */
89     set_irn_pinned(call, op_pin_state_floats);
90
91     hook_func_call(current_ir_graph, call);
92
93     ctx->changed = 1;
94   }
95   else if (get_irn_op(node) == op_Proj) {
96     /*
97      * Remove memory and exception Proj's from
98      * const function calls.
99      */
100     call = get_Proj_pred(node);
101     if ((get_irn_op(call) != op_Call) ||
102         (get_irn_op(get_Call_mem(call)) != op_NoMem))
103       return;
104
105     switch (get_Proj_proj(node)) {
106     case pn_Call_M_regular: {
107       ir_node *old_mem = get_irn_link(call);
108       if (old_mem) {
109         exchange(node, old_mem);
110         ctx->changed = 1;
111       }
112     } break;
113     case pn_Call_X_except:
114     case pn_Call_M_except:
115       exchange(node, new_Bad());
116       ctx->changed = 1;
117       break;
118     default: ;
119     }
120   }
121 }
122
123 /*
124  * optimize function calls by handling const functions
125  */
126 void optimize_funccalls(int force_run)
127 {
128   int i, j;
129   int change;
130   unsigned num_pure = 0;
131
132   if (! get_opt_real_function_call())
133     return;
134
135   /* first step: detect, which functions are const, i.e. do NOT touch any memory */
136   for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
137     ir_graph *irg  = get_irp_irg(i);
138     ir_node *end   = get_irg_end(irg);
139     ir_node *endbl = get_nodes_block(end);
140
141     change = 0;
142
143     if (get_irg_additional_properties(irg) & mtp_property_const) {
144       /* already marked as a const function */
145       ++num_pure;
146     }
147     else {
148       /* visit every Return */
149       for (j = get_Block_n_cfgpreds(endbl) - 1; j >= 0; --j) {
150         ir_node *node = get_Block_cfgpred(endbl, j);
151         ir_op   *op   = get_irn_op(node);
152         ir_node *mem;
153
154         /* Bad nodes usually do NOT produce anything, so it's ok */
155         if (op == op_Bad)
156           continue;
157
158         if (op == op_Return) {
159           mem = get_Return_mem(node);
160
161           /* Bad nodes usually do NOT produce anything, so it's ok */
162           if (is_Bad(mem))
163             continue;
164
165           change = mem != get_irg_initial_mem(irg);
166           if (change)
167             break;
168         }
169         else {
170           /* exception found */
171           change = 1;
172           break;
173         }
174       }
175
176       if (! change) {
177         /* check, if a keep-alive exists */
178         for (j = get_End_n_keepalives(end) - 1; j >= 0; --j) {
179           ir_node *mem = get_End_keepalive(end, j);
180
181           if (mode_M != get_irn_mode(mem))
182             continue;
183
184           change = mem != get_irg_initial_mem(irg);
185           if (change)
186             break;
187         }
188       }
189
190       if (! change) {
191         /* no memory changes found, it's a const function */
192         set_irg_additional_property(irg, mtp_property_const);
193         ++num_pure;
194       }
195     }
196   }
197
198   if (force_run || num_pure > 0) {
199     env_t ctx;
200
201     ctx.n_calls_removed_SymConst = 0;
202     ctx.n_calls_removed_Sel = 0;
203
204     /* all calls of pure functions can be transformed into FuncCalls */
205     for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
206       ir_graph *irg  = get_irp_irg(i);
207
208       /* no need to do this on const functions */
209       if ((get_irg_additional_properties(irg) & mtp_property_const) == 0) {
210         ctx.changed = 0;
211         irg_walk_graph(irg, NULL, rem_mem_from_const_fkt_calls, &ctx);
212
213         if (ctx.changed) {
214           /* changes were done including exception edges */
215           set_irg_outs_inconsistent(irg);
216           set_irg_doms_inconsistent(irg);
217           set_irg_loopinfo_state(current_ir_graph, loopinfo_cf_inconsistent);
218         }
219       }
220     }
221
222     if (get_firm_verbosity()) {
223       printf("Detected %d graphs without side effects.\n", num_pure);
224       printf("Optimizes %d(SymConst) + %d(Sel) calls to const functions.\n",
225              ctx.n_calls_removed_SymConst, ctx.n_calls_removed_Sel);
226     }
227   }
228   else {
229     if (get_firm_verbosity()) {
230       printf("No graphs without side effects detected\n");
231     }
232   }
233 }