2 * Copyright (C) 1995-2007 University of Karlsruhe. All right reserved.
4 * This file is part of libFirm.
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.
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.
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
22 * @brief Optimization of function calls.
23 * @author Michael Beck
30 #include <adt/raw_bitset.h>
32 #include "funccall_t.h"
35 #include "irgraph_t.h"
39 #include "dbginfo_t.h"
45 DEBUG_ONLY(static firm_dbg_module_t *dbg;)
48 * The walker environment for rem_mem_from_const_fkt_calls
50 typedef struct _env_t {
51 unsigned n_calls_SymConst;
53 ir_node *const_call_list; /**< The list of all const function calls that will be changed. */
54 ir_node *pure_call_list; /**< The list of all pure function calls that will be changed. */
55 ir_node *proj_list; /**< The list of all potential Proj nodes that must be fixed. */
58 /** Ready IRG's are marked in the ready set. */
59 static unsigned *ready_set;
61 /** IRG's that are in progress are marked here. */
62 static unsigned *busy_set;
65 * We misuse the mtp_temporary flag as temporary here.
66 * The is ok, as we cannot set or get it anyway.
68 #define mtp_temporary mtp_property_inherited
71 * Collect all calls to const and pure functions
72 * to lists. Collect all Proj(Call) nodes into a Proj list.
74 static void collect_calls(ir_node *node, void *env) {
83 /* set the link to NULL for all non-const/pure calls */
84 set_irn_link(call, NULL);
85 ptr = get_Call_ptr(call);
86 if (is_SymConst(ptr) && get_SymConst_kind(ptr) == symconst_addr_ent) {
87 ent = get_SymConst_entity(ptr);
89 mode = get_entity_additional_properties(ent);
90 if ((mode & (mtp_property_const|mtp_property_pure)) == 0)
92 ++ctx->n_calls_SymConst;
93 } else if (get_opt_closed_world() &&
95 get_irg_callee_info_state(current_ir_graph) == irg_callee_info_consistent) {
96 /* If all possible callees are const functions, we can remove the memory edge. */
97 int i, n_callees = get_Call_n_callees(call);
99 /* This is kind of strange: dying code or a Call that will raise an exception
100 when executed as there is no implementation to call. So better not
105 /* note that const function are a subset of pure ones */
106 mode = mtp_property_const | mtp_property_pure;
107 for (i = 0; i < n_callees; ++i) {
108 ent = get_Call_callee(call, i);
109 if (ent == unknown_entity) {
110 /* we don't know which entity is called here */
113 mode &= get_entity_additional_properties(ent);
121 /* ok, if we get here we found a call to a const or a pure function */
122 if (mode & mtp_property_pure) {
123 set_irn_link(call, ctx->pure_call_list);
124 ctx->pure_call_list = call;
126 set_irn_link(call, ctx->const_call_list);
127 ctx->const_call_list = call;
129 } else if (is_Proj(node)) {
131 * Collect all memory and exception Proj's from
134 call = get_Proj_pred(node);
138 /* collect the Proj's in the Proj list */
139 switch (get_Proj_proj(node)) {
140 case pn_Call_M_regular:
141 case pn_Call_X_except:
142 case pn_Call_X_regular:
143 case pn_Call_M_except:
144 set_irn_link(node, ctx->proj_list);
145 ctx->proj_list = node;
151 } /* collect_calls */
154 * Fix the list of collected Calls.
156 * @param irg the graph that contained calls to pure functions
157 * @param call_list the list of all call sites of const functions
158 * @param proj_list the list of all memory/exception Proj's of this call sites
160 static void fix_const_call_list(ir_graph *irg, ir_node *call_list, ir_node *proj_list) {
161 ir_node *call, *next, *mem, *proj;
163 ir_graph *rem = current_ir_graph;
165 current_ir_graph = irg;
167 /* First step: fix all calls by removing it's memory input.
168 It's original memory input is preserved in their link fields. */
169 for (call = call_list; call; call = next) {
170 next = get_irn_link(call);
171 mem = get_Call_mem(call);
173 set_irn_link(call, mem);
174 set_Call_mem(call, get_irg_no_mem(irg));
177 * Sorrily we cannot simply set the node to 'float'.
178 * There is a reason for that:
180 * - The call might be inside a loop/if that is NOT entered
181 * and calls a endless function. Setting the call to float
182 * would allow to move it out from the loop/if causing this
183 * function be called even if the loop/if is not entered ...
185 * This could be fixed using post-dominators for calls and Pin nodes
186 * but need some more analyzes to ensure that a call that potential
187 * never returns is not executed before some code that generates
188 * observable states...
191 /* finally, this call can float
192 set_irn_pinned(call, op_pin_state_floats); */
193 hook_func_call(irg, call);
196 /* Second step: fix all Proj's */
197 for (proj = proj_list; proj; proj = next) {
198 next = get_irn_link(proj);
199 call = get_Proj_pred(proj);
200 mem = get_irn_link(call);
202 /* beware of calls in the pure call list */
203 if (! mem || get_irn_op(mem) == op_Call)
205 assert(get_irn_mode(mem) == mode_M);
207 switch (get_Proj_proj(proj)) {
208 case pn_Call_M_regular: {
209 /* in dead code there might be cycles where proj == mem */
214 case pn_Call_X_except:
215 case pn_Call_M_except:
217 exchange(proj, get_irg_bad(irg));
219 case pn_Call_X_regular: {
220 ir_node *block = get_nodes_block(call);
222 exchange(proj, new_r_Jmp(irg, block));
230 /* changes were done ... */
231 set_irg_outs_inconsistent(irg);
232 set_irg_loopinfo_state(irg, loopinfo_cf_inconsistent);
235 /* ... including exception edges */
236 set_irg_doms_inconsistent(irg);
238 current_ir_graph = rem;
239 } /* fix_call_list */
242 #define SET_IRG_READY(irg) rbitset_set(ready_set, get_irg_idx(irg))
243 #define IS_IRG_READY(irg) rbitset_is_set(ready_set, get_irg_idx(irg))
244 #define SET_IRG_BUSY(irg) rbitset_set(busy_set, get_irg_idx(irg))
245 #define CLEAR_IRG_BUSY(irg) rbitset_clear(busy_set, get_irg_idx(irg))
246 #define IS_IRG_BUSY(irg) rbitset_is_set(busy_set, get_irg_idx(irg))
249 static unsigned check_const_or_pure_function(ir_graph *irg, int top);
252 * Calculate the bigger mode of two. Handle the temporary flag right.
254 static unsigned mode_max(unsigned a, unsigned b) {
255 unsigned r, t = (a | b) & mtp_temporary;
259 if (a == mtp_no_property || b == mtp_no_property)
260 return mtp_no_property;
266 * Follow the memory chain starting at node and determine
269 * @return mtp_property_const if only calls of const functions are detected
270 * mtp_property_pure if only Loads and const/pure
272 * mtp_no_property else
274 static unsigned _follow_mem(ir_node *node) {
275 unsigned m, mode = mtp_property_const;
280 if (mode == mtp_no_property)
281 return mtp_no_property;
283 if (irn_visited(node))
286 mark_irn_visited(node);
288 switch (get_irn_opcode(node)) {
290 node = get_Proj_pred(node);
299 /* do a dfs search */
300 for (i = get_irn_arity(node) - 1; i >= 0; --i) {
301 m = _follow_mem(get_irn_n(node, i));
302 mode = mode_max(mode, m);
303 if (mode == mtp_no_property)
304 return mtp_no_property;
309 /* Beware volatile Loads are NOT allowed in pure functions. */
310 if (get_Load_volatility(node) == volatility_is_volatile)
311 return mtp_no_property;
312 mode = mode_max(mode, mtp_property_pure);
313 node = get_Load_mem(node);
317 /* A call is only tolerable if its either constant or pure. */
318 ptr = get_Call_ptr(node);
319 if (get_irn_op(ptr) == op_SymConst &&
320 get_SymConst_kind(ptr) == symconst_addr_ent) {
321 ir_entity *ent = get_SymConst_entity(ptr);
322 ir_graph *irg = get_entity_irg(ent);
324 if (irg == current_ir_graph) {
325 /* A self-recursive call. The mode did not depend on this call. */
326 } else if (irg == NULL) {
327 m = get_entity_additional_properties(ent) & (mtp_property_const|mtp_property_pure);
328 mode = mode_max(mode, m);
329 } else if (irg != NULL) {
330 /* we have a graph, analyze it. */
331 m = check_const_or_pure_function(irg, /*top=*/0);
332 mode = mode_max(mode, m);
335 return mtp_no_property;
336 node = get_Call_mem(node);
340 return mtp_no_property;
346 * Follow the memory chain starting at node and determine
349 * @return mtp_property_const if only calls of const functions are detected
350 * mtp_property_pure if only Loads and const/pure calls detected
351 * mtp_no_property else
353 static unsigned follow_mem(ir_node *node, unsigned mode) {
356 m = _follow_mem(node);
357 return mode_max(mode, m);
361 * Check if a graph represents a const function.
363 * @param irg the graph
364 * @param top top call
366 static unsigned check_const_or_pure_function(ir_graph *irg, int top) {
367 ir_node *end, *endbl;
369 unsigned mode = get_irg_additional_properties(irg);
370 ir_graph *rem = current_ir_graph;
372 if (mode & mtp_property_const) {
373 /* already marked as a const function */
374 return mtp_property_const;
376 if (mode & mtp_property_pure) {
377 /* already marked as a pure function */
378 return mtp_property_pure;
381 if (IS_IRG_READY(irg)) {
382 /* already checked */
383 return mtp_no_property;
385 if (IS_IRG_BUSY(irg)) {
386 /* we are still evaluate this method. Be optimistic,
387 return the best possible so far but mark the result as temporary. */
388 return mtp_temporary | mtp_property_const;
392 end = get_irg_end(irg);
393 endbl = get_nodes_block(end);
394 mode = mtp_property_const;
396 current_ir_graph = irg;
398 inc_irg_visited(irg);
399 /* mark the initial mem: recursion of follow_mem stops here */
400 mark_irn_visited(get_irg_initial_mem(irg));
402 /* visit every Return */
403 for (j = get_Block_n_cfgpreds(endbl) - 1; j >= 0; --j) {
404 ir_node *node = get_Block_cfgpred(endbl, j);
405 ir_op *op = get_irn_op(node);
408 /* Bad nodes usually do NOT produce anything, so it's ok */
412 if (op == op_Return) {
413 mem = get_Return_mem(node);
415 /* Bad nodes usually do NOT produce anything, so it's ok */
419 if (mem != get_irg_initial_mem(irg))
420 mode = mode_max(mode, follow_mem(mem, mode));
422 /* Exception found. Cannot be const or pure. */
423 mode = mtp_no_property;
426 if (mode == mtp_no_property)
430 if (mode != mtp_no_property) {
431 /* check, if a keep-alive exists */
432 for (j = get_End_n_keepalives(end) - 1; j >= 0; --j) {
433 ir_node *mem = get_End_keepalive(end, j);
435 if (mode_M != get_irn_mode(mem))
438 mode = mode_max(mode, follow_mem(mem, mode));
439 if (mode == mtp_no_property)
444 if (mode != mtp_no_property) {
445 if (top || (mode & mtp_temporary) == 0) {
446 /* We use the temporary flag here to mark optimistic result.
447 Set the property only if we are sure that it does NOT base on
448 temporary results OR if we are at top-level. */
449 set_irg_additional_property(irg, mode & ~mtp_temporary);
456 current_ir_graph = rem;
458 } /* check_const_or_pure_function */
461 * Handle calls to const functions.
463 static void handle_const_Calls(env_t *ctx) {
466 ctx->n_calls_SymConst = 0;
467 ctx->n_calls_Sel = 0;
469 /* all calls of const functions can be transformed */
470 for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
471 ir_graph *irg = get_irp_irg(i);
473 ctx->const_call_list = NULL;
474 ctx->pure_call_list = NULL;
475 ctx->proj_list = NULL;
476 irg_walk_graph(irg, NULL, collect_calls, ctx);
478 if (ctx->const_call_list)
479 fix_const_call_list(irg, ctx->const_call_list, ctx->proj_list);
481 } /* handle_const_Calls */
484 * optimize function calls by handling const functions
486 void optimize_funccalls(int force_run)
489 unsigned num_const = 0;
490 unsigned num_pure = 0;
492 /* prepare: mark all graphs as not analyzed */
493 n = get_irp_last_idx();
494 ready_set = rbitset_malloc(n);
495 busy_set = rbitset_malloc(n);
497 /* first step: detect, which functions are const, i.e. do NOT touch any memory */
498 DBG((dbg, LEVEL_2, "Detecting const and pure properties ...\n"));
499 for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
500 ir_graph *irg = get_irp_irg(i);
501 unsigned mode = check_const_or_pure_function(irg, /*top=*/1);
503 if (mode & mtp_property_const) {
505 DBG((dbg, LEVEL_2, "%+F has the const property\n", irg));
506 } else if (mode & mtp_property_pure) {
508 DBG((dbg, LEVEL_2, "%+F has the pure property\n", irg));
512 if (force_run || num_const > 0) {
515 handle_const_Calls(&ctx);
516 if (get_firm_verbosity()) {
517 DBG((dbg, LEVEL_1, "Detected %u const graphs, %u pure graphs.\n", num_const, num_pure));
518 DBG((dbg, LEVEL_1, "Optimizes %u(SymConst) + %u(Sel) calls to const functions.\n",
519 ctx.n_calls_SymConst, ctx.n_calls_Sel));
522 DBG((dbg, LEVEL_1, "No graphs without side effects detected\n"));
526 } /* optimize_funccalls */
528 /* initialize the funccall optimization */
529 void firm_init_funccalls(void)
531 FIRM_DBG_REGISTER(dbg, "firm.opt.funccalls");
532 } /* firm_init_funccalls */