-/* Coyright (C) 1998 - 2002 by Universitaet Karlsruhe
-* All rights reserved.
-*
-* Author: Christian Schaefer, Goetz Lindenmaier, Sebastian Felis
-*
-* Optimizations for a whole ir graph, i.e., a procedure.
-*/
+/*
+ * Project: libFIRM
+ * File name: ir/ir/irgopt.c
+ * Purpose: Optimizations for a whole ir graph, i.e., a procedure.
+ * Author: Christian Schaefer, Goetz Lindenmaier
+ * Modified by: Sebastian Felis
+ * Created:
+ * CVS-ID: $Id$
+ * Copyright: (c) 1998-2003 Universität Karlsruhe
+ * Licence: This file protected by GPL - GNU GENERAL PUBLIC LICENSE.
+ */
-/* $Id$ */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
# include <assert.h>
+# include <stdbool.h>
# include "irprog.h"
# include "irgopt.h"
# include "iropt_t.h"
# include "irgwalk.h"
# include "ircons.h"
-# include "misc.h"
# include "irgmod.h"
# include "array.h"
# include "pset.h"
+# include "eset.h"
# include "pdeq.h" /* Fuer code placement */
# include "irouts.h"
# include "irloop.h"
set_irn_link(n, NULL);
}
+#if 0 /* Old version. Avoids Ids.
+ This is not necessary: we do a postwalk, and get_irn_n
+ removes ids anyways. So it's much cheaper to call the
+ optimization less often and use the exchange() algorithm. */
static void
optimize_in_place_wrapper (ir_node *n, void *env) {
- int i;
+ int i, irn_arity;
ir_node *optimized, *old;
- for (i = 0; i < get_irn_arity(n); i++) {
- /* get?irn_n skips Id nodes, so comparison old != optimized does not
+ irn_arity = get_irn_arity(n);
+ for (i = 0; i < irn_arity; i++) {
+ /* get_irn_n skips Id nodes, so comparison old != optimized does not
show all optimizations. Therefore always set new predecessor. */
old = get_irn_n(n, i);
optimized = optimize_in_place_2(old);
if (optimized != n) exchange (n, optimized);
}
}
+#else
+static void
+optimize_in_place_wrapper (ir_node *n, void *env) {
+ ir_node *optimized = optimize_in_place_2(n);
+ if (optimized != n) exchange (n, optimized);
+}
+#endif
+
+
void
local_optimize_graph (ir_graph *irg) {
in a Block. */
static INLINE int
compute_new_arity(ir_node *b) {
- int i, res;
+ int i, res, irn_arity;
int irg_v, block_v;
irg_v = get_irg_block_visited(current_ir_graph);
return block_v - irg_v;
} else {
/* compute the number of good predecessors */
- res = get_irn_arity(b);
- for (i = 0; i < get_irn_arity(b); i++)
+ res = irn_arity = get_irn_arity(b);
+ for (i = 0; i < irn_arity; i++)
if (get_irn_opcode(get_irn_n(b, i)) == iro_Bad) res--;
/* save it in the flag. */
set_Block_block_visited(b, irg_v + res);
ir_node *nn, *block;
int new_arity;
+ /* The end node looses it's flexible in array. This doesn't matter,
+ as dead node elimination builds End by hand, inlineing doesn't use
+ the End node. */
+ //assert(n->op == op_End || ((_ARR_DESCR(n->in))->cookie != ARR_F_MAGIC));
+
if (get_irn_opcode(n) == iro_Block) {
block = NULL;
new_arity = compute_new_arity(n);
static void
copy_preds (ir_node *n, void *env) {
ir_node *nn, *block;
- int i, j;
+ int i, j, irn_arity;
nn = get_new_node(n);
if (get_irn_opcode(n) == iro_Block) {
/* Don't copy Bad nodes. */
j = 0;
- for (i = 0; i < get_irn_arity(n); i++)
+ irn_arity = get_irn_arity(n);
+ for (i = 0; i < irn_arity; i++)
if (get_irn_opcode(get_irn_n(n, i)) != iro_Bad) {
set_irn_n (nn, j, get_new_node(get_irn_n(n, i)));
/*if (is_backedge(n, i)) set_backedge(nn, j);*/
block = get_nodes_Block(n);
set_irn_n (nn, -1, get_new_node(block));
j = 0;
- for (i = 0; i < get_irn_arity(n); i++)
+ irn_arity = get_irn_arity(n);
+ for (i = 0; i < irn_arity; i++)
if (get_irn_opcode(get_irn_n(block, i)) != iro_Bad) {
set_irn_n (nn, j, get_new_node(get_irn_n(n, i)));
/*if (is_backedge(n, i)) set_backedge(nn, j);*/
if (get_irn_arity(n) == 1)
exchange(n, get_irn_n(n, 0));
} else {
- for (i = -1; i < get_irn_arity(n); i++)
+ irn_arity = get_irn_arity(n);
+ for (i = -1; i < irn_arity; i++)
set_irn_n (nn, i, get_new_node(get_irn_n(n, i)));
}
/* Now the new node is complete. We can add it to the hash table for cse.
copy_graph (void) {
ir_node *oe, *ne; /* old end, new end */
ir_node *ka; /* keep alive */
- int i;
+ int i, irn_arity;
oe = get_irg_end(current_ir_graph);
/* copy the end node by hand, allocate dynamic in array! */
/** ... and now the keep alives. **/
/* First pick the not marked block nodes and walk them. We must pick these
first as else we will oversee blocks reachable from Phis. */
- for (i = 0; i < get_irn_arity(oe); i++) {
+ irn_arity = get_irn_arity(oe);
+ for (i = 0; i < irn_arity; i++) {
ka = get_irn_n(oe, i);
if ((get_irn_op(ka) == op_Block) &&
(get_irn_visited(ka) < get_irg_visited(current_ir_graph))) {
}
/* Now pick the Phis. Here we will keep all! */
- for (i = 0; i < get_irn_arity(oe); i++) {
+ irn_arity = get_irn_arity(oe);
+ for (i = 0; i < irn_arity; i++) {
ka = get_irn_n(oe, i);
if ((get_irn_op(ka) == op_Phi)) {
if (get_irn_visited(ka) < get_irg_visited(current_ir_graph)) {
copy_preds(get_irg_bad(current_ir_graph), NULL);
}
set_irg_bad(current_ir_graph, get_new_node(get_irg_bad(current_ir_graph)));
+ /* GL removed: we need unknown with mode for analyses.
if (get_irn_link(get_irg_unknown(current_ir_graph)) == NULL) {
copy_node(get_irg_unknown(current_ir_graph), NULL);
copy_preds(get_irg_unknown(current_ir_graph), NULL);
}
set_irg_unknown(current_ir_graph, get_new_node(get_irg_unknown(current_ir_graph)));
+ */
}
/* Copies all reachable nodes to a new obstack. Removes bad inputs
/* Funcionality for inlining */
/**********************************************************************/
-/* Copy node for inlineing. Copies the node by calling copy_node and
- then updates the entity if it's a local one. env must be a pointer
- to the frame type of the procedure. The new entities must be in
- the link field of the entities. */
+/* Copy node for inlineing. Updates attributes that change when
+ * inlineing but not for dead node elimination.
+ *
+ * Copies the node by calling copy_node and then updates the entity if
+ * it's a local one. env must be a pointer of the frame type of the
+ * inlined procedure. The new entities must be in the link field of
+ * the entities. */
static INLINE void
copy_node_inline (ir_node *n, void *env) {
ir_node *new;
if (get_entity_owner(get_Sel_entity(n)) == frame_tp) {
set_Sel_entity(new, get_entity_link(get_Sel_entity(n)));
}
+ } else if (get_irn_op(n) == op_Block) {
+ new = get_new_node (n);
+ new->attr.block.irg = current_ir_graph;
}
}
+
void inline_method(ir_node *call, ir_graph *called_graph) {
ir_node *pre_call;
ir_node *post_call, *post_bl;
ir_node **cf_pred;
ir_node *ret, *phi;
ir_node *cf_op = NULL, *bl;
- int arity, n_ret, n_exc, n_res, i, j, rem_opt;
+ int arity, n_ret, n_exc, n_res, i, j, rem_opt, irn_arity;
type *called_frame;
if (!get_optimize() || !get_opt_inline()) return;
set_irg_current_block(current_ir_graph, post_bl); /* just to make sure */
/* -- archive keepalives -- */
- for (i = 0; i < get_irn_arity(end); i++)
+ irn_arity = get_irn_arity(end);
+ for (i = 0; i < irn_arity; i++)
add_End_keepalive(get_irg_end(current_ir_graph), get_irn_n(end, i));
- /* The new end node will die, but the in array is not on the obstack ... */
- free_End(end);
+
+ /* The new end node will die. We need not free as the in array is on the obstack:
+ copy_node only generated 'D' arrays. */
/* --
Return nodes by Jump nodes. -- */
free(res_pred);
free(cf_pred);
-/* --
- If the exception control flow from the Call directly branched to the
- end block we now have the following control flow predecessor pattern:
- ProjX -> Tuple -> Jmp.
- We must remove the Jmp along with it's empty block and add Jmp's
- predecessors as predecessors of this end block. -- */
- /* find the problematic predecessor of the end block. */
- end_bl = get_irg_end_block(current_ir_graph);
- for (i = 0; i < get_Block_n_cfgpreds(end_bl); i++) {
- cf_op = get_Block_cfgpred(end_bl, i);
- if (get_irn_op(cf_op) == op_Proj) {
- cf_op = get_Proj_pred(cf_op);
- if (get_irn_op(cf_op) == op_Tuple) {
- cf_op = get_Tuple_pred(cf_op, 1);
- assert(get_irn_op(cf_op) == op_Jmp);
- break;
+ if (n_exc > 0) {
+ /* -- If the exception control flow from the inlined Call directly
+ branched to the end block we now have the following control
+ flow predecessor pattern: ProjX -> Tuple -> Jmp. We must
+ remove the Jmp along with it's empty block and add Jmp's
+ predecessors as predecessors of this end block. No problem if
+ there is no exception, because then branches Bad to End which
+ is fine. -- */
+ /* find the problematic predecessor of the end block. */
+ end_bl = get_irg_end_block(current_ir_graph);
+ for (i = 0; i < get_Block_n_cfgpreds(end_bl); i++) {
+ cf_op = get_Block_cfgpred(end_bl, i);
+ if (get_irn_op(cf_op) == op_Proj) {
+ cf_op = get_Proj_pred(cf_op);
+ if ((get_irn_op(cf_op) == op_Tuple) && (cf_op == call)) {
+ // There are unoptimized tuples from inlineing before when no exc
+ assert(get_Proj_proj(get_Block_cfgpred(end_bl, i)) == pn_Call_X_except);
+ cf_op = get_Tuple_pred(cf_op, pn_Call_X_except);
+ assert(get_irn_op(cf_op) == op_Jmp);
+ break;
+ }
}
}
- }
- /* repair */
- if (i < get_Block_n_cfgpreds(end_bl)) {
- bl = get_nodes_Block(cf_op);
- arity = get_Block_n_cfgpreds(end_bl) + get_Block_n_cfgpreds(bl) - 1;
- cf_pred = (ir_node **) malloc (arity * sizeof (ir_node *));
- for (j = 0; j < i; j++)
- cf_pred[j] = get_Block_cfgpred(end_bl, j);
- for (j = j; j < i + get_Block_n_cfgpreds(bl); j++)
- cf_pred[j] = get_Block_cfgpred(bl, j-i);
- for (j = j; j < arity; j++)
- cf_pred[j] = get_Block_cfgpred(end_bl, j-get_Block_n_cfgpreds(bl) +1);
- set_irn_in(end_bl, arity, cf_pred);
- free(cf_pred);
+ /* repair */
+ if (i < get_Block_n_cfgpreds(end_bl)) {
+ bl = get_nodes_Block(cf_op);
+ arity = get_Block_n_cfgpreds(end_bl) + get_Block_n_cfgpreds(bl) - 1;
+ cf_pred = (ir_node **) malloc (arity * sizeof (ir_node *));
+ for (j = 0; j < i; j++)
+ cf_pred[j] = get_Block_cfgpred(end_bl, j);
+ for (j = j; j < i + get_Block_n_cfgpreds(bl); j++)
+ cf_pred[j] = get_Block_cfgpred(bl, j-i);
+ for (j = j; j < arity; j++)
+ cf_pred[j] = get_Block_cfgpred(end_bl, j-get_Block_n_cfgpreds(bl) +1);
+ set_irn_in(end_bl, arity, cf_pred);
+ free(cf_pred);
+ // Remove the exception pred from post-call Tuple.
+ set_Tuple_pred(call, pn_Call_X_except, new_Bad());
+ }
}
/* -- Turn cse back on. -- */
I didn't get a version with NEW_ARR_F to run. */
#define MAX_INLINE 1024
+/* given an Call node, returns the irg called. NULL if not
+ * known. */
+static ir_graph *get_call_called_irg(ir_node *call) {
+ ir_node *addr;
+ tarval *tv;
+ ir_graph *called_irg = NULL;
+
+ assert(get_irn_op(call) == op_Call);
+
+ addr = get_Call_ptr(call);
+ if (get_irn_op(addr) == op_Const) {
+ /* Check whether the constant is the pointer to a compiled entity. */
+ tv = get_Const_tarval(addr);
+ if (tarval_to_entity(tv))
+ called_irg = get_entity_irg(tarval_to_entity(tv));
+ }
+ return called_irg;
+}
+
static void collect_calls(ir_node *call, void *env) {
+
+ if (get_irn_op(call) != op_Call) return;
+
ir_node **calls = (ir_node **)env;
ir_node *addr;
tarval *tv;
ir_graph *called_irg;
- if (get_irn_op(call) != op_Call) return;
-
addr = get_Call_ptr(call);
if (get_irn_op(addr) == op_Const) {
/* Check whether the constant is the pointer to a compiled entity. */
current_ir_graph = rem;
}
+typedef struct {
+ int n_nodes; /* Nodes in graph except Id, Tuple, Proj, Start, End */
+ int n_nodes_orig; /* for statistics */
+ eset *call_nodes; /* All call nodes in this graph */
+ int n_call_nodes;
+ int n_call_nodes_orig; /* for statistics */
+ int n_callers; /* Number of known graphs that call this graphs. */
+ int n_callers_orig; /* for statistics */
+} inline_irg_env;
+
+static inline_irg_env *new_inline_irg_env(void) {
+ inline_irg_env *env = malloc(sizeof(inline_irg_env));
+ env->n_nodes = -2; /* uncount Start, End */
+ env->n_nodes_orig = -2; /* uncount Start, End */
+ env->call_nodes = eset_create();
+ env->n_call_nodes = 0;
+ env->n_call_nodes_orig = 0;
+ env->n_callers = 0;
+ env->n_callers_orig = 0;
+ return env;
+}
+
+static void free_inline_irg_env(inline_irg_env *env) {
+ eset_destroy(env->call_nodes);
+ free(env);
+}
+
+static void collect_calls2(ir_node *call, void *env) {
+ inline_irg_env *x = (inline_irg_env *)env;
+ ir_op *op = get_irn_op(call);
+
+ /* count nodes in irg */
+ if (op != op_Proj && op != op_Tuple && op != op_Sync) {
+ x->n_nodes++;
+ x->n_nodes_orig++;
+ }
+
+ if (op != op_Call) return;
+
+ /* collect all call nodes */
+ eset_insert(x->call_nodes, (void *)call);
+ x->n_call_nodes++;
+ x->n_call_nodes_orig++;
+
+ /* count all static callers */
+ ir_graph *callee = get_call_called_irg(call);
+ if (callee) {
+ ((inline_irg_env *)get_irg_link(callee))->n_callers++;
+ ((inline_irg_env *)get_irg_link(callee))->n_callers_orig++;
+ }
+}
+
+INLINE static int is_leave(ir_graph *irg) {
+ return (((inline_irg_env *)get_irg_link(irg))->n_call_nodes == 0);
+}
+
+INLINE static int is_smaller(ir_graph *callee, int size) {
+ return (((inline_irg_env *)get_irg_link(callee))->n_nodes < size);
+}
+
+
+/* Inlines small leave methods at call sites where the called address comes
+ from a Const node that references the entity representing the called
+ method.
+ The size argument is a rough measure for the code size of the method:
+ Methods where the obstack containing the firm graph is smaller than
+ size are inlined. */
+void inline_leave_functions(int maxsize, int leavesize, int size) {
+ inline_irg_env *env;
+ int i, n_irgs = get_irp_n_irgs();
+ ir_graph *rem = current_ir_graph;
+ int did_inline = 1;
+
+ if (!(get_optimize() && get_opt_inline())) return;
+
+ /* extend all irgs by a temporary datastructure for inlineing. */
+ for (i = 0; i < n_irgs; ++i)
+ set_irg_link(get_irp_irg(i), new_inline_irg_env());
+
+ /* Precompute information in temporary datastructure. */
+ for (i = 0; i < n_irgs; ++i) {
+ current_ir_graph = get_irp_irg(i);
+ assert(get_irg_phase_state(current_ir_graph) != phase_building);
+
+ irg_walk(get_irg_end(current_ir_graph), NULL, collect_calls2,
+ get_irg_link(current_ir_graph));
+ env = (inline_irg_env *)get_irg_link(current_ir_graph);
+ }
+
+ /* and now inline.
+ Inline leaves recursively -- we might construct new leaves. */
+ //int itercnt = 1;
+ while (did_inline) {
+ //printf("iteration %d\n", itercnt++);
+ did_inline = 0;
+ for (i = 0; i < n_irgs; ++i) {
+ ir_node *call;
+ eset *walkset;
+ int phiproj_computed = 0;
+
+ current_ir_graph = get_irp_irg(i);
+ env = (inline_irg_env *)get_irg_link(current_ir_graph);
+
+ /* we can not walk and change a set, nor remove from it.
+ So recompute.*/
+ walkset = env->call_nodes;
+ env->call_nodes = eset_create();
+ for (call = eset_first(walkset); call; call = eset_next(walkset)) {
+ ir_graph *callee = get_call_called_irg(call);
+ if (env->n_nodes > maxsize) break;
+ if (callee && is_leave(callee) && is_smaller(callee, leavesize)) {
+ if (!phiproj_computed) {
+ phiproj_computed = 1;
+ collect_phiprojs(current_ir_graph);
+ }
+ inline_irg_env *callee_env = (inline_irg_env *)get_irg_link(callee);
+ // printf(" %s: Inlineing %s.\n", get_entity_name(get_irg_entity(current_ir_graph)),
+ // get_entity_name(get_irg_entity(callee)));
+ inline_method(call, callee);
+ did_inline = 1;
+ env->n_call_nodes--;
+ eset_insert_all(env->call_nodes, callee_env->call_nodes);
+ env->n_call_nodes += callee_env->n_call_nodes;
+ env->n_nodes += callee_env->n_nodes;
+ callee_env->n_callers--;
+ } else {
+ eset_insert(env->call_nodes, call);
+ }
+ }
+ eset_destroy(walkset);
+ }
+ }
+
+ //printf("Non leaves\n");
+ /* inline other small functions. */
+ for (i = 0; i < n_irgs; ++i) {
+ ir_node *call;
+ eset *walkset;
+ int phiproj_computed = 0;
+
+ current_ir_graph = get_irp_irg(i);
+ env = (inline_irg_env *)get_irg_link(current_ir_graph);
+
+ /* we can not walk and change a set, nor remove from it.
+ So recompute.*/
+ walkset = env->call_nodes;
+ env->call_nodes = eset_create();
+ for (call = eset_first(walkset); call; call = eset_next(walkset)) {
+ ir_graph *callee = get_call_called_irg(call);
+ if (env->n_nodes > maxsize) break;
+ if (callee && is_smaller(callee, size)) {
+ if (!phiproj_computed) {
+ phiproj_computed = 1;
+ collect_phiprojs(current_ir_graph);
+ }
+ inline_irg_env *callee_env = (inline_irg_env *)get_irg_link(callee);
+ //printf(" %s: Inlineing %s.\n", get_entity_name(get_irg_entity(current_ir_graph)),
+ // get_entity_name(get_irg_entity(callee)));
+ inline_method(call, callee);
+ did_inline = 1;
+ env->n_call_nodes--;
+ eset_insert_all(env->call_nodes, callee_env->call_nodes);
+ env->n_call_nodes += callee_env->n_call_nodes;
+ env->n_nodes += callee_env->n_nodes;
+ callee_env->n_callers--;
+ } else {
+ eset_insert(env->call_nodes, call);
+ }
+ }
+ eset_destroy(walkset);
+ }
+
+ for (i = 0; i < n_irgs; ++i) {
+ current_ir_graph = get_irp_irg(i);
+#if 0
+ env = (inline_irg_env *)get_irg_link(current_ir_graph);
+ if ((env->n_call_nodes_orig != env->n_call_nodes) ||
+ (env->n_callers_orig != env->n_callers))
+ printf("Nodes:%3d ->%3d, calls:%3d ->%3d, callers:%3d ->%3d, -- %s\n",
+ env->n_nodes_orig, env->n_nodes, env->n_call_nodes_orig, env->n_call_nodes,
+ env->n_callers_orig, env->n_callers,
+ get_entity_name(get_irg_entity(current_ir_graph)));
+#endif
+ free_inline_irg_env((inline_irg_env *)get_irg_link(current_ir_graph));
+ }
+
+ current_ir_graph = rem;
+}
/********************************************************************/
/* Code Placement. Pinns all floating nodes to a block where they */
static void
place_floats_early (ir_node *n)
{
- int i, start;
+ int i, start, irn_arity;
/* we must not run into an infinite loop */
assert (irn_not_visited(n));
if ((get_irn_op(n) == op_Const) ||
(get_irn_op(n) == op_SymConst) ||
- (is_Bad(n))) {
+ (is_Bad(n)) ||
+ (get_irn_op(n) == op_Unknown)) {
/* These nodes will not be placed by the loop below. */
b = get_irg_start_block(current_ir_graph);
depth = 1;
}
/* find the block for this node. */
- for (i = 0; i < get_irn_arity(n); i++) {
+ irn_arity = get_irn_arity(n);
+ for (i = 0; i < irn_arity; i++) {
ir_node *dep = get_irn_n(n, i);
ir_node *dep_block;
if ((irn_not_visited(dep)) &&
/* Add predecessors of non floating nodes on worklist. */
start = (get_irn_op(n) == op_Block) ? 0 : -1;
- for (i = start; i < get_irn_arity(n); i++) {
+ irn_arity = get_irn_arity(n);
+ for (i = start; i < irn_arity; i++) {
ir_node *pred = get_irn_n(n, i);
if (irn_not_visited(pred)) {
pdeq_putr (worklist, pred);
if (get_irn_op(consumer) == op_Phi) {
/* our comsumer is a Phi-node, the effective use is in all those
blocks through which the Phi-node reaches producer */
- int i;
+ int i, irn_arity;
ir_node *phi_block = get_nodes_Block(consumer);
- for (i = 0; i < get_irn_arity(consumer); i++) {
+ irn_arity = get_irn_arity(consumer);
+ for (i = 0; i < irn_arity; i++) {
if (get_irn_n(consumer, i) == producer) {
block = get_nodes_Block(get_Block_cfgpred(phi_block, i));
}
while (irn_not_visited(b) && (!is_Bad(new)) && (new != b)) {
/* We would have to run gigo if new is bad, so we
promote it directly below. */
- assert(((b == new) || get_opt_control_flow_straightening() || get_opt_control_flow_weak_simplification()) &&
+ assert(((b == new) ||
+ get_opt_control_flow_straightening() ||
+ get_opt_control_flow_weak_simplification()) &&
("strange flag setting"));
exchange (b, new);
b = new;
(get_irn_arity(n) > 1)) {
arity = get_irn_arity(n);
+ if (n == get_irg_end_block(current_ir_graph))
+ return; // No use to add a block here.
+
for (i=0; i<arity; i++) {
pre = get_irn_n(n, i);
/* Predecessor has multiple sucessors. Insert new flow edge */
- if ((NULL != pre) && (op_Proj == get_irn_op(pre))) {
+ if ((NULL != pre) &&
+ (op_Proj == get_irn_op(pre)) &&
+ op_Raise != get_irn_op(skip_Proj(pre))) {
- /* set predeseccor array for new block */
+ /* set predecessor array for new block */
in = NEW_ARR_D (ir_node *, current_ir_graph->obst, 1);
/* set predecessor of new block */
in[0] = pre;