#include "toplev.h"
#include "except.h"
#include "cfgloop.h"
+#include "cfglayout.h"
+#include "hashtab.h"
/* This file contains functions for building the Control Flow Graph (CFG)
for a function tree. */
building of the CFG in code with lots of gotos. */
static GTY(()) varray_type label_to_block_map;
+/* This hash table allows us to efficiently lookup the one and only one
+ CASE_LABEL_EXPR which contains the LABEL_DECL for the target block
+ of one or more case statements. Efficient access to this node
+ allows us to efficiently update the case vector in response to
+ edge redirections and similar operations.
+
+ Right now this is only used to set up case label leaders. In the
+ future we hope to make this table more persistent and use it to
+ more efficiently update case labels. */
+
+struct edge_to_case_leader_elt
+{
+ /* The edge itself. Necessary for hashing and equality tests. */
+ edge e;
+
+ /* The "leader" for all the CASE_LABEL_EXPRs which transfer control
+ to E->dest. When we change the destination of E, we will need to
+ update the CASE_LEADER_OR_LABEL of this CASE_LABEL_EXPR (and no
+ others). */
+ tree case_label;
+};
+
+static htab_t edge_to_case_leader;
+
/* CFG statistics. */
struct cfg_stats_d
{
static void tree_make_forwarder_block (edge);
static bool thread_jumps (void);
static bool tree_forwarder_block_p (basic_block);
-static void bsi_commit_edge_inserts_1 (edge e);
static void tree_cfg2vcg (FILE *);
/* Flowgraph optimization and cleanup. */
static void tree_merge_blocks (basic_block, basic_block);
static bool tree_can_merge_blocks_p (basic_block, basic_block);
static void remove_bb (basic_block);
-static void group_case_labels (void);
-static void cleanup_dead_labels (void);
static bool cleanup_control_flow (void);
static bool cleanup_control_expr_graph (basic_block, block_stmt_iterator);
static edge find_taken_edge_cond_expr (basic_block, tree);
/* Initialize the basic block array. */
init_flow ();
+ profile_status = PROFILE_ABSENT;
n_basic_blocks = 0;
last_basic_block = 0;
VARRAY_BB_INIT (basic_block_info, initial_cfg_capacity, "basic_block_info");
if (found_computed_goto)
factor_computed_gotos ();
- /* Make sure there is always at least one block, even if its empty. */
+ /* Make sure there is always at least one block, even if it's empty. */
if (n_basic_blocks == 0)
create_empty_bb (ENTRY_BLOCK_PTR);
PROP_cfg, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_verify_stmts /* todo_flags_finish */
+ TODO_verify_stmts, /* todo_flags_finish */
+ 0 /* letter */
};
/* Search the CFG for any computed gotos. If found, factor them to a
create_block_annotation (basic_block bb)
{
/* Verify that the tree_annotations field is clear. */
- if (bb->tree_annotations)
- abort ();
+ gcc_assert (!bb->tree_annotations);
bb->tree_annotations = ggc_alloc_cleared (sizeof (struct bb_ann_d));
}
{
basic_block bb;
- if (e)
- abort ();
+ gcc_assert (!e);
- /* Create and initialize a new basic block. */
+ /* Create and initialize a new basic block. Since alloc_block uses
+ ggc_alloc_cleared to allocate a basic block, we do not have to
+ clear the newly allocated basic block here. */
bb = alloc_block ();
- memset (bb, 0, sizeof (*bb));
bb->index = last_basic_block;
bb->flags = BB_NEW;
/* Finally, if no edges were created above, this is a regular
basic block that only needs a fallthru edge. */
- if (bb->succ == NULL)
+ if (EDGE_COUNT (bb->succs) == 0)
make_edge (bb, bb->next_bb, EDGE_FALLTHRU);
}
/* We do not care about fake edges, so remove any that the CFG
builder inserted for completeness. */
- remove_fake_edges ();
+ remove_fake_exit_edges ();
/* Clean up the graph and warn for unreachable code. */
cleanup_tree_cfg ();
make_ctrl_stmt_edges (basic_block bb)
{
tree last = last_stmt (bb);
- tree first = first_stmt (bb);
-
-#if defined ENABLE_CHECKING
- if (last == NULL_TREE)
- abort();
-#endif
-
- if (TREE_CODE (first) == LABEL_EXPR
- && DECL_NONLOCAL (LABEL_EXPR_LABEL (first)))
- make_edge (ENTRY_BLOCK_PTR, bb, EDGE_ABNORMAL);
+ gcc_assert (last);
switch (TREE_CODE (last))
{
case GOTO_EXPR:
case RESX_EXPR:
make_eh_edges (last);
/* Yet another NORETURN hack. */
- if (bb->succ == NULL)
+ if (EDGE_COUNT (bb->succs) == 0)
make_edge (bb, EXIT_BLOCK_PTR, EDGE_FAKE);
break;
default:
- abort ();
+ gcc_unreachable ();
}
}
static void
make_exit_edges (basic_block bb)
{
- tree last = last_stmt (bb);
-
- if (last == NULL_TREE)
- abort ();
+ tree last = last_stmt (bb), op;
+ gcc_assert (last);
switch (TREE_CODE (last))
{
case CALL_EXPR:
/* A MODIFY_EXPR may have a CALL_EXPR on its RHS and the CALL_EXPR
may have an abnormal edge. Search the RHS for this case and
create any required edges. */
- if (TREE_CODE (TREE_OPERAND (last, 1)) == CALL_EXPR
- && TREE_SIDE_EFFECTS (TREE_OPERAND (last, 1))
+ op = get_call_expr_in (last);
+ if (op && TREE_SIDE_EFFECTS (op)
&& current_function_has_nonlocal_label)
make_goto_expr_edges (bb);
break;
default:
- abort ();
+ gcc_unreachable ();
}
}
basic_block then_bb, else_bb;
tree then_label, else_label;
-#if defined ENABLE_CHECKING
- if (entry == NULL_TREE || TREE_CODE (entry) != COND_EXPR)
- abort ();
-#endif
+ gcc_assert (entry);
+ gcc_assert (TREE_CODE (entry) == COND_EXPR);
/* Entry basic blocks for each component. */
then_label = GOTO_DESTINATION (COND_EXPR_THEN (entry));
make_edge (bb, else_bb, EDGE_FALSE_VALUE);
}
+/* Hashing routine for EDGE_TO_CASE_LEADER. */
+
+static hashval_t
+edge_to_case_leader_hash (const void *p)
+{
+ edge e = ((struct edge_to_case_leader_elt *)p)->e;
+
+ /* Hash on the edge itself (which is a pointer). */
+ return htab_hash_pointer (e);
+}
+
+/* Equality routine for EDGE_TO_CASE_LEADER, edges are unique, so testing
+ for equality is just a pointer comparison. */
+
+static int
+edge_to_case_leader_eq (const void *p1, const void *p2)
+{
+ edge e1 = ((struct edge_to_case_leader_elt *)p1)->e;
+ edge e2 = ((struct edge_to_case_leader_elt *)p2)->e;
+
+ return e1 == e2;
+}
+
+/* Record that CASE_LABEL (a CASE_LABEL_EXPR) references edge E. */
+
+static void
+record_switch_edge (edge e, tree case_label)
+{
+ struct edge_to_case_leader_elt *elt;
+ void **slot;
+
+ /* Build a hash table element so we can see if E is already
+ in the table. */
+ elt = xmalloc (sizeof (struct edge_to_case_leader_elt));
+ elt->e = e;
+ elt->case_label = case_label;
+
+ slot = htab_find_slot (edge_to_case_leader, elt, INSERT);
+
+ if (*slot == NULL)
+ {
+ /* E was not in the hash table. Install E into the hash table. */
+ *slot = (void *)elt;
+ }
+ else
+ {
+ /* E was already in the hash table. Free ELT as we do not need it
+ anymore. */
+ free (elt);
+
+ /* Get the entry stored in the hash table. */
+ elt = (struct edge_to_case_leader_elt *) *slot;
+
+ /* Make ELT->case_label the leader for CASE_LABEL. */
+ CASE_LEADER_OR_LABEL (case_label) = elt->case_label;
+ }
+}
+
+/* Subroutine of get_case_leader_for_edge; returns the case leader for the
+ chain of CASE_LABEL_EXPRs associated with E using a hash table lookup. */
+
+static tree
+get_case_leader_for_edge_hash (edge e)
+{
+ struct edge_to_case_leader_elt elt, *elt_p;
+ void **slot;
+
+ elt.e = e;
+ elt.case_label = NULL;
+ slot = htab_find_slot (edge_to_case_leader, &elt, NO_INSERT);
+
+ if (slot)
+ {
+ tree t;
+
+ elt_p = (struct edge_to_case_leader_elt *)*slot;
+ t = elt_p->case_label;
+
+ while (TREE_CODE (CASE_LEADER_OR_LABEL (t)) == CASE_LABEL_EXPR)
+ t = CASE_LEADER_OR_LABEL (t);
+ return t;
+ }
+
+ return NULL;
+}
+
+/* Given an edge E, return the case leader for the chain of CASE_LABEL_EXPRs
+ which use E. */
+
+static tree
+get_case_leader_for_edge (edge e)
+{
+ tree vec, stmt;
+ size_t i, n;
+
+ /* If we have a hash table, then use it as it's significantly faster. */
+ if (edge_to_case_leader)
+ return get_case_leader_for_edge_hash (e);
+
+ /* No hash table. We have to walk the case vector. */
+ stmt = bsi_stmt (bsi_last (e->src));
+ vec = SWITCH_LABELS (stmt);
+ n = TREE_VEC_LENGTH (vec);
+
+ for (i = 0; i < n; i++)
+ {
+ tree elt = TREE_VEC_ELT (vec, i);
+ tree t = CASE_LEADER_OR_LABEL (elt);
+
+ if (TREE_CODE (t) == LABEL_DECL
+ && label_to_block (t) == e->dest)
+ return elt;
+ }
+
+ abort ();
+}
/* Create the edges for a SWITCH_EXPR starting at block BB.
At this point, the switch body has been lowered and the
vec = SWITCH_LABELS (entry);
n = TREE_VEC_LENGTH (vec);
+ edge_to_case_leader
+ = htab_create (n, edge_to_case_leader_hash, edge_to_case_leader_eq, free);
+
for (i = 0; i < n; ++i)
{
tree lab = CASE_LABEL (TREE_VEC_ELT (vec, i));
basic_block label_bb = label_to_block (lab);
- make_edge (bb, label_bb, 0);
+ edge e = make_edge (bb, label_bb, 0);
+
+ if (!e)
+ e = find_edge (bb, label_bb);
+
+ record_switch_edge (e, TREE_VEC_ELT (vec, i));
}
+ htab_delete (edge_to_case_leader);
+ edge_to_case_leader = NULL;
}
{
int uid = LABEL_DECL_UID (dest);
- /* We would die hard when faced by undefined label. Emit label to
- very first basic block. This will hopefully make even the dataflow
+ /* We would die hard when faced by an undefined label. Emit a label to
+ the very first basic block. This will hopefully make even the dataflow
and undefined variable warnings quite right. */
if ((errorcount || sorrycount) && uid < 0)
{
}
/* Degenerate case of computed goto with no labels. */
- if (!for_call && !bb->succ)
+ if (!for_call && EDGE_COUNT (bb->succs) == 0)
make_edge (bb, EXIT_BLOCK_PTR, EDGE_FAKE);
}
/* Remove unreachable blocks and other miscellaneous clean up work. */
-void
+bool
cleanup_tree_cfg (void)
{
- bool something_changed = true;
+ bool retval = false;
timevar_push (TV_TREE_CLEANUP_CFG);
- /* These three transformations can cascade, so we iterate on them until
- nothing changes. */
- while (something_changed)
+ retval = cleanup_control_flow ();
+ retval |= delete_unreachable_blocks ();
+ retval |= thread_jumps ();
+
+#ifdef ENABLE_CHECKING
+ if (retval)
{
- something_changed = cleanup_control_flow ();
- something_changed |= thread_jumps ();
- something_changed |= delete_unreachable_blocks ();
+ gcc_assert (!cleanup_control_flow ());
+ gcc_assert (!delete_unreachable_blocks ());
+ gcc_assert (!thread_jumps ());
}
+#endif
/* Merging the blocks creates no new opportunities for the other
optimizations, so do it here. */
verify_flow_info ();
#endif
timevar_pop (TV_TREE_CLEANUP_CFG);
+ return retval;
}
tree old_label = get_eh_region_tree_label (region);
if (old_label)
{
- tree new_label = label_for_bb[label_to_block (old_label)->index];
+ tree new_label;
+ basic_block bb = label_to_block (old_label);
+
+ /* ??? After optimizing, there may be EH regions with labels
+ that have already been removed from the function body, so
+ there is no basic block for them. */
+ if (! bb)
+ return;
+
+ new_label = label_for_bb[bb->index];
set_eh_region_tree_label (region, new_label);
}
}
return label_for_bb[bb->index];
}
-/* Cleanup redundant labels. This is a three-steo process:
+/* Cleanup redundant labels. This is a three-step process:
1) Find the leading label for each block.
2) Redirect all references to labels to the leading labels.
3) Cleanup all useless labels. */
-static void
+void
cleanup_dead_labels (void)
{
basic_block bb;
label_for_bb = xcalloc (last_basic_block, sizeof (tree));
/* Find a suitable label for each block. We use the first user-defined
- label is there is one, or otherwise just the first label we see. */
+ label if there is one, or otherwise just the first label we see. */
FOR_EACH_BB (bb)
{
block_stmt_iterator i;
/* Replace all destination labels. */
for (i = 0; i < n; ++i)
- CASE_LABEL (TREE_VEC_ELT (vec, i))
- = main_block_label (CASE_LABEL (TREE_VEC_ELT (vec, i)));
-
+ {
+ tree elt = TREE_VEC_ELT (vec, i);
+ tree label = main_block_label (CASE_LABEL (elt));
+ CASE_LEADER_OR_LABEL (elt) = label;
+ }
break;
}
same label.
Eg. three separate entries 1: 2: 3: become one entry 1..3: */
-static void
+void
group_case_labels (void)
{
basic_block bb;
tree labels = SWITCH_LABELS (stmt);
int old_size = TREE_VEC_LENGTH (labels);
int i, j, new_size = old_size;
- tree default_label = TREE_VEC_ELT (labels, old_size - 1);
+ tree default_case = TREE_VEC_ELT (labels, old_size - 1);
+ tree default_label;
+
+ /* The default label is always the last case in a switch
+ statement after gimplification. */
+ default_label = CASE_LABEL (default_case);
/* Look for possible opportunities to merge cases.
Ignore the last element of the label vector because it
must be the default case. */
i = 0;
- while (i < old_size - 2)
+ while (i < old_size - 1)
{
tree base_case, base_label, base_high, type;
base_case = TREE_VEC_ELT (labels, i);
- if (! base_case)
- abort ();
-
+ gcc_assert (base_case);
base_label = CASE_LABEL (base_case);
/* Discard cases that have the same destination as the
{
TREE_VEC_ELT (labels, i) = NULL_TREE;
i++;
+ new_size--;
continue;
}
type = TREE_TYPE (CASE_LOW (base_case));
base_high = CASE_HIGH (base_case) ?
CASE_HIGH (base_case) : CASE_LOW (base_case);
-
+ i++;
/* Try to merge case labels. Break out when we reach the end
of the label vector or when we cannot merge the next case
label with the current one. */
- while (i < old_size - 2)
+ while (i < old_size - 1)
{
- tree merge_case = TREE_VEC_ELT (labels, ++i);
+ tree merge_case = TREE_VEC_ELT (labels, i);
tree merge_label = CASE_LABEL (merge_case);
tree t = int_const_binop (PLUS_EXPR, base_high,
integer_one_node, 1);
CASE_HIGH (base_case) = base_high;
TREE_VEC_ELT (labels, i) = NULL_TREE;
new_size--;
+ i++;
}
else
break;
tree stmt;
block_stmt_iterator bsi;
- if (!a->succ
- || a->succ->succ_next)
+ if (EDGE_COUNT (a->succs) != 1)
return false;
- if (a->succ->flags & EDGE_ABNORMAL)
+ if (EDGE_SUCC (a, 0)->flags & EDGE_ABNORMAL)
return false;
- if (a->succ->dest != b)
+ if (EDGE_SUCC (a, 0)->dest != b)
return false;
if (b == EXIT_BLOCK_PTR)
return false;
- if (b->pred->pred_next)
+ if (EDGE_COUNT (b->preds) > 1)
return false;
/* If A ends by a statement causing exceptions or something similar, we
/* Ensure that B follows A. */
move_block_after (b, a);
- if (!(a->succ->flags & EDGE_FALLTHRU))
- abort ();
-
- if (last_stmt (a)
- && stmt_ends_bb_p (last_stmt (a)))
- abort ();
+ gcc_assert (EDGE_SUCC (a, 0)->flags & EDGE_FALLTHRU);
+ gcc_assert (!last_stmt (a) || !stmt_ends_bb_p (last_stmt (a)));
/* Remove labels from B and set bb_for_stmt to A for other statements. */
for (bsi = bsi_start (b); !bsi_end_p (bsi);)
else_has_label = data->has_label;
data->has_label = save_has_label | then_has_label | else_has_label;
- fold_stmt (stmt_p);
then_clause = COND_EXPR_THEN (*stmt_p);
else_clause = COND_EXPR_ELSE (*stmt_p);
cond = COND_EXPR_COND (*stmt_p);
static void
remove_useless_stmts_1 (tree *tp, struct rus_data *data)
{
- tree t = *tp;
+ tree t = *tp, op;
switch (TREE_CODE (t))
{
case MODIFY_EXPR:
data->last_goto = NULL;
fold_stmt (tp);
- if (TREE_CODE (TREE_OPERAND (t, 1)) == CALL_EXPR)
+ op = get_call_expr_in (t);
+ if (op)
{
- update_call_expr_flags (TREE_OPERAND (t, 1));
- notice_special_calls (TREE_OPERAND (t, 1));
+ update_call_expr_flags (op);
+ notice_special_calls (op);
}
if (tree_could_throw_p (t))
data->may_throw = true;
}
}
break;
- case SWITCH_EXPR:
+ case ASM_EXPR:
fold_stmt (tp);
data->last_goto = NULL;
break;
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func /* todo_flags_finish */
+ TODO_dump_func, /* todo_flags_finish */
+ 0 /* letter */
};
/* Check whether we come here from a condition, and if so, get the
condition. */
- if (!bb->pred
- || bb->pred->pred_next
- || !(bb->pred->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE)))
+ if (EDGE_COUNT (bb->preds) != 1
+ || !(EDGE_PRED (bb, 0)->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE)))
return;
- cond = COND_EXPR_COND (last_stmt (bb->pred->src));
+ cond = COND_EXPR_COND (last_stmt (EDGE_PRED (bb, 0)->src));
if (TREE_CODE (cond) == VAR_DECL || TREE_CODE (cond) == PARM_DECL)
{
var = cond;
- val = (bb->pred->flags & EDGE_FALSE_VALUE
+ val = (EDGE_PRED (bb, 0)->flags & EDGE_FALSE_VALUE
? boolean_false_node : boolean_true_node);
}
else if (TREE_CODE (cond) == TRUTH_NOT_EXPR
|| TREE_CODE (TREE_OPERAND (cond, 0)) == PARM_DECL))
{
var = TREE_OPERAND (cond, 0);
- val = (bb->pred->flags & EDGE_FALSE_VALUE
+ val = (EDGE_PRED (bb, 0)->flags & EDGE_FALSE_VALUE
? boolean_true_node : boolean_false_node);
}
else
{
- if (bb->pred->flags & EDGE_FALSE_VALUE)
+ if (EDGE_PRED (bb, 0)->flags & EDGE_FALSE_VALUE)
cond = invert_truthvalue (cond);
if (TREE_CODE (cond) == EQ_EXPR
&& (TREE_CODE (TREE_OPERAND (cond, 0)) == VAR_DECL
continue;
}
- /* Invalidate the var if we encounter something that could modify it. */
+ /* Invalidate the var if we encounter something that could modify it.
+ Likewise for the value it was previously set to. Note that we only
+ consider values that are either a VAR_DECL or PARM_DECL so we
+ can test for conflict very simply. */
if (TREE_CODE (stmt) == ASM_EXPR
- || TREE_CODE (stmt) == VA_ARG_EXPR
|| (TREE_CODE (stmt) == MODIFY_EXPR
&& (TREE_OPERAND (stmt, 0) == var
- || TREE_OPERAND (stmt, 0) == val
- || TREE_CODE (TREE_OPERAND (stmt, 1)) == VA_ARG_EXPR)))
+ || TREE_OPERAND (stmt, 0) == val)))
return;
bsi_next (&bsi);
}
/* Remove edges to BB's successors. */
- while (bb->succ != NULL)
- ssa_remove_edge (bb->succ);
+ while (EDGE_COUNT (bb->succs) > 0)
+ ssa_remove_edge (EDGE_SUCC (bb, 0));
}
}
/* Remove all the instructions in the block. */
- for (i = bsi_start (bb); !bsi_end_p (i); bsi_remove (&i))
+ for (i = bsi_start (bb); !bsi_end_p (i);)
{
tree stmt = bsi_stmt (i);
+ if (TREE_CODE (stmt) == LABEL_EXPR
+ && FORCED_LABEL (LABEL_EXPR_LABEL (stmt)))
+ {
+ basic_block new_bb = bb->prev_bb;
+ block_stmt_iterator new_bsi = bsi_after_labels (new_bb);
+
+ bsi_remove (&i);
+ bsi_insert_after (&new_bsi, stmt, BSI_NEW_STMT);
+ }
+ else
+ {
+ release_defs (stmt);
- set_bb_for_stmt (stmt, NULL);
+ set_bb_for_stmt (stmt, NULL);
+ bsi_remove (&i);
+ }
/* Don't warn for removed gotos. Gotos are often removed due to
jump threading, thus resulting in bogus warnings. Not great,
remove_phi_nodes_and_edges_for_unreachable_block (bb);
}
-
-/* Examine BB to determine if it is a forwarding block (a block which only
- transfers control to a new destination). If BB is a forwarding block,
- then return the edge leading to the ultimate destination. */
-
-edge
-tree_block_forwards_to (basic_block bb)
-{
- block_stmt_iterator bsi;
- bb_ann_t ann = bb_ann (bb);
- tree stmt;
-
- /* If this block is not forwardable, then avoid useless work. */
- if (! ann->forwardable)
- return NULL;
-
- /* Set this block to not be forwardable. This prevents infinite loops since
- any block currently under examination is considered non-forwardable. */
- ann->forwardable = 0;
-
- /* No forwarding is possible if this block is a special block (ENTRY/EXIT),
- this block has more than one successor, this block's single successor is
- reached via an abnormal edge, this block has phi nodes, or this block's
- single successor has phi nodes. */
- if (bb == EXIT_BLOCK_PTR
- || bb == ENTRY_BLOCK_PTR
- || !bb->succ
- || bb->succ->succ_next
- || bb->succ->dest == EXIT_BLOCK_PTR
- || (bb->succ->flags & EDGE_ABNORMAL) != 0
- || phi_nodes (bb)
- || phi_nodes (bb->succ->dest))
- return NULL;
-
- /* Walk past any labels at the start of this block. */
- for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
- {
- stmt = bsi_stmt (bsi);
- if (TREE_CODE (stmt) != LABEL_EXPR)
- break;
- }
-
- /* If we reached the end of this block we may be able to optimize this
- case. */
- if (bsi_end_p (bsi))
- {
- edge dest;
-
- /* Recursive call to pick up chains of forwarding blocks. */
- dest = tree_block_forwards_to (bb->succ->dest);
-
- /* If none found, we forward to bb->succ at minimum. */
- if (!dest)
- dest = bb->succ;
-
- ann->forwardable = 1;
- return dest;
- }
-
- /* No forwarding possible. */
- return NULL;
-}
-
-
/* Try to remove superfluous control structures. */
static bool
bool retval = false;
tree expr = bsi_stmt (bsi), val;
- if (bb->succ->succ_next)
+ if (EDGE_COUNT (bb->succs) > 1)
{
- edge e, next;
+ edge e;
+ edge_iterator ei;
switch (TREE_CODE (expr))
{
break;
default:
- abort ();
+ gcc_unreachable ();
}
taken_edge = find_taken_edge (bb, val);
return false;
/* Remove all the edges except the one that is always executed. */
- for (e = bb->succ; e; e = next)
+ for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); )
{
- next = e->succ_next;
if (e != taken_edge)
{
taken_edge->probability += e->probability;
ssa_remove_edge (e);
retval = true;
}
+ else
+ ei_next (&ei);
}
if (taken_edge->probability > REG_BR_PROB_BASE)
taken_edge->probability = REG_BR_PROB_BASE;
}
else
- taken_edge = bb->succ;
+ taken_edge = EDGE_SUCC (bb, 0);
bsi_remove (&bsi);
taken_edge->flags = EDGE_FALLTHRU;
/* We removed some paths from the cfg. */
- if (dom_computed[CDI_DOMINATORS] >= DOM_CONS_OK)
- dom_computed[CDI_DOMINATORS] = DOM_CONS_OK;
+ free_dominance_info (CDI_DOMINATORS);
return retval;
}
-/* Given a control block BB and a constant value VAL, return the edge that
- will be taken out of the block. If VAL does not match a unique edge,
- NULL is returned. */
+/* Given a basic block BB ending with COND_EXPR or SWITCH_EXPR, and a
+ predicate VAL, return the edge that will be taken out of the block.
+ If VAL does not match a unique edge, NULL is returned. */
edge
find_taken_edge (basic_block bb, tree val)
stmt = last_stmt (bb);
-#if defined ENABLE_CHECKING
- if (stmt == NULL_TREE || !is_ctrl_stmt (stmt))
- abort ();
-#endif
+ gcc_assert (stmt);
+ gcc_assert (is_ctrl_stmt (stmt));
+ gcc_assert (val);
+
+ /* If VAL is a predicate of the form N RELOP N, where N is an
+ SSA_NAME, we can usually determine its truth value. */
+ if (COMPARISON_CLASS_P (val))
+ val = fold (val);
/* If VAL is not a constant, we can't determine which edge might
be taken. */
- if (val == NULL || !really_constant_p (val))
+ if (!really_constant_p (val))
return NULL;
if (TREE_CODE (stmt) == COND_EXPR)
if (TREE_CODE (stmt) == SWITCH_EXPR)
return find_taken_edge_switch_expr (bb, val);
- return bb->succ;
+ gcc_unreachable ();
}
extract_true_false_edges_from_block (bb, &true_edge, &false_edge);
- /* If both edges of the branch lead to the same basic block, it doesn't
- matter which edge is taken. */
- if (true_edge->dest == false_edge->dest)
- return true_edge;
-
/* Otherwise, try to determine which branch of the if() will be taken.
If VAL is a constant but it can't be reduced to a 0 or a 1, then
we don't really know which edge will be taken at runtime. This
dest_bb = label_to_block (CASE_LABEL (taken_case));
e = find_edge (bb, dest_bb);
- if (!e)
- abort ();
+ gcc_assert (e);
return e;
}
n1 = phi_arg_from_edge (phi, e1);
n2 = phi_arg_from_edge (phi, e2);
-#ifdef ENABLE_CHECKING
- if (n1 < 0 || n2 < 0)
- abort ();
-#endif
+ gcc_assert (n1 >= 0);
+ gcc_assert (n2 >= 0);
val1 = PHI_ARG_DEF (phi, n1);
val2 = PHI_ARG_DEF (phi, n2);
}
-/* Computing the Dominance Frontier:
-
- As described in Morgan, section 3.5, this may be done simply by
- walking the dominator tree bottom-up, computing the frontier for
- the children before the parent. When considering a block B,
- there are two cases:
-
- (1) A flow graph edge leaving B that does not lead to a child
- of B in the dominator tree must be a block that is either equal
- to B or not dominated by B. Such blocks belong in the frontier
- of B.
-
- (2) Consider a block X in the frontier of one of the children C
- of B. If X is not equal to B and is not dominated by B, it
- is in the frontier of B. */
-
-static void
-compute_dominance_frontiers_1 (bitmap *frontiers, basic_block bb, sbitmap done)
-{
- edge e;
- basic_block c;
-
- SET_BIT (done, bb->index);
-
- /* Do the frontier of the children first. Not all children in the
- dominator tree (blocks dominated by this one) are children in the
- CFG, so check all blocks. */
- for (c = first_dom_son (CDI_DOMINATORS, bb);
- c;
- c = next_dom_son (CDI_DOMINATORS, c))
- {
- if (! TEST_BIT (done, c->index))
- compute_dominance_frontiers_1 (frontiers, c, done);
- }
-
- /* Find blocks conforming to rule (1) above. */
- for (e = bb->succ; e; e = e->succ_next)
- {
- if (e->dest == EXIT_BLOCK_PTR)
- continue;
- if (get_immediate_dominator (CDI_DOMINATORS, e->dest) != bb)
- bitmap_set_bit (frontiers[bb->index], e->dest->index);
- }
-
- /* Find blocks conforming to rule (2). */
- for (c = first_dom_son (CDI_DOMINATORS, bb);
- c;
- c = next_dom_son (CDI_DOMINATORS, c))
- {
- int x;
-
- EXECUTE_IF_SET_IN_BITMAP (frontiers[c->index], 0, x,
- {
- if (get_immediate_dominator (CDI_DOMINATORS, BASIC_BLOCK (x)) != bb)
- bitmap_set_bit (frontiers[bb->index], x);
- });
- }
-}
-
-
-void
-compute_dominance_frontiers (bitmap *frontiers)
-{
- sbitmap done = sbitmap_alloc (last_basic_block);
-
- timevar_push (TV_DOM_FRONTIERS);
-
- sbitmap_zero (done);
-
- compute_dominance_frontiers_1 (frontiers, ENTRY_BLOCK_PTR->succ->dest, done);
-
- sbitmap_free (done);
-
- timevar_pop (TV_DOM_FRONTIERS);
-}
-
-
-
/*---------------------------------------------------------------------------
Debugging functions
---------------------------------------------------------------------------*/
{
static long max_num_merged_labels = 0;
unsigned long size, total = 0;
- long n_edges;
+ int n_edges;
basic_block bb;
const char * const fmt_str = "%-30s%-13s%12s\n";
- const char * const fmt_str_1 = "%-30s%13lu%11lu%c\n";
+ const char * const fmt_str_1 = "%-30s%13d%11lu%c\n";
const char * const fmt_str_3 = "%-43s%11lu%c\n";
const char *funcname
= lang_hooks.decl_printable_name (current_function_decl, 2);
size = n_basic_blocks * sizeof (struct basic_block_def);
total += size;
- fprintf (file, fmt_str_1, "Basic blocks", n_basic_blocks, SCALE (size),
- LABEL (size));
+ fprintf (file, fmt_str_1, "Basic blocks", n_basic_blocks,
+ SCALE (size), LABEL (size));
n_edges = 0;
FOR_EACH_BB (bb)
- {
- edge e;
- for (e = bb->succ; e; e = e->succ_next)
- n_edges++;
- }
+ n_edges += EDGE_COUNT (bb->succs);
size = n_edges * sizeof (struct edge_def);
total += size;
fprintf (file, fmt_str_1, "Edges", n_edges, SCALE (size), LABEL (size));
tree_cfg2vcg (FILE *file)
{
edge e;
+ edge_iterator ei;
basic_block bb;
const char *funcname
= lang_hooks.decl_printable_name (current_function_decl, 2);
fprintf (file, "node: { title: \"EXIT\" label: \"EXIT\" }\n");
/* Write blocks and edges. */
- for (e = ENTRY_BLOCK_PTR->succ; e; e = e->succ_next)
+ FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs)
{
fprintf (file, "edge: { sourcename: \"ENTRY\" targetname: \"%d\"",
e->dest->index);
bb->index, bb->index, head_name, head_line, end_name,
end_line);
- for (e = bb->succ; e; e = e->succ_next)
+ FOR_EACH_EDGE (e, ei, bb->succs)
{
if (e->dest == EXIT_BLOCK_PTR)
fprintf (file, "edge: { sourcename: \"%d\" targetname: \"EXIT\"", bb->index);
bool
is_ctrl_altering_stmt (tree t)
{
- tree call = t;
-
-#if defined ENABLE_CHECKING
- if (t == NULL)
- abort ();
-#endif
+ tree call;
- switch (TREE_CODE (t))
+ gcc_assert (t);
+ call = get_call_expr_in (t);
+ if (call)
{
- case MODIFY_EXPR:
- /* A MODIFY_EXPR with a rhs of a call has the characteristics
- of the call. */
- call = TREE_OPERAND (t, 1);
- if (TREE_CODE (call) != CALL_EXPR)
- break;
- /* FALLTHRU */
-
- case CALL_EXPR:
/* A non-pure/const CALL_EXPR alters flow control if the current
function has nonlocal labels. */
- if (TREE_SIDE_EFFECTS (t)
- && current_function_has_nonlocal_label)
+ if (TREE_SIDE_EFFECTS (call) && current_function_has_nonlocal_label)
return true;
/* A CALL_EXPR also alters control flow if it does not return. */
if (call_expr_flags (call) & (ECF_NORETURN | ECF_LONGJMP))
return true;
- break;
-
- default:
- return false;
}
/* If a statement can throw, it alters control flow. */
bool
simple_goto_p (tree expr)
{
- return (TREE_CODE (expr) == GOTO_EXPR
- && TREE_CODE (GOTO_DESTINATION (expr)) == LABEL_DECL
- && (decl_function_context (GOTO_DESTINATION (expr))
- == current_function_decl));
+ return (TREE_CODE (expr) == GOTO_EXPR
+ && TREE_CODE (GOTO_DESTINATION (expr)) == LABEL_DECL);
}
basic_block bb;
block_stmt_iterator last;
edge e;
+ edge_iterator ei;
tree stmt, label;
FOR_EACH_BB (bb)
from cfg_remove_useless_stmts here since it violates the
invariants for tree--cfg correspondence and thus fits better
here where we do it anyway. */
- for (e = bb->succ; e; e = e->succ_next)
+ FOR_EACH_EDGE (e, ei, bb->succs)
{
if (e->dest != bb->next_bb)
continue;
else if (e->flags & EDGE_FALSE_VALUE)
COND_EXPR_ELSE (stmt) = build_empty_stmt ();
else
- abort ();
+ gcc_unreachable ();
e->flags |= EDGE_FALLTHRU;
}
{
/* Remove the RETURN_EXPR if we may fall though to the exit
instead. */
- if (!bb->succ
- || bb->succ->succ_next
- || bb->succ->dest != EXIT_BLOCK_PTR)
- abort ();
+ gcc_assert (EDGE_COUNT (bb->succs) == 1);
+ gcc_assert (EDGE_SUCC (bb, 0)->dest == EXIT_BLOCK_PTR);
if (bb->next_bb == EXIT_BLOCK_PTR
&& !TREE_OPERAND (stmt, 0))
{
bsi_remove (&last);
- bb->succ->flags |= EDGE_FALLTHRU;
+ EDGE_SUCC (bb, 0)->flags |= EDGE_FALLTHRU;
}
continue;
}
continue;
/* Find a fallthru edge and emit the goto if necessary. */
- for (e = bb->succ; e; e = e->succ_next)
+ FOR_EACH_EDGE (e, ei, bb->succs)
if (e->flags & EDGE_FALLTHRU)
break;
if (!e || e->dest == bb->next_bb)
continue;
- if (e->dest == EXIT_BLOCK_PTR)
- abort ();
-
+ gcc_assert (e->dest != EXIT_BLOCK_PTR);
label = tree_block_label (e->dest);
stmt = build1 (GOTO_EXPR, void_type_node, label);
void
set_bb_for_stmt (tree t, basic_block bb)
{
- if (TREE_CODE (t) == STATEMENT_LIST)
+ if (TREE_CODE (t) == PHI_NODE)
+ PHI_BB (t) = bb;
+ else if (TREE_CODE (t) == STATEMENT_LIST)
{
tree_stmt_iterator i;
for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i))
VARRAY_GROW (label_to_block_map, 3 * uid / 2);
}
else
- {
-#ifdef ENABLE_CHECKING
- /* We're moving an existing label. Make sure that we've
- removed it from the old block. */
- if (bb && VARRAY_BB (label_to_block_map, uid))
- abort ();
-#endif
- }
+ /* We're moving an existing label. Make sure that we've
+ removed it from the old block. */
+ gcc_assert (!bb || !VARRAY_BB (label_to_block_map, uid));
VARRAY_BB (label_to_block_map, uid) = bb;
}
}
}
+/* Finds iterator for STMT. */
+
+extern block_stmt_iterator
+bsi_for_stmt (tree stmt)
+{
+ block_stmt_iterator bsi;
+
+ for (bsi = bsi_start (bb_for_stmt (stmt)); !bsi_end_p (bsi); bsi_next (&bsi))
+ if (bsi_stmt (bsi) == stmt)
+ return bsi;
+
+ gcc_unreachable ();
+}
/* Insert statement (or statement list) T before the statement
pointed-to by iterator I. M specifies how to update iterator I
bsi_insert_before (block_stmt_iterator *i, tree t, enum bsi_iterator_update m)
{
set_bb_for_stmt (t, i->bb);
- modify_stmt (t);
tsi_link_before (&i->tsi, t, m);
+ modify_stmt (t);
}
bsi_insert_after (block_stmt_iterator *i, tree t, enum bsi_iterator_update m)
{
set_bb_for_stmt (t, i->bb);
- modify_stmt (t);
tsi_link_after (&i->tsi, t, m);
+ modify_stmt (t);
}
{
tree t = bsi_stmt (*i);
set_bb_for_stmt (t, NULL);
- modify_stmt (t);
tsi_delink (&i->tsi);
}
In all cases, the returned *BSI points to the correct location. The
return value is true if insertion should be done after the location,
- or false if it should be done before the location. */
+ or false if it should be done before the location. If new basic block
+ has to be created, it is stored in *NEW_BB. */
static bool
-tree_find_edge_insert_loc (edge e, block_stmt_iterator *bsi)
+tree_find_edge_insert_loc (edge e, block_stmt_iterator *bsi,
+ basic_block *new_bb)
{
basic_block dest, src;
tree tmp;
The requirement for no PHI nodes could be relaxed. Basically we
would have to examine the PHIs to prove that none of them used
- the value set by the statement we want to insert on E. That
+ the value set by the statement we want to insert on E. That
hardly seems worth the effort. */
- if (dest->pred->pred_next == NULL
+ if (EDGE_COUNT (dest->preds) == 1
&& ! phi_nodes (dest)
&& dest != EXIT_BLOCK_PTR)
{
Except for the entry block. */
src = e->src;
if ((e->flags & EDGE_ABNORMAL) == 0
- && src->succ->succ_next == NULL
+ && EDGE_COUNT (src->succs) == 1
&& src != ENTRY_BLOCK_PTR)
{
*bsi = bsi_last (src);
tree op = TREE_OPERAND (tmp, 0);
if (!is_gimple_val (op))
{
- if (TREE_CODE (op) != MODIFY_EXPR)
- abort ();
+ gcc_assert (TREE_CODE (op) == MODIFY_EXPR);
bsi_insert_before (bsi, op, BSI_NEW_STMT);
TREE_OPERAND (tmp, 0) = TREE_OPERAND (op, 0);
}
/* Otherwise, create a new basic block, and split this edge. */
dest = split_edge (e);
- e = dest->pred;
+ if (new_bb)
+ *new_bb = dest;
+ e = EDGE_PRED (dest, 0);
goto restart;
}
/* This routine will commit all pending edge insertions, creating any new
- basic blocks which are necessary.
-
- If specified, NEW_BLOCKS returns a count of the number of new basic
- blocks which were created. */
+ basic blocks which are necessary. */
void
-bsi_commit_edge_inserts (int *new_blocks)
+bsi_commit_edge_inserts (void)
{
basic_block bb;
edge e;
- int blocks;
-
- blocks = n_basic_blocks;
+ edge_iterator ei;
- bsi_commit_edge_inserts_1 (ENTRY_BLOCK_PTR->succ);
+ bsi_commit_one_edge_insert (EDGE_SUCC (ENTRY_BLOCK_PTR, 0), NULL);
FOR_EACH_BB (bb)
- for (e = bb->succ; e; e = e->succ_next)
- bsi_commit_edge_inserts_1 (e);
-
- if (new_blocks)
- *new_blocks = n_basic_blocks - blocks;
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ bsi_commit_one_edge_insert (e, NULL);
}
-/* Commit insertions pending at edge E. */
+/* Commit insertions pending at edge E. If a new block is created, set NEW_BB
+ to this block, otherwise set it to NULL. */
-static void
-bsi_commit_edge_inserts_1 (edge e)
+void
+bsi_commit_one_edge_insert (edge e, basic_block *new_bb)
{
+ if (new_bb)
+ *new_bb = NULL;
if (PENDING_STMT (e))
{
block_stmt_iterator bsi;
PENDING_STMT (e) = NULL_TREE;
- if (tree_find_edge_insert_loc (e, &bsi))
+ if (tree_find_edge_insert_loc (e, &bsi, new_bb))
bsi_insert_after (&bsi, stmt, BSI_NEW_STMT);
else
bsi_insert_before (&bsi, stmt, BSI_NEW_STMT);
append_to_statement_list (stmt, &PENDING_STMT (e));
}
+/* Similar to bsi_insert_on_edge+bsi_commit_edge_inserts. If new block has to
+ be created, it is returned. */
-/* Specialized edge insertion for SSA-PRE. FIXME: This should
- probably disappear. The only reason it's here is because PRE needs
- the call to tree_find_edge_insert_loc(). */
-
-void pre_insert_on_edge (edge e, tree stmt);
-
-void
-pre_insert_on_edge (edge e, tree stmt)
+basic_block
+bsi_insert_on_edge_immediate (edge e, tree stmt)
{
block_stmt_iterator bsi;
+ basic_block new_bb = NULL;
- if (PENDING_STMT (e))
- abort ();
+ gcc_assert (!PENDING_STMT (e));
- if (tree_find_edge_insert_loc (e, &bsi))
+ if (tree_find_edge_insert_loc (e, &bsi, &new_bb))
bsi_insert_after (&bsi, stmt, BSI_NEW_STMT);
else
bsi_insert_before (&bsi, stmt, BSI_NEW_STMT);
-}
+ return new_bb;
+}
/*---------------------------------------------------------------------------
Tree specific functions for CFG manipulation
edge new_edge, e;
tree phi;
int i, num_elem;
+ edge_iterator ei;
/* Abnormal edges cannot be split. */
- if (edge_in->flags & EDGE_ABNORMAL)
- abort ();
+ gcc_assert (!(edge_in->flags & EDGE_ABNORMAL));
src = edge_in->src;
dest = edge_in->dest;
/* Place the new block in the block list. Try to keep the new block
near its "logical" location. This is of most help to humans looking
at debugging dumps. */
- for (e = dest->pred; e; e = e->pred_next)
+ FOR_EACH_EDGE (e, ei, dest->preds)
if (e->src->next_bb == dest)
break;
if (!e)
after_bb = edge_in->src;
new_bb = create_empty_bb (after_bb);
+ new_bb->frequency = EDGE_FREQUENCY (edge_in);
+ new_bb->count = edge_in->count;
new_edge = make_edge (new_bb, dest, EDGE_FALLTHRU);
+ new_edge->probability = REG_BR_PROB_BASE;
+ new_edge->count = edge_in->count;
/* Find all the PHI arguments on the original edge, and change them to
the new edge. Do it before redirection, so that the argument does not
}
}
- if (!redirect_edge_and_branch (edge_in, new_bb))
- abort ();
-
- if (PENDING_STMT (edge_in))
- abort ();
+ e = redirect_edge_and_branch (edge_in, new_bb);
+ gcc_assert (e);
+ gcc_assert (!PENDING_STMT (edge_in));
return new_bb;
}
We check for constants explicitly since they are not considered
gimple invariants if they overflowed. */
#define CHECK_OP(N, MSG) \
- do { if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (t, N))) != 'c' \
- && !is_gimple_val (TREE_OPERAND (t, N))) \
+ do { if (!CONSTANT_CLASS_P (TREE_OPERAND (t, N)) \
+ && !is_gimple_val (TREE_OPERAND (t, N))) \
{ error (MSG); return TREE_OPERAND (t, N); }} while (0)
switch (TREE_CODE (t))
break;
case COND_EXPR:
- x = TREE_OPERAND (t, 0);
+ x = COND_EXPR_COND (t);
if (TREE_CODE (TREE_TYPE (x)) != BOOLEAN_TYPE)
{
error ("non-boolean used in condition");
t = TREE_OPERAND (t, 0);
}
- if (TREE_CODE_CLASS (TREE_CODE (t)) != 'c'
- && !is_gimple_lvalue (t))
+ if (!CONSTANT_CLASS_P (t) && !is_gimple_lvalue (t))
{
error ("Invalid reference prefix.");
return t;
{
if (!tree_could_throw_p (stmt))
{
- error ("Statement marked for throw, but doesn't.");
+ error ("Statement marked for throw, but doesn%'t.");
goto fail;
}
if (!last_in_block && tree_can_throw_internal (stmt))
static bool
tree_node_can_be_shared (tree t)
{
- if (TYPE_P (t) || DECL_P (t)
+ if (IS_TYPE_OR_DECL_P (t)
/* We check for constants explicitly since they are not considered
gimple invariants if they overflowed. */
- || TREE_CODE_CLASS (TREE_CODE (t)) == 'c'
+ || CONSTANT_CLASS_P (t)
|| is_gimple_min_invariant (t)
|| TREE_CODE (t) == SSA_NAME)
return true;
+ if (TREE_CODE (t) == CASE_LABEL_EXPR)
+ return true;
+
while (((TREE_CODE (t) == ARRAY_REF || TREE_CODE (t) == ARRAY_RANGE_REF)
/* We check for constants explicitly since they are not considered
gimple invariants if they overflowed. */
- && (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (t, 1))) == 'c'
+ && (CONSTANT_CLASS_P (TREE_OPERAND (t, 1))
|| is_gimple_min_invariant (TREE_OPERAND (t, 1))))
|| (TREE_CODE (t) == COMPONENT_REF
|| TREE_CODE (t) == REALPART_EXPR
block_stmt_iterator bsi;
tree stmt;
edge e;
+ edge_iterator ei;
if (ENTRY_BLOCK_PTR->stmt_list)
{
err = 1;
}
- for (e = EXIT_BLOCK_PTR->pred; e; e = e->pred_next)
+ FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
if (e->flags & EDGE_FALLTHRU)
{
error ("Fallthru to exit from bb %d\n", e->src->index);
if (label_to_block (LABEL_EXPR_LABEL (bsi_stmt (bsi))) != bb)
{
+ tree stmt = bsi_stmt (bsi);
error ("Label %s to block does not match in bb %d\n",
- IDENTIFIER_POINTER (DECL_NAME (bsi_stmt (bsi))),
+ IDENTIFIER_POINTER (DECL_NAME (LABEL_EXPR_LABEL (stmt))),
bb->index);
err = 1;
}
if (decl_function_context (LABEL_EXPR_LABEL (bsi_stmt (bsi)))
!= current_function_decl)
{
+ tree stmt = bsi_stmt (bsi);
error ("Label %s has incorrect context in bb %d\n",
- IDENTIFIER_POINTER (DECL_NAME (bsi_stmt (bsi))),
+ IDENTIFIER_POINTER (DECL_NAME (LABEL_EXPR_LABEL (stmt))),
bb->index);
err = 1;
}
if (is_ctrl_stmt (stmt))
{
- for (e = bb->succ; e; e = e->succ_next)
+ FOR_EACH_EDGE (e, ei, bb->succs)
if (e->flags & EDGE_FALLTHRU)
{
error ("Fallthru edge after a control statement in bb %d \n",
|| !(false_edge->flags & EDGE_FALSE_VALUE)
|| (true_edge->flags & (EDGE_FALLTHRU | EDGE_ABNORMAL))
|| (false_edge->flags & (EDGE_FALLTHRU | EDGE_ABNORMAL))
- || bb->succ->succ_next->succ_next)
+ || EDGE_COUNT (bb->succs) >= 3)
{
error ("Wrong outgoing edge flags at end of bb %d\n",
bb->index);
if (!has_label_p (true_edge->dest,
GOTO_DESTINATION (COND_EXPR_THEN (stmt))))
{
- error ("`then' label does not match edge at end of bb %d\n",
+ error ("%<then%> label does not match edge at end of bb %d\n",
bb->index);
err = 1;
}
if (!has_label_p (false_edge->dest,
GOTO_DESTINATION (COND_EXPR_ELSE (stmt))))
{
- error ("`else' label does not match edge at end of bb %d\n",
+ error ("%<else%> label does not match edge at end of bb %d\n",
bb->index);
err = 1;
}
{
/* FIXME. We should double check that the labels in the
destination blocks have their address taken. */
- for (e = bb->succ; e; e = e->succ_next)
+ FOR_EACH_EDGE (e, ei, bb->succs)
if ((e->flags & (EDGE_FALLTHRU | EDGE_TRUE_VALUE
| EDGE_FALSE_VALUE))
|| !(e->flags & EDGE_ABNORMAL))
break;
case RETURN_EXPR:
- if (!bb->succ || bb->succ->succ_next
- || (bb->succ->flags & (EDGE_FALLTHRU | EDGE_ABNORMAL
+ if (EDGE_COUNT (bb->succs) != 1
+ || (EDGE_SUCC (bb, 0)->flags & (EDGE_FALLTHRU | EDGE_ABNORMAL
| EDGE_TRUE_VALUE | EDGE_FALSE_VALUE)))
{
error ("Wrong outgoing edge flags at end of bb %d\n", bb->index);
err = 1;
}
- if (bb->succ->dest != EXIT_BLOCK_PTR)
+ if (EDGE_SUCC (bb, 0)->dest != EXIT_BLOCK_PTR)
{
error ("Return edge does not point to exit in bb %d\n",
bb->index);
tree lab = CASE_LABEL (TREE_VEC_ELT (vec, i));
basic_block label_bb = label_to_block (lab);
- if (label_bb->aux && label_bb->aux != (void *)1)
- abort ();
+ gcc_assert (!label_bb->aux || label_bb->aux == (void *)1);
label_bb->aux = (void *)1;
}
err = 1;
}
- for (e = bb->succ; e; e = e->succ_next)
+ FOR_EACH_EDGE (e, ei, bb->succs)
{
if (!e->dest->aux)
{
}
}
- for (e = bb->succ; e; e = e->succ_next)
+ FOR_EACH_EDGE (e, ei, bb->succs)
e->dest->aux = (void *)0;
}
}
-/* Updates phi nodes after creating forwarder block joined
+/* Updates phi nodes after creating a forwarder block joined
by edge FALLTHRU. */
static void
tree_make_forwarder_block (edge fallthru)
{
edge e;
+ edge_iterator ei;
basic_block dummy, bb;
- tree phi, new_phi, var, prev, next;
+ tree phi, new_phi, var;
dummy = fallthru->src;
bb = fallthru->dest;
- if (!bb->pred->pred_next)
+ if (EDGE_COUNT (bb->preds) == 1)
return;
/* If we redirected a branch we must create new phi nodes at the
}
/* Ensure that the PHI node chain is in the same order. */
- prev = NULL;
- for (phi = phi_nodes (bb); phi; phi = next)
- {
- next = PHI_CHAIN (phi);
- PHI_CHAIN (phi) = prev;
- prev = phi;
- }
- set_phi_nodes (bb, prev);
+ set_phi_nodes (bb, phi_reverse (phi_nodes (bb)));
/* Add the arguments we have stored on edges. */
- for (e = bb->pred; e; e = e->pred_next)
+ FOR_EACH_EDGE (e, ei, bb->preds)
{
if (e == fallthru)
continue;
- for (phi = phi_nodes (bb), var = PENDING_STMT (e);
- phi;
- phi = PHI_CHAIN (phi), var = TREE_CHAIN (var))
- add_phi_arg (&phi, TREE_VALUE (var), e);
-
- PENDING_STMT (e) = NULL;
+ flush_pending_stmts (e);
}
}
/* Return true if basic block BB does nothing except pass control
flow to another block and that we can safely insert a label at
- the start of the successor block. */
+ the start of the successor block.
+
+ As a precondition, we require that BB be not equal to
+ ENTRY_BLOCK_PTR. */
static bool
tree_forwarder_block_p (basic_block bb)
{
block_stmt_iterator bsi;
edge e;
+ edge_iterator ei;
- /* If we have already determined that this block is not forwardable,
- then no further checks are necessary. */
- if (! bb_ann (bb)->forwardable)
- return false;
-
- /* BB must have a single outgoing normal edge. Otherwise it can not be
- a forwarder block. */
- if (!bb->succ
- || bb->succ->succ_next
- || bb->succ->dest == EXIT_BLOCK_PTR
- || (bb->succ->flags & EDGE_ABNORMAL)
- || bb == ENTRY_BLOCK_PTR)
- {
- bb_ann (bb)->forwardable = 0;
- return false;
- }
+ /* BB must have a single outgoing edge. */
+ if (EDGE_COUNT (bb->succs) != 1
+ /* BB can not have any PHI nodes. This could potentially be
+ relaxed early in compilation if we re-rewrote the variables
+ appearing in any PHI nodes in forwarder blocks. */
+ || phi_nodes (bb)
+ /* BB may not be a predecessor of EXIT_BLOCK_PTR. */
+ || EDGE_SUCC (bb, 0)->dest == EXIT_BLOCK_PTR
+ /* BB may not have an abnormal outgoing edge. */
+ || (EDGE_SUCC (bb, 0)->flags & EDGE_ABNORMAL))
+ return false;
+
+#if ENABLE_CHECKING
+ gcc_assert (bb != ENTRY_BLOCK_PTR);
+#endif
/* Successors of the entry block are not forwarders. */
- for (e = ENTRY_BLOCK_PTR->succ; e; e = e->succ_next)
+ FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs)
if (e->dest == bb)
- {
- bb_ann (bb)->forwardable = 0;
- return false;
- }
-
- /* BB can not have any PHI nodes. This could potentially be relaxed
- early in compilation if we re-rewrote the variables appearing in
- any PHI nodes in forwarder blocks. */
- if (phi_nodes (bb))
- {
- bb_ann (bb)->forwardable = 0;
- return false;
- }
+ return false;
/* Now walk through the statements. We can ignore labels, anything else
means this is not a forwarder block. */
break;
default:
- bb_ann (bb)->forwardable = 0;
return false;
}
}
return true;
}
+/* Thread jumps from BB. */
-/* Thread jumps over empty statements.
-
- This code should _not_ thread over obviously equivalent conditions
- as that requires nontrivial updates to the SSA graph. */
-
static bool
-thread_jumps (void)
+thread_jumps_from_bb (basic_block bb)
{
- edge e, next, last, old;
- basic_block bb, dest, tmp;
- tree phi;
- int arg;
+ edge_iterator ei;
+ edge e;
bool retval = false;
- FOR_EACH_BB (bb)
- bb_ann (bb)->forwardable = 1;
-
- FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR, next_bb)
+ /* Examine each of our block's successors to see if it is
+ forwardable. */
+ for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); )
{
- /* Don't waste time on unreachable blocks. */
- if (!bb->pred)
- continue;
+ int freq;
+ gcov_type count;
+ edge last, old;
+ basic_block dest, tmp, curr, old_dest;
+ tree phi;
+ int arg;
- /* Nor on forwarders. */
- if (tree_forwarder_block_p (bb))
- continue;
-
- /* This block is now part of a forwarding path, mark it as not
- forwardable so that we can detect loops. This bit will be
- reset below. */
- bb_ann (bb)->forwardable = 0;
-
- /* Examine each of our block's successors to see if it is
- forwardable. */
- for (e = bb->succ; e; e = next)
+ /* If the edge is abnormal or its destination is not
+ forwardable, then there's nothing to do. */
+ if ((e->flags & EDGE_ABNORMAL)
+ || !bb_ann (e->dest)->forwardable)
{
- next = e->succ_next;
-
- /* If the edge is abnormal or its destination is not
- forwardable, then there's nothing to do. */
- if ((e->flags & EDGE_ABNORMAL)
- || !tree_forwarder_block_p (e->dest))
- continue;
-
- /* Now walk through as many forwarder block as possible to
- find the ultimate destination we want to thread our jump
- to. */
- last = e->dest->succ;
- bb_ann (e->dest)->forwardable = 0;
- for (dest = e->dest->succ->dest;
- tree_forwarder_block_p (dest);
- last = dest->succ,
- dest = dest->succ->dest)
- {
- /* An infinite loop detected. We redirect the edge anyway, so
- that the loop is shrunk into single basic block. */
- if (!bb_ann (dest)->forwardable)
- break;
-
- if (dest->succ->dest == EXIT_BLOCK_PTR)
- break;
+ ei_next (&ei);
+ continue;
+ }
- bb_ann (dest)->forwardable = 0;
- }
+ /* Now walk through as many forwarder blocks as possible to find
+ the ultimate destination we want to thread our jump to. */
+ last = EDGE_SUCC (e->dest, 0);
+ bb_ann (e->dest)->forwardable = 0;
+ for (dest = EDGE_SUCC (e->dest, 0)->dest;
+ bb_ann (dest)->forwardable;
+ last = EDGE_SUCC (dest, 0),
+ dest = EDGE_SUCC (dest, 0)->dest)
+ bb_ann (dest)->forwardable = 0;
+
+ /* Reset the forwardable marks to 1. */
+ for (tmp = e->dest;
+ tmp != dest;
+ tmp = EDGE_SUCC (tmp, 0)->dest)
+ bb_ann (tmp)->forwardable = 1;
+
+ if (dest == e->dest)
+ {
+ ei_next (&ei);
+ continue;
+ }
- /* Reset the forwardable marks to 1. */
- for (tmp = e->dest;
- tmp != dest;
- tmp = tmp->succ->dest)
- bb_ann (tmp)->forwardable = 1;
-
- if (dest == e->dest)
- continue;
-
- old = find_edge (bb, dest);
- if (old)
+ old = find_edge (bb, dest);
+ if (old)
+ {
+ /* If there already is an edge, check whether the values in
+ phi nodes differ. */
+ if (!phi_alternatives_equal (dest, last, old))
{
- /* If there already is an edge, check whether the values
- in phi nodes differ. */
- if (!phi_alternatives_equal (dest, last, old))
+ /* The previous block is forwarder. Redirect our jump
+ to that target instead since we know it has no PHI
+ nodes that will need updating. */
+ dest = last->src;
+
+ /* That might mean that no forwarding at all is
+ possible. */
+ if (dest == e->dest)
{
- /* The previous block is forwarder. Redirect our jump
- to that target instead since we know it has no PHI
- nodes that will need updating. */
- dest = last->src;
-
- /* That might mean that no forwarding at all is possible. */
- if (dest == e->dest)
- continue;
-
- old = find_edge (bb, dest);
+ ei_next (&ei);
+ continue;
}
+
+ old = find_edge (bb, dest);
}
+ }
- /* Perform the redirection. */
- retval = true;
- e = redirect_edge_and_branch (e, dest);
+ /* Perform the redirection. */
+ retval = true;
+ count = e->count;
+ freq = EDGE_FREQUENCY (e);
+ old_dest = e->dest;
+ e = redirect_edge_and_branch (e, dest);
+
+ /* Update the profile. */
+ if (profile_status != PROFILE_ABSENT)
+ for (curr = old_dest;
+ curr != dest;
+ curr = EDGE_SUCC (curr, 0)->dest)
+ {
+ curr->frequency -= freq;
+ if (curr->frequency < 0)
+ curr->frequency = 0;
+ curr->count -= count;
+ if (curr->count < 0)
+ curr->count = 0;
+ EDGE_SUCC (curr, 0)->count -= count;
+ if (EDGE_SUCC (curr, 0)->count < 0)
+ EDGE_SUCC (curr, 0)->count = 0;
+ }
+
+ if (!old)
+ {
+ /* Update PHI nodes. We know that the new argument should
+ have the same value as the argument associated with LAST.
+ Otherwise we would have changed our target block
+ above. */
+ for (phi = phi_nodes (dest); phi; phi = PHI_CHAIN (phi))
+ {
+ arg = phi_arg_from_edge (phi, last);
+ gcc_assert (arg >= 0);
+ add_phi_arg (&phi, PHI_ARG_DEF (phi, arg), e);
+ }
+ }
+
+ /* Remove the unreachable blocks (observe that if all blocks
+ were reachable before, only those in the path we threaded
+ over and did not have any predecessor outside of the path
+ become unreachable). */
+ for (; old_dest != dest; old_dest = tmp)
+ {
+ tmp = EDGE_SUCC (old_dest, 0)->dest;
- /* TODO -- updating dominators in this case is simple. */
- free_dominance_info (CDI_DOMINATORS);
+ if (EDGE_COUNT (old_dest->preds) > 0)
+ break;
+
+ delete_basic_block (old_dest);
+ }
- if (!old)
+ /* Update the dominators. */
+ if (dom_info_available_p (CDI_DOMINATORS))
+ {
+ /* If the dominator of the destination was in the
+ path, set its dominator to the start of the
+ redirected edge. */
+ if (get_immediate_dominator (CDI_DOMINATORS, old_dest) == NULL)
+ set_immediate_dominator (CDI_DOMINATORS, old_dest, bb);
+
+ /* Now proceed like if we forwarded just over one edge at a
+ time. Algorithm for forwarding edge S --> A over
+ edge A --> B then is
+
+ if (idom (B) == A
+ && !dominated_by (S, B))
+ idom (B) = idom (A);
+ recount_idom (A); */
+
+ for (; old_dest != dest; old_dest = tmp)
{
- /* Update PHI nodes. We know that the new argument should
- have the same value as the argument associated with LAST.
- Otherwise we would have changed our target block above. */
- for (phi = phi_nodes (dest); phi; phi = PHI_CHAIN (phi))
+ basic_block dom;
+
+ tmp = EDGE_SUCC (old_dest, 0)->dest;
+
+ if (get_immediate_dominator (CDI_DOMINATORS, tmp) == old_dest
+ && !dominated_by_p (CDI_DOMINATORS, bb, tmp))
{
- arg = phi_arg_from_edge (phi, last);
- if (arg < 0)
- abort ();
- add_phi_arg (&phi, PHI_ARG_DEF (phi, arg), e);
+ dom = get_immediate_dominator (CDI_DOMINATORS, old_dest);
+ set_immediate_dominator (CDI_DOMINATORS, tmp, dom);
}
+
+ dom = recount_dominator (CDI_DOMINATORS, old_dest);
+ set_immediate_dominator (CDI_DOMINATORS, old_dest, dom);
+ }
+ }
+ }
+
+ return retval;
+}
+
+
+/* Thread jumps over empty statements.
+
+ This code should _not_ thread over obviously equivalent conditions
+ as that requires nontrivial updates to the SSA graph.
+
+ As a precondition, we require that all basic blocks be reachable.
+ That is, there should be no opportunities left for
+ delete_unreachable_blocks. */
+
+static bool
+thread_jumps (void)
+{
+ basic_block bb;
+ bool retval = false;
+ basic_block *worklist = xmalloc (sizeof (basic_block) * last_basic_block);
+ basic_block *current = worklist;
+
+ FOR_EACH_BB (bb)
+ {
+ bb_ann (bb)->forwardable = tree_forwarder_block_p (bb);
+ bb->flags &= ~BB_VISITED;
+ }
+
+ /* We pretend to have ENTRY_BLOCK_PTR in WORKLIST. This way,
+ ENTRY_BLOCK_PTR will never be entered into WORKLIST. */
+ ENTRY_BLOCK_PTR->flags |= BB_VISITED;
+
+ /* Initialize WORKLIST by putting non-forwarder blocks that
+ immediately precede forwarder blocks because those are the ones
+ that we know we can thread jumps from. We use BB_VISITED to
+ indicate whether a given basic block is in WORKLIST or not,
+ thereby avoiding duplicates in WORKLIST. */
+ FOR_EACH_BB (bb)
+ {
+ edge_iterator ei;
+ edge e;
+
+ /* We are not interested in finding non-forwarder blocks
+ directly. We want to find non-forwarder blocks as
+ predecessors of a forwarder block. */
+ if (!bb_ann (bb)->forwardable)
+ continue;
+
+ /* Now we know BB is a forwarder block. Visit each of its
+ incoming edges and add to WORKLIST all non-forwarder blocks
+ among BB's predecessors. */
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ {
+ /* We don't want to put a duplicate into WORKLIST. */
+ if ((e->src->flags & BB_VISITED) == 0
+ /* We are not interested in threading jumps from a forwarder
+ block. */
+ && !bb_ann (e->src)->forwardable)
+ {
+ e->src->flags |= BB_VISITED;
+ *current++ = e->src;
}
}
+ }
+
+ /* Now let's drain WORKLIST. */
+ while (worklist != current)
+ {
+ bb = *--current;
+
+ /* BB is no longer in WORKLIST, so clear BB_VISITED. */
+ bb->flags &= ~BB_VISITED;
+
+ if (thread_jumps_from_bb (bb))
+ {
+ retval = true;
+
+ if (tree_forwarder_block_p (bb))
+ {
+ edge_iterator ej;
+ edge f;
- /* Reset the forwardable bit on our block since it's no longer in
- a forwarding chain path. */
- bb_ann (bb)->forwardable = 1;
+ bb_ann (bb)->forwardable = true;
+
+ /* Attempts to thread through BB may have been blocked
+ because BB was not a forwarder block before. Now
+ that BB is a forwarder block, we should revisit BB's
+ predecessors. */
+ FOR_EACH_EDGE (f, ej, bb->preds)
+ {
+ /* We don't want to put a duplicate into WORKLIST. */
+ if ((f->src->flags & BB_VISITED) == 0
+ /* We are not interested in threading jumps from a
+ forwarder block. */
+ && !bb_ann (f->src)->forwardable)
+ {
+ f->src->flags |= BB_VISITED;
+ *current++ = f->src;
+ }
+ }
+ }
+ }
}
+ ENTRY_BLOCK_PTR->flags &= ~BB_VISITED;
+
+ free (worklist);
+
return retval;
}
edge tmp;
block_stmt_iterator b;
tree stmt;
+ edge_iterator ei;
/* Verify that all targets will be TARGET. */
- for (tmp = src->succ; tmp; tmp = tmp->succ_next)
+ FOR_EACH_EDGE (tmp, ei, src->succs)
if (tmp->dest != target && tmp != e)
break;
case GOTO_EXPR:
/* No non-abnormal edges should lead from a non-simple goto, and
simple ones should be represented implicitly. */
- abort ();
+ gcc_unreachable ();
case SWITCH_EXPR:
{
- tree vec = SWITCH_LABELS (stmt);
- size_t i, n = TREE_VEC_LENGTH (vec);
+ edge e2;
- for (i = 0; i < n; ++i)
+ /* We need to update the LABEL_DECL in the switch vector to
+ reflect the edge redirection.
+
+ There is precisely one CASE_LABEL_EXPR in the switch vector
+ which needs updating. Either its label needs to be updated
+ or it needs to be directed to a new case leader. */
+ e2 = find_edge (e->src, dest);
+ if (e2)
+ {
+ /* In this case we need to change the case leader for the
+ current leader of E to be the case leader for E2. */
+ tree e_leader = get_case_leader_for_edge (e);
+ tree e2_leader = get_case_leader_for_edge (e2);
+ CASE_LEADER_OR_LABEL (e_leader) = e2_leader;
+ }
+ else
{
- tree elt = TREE_VEC_ELT (vec, i);
- if (label_to_block (CASE_LABEL (elt)) == e->dest)
- CASE_LABEL (elt) = label;
+ /* No edge exists from E->src to DEST, so we will simply
+ change E->dest. The case leader does not change, but
+ the LABEL_DECL for the leader does change. */
+ CASE_LEADER_OR_LABEL (get_case_leader_for_edge (e)) = label;
}
+ break;
}
- break;
case RETURN_EXPR:
bsi_remove (&bsi);
default:
/* Otherwise it must be a fallthru edge, and we don't need to
do anything besides redirecting it. */
- if (!(e->flags & EDGE_FALLTHRU))
- abort ();
+ gcc_assert (e->flags & EDGE_FALLTHRU);
break;
}
tree_redirect_edge_and_branch_force (edge e, basic_block dest)
{
e = tree_redirect_edge_and_branch (e, dest);
- if (!e)
- abort ();
+ gcc_assert (e);
return NULL;
}
tree act;
basic_block new_bb;
edge e;
+ edge_iterator ei;
new_bb = create_empty_bb (bb);
/* Redirect the outgoing edges. */
- new_bb->succ = bb->succ;
- bb->succ = NULL;
- for (e = new_bb->succ; e; e = e->succ_next)
+ new_bb->succs = bb->succs;
+ bb->succs = NULL;
+ FOR_EACH_EDGE (e, ei, new_bb->succs)
e->src = new_bb;
if (stmt && TREE_CODE ((tree) stmt) == LABEL_EXPR)
return true;
}
-
/* Create a duplicate of the basic block BB. NOTE: This does not
preserve SSA form. */
{
basic_block new_bb;
block_stmt_iterator bsi, bsi_tgt;
+ tree phi, val;
+ ssa_op_iter op_iter;
new_bb = create_empty_bb (EXIT_BLOCK_PTR->prev_bb);
+
+ /* First copy the phi nodes. We do not copy phi node arguments here,
+ since the edges are not ready yet. Keep the chain of phi nodes in
+ the same order, so that we can add them later. */
+ for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
+ {
+ mark_for_rewrite (PHI_RESULT (phi));
+ create_phi_node (PHI_RESULT (phi), new_bb);
+ }
+ set_phi_nodes (new_bb, phi_reverse (phi_nodes (new_bb)));
+
bsi_tgt = bsi_start (new_bb);
for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
{
if (TREE_CODE (stmt) == LABEL_EXPR)
continue;
+ /* Record the definitions. */
+ get_stmt_operands (stmt);
+
+ FOR_EACH_SSA_TREE_OPERAND (val, stmt, op_iter, SSA_OP_ALL_DEFS)
+ mark_for_rewrite (val);
+
copy = unshare_expr (stmt);
/* Copy also the virtual operands. */
return new_bb;
}
+/* Basic block BB_COPY was created by code duplication. Add phi node
+ arguments for edges going out of BB_COPY. The blocks that were
+ duplicated have rbi->duplicated set to one. */
+
+void
+add_phi_args_after_copy_bb (basic_block bb_copy)
+{
+ basic_block bb, dest;
+ edge e, e_copy;
+ edge_iterator ei;
+ tree phi, phi_copy, phi_next, def;
+
+ bb = bb_copy->rbi->original;
+
+ FOR_EACH_EDGE (e_copy, ei, bb_copy->succs)
+ {
+ if (!phi_nodes (e_copy->dest))
+ continue;
+
+ if (e_copy->dest->rbi->duplicated)
+ dest = e_copy->dest->rbi->original;
+ else
+ dest = e_copy->dest;
+
+ e = find_edge (bb, dest);
+ if (!e)
+ {
+ /* During loop unrolling the target of the latch edge is copied.
+ In this case we are not looking for edge to dest, but to
+ duplicated block whose original was dest. */
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ if (e->dest->rbi->duplicated
+ && e->dest->rbi->original == dest)
+ break;
+
+ gcc_assert (e != NULL);
+ }
+
+ for (phi = phi_nodes (e->dest), phi_copy = phi_nodes (e_copy->dest);
+ phi;
+ phi = phi_next, phi_copy = PHI_CHAIN (phi_copy))
+ {
+ phi_next = PHI_CHAIN (phi);
+
+ gcc_assert (PHI_RESULT (phi) == PHI_RESULT (phi_copy));
+ def = PHI_ARG_DEF_FROM_EDGE (phi, e);
+ add_phi_arg (&phi_copy, def, e_copy);
+ }
+ }
+}
+
+/* Blocks in REGION_COPY array of length N_REGION were created by
+ duplication of basic blocks. Add phi node arguments for edges
+ going from these blocks. */
+
+void
+add_phi_args_after_copy (basic_block *region_copy, unsigned n_region)
+{
+ unsigned i;
+
+ for (i = 0; i < n_region; i++)
+ region_copy[i]->rbi->duplicated = 1;
+
+ for (i = 0; i < n_region; i++)
+ add_phi_args_after_copy_bb (region_copy[i]);
+
+ for (i = 0; i < n_region; i++)
+ region_copy[i]->rbi->duplicated = 0;
+}
+
+/* Maps the old ssa name FROM_NAME to TO_NAME. */
+
+struct ssa_name_map_entry
+{
+ tree from_name;
+ tree to_name;
+};
+
+/* Hash function for ssa_name_map_entry. */
+
+static hashval_t
+ssa_name_map_entry_hash (const void *entry)
+{
+ const struct ssa_name_map_entry *en = entry;
+ return SSA_NAME_VERSION (en->from_name);
+}
+
+/* Equality function for ssa_name_map_entry. */
+
+static int
+ssa_name_map_entry_eq (const void *in_table, const void *ssa_name)
+{
+ const struct ssa_name_map_entry *en = in_table;
+
+ return en->from_name == ssa_name;
+}
+
+/* Allocate duplicates of ssa names in list DEFINITIONS and store the mapping
+ to MAP. */
+
+void
+allocate_ssa_names (bitmap definitions, htab_t *map)
+{
+ tree name;
+ struct ssa_name_map_entry *entry;
+ PTR *slot;
+ unsigned ver;
+ bitmap_iterator bi;
+
+ if (!*map)
+ *map = htab_create (10, ssa_name_map_entry_hash,
+ ssa_name_map_entry_eq, free);
+ EXECUTE_IF_SET_IN_BITMAP (definitions, 0, ver, bi)
+ {
+ name = ssa_name (ver);
+ slot = htab_find_slot_with_hash (*map, name, SSA_NAME_VERSION (name),
+ INSERT);
+ if (*slot)
+ entry = *slot;
+ else
+ {
+ entry = xmalloc (sizeof (struct ssa_name_map_entry));
+ entry->from_name = name;
+ *slot = entry;
+ }
+ entry->to_name = duplicate_ssa_name (name, SSA_NAME_DEF_STMT (name));
+ }
+}
+
+/* Rewrite the definition DEF in statement STMT to new ssa name as specified
+ by the mapping MAP. */
+
+static void
+rewrite_to_new_ssa_names_def (def_operand_p def, tree stmt, htab_t map)
+{
+ tree name = DEF_FROM_PTR (def);
+ struct ssa_name_map_entry *entry;
+
+ gcc_assert (TREE_CODE (name) == SSA_NAME);
+
+ entry = htab_find_with_hash (map, name, SSA_NAME_VERSION (name));
+ if (!entry)
+ return;
+
+ SET_DEF (def, entry->to_name);
+ SSA_NAME_DEF_STMT (entry->to_name) = stmt;
+}
+
+/* Rewrite the USE to new ssa name as specified by the mapping MAP. */
+
+static void
+rewrite_to_new_ssa_names_use (use_operand_p use, htab_t map)
+{
+ tree name = USE_FROM_PTR (use);
+ struct ssa_name_map_entry *entry;
+
+ if (TREE_CODE (name) != SSA_NAME)
+ return;
+
+ entry = htab_find_with_hash (map, name, SSA_NAME_VERSION (name));
+ if (!entry)
+ return;
+
+ SET_USE (use, entry->to_name);
+}
+
+/* Rewrite the ssa names in basic block BB to new ones as specified by the
+ mapping MAP. */
+
+void
+rewrite_to_new_ssa_names_bb (basic_block bb, htab_t map)
+{
+ unsigned i;
+ edge e;
+ edge_iterator ei;
+ tree phi, stmt;
+ block_stmt_iterator bsi;
+ use_optype uses;
+ vuse_optype vuses;
+ def_optype defs;
+ v_may_def_optype v_may_defs;
+ v_must_def_optype v_must_defs;
+ stmt_ann_t ann;
+
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ if (e->flags & EDGE_ABNORMAL)
+ break;
+
+ for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
+ {
+ rewrite_to_new_ssa_names_def (PHI_RESULT_PTR (phi), phi, map);
+ if (e)
+ SSA_NAME_OCCURS_IN_ABNORMAL_PHI (PHI_RESULT (phi)) = 1;
+ }
+
+ for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+ {
+ stmt = bsi_stmt (bsi);
+ get_stmt_operands (stmt);
+ ann = stmt_ann (stmt);
+
+ uses = USE_OPS (ann);
+ for (i = 0; i < NUM_USES (uses); i++)
+ rewrite_to_new_ssa_names_use (USE_OP_PTR (uses, i), map);
+
+ defs = DEF_OPS (ann);
+ for (i = 0; i < NUM_DEFS (defs); i++)
+ rewrite_to_new_ssa_names_def (DEF_OP_PTR (defs, i), stmt, map);
+
+ vuses = VUSE_OPS (ann);
+ for (i = 0; i < NUM_VUSES (vuses); i++)
+ rewrite_to_new_ssa_names_use (VUSE_OP_PTR (vuses, i), map);
+
+ v_may_defs = V_MAY_DEF_OPS (ann);
+ for (i = 0; i < NUM_V_MAY_DEFS (v_may_defs); i++)
+ {
+ rewrite_to_new_ssa_names_use
+ (V_MAY_DEF_OP_PTR (v_may_defs, i), map);
+ rewrite_to_new_ssa_names_def
+ (V_MAY_DEF_RESULT_PTR (v_may_defs, i), stmt, map);
+ }
+
+ v_must_defs = V_MUST_DEF_OPS (ann);
+ for (i = 0; i < NUM_V_MUST_DEFS (v_must_defs); i++)
+ {
+ rewrite_to_new_ssa_names_def
+ (V_MUST_DEF_RESULT_PTR (v_must_defs, i), stmt, map);
+ rewrite_to_new_ssa_names_use
+ (V_MUST_DEF_KILL_PTR (v_must_defs, i), map);
+ }
+ }
+
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ for (phi = phi_nodes (e->dest); phi; phi = PHI_CHAIN (phi))
+ {
+ rewrite_to_new_ssa_names_use
+ (PHI_ARG_DEF_PTR_FROM_EDGE (phi, e), map);
+
+ if (e->flags & EDGE_ABNORMAL)
+ {
+ tree op = PHI_ARG_DEF_FROM_EDGE (phi, e);
+ SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op) = 1;
+ }
+ }
+}
+
+/* Rewrite the ssa names in N_REGION blocks REGION to the new ones as specified
+ by the mapping MAP. */
+
+void
+rewrite_to_new_ssa_names (basic_block *region, unsigned n_region, htab_t map)
+{
+ unsigned r;
+
+ for (r = 0; r < n_region; r++)
+ rewrite_to_new_ssa_names_bb (region[r], map);
+}
+
+/* Duplicates a REGION (set of N_REGION basic blocks) with just a single
+ important exit edge EXIT. By important we mean that no SSA name defined
+ inside region is live over the other exit edges of the region. All entry
+ edges to the region must go to ENTRY->dest. The edge ENTRY is redirected
+ to the duplicate of the region. SSA form, dominance and loop information
+ is updated. The new basic blocks are stored to REGION_COPY in the same
+ order as they had in REGION, provided that REGION_COPY is not NULL.
+ The function returns false if it is unable to copy the region,
+ true otherwise. */
+
+bool
+tree_duplicate_sese_region (edge entry, edge exit,
+ basic_block *region, unsigned n_region,
+ basic_block *region_copy)
+{
+ unsigned i, n_doms, ver;
+ bool free_region_copy = false, copying_header = false;
+ struct loop *loop = entry->dest->loop_father;
+ edge exit_copy;
+ bitmap definitions;
+ tree phi;
+ basic_block *doms;
+ htab_t ssa_name_map = NULL;
+ edge redirected;
+ bitmap_iterator bi;
+
+ if (!can_copy_bbs_p (region, n_region))
+ return false;
+
+ /* Some sanity checking. Note that we do not check for all possible
+ missuses of the functions. I.e. if you ask to copy something weird,
+ it will work, but the state of structures probably will not be
+ correct. */
+
+ for (i = 0; i < n_region; i++)
+ {
+ /* We do not handle subloops, i.e. all the blocks must belong to the
+ same loop. */
+ if (region[i]->loop_father != loop)
+ return false;
+
+ if (region[i] != entry->dest
+ && region[i] == loop->header)
+ return false;
+ }
+
+ loop->copy = loop;
+
+ /* In case the function is used for loop header copying (which is the primary
+ use), ensure that EXIT and its copy will be new latch and entry edges. */
+ if (loop->header == entry->dest)
+ {
+ copying_header = true;
+ loop->copy = loop->outer;
+
+ if (!dominated_by_p (CDI_DOMINATORS, loop->latch, exit->src))
+ return false;
+
+ for (i = 0; i < n_region; i++)
+ if (region[i] != exit->src
+ && dominated_by_p (CDI_DOMINATORS, region[i], exit->src))
+ return false;
+ }
+
+ if (!region_copy)
+ {
+ region_copy = xmalloc (sizeof (basic_block) * n_region);
+ free_region_copy = true;
+ }
+
+ gcc_assert (!any_marked_for_rewrite_p ());
+
+ /* Record blocks outside the region that are duplicated by something
+ inside. */
+ doms = xmalloc (sizeof (basic_block) * n_basic_blocks);
+ n_doms = get_dominated_by_region (CDI_DOMINATORS, region, n_region, doms);
+
+ copy_bbs (region, n_region, region_copy, &exit, 1, &exit_copy, loop);
+ definitions = marked_ssa_names ();
+
+ if (copying_header)
+ {
+ loop->header = exit->dest;
+ loop->latch = exit->src;
+ }
+
+ /* Redirect the entry and add the phi node arguments. */
+ redirected = redirect_edge_and_branch (entry, entry->dest->rbi->copy);
+ gcc_assert (redirected != NULL);
+ flush_pending_stmts (entry);
+
+ /* Concerning updating of dominators: We must recount dominators
+ for entry block and its copy. Anything that is outside of the region, but
+ was dominated by something inside needs recounting as well. */
+ set_immediate_dominator (CDI_DOMINATORS, entry->dest, entry->src);
+ doms[n_doms++] = entry->dest->rbi->original;
+ iterate_fix_dominators (CDI_DOMINATORS, doms, n_doms);
+ free (doms);
+
+ /* Add the other phi node arguments. */
+ add_phi_args_after_copy (region_copy, n_region);
+
+ /* Add phi nodes for definitions at exit. TODO -- once we have immediate
+ uses, it should be possible to emit phi nodes just for definitions that
+ are used outside region. */
+ EXECUTE_IF_SET_IN_BITMAP (definitions, 0, ver, bi)
+ {
+ tree name = ssa_name (ver);
+
+ phi = create_phi_node (name, exit->dest);
+ add_phi_arg (&phi, name, exit);
+ add_phi_arg (&phi, name, exit_copy);
+
+ SSA_NAME_DEF_STMT (name) = phi;
+ }
+
+ /* And create new definitions inside region and its copy. TODO -- once we
+ have immediate uses, it might be better to leave definitions in region
+ unchanged, create new ssa names for phi nodes on exit, and rewrite
+ the uses, to avoid changing the copied region. */
+ allocate_ssa_names (definitions, &ssa_name_map);
+ rewrite_to_new_ssa_names (region, n_region, ssa_name_map);
+ allocate_ssa_names (definitions, &ssa_name_map);
+ rewrite_to_new_ssa_names (region_copy, n_region, ssa_name_map);
+ htab_delete (ssa_name_map);
+
+ if (free_region_copy)
+ free (region_copy);
+
+ unmark_all_for_rewrite ();
+ BITMAP_XFREE (definitions);
+
+ return true;
+}
/* Dump FUNCTION_DECL FN to file FILE using FLAGS (see TDF_* in tree.h) */
if (basic_block_info)
{
/* Make a CFG based dump. */
+ check_bb_profile (ENTRY_BLOCK_PTR, file);
if (!ignore_topmost_bind)
fprintf (file, "{\n");
dump_generic_bb (file, bb, 2, flags);
fprintf (file, "}\n");
+ check_bb_profile (EXIT_BLOCK_PTR, file);
}
else
{
/* Pretty print of the loops intermediate representation. */
static void print_loop (FILE *, struct loop *, int);
-static void print_pred_bbs (FILE *, edge);
-static void print_succ_bbs (FILE *, edge);
+static void print_pred_bbs (FILE *, basic_block bb);
+static void print_succ_bbs (FILE *, basic_block bb);
/* Print the predecessors indexes of edge E on FILE. */
static void
-print_pred_bbs (FILE *file, edge e)
+print_pred_bbs (FILE *file, basic_block bb)
{
- if (e == NULL)
- return;
-
- else if (e->pred_next == NULL)
+ edge e;
+ edge_iterator ei;
+
+ FOR_EACH_EDGE (e, ei, bb->preds)
fprintf (file, "bb_%d", e->src->index);
-
- else
- {
- fprintf (file, "bb_%d, ", e->src->index);
- print_pred_bbs (file, e->pred_next);
- }
}
/* Print the successors indexes of edge E on FILE. */
static void
-print_succ_bbs (FILE *file, edge e)
+print_succ_bbs (FILE *file, basic_block bb)
{
- if (e == NULL)
- return;
- else if (e->succ_next == NULL)
- fprintf (file, "bb_%d", e->dest->index);
- else
- {
- fprintf (file, "bb_%d, ", e->dest->index);
- print_succ_bbs (file, e->succ_next);
- }
+ edge e;
+ edge_iterator ei;
+
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ fprintf (file, "bb_%d", e->src->index);
}
{
/* Print the basic_block's header. */
fprintf (file, "%s bb_%d (preds = {", s_indent, bb->index);
- print_pred_bbs (file, bb->pred);
+ print_pred_bbs (file, bb);
fprintf (file, "}, succs = {");
- print_succ_bbs (file, bb->succ);
+ print_succ_bbs (file, bb);
fprintf (file, "})\n");
/* Print the basic_block's body. */
tree_block_ends_with_call_p (basic_block bb)
{
block_stmt_iterator bsi = bsi_last (bb);
- tree t = tsi_stmt (bsi.tsi);
-
- if (TREE_CODE (t) == RETURN_EXPR && TREE_OPERAND (t, 0))
- t = TREE_OPERAND (t, 0);
-
- if (TREE_CODE (t) == MODIFY_EXPR)
- t = TREE_OPERAND (t, 1);
-
- return TREE_CODE (t) == CALL_EXPR;
+ return get_call_expr_in (bsi_stmt (bsi)) != NULL;
}
static bool
need_fake_edge_p (tree t)
{
- if (TREE_CODE (t) == RETURN_EXPR && TREE_OPERAND (t, 0))
- t = TREE_OPERAND (t, 0);
-
- if (TREE_CODE (t) == MODIFY_EXPR)
- t = TREE_OPERAND (t, 1);
+ tree call;
/* NORETURN and LONGJMP calls already have an edge to exit.
CONST, PURE and ALWAYS_RETURN calls do not need one.
figured out from the RTL in mark_constant_function, and
the counter incrementation code from -fprofile-arcs
leads to different results from -fbranch-probabilities. */
- if (TREE_CODE (t) == CALL_EXPR
- && !(call_expr_flags (t) &
- (ECF_NORETURN | ECF_LONGJMP | ECF_ALWAYS_RETURN)))
+ call = get_call_expr_in (t);
+ if (call
+ && !(call_expr_flags (call) &
+ (ECF_NORETURN | ECF_LONGJMP | ECF_ALWAYS_RETURN)))
return true;
if (TREE_CODE (t) == ASM_EXPR
Handle this by adding a dummy instruction in a new last basic block. */
if (check_last_block)
{
+ edge_iterator ei;
basic_block bb = EXIT_BLOCK_PTR->prev_bb;
block_stmt_iterator bsi = bsi_last (bb);
tree t = NULL_TREE;
{
edge e;
- for (e = bb->succ; e; e = e->succ_next)
+ FOR_EACH_EDGE (e, ei, bb->succs)
if (e->dest == EXIT_BLOCK_PTR)
{
bsi_insert_on_edge (e, build_empty_stmt ());
- bsi_commit_edge_inserts ((int *)NULL);
+ bsi_commit_edge_inserts ();
break;
}
}
mark that edge as fake and remove it later. */
#ifdef ENABLE_CHECKING
if (stmt == last_stmt)
- for (e = bb->succ; e; e = e->succ_next)
- if (e->dest == EXIT_BLOCK_PTR)
- abort ();
+ {
+ edge_iterator ei;
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ gcc_assert (e->dest != EXIT_BLOCK_PTR);
+ }
#endif
/* Note that the following may create a new basic block
tree_purge_dead_eh_edges (basic_block bb)
{
bool changed = false;
- edge e, next;
+ edge e;
+ edge_iterator ei;
tree stmt = last_stmt (bb);
if (stmt && tree_can_throw_internal (stmt))
return false;
- for (e = bb->succ; e ; e = next)
+ for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); )
{
- next = e->succ_next;
if (e->flags & EDGE_EH)
{
ssa_remove_edge (e);
changed = true;
}
+ else
+ ei_next (&ei);
}
+ /* Removal of dead EH edges might change dominators of not
+ just immediate successors. E.g. when bb1 is changed so that
+ it no longer can throw and bb1->bb3 and bb1->bb4 are dead
+ eh edges purged by this function in:
+ 0
+ / \
+ v v
+ 1-->2
+ / \ |
+ v v |
+ 3-->4 |
+ \ v
+ --->5
+ |
+ -
+ idom(bb5) must be recomputed. For now just free the dominance
+ info. */
+ if (changed)
+ free_dominance_info (CDI_DOMINATORS);
+
return changed;
}
tree_purge_all_dead_eh_edges (bitmap blocks)
{
bool changed = false;
- size_t i;
+ unsigned i;
+ bitmap_iterator bi;
- EXECUTE_IF_SET_IN_BITMAP (blocks, 0, i,
- { changed |= tree_purge_dead_eh_edges (BASIC_BLOCK (i)); });
+ EXECUTE_IF_SET_IN_BITMAP (blocks, 0, i, bi)
+ {
+ changed |= tree_purge_dead_eh_edges (BASIC_BLOCK (i));
+ }
return changed;
}
{
basic_block bb;
edge e;
+ edge_iterator ei;
FOR_ALL_BB (bb)
{
- for (e = bb->succ; e ; e = e->succ_next)
+ FOR_EACH_EDGE (e, ei, bb->succs)
if (EDGE_CRITICAL_P (e) && !(e->flags & EDGE_ABNORMAL))
{
split_edge (e);
PROP_no_crit_edges, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func, /* todo_flags_finish */
+ TODO_dump_func, /* todo_flags_finish */
+ 0 /* letter */
};
+
+\f
+/* Return EXP if it is a valid GIMPLE rvalue, else gimplify it into
+ a temporary, make sure and register it to be renamed if necessary,
+ and finally return the temporary. Put the statements to compute
+ EXP before the current statement in BSI. */
+
+tree
+gimplify_val (block_stmt_iterator *bsi, tree type, tree exp)
+{
+ tree t, new_stmt, orig_stmt;
+
+ if (is_gimple_val (exp))
+ return exp;
+
+ t = make_rename_temp (type, NULL);
+ new_stmt = build (MODIFY_EXPR, type, t, exp);
+
+ orig_stmt = bsi_stmt (*bsi);
+ SET_EXPR_LOCUS (new_stmt, EXPR_LOCUS (orig_stmt));
+ TREE_BLOCK (new_stmt) = TREE_BLOCK (orig_stmt);
+
+ bsi_insert_before (bsi, new_stmt, BSI_SAME_STMT);
+
+ return t;
+}
+
+/* Build a ternary operation and gimplify it. Emit code before BSI.
+ Return the gimple_val holding the result. */
+
+tree
+gimplify_build3 (block_stmt_iterator *bsi, enum tree_code code,
+ tree type, tree a, tree b, tree c)
+{
+ tree ret;
+
+ ret = fold (build3 (code, type, a, b, c));
+ STRIP_NOPS (ret);
+
+ return gimplify_val (bsi, type, ret);
+}
+
+/* Build a binary operation and gimplify it. Emit code before BSI.
+ Return the gimple_val holding the result. */
+
+tree
+gimplify_build2 (block_stmt_iterator *bsi, enum tree_code code,
+ tree type, tree a, tree b)
+{
+ tree ret;
+
+ ret = fold (build2 (code, type, a, b));
+ STRIP_NOPS (ret);
+
+ return gimplify_val (bsi, type, ret);
+}
+
+/* Build a unary operation and gimplify it. Emit code before BSI.
+ Return the gimple_val holding the result. */
+
+tree
+gimplify_build1 (block_stmt_iterator *bsi, enum tree_code code, tree type,
+ tree a)
+{
+ tree ret;
+
+ ret = fold (build1 (code, type, a));
+ STRIP_NOPS (ret);
+
+ return gimplify_val (bsi, type, ret);
+}
+
+
\f
/* Emit return warnings. */
#endif
tree last;
edge e;
+ edge_iterator ei;
if (warn_missing_noreturn
&& !TREE_THIS_VOLATILE (cfun->decl)
- && EXIT_BLOCK_PTR->pred == NULL
+ && EDGE_COUNT (EXIT_BLOCK_PTR->preds) == 0
&& !lang_hooks.function.missing_noreturn_ok_p (cfun->decl))
- warning ("%Jfunction might be possible candidate for attribute `noreturn'",
+ warning ("%Jfunction might be possible candidate for "
+ "attribute %<noreturn%>",
cfun->decl);
/* If we have a path to EXIT, then we do return. */
if (TREE_THIS_VOLATILE (cfun->decl)
- && EXIT_BLOCK_PTR->pred != NULL)
+ && EDGE_COUNT (EXIT_BLOCK_PTR->preds) > 0)
{
#ifdef USE_MAPPED_LOCATION
location = UNKNOWN_LOCATION;
#else
locus = NULL;
#endif
- for (e = EXIT_BLOCK_PTR->pred; e ; e = e->pred_next)
+ FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
{
last = last_stmt (e->src);
if (TREE_CODE (last) == RETURN_EXPR
#ifdef USE_MAPPED_LOCATION
if (location == UNKNOWN_LOCATION)
location = cfun->function_end_locus;
- warning ("%H`noreturn' function does return", &location);
+ warning ("%H%<noreturn%> function does return", &location);
#else
if (!locus)
locus = &cfun->function_end_locus;
- warning ("%H`noreturn' function does return", locus);
+ warning ("%H%<noreturn%> function does return", locus);
#endif
}
/* If we see "return;" in some basic block, then we do reach the end
without returning a value. */
else if (warn_return_type
- && EXIT_BLOCK_PTR->pred != NULL
+ && EDGE_COUNT (EXIT_BLOCK_PTR->preds) > 0
&& !VOID_TYPE_P (TREE_TYPE (TREE_TYPE (cfun->decl))))
{
- for (e = EXIT_BLOCK_PTR->pred; e ; e = e->pred_next)
+ FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
{
tree last = last_stmt (e->src);
if (TREE_CODE (last) == RETURN_EXPR
edge *true_edge,
edge *false_edge)
{
- edge e = b->succ;
+ edge e = EDGE_SUCC (b, 0);
if (e->flags & EDGE_TRUE_VALUE)
{
*true_edge = e;
- *false_edge = e->succ_next;
+ *false_edge = EDGE_SUCC (b, 1);
}
else
{
*false_edge = e;
- *true_edge = e->succ_next;
+ *true_edge = EDGE_SUCC (b, 1);
}
}
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- 0 /* todo_flags_finish */
+ 0, /* todo_flags_finish */
+ 0 /* letter */
};
#include "gt-tree-cfg.h"