static int tree_verify_flow_info (void);
static void tree_make_forwarder_block (edge);
static void tree_cfg2vcg (FILE *);
+static inline void change_bb_for_stmt (tree t, basic_block bb);
/* Flowgraph optimization and cleanup. */
static void tree_merge_blocks (basic_block, basic_block);
/* If this function receives a nonlocal goto, then we need to
make edges from this call site to all the nonlocal goto
handlers. */
- if (TREE_SIDE_EFFECTS (last)
- && current_function_has_nonlocal_label)
- make_goto_expr_edges (bb);
+ if (tree_can_make_abnormal_goto (last))
+ make_abnormal_goto_edges (bb, true);
/* If this statement has reachable exception handlers, then
create abnormal edges to them. */
/* 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. */
- tree op = get_call_expr_in (last);
- if (op && TREE_SIDE_EFFECTS (op)
- && current_function_has_nonlocal_label)
- make_goto_expr_edges (bb);
+ if (tree_can_make_abnormal_goto (last))
+ make_abnormal_goto_edges (bb, true);
make_eh_edges (last);
}
return VEC_index (basic_block, ifun->cfg->x_label_to_block_map, uid);
}
+/* Create edges for an abnormal goto statement at block BB. If FOR_CALL
+ is true, the source statement is a CALL_EXPR instead of a GOTO_EXPR. */
+
+void
+make_abnormal_goto_edges (basic_block bb, bool for_call)
+{
+ basic_block target_bb;
+ block_stmt_iterator bsi;
+
+ FOR_EACH_BB (target_bb)
+ for (bsi = bsi_start (target_bb); !bsi_end_p (bsi); bsi_next (&bsi))
+ {
+ tree target = bsi_stmt (bsi);
+
+ if (TREE_CODE (target) != LABEL_EXPR)
+ break;
+
+ target = LABEL_EXPR_LABEL (target);
+
+ /* Make an edge to every label block that has been marked as a
+ potential target for a computed goto or a non-local goto. */
+ if ((FORCED_LABEL (target) && !for_call)
+ || (DECL_NONLOCAL (target) && for_call))
+ {
+ make_edge (bb, target_bb, EDGE_ABNORMAL);
+ break;
+ }
+ }
+}
+
/* Create edges for a goto statement at block BB. */
static void
make_goto_expr_edges (basic_block bb)
{
- tree goto_t;
- basic_block target_bb;
- bool for_call;
block_stmt_iterator last = bsi_last (bb);
+ tree goto_t = bsi_stmt (last);
- goto_t = bsi_stmt (last);
-
- /* If the last statement is not a GOTO (i.e., it is a RETURN_EXPR,
- CALL_EXPR or MODIFY_EXPR), then the edge is an abnormal edge resulting
- from a nonlocal goto. */
- if (TREE_CODE (goto_t) != GOTO_EXPR)
- for_call = true;
- else
+ /* A simple GOTO creates normal edges. */
+ if (simple_goto_p (goto_t))
{
tree dest = GOTO_DESTINATION (goto_t);
- for_call = false;
-
- /* A GOTO to a local label creates normal edges. */
- if (simple_goto_p (goto_t))
- {
- edge e = make_edge (bb, label_to_block (dest), EDGE_FALLTHRU);
+ edge e = make_edge (bb, label_to_block (dest), EDGE_FALLTHRU);
#ifdef USE_MAPPED_LOCATION
- e->goto_locus = EXPR_LOCATION (goto_t);
+ e->goto_locus = EXPR_LOCATION (goto_t);
#else
- e->goto_locus = EXPR_LOCUS (goto_t);
+ e->goto_locus = EXPR_LOCUS (goto_t);
#endif
- bsi_remove (&last, true);
- return;
- }
-
- /* Nothing more to do for nonlocal gotos. */
- if (TREE_CODE (dest) == LABEL_DECL)
- return;
-
- /* Computed gotos remain. */
+ bsi_remove (&last, true);
+ return;
}
- /* Look for the block starting with the destination label. In the
- case of a computed goto, make an edge to any label block we find
- in the CFG. */
- FOR_EACH_BB (target_bb)
- {
- block_stmt_iterator bsi;
-
- for (bsi = bsi_start (target_bb); !bsi_end_p (bsi); bsi_next (&bsi))
- {
- tree target = bsi_stmt (bsi);
-
- if (TREE_CODE (target) != LABEL_EXPR)
- break;
-
- if (
- /* Computed GOTOs. Make an edge to every label block that has
- been marked as a potential target for a computed goto. */
- (FORCED_LABEL (LABEL_EXPR_LABEL (target)) && !for_call)
- /* Nonlocal GOTO target. Make an edge to every label block
- that has been marked as a potential target for a nonlocal
- goto. */
- || (DECL_NONLOCAL (LABEL_EXPR_LABEL (target)) && for_call))
- {
- make_edge (bb, target_bb, EDGE_ABNORMAL);
- break;
- }
- }
- }
+ /* A computed GOTO creates abnormal edges. */
+ make_abnormal_goto_edges (bb, false);
}
}
else
{
- set_bb_for_stmt (bsi_stmt (bsi), a);
+ change_bb_for_stmt (bsi_stmt (bsi), a);
bsi_next (&bsi);
}
}
}
-/* Checks whether EXPR is a simple local goto. */
+/* Return true if T is a simple local goto. */
+
+bool
+simple_goto_p (tree t)
+{
+ return (TREE_CODE (t) == GOTO_EXPR
+ && TREE_CODE (GOTO_DESTINATION (t)) == LABEL_DECL);
+}
+
+
+/* Return true if T can make an abnormal transfer of control flow.
+ Transfers of control flow associated with EH are excluded. */
bool
-simple_goto_p (tree expr)
+tree_can_make_abnormal_goto (tree t)
{
- return (TREE_CODE (expr) == GOTO_EXPR
- && TREE_CODE (GOTO_DESTINATION (expr)) == LABEL_DECL);
+ if (computed_goto_p (t))
+ return true;
+ if (TREE_CODE (t) == MODIFY_EXPR)
+ t = TREE_OPERAND (t, 1);
+ if (TREE_CODE (t) == WITH_SIZE_EXPR)
+ t = TREE_OPERAND (t, 0);
+ if (TREE_CODE (t) == CALL_EXPR)
+ return TREE_SIDE_EFFECTS (t) && current_function_has_nonlocal_label;
+ return false;
}
}
}
+/* Faster version of set_bb_for_stmt that assume that statement is being moved
+ from one basic block to another.
+ For BB splitting we can run into quadratic case, so performance is quite
+ important and knowing that the tables are big enough, change_bb_for_stmt
+ can inline as leaf function. */
+static inline void
+change_bb_for_stmt (tree t, basic_block bb)
+{
+ get_stmt_ann (t)->bb = bb;
+ if (TREE_CODE (t) == LABEL_EXPR)
+ VEC_replace (basic_block, label_to_block_map,
+ LABEL_DECL_UID (LABEL_EXPR_LABEL (t)), bb);
+}
+
/* Finds iterator for STMT. */
extern block_stmt_iterator
PENDING_STMT (old_edge) = NULL;
}
-/* Returns the basic block after that the new basic block created
+/* Returns the basic block after which the new basic block created
by splitting edge EDGE_IN should be placed. Tries to keep the new block
near its "logical" location. This is of most help to humans looking
at debugging dumps. */
case NOP_EXPR:
case CONVERT_EXPR:
case FIX_TRUNC_EXPR:
- case FIX_CEIL_EXPR:
- case FIX_FLOOR_EXPR:
- case FIX_ROUND_EXPR:
case FLOAT_EXPR:
case NEGATE_EXPR:
case ABS_EXPR:
}
}
+ if (TREE_CODE (stmt) != COND_EXPR)
+ {
+ /* Verify that there are no edges with EDGE_TRUE/FALSE_FLAG set
+ after anything else but if statement. */
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ if (e->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE))
+ {
+ error ("true/false edge after a non-COND_EXPR in bb %d",
+ bb->index);
+ err = 1;
+ }
+ }
+
switch (TREE_CODE (stmt))
{
case COND_EXPR:
edge ret;
tree label, stmt;
- if (e->flags & (EDGE_ABNORMAL_CALL | EDGE_EH))
+ if (e->flags & EDGE_ABNORMAL)
return NULL;
if (e->src != ENTRY_BLOCK_PTR
static basic_block
tree_split_block (basic_block bb, void *stmt)
{
- block_stmt_iterator bsi, bsi_tgt;
+ block_stmt_iterator bsi;
+ tree_stmt_iterator tsi_tgt;
tree act;
basic_block new_bb;
edge e;
}
}
- bsi_tgt = bsi_start (new_bb);
- while (!bsi_end_p (bsi))
- {
- act = bsi_stmt (bsi);
- bsi_remove (&bsi, false);
- bsi_insert_after (&bsi_tgt, act, BSI_NEW_STMT);
- }
+ if (bsi_end_p (bsi))
+ return new_bb;
+
+ /* Split the statement list - avoid re-creating new containers as this
+ brings ugly quadratic memory consumption in the inliner.
+ (We are still quadratic since we need to update stmt BB pointers,
+ sadly.) */
+ new_bb->stmt_list = tsi_split_statement_list_before (&bsi.tsi);
+ for (tsi_tgt = tsi_start (new_bb->stmt_list);
+ !tsi_end_p (tsi_tgt); tsi_next (&tsi_tgt))
+ change_bb_for_stmt (tsi_stmt (tsi_tgt), new_bb);
return new_bb;
}
return blocks_split;
}
+/* Purge dead abnormal call edges from basic block BB. */
+
+bool
+tree_purge_dead_abnormal_call_edges (basic_block bb)
+{
+ bool changed = tree_purge_dead_eh_edges (bb);
+
+ if (current_function_has_nonlocal_label)
+ {
+ tree stmt = last_stmt (bb);
+ edge_iterator ei;
+ edge e;
+
+ if (!(stmt && tree_can_make_abnormal_goto (stmt)))
+ for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); )
+ {
+ if (e->flags & EDGE_ABNORMAL)
+ {
+ remove_edge (e);
+ changed = true;
+ }
+ else
+ ei_next (&ei);
+ }
+
+ /* See tree_purge_dead_eh_edges below. */
+ if (changed)
+ free_dominance_info (CDI_DOMINATORS);
+ }
+
+ return changed;
+}
+
+/* Purge dead EH edges from basic block BB. */
+
bool
tree_purge_dead_eh_edges (basic_block bb)
{
TREE_BLOCK (new_stmt) = TREE_BLOCK (orig_stmt);
bsi_insert_before (bsi, new_stmt, BSI_SAME_STMT);
+ if (in_ssa_p)
+ mark_new_vars_to_rename (new_stmt);
return t;
}