Commit a95c36ee authored by Matthias Braun's avatar Matthias Braun
Browse files

redo endless loop/keep handling

For details see endless loops section on the homepage.
parent 02ae8c66
......@@ -104,9 +104,12 @@ static void try_remove_unnecessary_phi(ir_node *phi)
/* See if all inputs are either pointing to a single value or
* are self references. */
bool have_self_loop = false;
foreach_irn_in(phi, i, in) {
if (in == phi)
if (in == phi) {
have_self_loop = true;
continue;
}
if (in == phi_value)
continue;
/** found a different value from the one we already found, can't remove
......@@ -118,6 +121,11 @@ static void try_remove_unnecessary_phi(ir_node *phi)
if (phi_value == NULL)
return;
/* Do not remove PhiM with self-loops as potentially endless loops are
* observable, see comment in equivalent_node_Phi() to learn more. */
if (have_self_loop && get_irn_mode(phi) == mode_M)
return;
/* if we're here then all phi inputs have been either phi_value
* or self-references, we can replace the phi by phi_value. */
exchange(phi, phi_value);
......@@ -164,6 +172,12 @@ static ir_node *set_phi_arguments(ir_node *phi, int pos)
verify_new_node(irg, phi);
try_remove_unnecessary_phi(phi);
/* To solve the problem of (potentially) endless loops being observable
* behaviour we add a keep-alive edge too all PhiM nodes. */
if (mode == mode_M && !is_Id(phi))
keep_alive(phi);
return phi;
}
......
......@@ -602,6 +602,23 @@ void remove_End_keepalive(ir_node *end, const ir_node *irn)
remove_irn_n(end, idx);
}
void remove_keep_alive(const ir_node *irn)
{
ir_graph *irg = get_irn_irg(irn);
ir_node *end = get_irg_end(irg);
for (int i = get_End_n_keepalives(end);;) {
if (i-- == 0)
return;
ir_node *old_ka = end->in[1 + END_KEEPALIVE_OFFSET + i];
/* find irn */
if (old_ka == irn)
set_irn_n(end, END_KEEPALIVE_OFFSET+i, new_r_Bad(irg, get_irn_mode(irn)));
}
}
void remove_End_Bads_and_doublets(ir_node *end)
{
pset_new_t keeps;
......@@ -1128,20 +1145,3 @@ ir_switch_table *ir_switch_table_duplicate(ir_graph *irg,
}
return res;
}
bool only_used_by_keepalive(const ir_node *node)
{
bool kept = false;
foreach_out_edge(node, edge) {
ir_node *succ = get_edge_src_irn(edge);
if (is_End(succ) || (is_Proj(succ) && only_used_by_keepalive(succ))) {
kept = true;
continue;
}
/* found a real user */
return false;
}
return kept;
}
......@@ -538,12 +538,10 @@ static inline const ir_switch_table_entry *ir_switch_table_get_entry_const(
void ir_register_getter_ops(void);
/**
* because firm keepalive edges are a broken concept, we have to make sure that
* nodes which are only held by a keepalive edges are never moved again.
* This function returns true in this case.
*/
bool only_used_by_keepalive(const ir_node *node);
/** remove keep alive edge to node by rerouting the edge to a Bad node.
* (rerouting is preferable to removing when we are in a walker which also
* accesses the End node) */
void remove_keep_alive(const ir_node *kept_node);
/**
* Create a node similar to @p old. Except for @p block and @p in all aspects
......
......@@ -1382,26 +1382,34 @@ static ir_node *equivalent_node_Bitcast(ir_node *n)
return n;
}
static bool is_kept_alive(const ir_node *node)
{
const ir_graph *const irg = get_irn_irg(node);
const ir_node *const end = get_irg_end(irg);
foreach_irn_in(end, i, kept) {
if (node == kept)
return true;
}
return false;
}
/**
* - fold Phi-nodes, iff they have only one predecessor except
* themselves.
*/
static ir_node *equivalent_node_Phi(ir_node *n)
{
ir_node *oldn = n;
ir_node *first_val = NULL; /* to shutup gcc */
if (!get_opt_optimize() &&
!irg_is_constrained(get_irn_irg(n), IR_GRAPH_CONSTRAINT_CONSTRUCTION))
return n;
int n_preds = get_Phi_n_preds(n);
/* Phi of dead Region without predecessors. */
int n_preds = get_Phi_n_preds(n);
if (n_preds == 0)
return n;
/* Find first non-self-referencing input */
ir_node *first_val = NULL;
int i;
for (i = 0; i < n_preds; ++i) {
first_val = get_Phi_pred(n, i);
......@@ -1413,20 +1421,40 @@ static ir_node *equivalent_node_Phi(ir_node *n)
}
/* search for rest of inputs, determine if any of these
are non-self-referencing */
while (++i < n_preds) {
* are non-self-referencing */
bool had_self_loop = false;
for (++i; i < n_preds; ++i) {
const ir_node *scnd_val = get_Phi_pred(n, i);
if (scnd_val != n && scnd_val != first_val) {
break;
if (scnd_val == n) {
had_self_loop = true;
continue;
}
/* more than 1 unique value found? abort */
if (scnd_val != first_val)
return n;
}
if (i >= n_preds && !is_Dummy(first_val)) {
/* Fold, if no multiple distinct non-self-referencing inputs */
n = first_val;
DBG_OPT_PHI(oldn, n);
}
/* if we are here then all inputs are either self-loops or first_val */
if (is_Dummy(first_val))
return n;
/* Subtle special case: (Potentially) endless loops are observable behaviour
* and must be part of the memory chain. If there are no other memory
* operations in a loop we still are not allowed to remove the PhiM unless
* we can prove that the loop terminates. */
if (get_irn_mode(n) == mode_M) {
/* We currently assume that PhiMs that have a keep-alive edge are in a
* potentially endless loops. PhiM without a keep alive edge is a sign
* that we are sure that the loop terminates. */
if (had_self_loop && is_kept_alive(n)) {
return n;
}
/* The PhiM will be removed, we can remove keep-alive edges to it as
* well. */
remove_keep_alive(n);
}
DBG_OPT_PHI(n, first_val);
return first_val;
}
/**
......@@ -3775,8 +3803,6 @@ static ir_node *transform_node_Cond(ir_node *n)
};
turn_into_tuple(n, ARRAY_SIZE(in), in);
/* Since we may produce an endless loop, we have to keep the block. */
keep_alive(blk);
clear_irg_properties(irg, IR_GRAPH_PROPERTY_NO_UNREACHABLE_CODE);
}
return n;
......@@ -7409,10 +7435,6 @@ int identities_cmp(const void *elt, const void *key)
if (!block_dominates(block_a, block_b)
&& !block_dominates(block_b, block_a))
return 1;
/* respect the workaround rule: do not move nodes which are only
* held by keepalive edges */
if (only_used_by_keepalive(a) || only_used_by_keepalive(b))
return 1;
}
}
......
......@@ -417,6 +417,30 @@ static int verify_node_Start(const ir_node *n)
return check_mode(n, mode_T);
}
static int verify_node_End(const ir_node *n)
{
bool fine = check_mode(n, mode_X);
/* check that only blocks, PhiM and (noreturn) Call nodes are connected
* by keep-alive edges */
ir_graph *irg = get_irn_irg(n);
if (!irg_is_constrained(irg, IR_GRAPH_CONSTRAINT_BACKEND)) {
foreach_irn_in(n, i, kept) {
/* endless loop handling may keep PhiM and Block nodes */
if (is_Block(kept) || (is_Phi(kept) && get_irn_mode(kept) == mode_M))
continue;
/* noreturn calls are currently kept with keep-alive edges */
if (is_Call(kept))
continue;
if (is_Bad(kept))
continue;
warn(n, "keep-alive edge only allowed on Block, PhiM and Call node, found %+F",
kept);
fine = false;
}
}
return fine;
}
static int verify_node_Jmp(const ir_node *n)
{
return check_mode(n, mode_X);
......@@ -1297,6 +1321,7 @@ void ir_register_verify_node_ops(void)
register_verify_node_func(op_CopyB, verify_node_CopyB);
register_verify_node_func(op_Deleted, verify_node_Deleted);
register_verify_node_func(op_Div, verify_node_Div);
register_verify_node_func(op_End, verify_node_End);
register_verify_node_func(op_Eor, verify_node_Eor);
register_verify_node_func(op_Free, verify_node_Free);
register_verify_node_func(op_IJmp, verify_node_IJmp);
......
......@@ -69,6 +69,8 @@ static void block_remove_bads(ir_node *block)
/* shortcut if only 1 phi input is left */
if (new_max == 1) {
if (get_irn_mode(phi) == mode_M)
remove_keep_alive(phi);
ir_node *new_node = new_in[0];
/* can happen inside unreachable endless loops */
if (new_node == phi)
......@@ -114,6 +116,8 @@ void remove_bads(ir_graph *irg)
}
DEL_ARR_F(blocks_to_process);
remove_End_Bads_and_doublets(get_irg_end(irg));
if (n_to_process > 0) {
confirm_irg_properties(irg,
IR_GRAPH_PROPERTY_NO_UNREACHABLE_CODE
......
......@@ -58,7 +58,7 @@ static void place_floats_early(ir_node *n, waitq *worklist)
* This works because in firm each cycle contains a Phi or Block node
* (which are pinned)
*/
if (get_irn_pinned(n) != op_pin_state_floats || only_used_by_keepalive(n)) {
if (get_irn_pinned(n) != op_pin_state_floats) {
/* we cannot move pinned nodes */
foreach_irn_in(n, i, pred) {
pdeq_putr(worklist, pred);
......@@ -255,12 +255,8 @@ static ir_node *get_deepest_common_dom_ancestor(ir_node *node, ir_node *dca)
dca = consumer_dom_dca(dca, succ, node);
}
}
/* respect the keepalive rule: if our only user is a keepalive, then we must
* not move the node any further */
if (dca == NULL) {
assert(only_used_by_keepalive(node));
if (dca == NULL)
return get_nodes_block(node);
}
foreach_out_edge_kind(node, edge, EDGE_KIND_DEP) {
ir_node *succ = get_edge_src_irn(edge);
......
......@@ -111,6 +111,7 @@ struct node_t {
bool on_cprop:1; /**< Set, if this node is on the partition.cprop list. */
bool on_fallen:1; /**< Set, if this node is on the fallen list. */
bool is_follower:1; /**< Set, if this node is a follower. */
bool is_kept_alive:1;/**< node has a keep-alive edge. */
unsigned flagged:2; /**< 2 Bits, set if this node was visited by race 1 or 2. */
};
......@@ -2341,6 +2342,13 @@ static node_t *identity_Phi(node_t *node)
ir_node *block = get_nodes_block(phi);
node_t *n_part = NULL;
/* special rule: kept PhiM nodes have to create their own partition
* (as they represent the observable behaviour of a loop running endless) */
if (node->is_kept_alive) {
assert(get_irn_mode(phi) == mode_M);
return node;
}
for (int i = get_Phi_n_preds(phi); i-- > 0; ) {
node_t *pred_X = get_irn_node(get_Block_cfgpred(block, i));
if (pred_X->type.tv == tarval_bottom)
......@@ -2789,15 +2797,6 @@ static void apply_cf(ir_node *block, void *ctx)
if (pred_bl->flagged == 0) {
pred_bl->flagged = 3;
if (is_reachable(pred_bl)) {
/*
* We will remove an edge from block to its pred.
* This might leave the pred block as an endless loop
*/
if (!is_backedge(block, i))
keep_alive(pred_bl->node);
}
}
}
}
......@@ -2844,15 +2843,6 @@ static void apply_cf(ir_node *block, void *ctx)
if (!is_Bad(pred_bl->node) && pred_bl->flagged == 0) {
pred_bl->flagged = 3;
if (is_reachable(pred_bl)) {
/*
* We will remove an edge from block to its pred.
* This might leave the pred block as an endless loop
*/
if (!is_backedge(block, i))
keep_alive(pred_bl->node);
}
}
}
}
......@@ -2862,6 +2852,7 @@ static void apply_cf(ir_node *block, void *ctx)
return;
/* fix Phi's */
ir_graph *irg = get_Block_irg(block);
ir_node **ins = ALLOCAN(ir_node*, n);
for (ir_node *next, *phi = get_Block_phis(block); phi != NULL; phi = next) {
node_t *node = get_irn_node(phi);
......@@ -2870,7 +2861,6 @@ static void apply_cf(ir_node *block, void *ctx)
if (is_tarval(node->type.tv) && tarval_is_constant(node->type.tv)) {
/* this Phi is replaced by a constant */
ir_tarval *tv = node->type.tv;
ir_graph *irg = get_Block_irg(block);
ir_node *c = new_r_Const(irg, tv);
set_irn_node(c, node);
......@@ -2893,6 +2883,10 @@ static void apply_cf(ir_node *block, void *ctx)
ir_node *s = ins[0];
node_t *phi_node = get_irn_node(phi);
if (get_irn_mode(phi) == mode_M) {
remove_keep_alive(phi);
}
node->node = s;
DB((dbg, LEVEL_1, "%+F is replaced by %+F because of cf change\n", phi, s));
DBG_OPT_COMBO(phi, s, FS_OPT_COMBO_FOLLOWER);
......@@ -3293,6 +3287,13 @@ void combo(ir_graph *irg)
add_to_worklist(env.initial, &env);
irg_walk_graph(irg, create_initial_partitions, init_block_phis, &env);
/* mark all kept PhiM nodes, as we must not remove them */
ir_node *end = get_irg_end(irg);
for (int i = 0, n_keeps = get_End_n_keepalives(end); i < n_keeps; ++i) {
ir_node *kept = get_End_keepalive(end, i);
get_irn_node(kept)->is_kept_alive = true;
}
/* set the hook: from now, every node has a partition and a type */
DEBUG_ONLY(set_dump_node_vcgattr_hook(dump_partition_hook);)
......@@ -3324,7 +3325,7 @@ void combo(ir_graph *irg)
/* 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_cf(). */
apply_end(get_irg_end(irg), &env);
apply_end(end, &env);
/* need a freshly computed dominance tree (after killing unreachable code
* it is not valid anymore) */
......
......@@ -193,6 +193,8 @@ static void split_block(ir_node *block, int i, int j)
for (k = 0; k != j; ++k) pred_ins[k] = get_irn_n(phi, k);
for (; k != new_pred_arity; ++k) pred_ins[k] = get_irn_n(phi, k + 1);
if (k == 1) {
if (get_irn_mode(phi) == mode_M)
remove_keep_alive(phi);
exchange(phi, pred_ins[0]);
} else {
set_irn_in(phi, k, pred_ins);
......@@ -221,6 +223,8 @@ static void prepare_path(ir_node *block, int i, const ir_node *dependency)
next = get_Phi_next(phi);
ir_node *operand = get_irn_n(phi, 0);
if (get_irn_mode(phi) == mode_M)
remove_keep_alive(phi);
exchange(phi, operand);
}
......@@ -331,6 +335,8 @@ restart:;
if (val_i == val_j) {
mux = val_i;
DB((dbg, LEVEL_2, "Generating no Mux, because both values are equal\n"));
if (get_irn_mode(phi) == mode_M)
remove_keep_alive(phi);
} else {
ir_node *t, *f;
......
......@@ -311,7 +311,7 @@ static void copy_and_fix(const jumpthreading_env_t *env, ir_node *block,
construct_ssa(block, node, copy_block, copy_node);
}
/* make sure new nodes are kept alive if old nodes were */
/* make sure copied PhiM nodes are kept alive if old nodes were */
ir_graph *irg = get_irn_irg(block);
ir_node *end = get_irg_end(irg);
for (int i = 0, arity = get_End_n_keepalives(end); i < arity; ++i) {
......@@ -319,8 +319,11 @@ static void copy_and_fix(const jumpthreading_env_t *env, ir_node *block,
if (get_irn_visited(keep) < env->visited_nr || is_Block(keep))
continue;
ir_node *copy = get_irn_link(keep);
if (is_Phi(keep) && is_Phi(copy)) {
assert(get_irn_mode(keep) == mode_M);
add_End_keepalive(end, copy);
}
}
}
/**
......
......@@ -1310,6 +1310,7 @@ static changes_t optimize_phi(ir_node *phi, walk_env_t *wenv)
}
/* sixth step: replace old Phi */
remove_keep_alive(phi);
exchange(phi, projM);
return res | DF_CHANGED;
......
......@@ -737,7 +737,7 @@ static void classify_iv(scc *pscc, iv_env *env)
}
/* found an induction variable */
DB((dbg, LEVEL_2, " Found an induction variable:\n "));
if (only_phi && num_outside == 1) {
if (only_phi && num_outside == 1 && get_irn_mode(pscc->head) != mode_M) {
/* a phi cycle with only one real predecessor can be collapsed */
DB((dbg, LEVEL_2, " Found an USELESS Phi cycle:\n "));
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment