#include "debug.h"
#include "array_t.h"
#include "error.h"
+#include "irnodeset.h"
#include "tv_t.h"
/* define this to check the consistency of partitions */
#define CHECK_PARTITIONS
-/* allow optimization of non-strict programs */
-#define WITH_UNKNOWN
-
typedef struct node_t node_t;
typedef struct partition_t partition_t;
typedef struct opcode_key_t opcode_key_t;
union {
long proj; /**< For Proj nodes, its proj number */
ir_entity *ent; /**< For Sel Nodes, its entity */
+ int intVal; /**< For Conv/Div Nodes: strict/remainderless */
} u;
};
partition_t *initial; /**< The initial partition. */
set *opcode2id_map; /**< The opcodeMode->id map. */
pmap *type2id_map; /**< The type->id map. */
+ ir_node **kept_memory; /**< Array of memory nodes that must be kept. */
int end_idx; /**< -1 for local and 0 for global congruences. */
int lambda_input; /**< Captured argument for lambda_partition(). */
unsigned modified:1; /**< Set, if the graph was modified. */
- unsigned commutative:1; /**< Set, if commutation nodes should be handled specially. */
unsigned unopt_cf:1; /**< If set, control flow is not optimized due to Unknown. */
+ /* options driving the optimization */
+ unsigned commutative:1; /**< Set, if commutation nodes should be handled specially. */
+ unsigned opt_unknown:1; /**< Set, if non-strict programs should be optimized. */
#ifdef DEBUG_libfirm
partition_t *dbg_list; /**< List of all partitions. */
#endif
/** Type of the what function. */
typedef void *(*what_func)(const node_t *node, environment_t *env);
-#define get_irn_node(follower) ((node_t *)get_irn_link(follower))
-#define set_irn_node(follower, node) set_irn_link(follower, node)
+#define get_irn_node(irn) ((node_t *)get_irn_link(irn))
+#define set_irn_node(irn, node) set_irn_link(irn, node)
/* we do NOT use tarval_unreachable here, instead we use Top for this purpose */
#undef tarval_unreachable
/** Next partition number. */
DEBUG_ONLY(static unsigned part_nr = 0);
-/** The tarval returned by Unknown nodes. */
-#ifdef WITH_UNKNOWN
-#define tarval_UNKNOWN tarval_top
-#else
-#define tarval_UNKNOWN tarval_bad
-#endif
+/** The tarval returned by Unknown nodes: set to either tarval_bad OR tarval_top. */
+static tarval *tarval_UNKNOWN;
/* forward */
static node_t *identity(node_t *node);
case iro_Sel:
key.u.ent = get_Sel_entity(irn);
break;
+ case iro_Conv:
+ key.u.intVal = get_Conv_strict(irn);
+ break;
+ case iro_Div:
+ key.u.intVal = is_Div_remainderless(irn);
+ break;
default:
break;
}
case iro_Sel:
assert(key.u.ent == get_Sel_entity(irn));
break;
+ case iro_Conv:
+ assert(key.u.intVal == get_Conv_strict(irn));
+ break;
+ case iro_Div:
+ assert(key.u.intVal == is_Div_remainderless(irn));
+ break;
default:
break;
}
(void) size;
return o1->code != o2->code || o1->mode != o2->mode ||
o1->arity != o2->arity ||
- o1->u.proj != o2->u.proj || o1->u.ent != o2->u.ent;
+ o1->u.proj != o2->u.proj || o1->u.ent != o2->u.ent ||
+ o1->u.intVal != o2->u.intVal;
} /* cmp_opcode */
/**
static node_t *create_partition_node(ir_node *irn, partition_t *part, environment_t *env) {
/* create a partition node and place it in the partition */
node_t *node = obstack_alloc(&env->obst, sizeof(*node));
- tarval *init = tarval_top;
-
- if (is_Proj(irn) && get_irn_mode(irn) == mode_M) {
- /* mode_M Proj's must never be removed */
- init = tarval_bottom;
- }
INIT_LIST_HEAD(&node->node_list);
INIT_LIST_HEAD(&node->cprop_list);
node->part = part;
node->next = NULL;
node->race_next = NULL;
- node->type.tv = init;
+ node->type.tv = tarval_top;
node->max_user_input = 0;
node->next_edge = 0;
node->n_followers = 0;
/**
* Collect commutative nodes to the touched list.
*
+ * @param X the partition of the list
* @param list the list which contains the nodes that must be evaluated
* @param env the environment
*/
-static void collect_commutative_touched(list_head *list, environment_t *env) {
+static void collect_commutative_touched(partition_t *X, list_head *list, environment_t *env) {
+ int first = 1;
+ int both_input = 0;
node_t *x, *y;
list_for_each_entry(node_t, x, list, node_list) {
/* Partitions of constants should not be split simply because their Nodes have unequal
functions or incongruent inputs. */
if (type_is_neither_top_nor_const(y->type)) {
- add_to_touched(y, env);
+ int other_idx = edge->pos ^ 1;
+ node_t *other = get_irn_node(get_irn_n(succ, other_idx));
+ int equal = X == other->part;
+
+ /*
+ * Note: op(a, a) is NOT congruent to op(a, b).
+ * So, either all touch nodes must have both inputs congruent,
+ * or not. We decide this by the first occurred node.
+ */
+ if (first) {
+ first = 0;
+ both_input = equal;
+ }
+ if (both_input == equal)
+ add_to_touched(y, env);
}
}
}
/* empty the touched set: already done, just clear the list */
env->touched = NULL;
- collect_commutative_touched(&X->Leader, env);
- collect_commutative_touched(&X->Follower, env);
+ collect_commutative_touched(X, &X->Leader, env);
+ collect_commutative_touched(X, &X->Follower, env);
for (Z = env->touched; Z != NULL; Z = N) {
node_t *e;
case iro_Sel:
key.u.ent = get_Sel_entity(irn);
break;
+ case iro_Conv:
+ key.u.intVal = get_Conv_strict(irn);
+ break;
+ case iro_Div:
+ key.u.intVal = is_Div_remainderless(irn);
+ break;
default:
break;
}
static void default_compute(node_t *node) {
int i;
ir_node *irn = node->node;
- node_t *block = get_irn_node(get_nodes_block(irn));
-
- if (block->type.tv == tarval_unreachable) {
- node->type.tv = tarval_top;
- return;
- }
/* if any of the data inputs have type top, the result is type top */
for (i = get_irn_arity(irn) - 1; i >= 0; --i) {
node->type.tv = tarval_reachable;
} else {
assert(selector->type.tv == tarval_top);
-#ifdef WITH_UNKNOWN
- /* any condition based on Top is "!=" */
- node->type.tv = tarval_unreachable;
-#else
- node->type.tv = tarval_unreachable;
-#endif
+ if (tarval_UNKNOWN == tarval_top) {
+ /* any condition based on Top is "!=" */
+ node->type.tv = tarval_unreachable;
+ } else {
+ node->type.tv = tarval_unreachable;
+ }
}
} else {
assert(pnc == pn_Cond_false);
node->type.tv = tarval_reachable;
} else {
assert(selector->type.tv == tarval_top);
-#ifdef WITH_UNKNOWN
- /* any condition based on Top is "!=" */
- node->type.tv = tarval_reachable;
-#else
- node->type.tv = tarval_unreachable;
-#endif
+ if (tarval_UNKNOWN == tarval_top) {
+ /* any condition based on Top is "!=" */
+ node->type.tv = tarval_reachable;
+ } else {
+ node->type.tv = tarval_unreachable;
+ }
}
}
} else {
if (selector->type.tv == tarval_bottom) {
node->type.tv = tarval_reachable;
} else if (selector->type.tv == tarval_top) {
-#ifdef WITH_UNKNOWN
- if (pnc == get_Cond_defaultProj(cond)) {
+ if (tarval_UNKNOWN == tarval_top &&
+ pnc == get_Cond_defaultProj(cond)) {
/* a switch based of Top is always "default" */
node->type.tv = tarval_reachable;
- } else
-#endif
+ } else {
node->type.tv = tarval_unreachable;
+ }
} else {
long value = get_tarval_long(selector->type.tv);
if (pnc == get_Cond_defaultProj(cond)) {
ir_node *irn = node->node;
compute_func func;
+#ifndef VERIFY_MONOTONE
/*
* Once a node reaches bottom, the type cannot fall further
* in the lattice and we can stop computation.
- * This reduces further checking for ProjM not allowed to raise
- * its type below...
+ * Do not take this exit if the monotony verifier is
+ * enabled to catch errors.
*/
if (node->type.tv == tarval_bottom)
return;
+#endif
if (is_no_Block(irn)) {
/* for pinned nodes, check its control input */
if (is_tarval(node->type.tv) && tarval_is_constant(node->type.tv)) {
/* this Phi is replaced by a constant */
tarval *tv = node->type.tv;
- ir_node *c = new_r_Const(current_ir_graph, block, get_tarval_mode(tv), tv);
+ ir_node *c = new_Const(tv);
set_irn_node(c, node);
node->node = c;
static void exchange_leader(ir_node *irn, ir_node *leader) {
ir_mode *mode = get_irn_mode(irn);
if (mode != get_irn_mode(leader)) {
- /* The conv is a no-op, so we are fre to place in
+ /* The conv is a no-op, so we are free to place it
* either in the block of the leader OR in irn's block.
* Probably placing it into leaders block might reduce
* the number of Conv due to CSE. */
leader = new_rd_Conv(dbg, current_ir_graph, block, leader, mode);
}
exchange(irn, leader);
-}
+} /* exchange_leader */
+
+/**
+ * Check, if all users of a mode_M node are dead. Use
+ * the Def-Use edges for this purpose, as they still
+ * reflect the situation.
+ */
+static int all_users_are_dead(const ir_node *irn) {
+ int i, n = get_irn_n_outs(irn);
+
+ for (i = 1; i <= n; ++i) {
+ const ir_node *succ = irn->out[i].use;
+ const node_t *block = get_irn_node(get_nodes_block(succ));
+ const node_t *node;
+
+ if (block->type.tv == tarval_unreachable) {
+ /* block is unreachable */
+ continue;
+ }
+ node = get_irn_node(succ);
+ if (node->type.tv != tarval_top) {
+ /* found a reachable user */
+ return 0;
+ }
+ }
+ /* all users are unreachable */
+ return 1;
+} /* all_user_are_dead */
+
+/**
+ * Walker: Find reachable mode_M nodes that have only
+ * unreachable users. These nodes must be kept later.
+ */
+static void find_kept_memory(ir_node *irn, void *ctx) {
+ environment_t *env = ctx;
+ node_t *node, *block;
+
+ if (get_irn_mode(irn) != mode_M)
+ return;
+
+ block = get_irn_node(get_nodes_block(irn));
+ if (block->type.tv == tarval_unreachable)
+ return;
+
+ node = get_irn_node(irn);
+ if (node->type.tv == tarval_top)
+ return;
+
+ /* ok, we found a live memory node. */
+ if (all_users_are_dead(irn)) {
+ DB((dbg, LEVEL_1, "%+F must be kept\n", irn));
+ ARR_APP1(ir_node *, env->kept_memory, irn);
+ }
+} /* find_kept_memory */
/**
* Post-Walker, apply the analysis results;
if (is_Block(irn) || is_End(irn) || is_Bad(irn)) {
/* blocks already handled, do not touch the End node */
} else {
- node_t *block = get_irn_node(get_nodes_block(irn));
- ir_mode *mode = get_irn_mode(irn);
+ node_t *block = get_irn_node(get_nodes_block(irn));
if (block->type.tv == tarval_unreachable) {
ir_node *bad = get_irg_bad(current_ir_graph);
DB((dbg, LEVEL_1, "%+F is unreachable\n", irn));
exchange(irn, bad);
env->modified = 1;
- } else if (mode == mode_M && is_Proj(irn)) {
- ir_node *pred = get_Proj_pred(irn);
- node_t *pnode = get_irn_node(pred);
-
- if (pnode->type.tv == tarval_top) {
- /* skip the predecessor */
- ir_node *mem = get_memop_mem(pred);
- node->node = mem;
- DB((dbg, LEVEL_1, "%+F computes Top, replaced by %+F\n", irn, mem));
- exchange(irn, mem);
- env->modified = 1;
- }
} else if (node->type.tv == tarval_top) {
- if (mode == mode_T) {
+ ir_mode *mode = get_irn_mode(irn);
+
+ if (mode == mode_M) {
+ /* never kill a mode_M node */
+ if (is_Proj(irn)) {
+ ir_node *pred = get_Proj_pred(irn);
+ node_t *pnode = get_irn_node(pred);
+
+ if (pnode->type.tv == tarval_top) {
+ /* skip the predecessor */
+ ir_node *mem = get_memop_mem(pred);
+ node->node = mem;
+ DB((dbg, LEVEL_1, "%+F computes Top, replaced by %+F\n", irn, mem));
+ exchange(irn, mem);
+ env->modified = 1;
+ }
+ }
+ /* leave other nodes, especially PhiM */
+ } else if (mode == mode_T) {
/* Do not kill mode_T nodes, kill their Projs */
} else if (! is_Unknown(irn)) {
/* don't kick away Unknown's, they might be still needed */
*/
if (! is_Const(irn) && get_irn_mode(irn) != mode_T) {
/* can be replaced by a constant */
- ir_node *c = new_r_Const(current_ir_graph, block->node, get_tarval_mode(tv), tv);
+ ir_node *c = new_Const(tv);
set_irn_node(c, node);
node->node = c;
DB((dbg, LEVEL_1, "%+F is replaced by %+F\n", irn, c));
SET(Max);
if (op_Min != NULL)
SET(Min);
-
} /* set_compute_functions */
+/**
+ * Add memory keeps.
+ */
+static void add_memory_keeps(ir_node **kept_memory, int len) {
+ ir_node *end = get_irg_end(current_ir_graph);
+ int i;
+ ir_nodeset_t set;
+
+ ir_nodeset_init(&set);
+
+ /* check, if those nodes are already kept */
+ for (i = get_End_n_keepalives(end) - 1; i >= 0; --i)
+ ir_nodeset_insert(&set, get_End_keepalive(end, i));
+
+ for (i = len - 1; i >= 0; --i) {
+ ir_node *ka = kept_memory[i];
+
+ if (! ir_nodeset_contains(&set, ka)) {
+ add_End_keepalive(end, ka);
+ }
+ }
+ ir_nodeset_destroy(&set);
+} /* add_memory_keeps */
+
void combo(ir_graph *irg) {
environment_t env;
ir_node *initial_bl;
node_t *start;
ir_graph *rem = current_ir_graph;
+ int len;
current_ir_graph = irg;
#endif
env.opcode2id_map = new_set(cmp_opcode, iro_Last * 4);
env.type2id_map = pmap_create();
+ env.kept_memory = NEW_ARR_F(ir_node *, 0);
env.end_idx = get_opt_global_cse() ? 0 : -1;
env.lambda_input = 0;
- env.commutative = 1;
env.modified = 0;
env.unopt_cf = 0;
+ /* options driving the optimization */
+ env.commutative = 1;
+ env.opt_unknown = 1;
assure_irg_outs(irg);
assure_cf_loop(irg);
set_compute_functions();
DEBUG_ONLY(part_nr = 0);
- ir_reserve_resources(irg, IR_RESOURCE_IRN_LINK);
+ ir_reserve_resources(irg, IR_RESOURCE_IRN_LINK | IR_RESOURCE_PHI_LIST);
+
+ if (env.opt_unknown)
+ tarval_UNKNOWN = tarval_top;
+ else
+ tarval_UNKNOWN = tarval_bad;
/* create the initial partition and place it on the work list */
env.initial = new_partition(&env);
#endif
/* apply the result */
+
+ /* check, which nodes must be kept */
+ irg_walk_graph(irg, NULL, find_kept_memory, &env);
+
+ /* kill unreachable control flow */
irg_block_walk_graph(irg, NULL, apply_cf, &env);
/* Kill keep-alives of dead blocks: this speeds up apply_result()
* and fixes assertion because dead cf to dead blocks is NOT removed by
apply_end(get_irg_end(irg), &env);
irg_walk_graph(irg, NULL, apply_result, &env);
+ len = ARR_LEN(env.kept_memory);
+ if (len > 0)
+ add_memory_keeps(env.kept_memory, len);
+
if (env.unopt_cf) {
DB((dbg, LEVEL_1, "Unoptimized Control Flow left"));
}
set_irg_loopinfo_inconsistent(irg);
}
- ir_free_resources(irg, IR_RESOURCE_IRN_LINK);
+ ir_free_resources(irg, IR_RESOURCE_IRN_LINK | IR_RESOURCE_PHI_LIST);
/* remove the partition hook */
DEBUG_ONLY(set_dump_node_vcgattr_hook(NULL));
+ DEL_ARR_F(env.kept_memory);
pmap_destroy(env.type2id_map);
del_set(env.opcode2id_map);
obstack_free(&env.obst, NULL);