summaryrefslogtreecommitdiff
path: root/gcc/cfgrtl.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/cfgrtl.c')
-rw-r--r--gcc/cfgrtl.c260
1 files changed, 203 insertions, 57 deletions
diff --git a/gcc/cfgrtl.c b/gcc/cfgrtl.c
index 151502087ac..81722ac1b33 100644
--- a/gcc/cfgrtl.c
+++ b/gcc/cfgrtl.c
@@ -74,7 +74,7 @@ rtx tail_recursion_label_list;
static int can_delete_note_p PARAMS ((rtx));
static int can_delete_label_p PARAMS ((rtx));
-static void commit_one_edge_insertion PARAMS ((edge));
+static void commit_one_edge_insertion PARAMS ((edge, int));
static bool try_redirect_by_replacing_jump PARAMS ((edge, basic_block));
static rtx last_loop_beg_note PARAMS ((rtx));
static bool back_edge_of_syntactic_loop_p PARAMS ((basic_block, basic_block));
@@ -178,6 +178,26 @@ delete_insn (insn)
return next;
}
+/* Like delete_insn but also purge dead edges from BB. */
+rtx
+delete_insn_and_edges (insn)
+ rtx insn;
+{
+ rtx x;
+ bool purge = false;
+
+ if (basic_block_for_insn
+ && INSN_P (insn)
+ && (unsigned int)INSN_UID (insn) < basic_block_for_insn->num_elements
+ && BLOCK_FOR_INSN (insn)
+ && BLOCK_FOR_INSN (insn)->end == insn)
+ purge = true;
+ x = delete_insn (insn);
+ if (purge)
+ purge_dead_edges (BLOCK_FOR_INSN (insn));
+ return x;
+}
+
/* Unlink a chain of insns between START and FINISH, leaving notes
that must be paired. */
@@ -203,6 +223,24 @@ delete_insn_chain (start, finish)
start = next;
}
}
+
+/* Like delete_insn but also purge dead edges from BB. */
+void
+delete_insn_chain_and_edges (first, last)
+ rtx first, last;
+{
+ bool purge = false;
+
+ if (basic_block_for_insn
+ && INSN_P (last)
+ && (unsigned int)INSN_UID (last) < basic_block_for_insn->num_elements
+ && BLOCK_FOR_INSN (last)
+ && BLOCK_FOR_INSN (last)->end == last)
+ purge = true;
+ delete_insn_chain (first, last);
+ if (purge)
+ purge_dead_edges (BLOCK_FOR_INSN (last));
+}
/* Create a new basic block consisting of the instructions between HEAD and END
inclusive. This function is designed to allow fast BB construction - reuses
@@ -1271,8 +1309,9 @@ insert_insn_on_edge (pattern, e)
/* Update the CFG for the instructions queued on edge E. */
static void
-commit_one_edge_insertion (e)
+commit_one_edge_insertion (e, watch_calls)
edge e;
+ int watch_calls;
{
rtx before = NULL_RTX, after = NULL_RTX, insns, tmp, last;
basic_block bb;
@@ -1281,63 +1320,84 @@ commit_one_edge_insertion (e)
insns = e->insns;
e->insns = NULL_RTX;
- /* Figure out where to put these things. If the destination has
- one predecessor, insert there. Except for the exit block. */
- if (e->dest->pred->pred_next == NULL
- && e->dest != EXIT_BLOCK_PTR)
+ /* Special case -- avoid inserting code between call and storing
+ its return value. */
+ if (watch_calls && (e->flags & EDGE_FALLTHRU) && !e->dest->pred->pred_next
+ && e->src != ENTRY_BLOCK_PTR
+ && GET_CODE (e->src->end) == CALL_INSN)
{
- bb = e->dest;
+ rtx next = next_nonnote_insn (e->src->end);
- /* Get the location correct wrt a code label, and "nice" wrt
- a basic block note, and before everything else. */
- tmp = bb->head;
- if (GET_CODE (tmp) == CODE_LABEL)
- tmp = NEXT_INSN (tmp);
- if (NOTE_INSN_BASIC_BLOCK_P (tmp))
- tmp = NEXT_INSN (tmp);
- if (tmp == bb->head)
- before = tmp;
- else
- after = PREV_INSN (tmp);
+ after = e->dest->head;
+ /* The first insn after the call may be a stack pop, skip it. */
+ while (next
+ && keep_with_call_p (next))
+ {
+ after = next;
+ next = next_nonnote_insn (next);
+ }
+ bb = e->dest;
}
-
- /* If the source has one successor and the edge is not abnormal,
- insert there. Except for the entry block. */
- else if ((e->flags & EDGE_ABNORMAL) == 0
- && e->src->succ->succ_next == NULL
- && e->src != ENTRY_BLOCK_PTR)
+ if (!before && !after)
{
- bb = e->src;
-
- /* It is possible to have a non-simple jump here. Consider a target
- where some forms of unconditional jumps clobber a register. This
- happens on the fr30 for example.
-
- We know this block has a single successor, so we can just emit
- the queued insns before the jump. */
- if (GET_CODE (bb->end) == JUMP_INSN)
- for (before = bb->end;
- GET_CODE (PREV_INSN (before)) == NOTE
- && NOTE_LINE_NUMBER (PREV_INSN (before)) == NOTE_INSN_LOOP_BEG;
- before = PREV_INSN (before))
- ;
- else
+ /* Figure out where to put these things. If the destination has
+ one predecessor, insert there. Except for the exit block. */
+ if (e->dest->pred->pred_next == NULL && e->dest != EXIT_BLOCK_PTR)
{
- /* We'd better be fallthru, or we've lost track of what's what. */
- if ((e->flags & EDGE_FALLTHRU) == 0)
- abort ();
+ bb = e->dest;
+
+ /* Get the location correct wrt a code label, and "nice" wrt
+ a basic block note, and before everything else. */
+ tmp = bb->head;
+ if (GET_CODE (tmp) == CODE_LABEL)
+ tmp = NEXT_INSN (tmp);
+ if (NOTE_INSN_BASIC_BLOCK_P (tmp))
+ tmp = NEXT_INSN (tmp);
+ if (tmp == bb->head)
+ before = tmp;
+ else if (tmp)
+ after = PREV_INSN (tmp);
+ else
+ after = get_last_insn ();
+ }
+
+ /* If the source has one successor and the edge is not abnormal,
+ insert there. Except for the entry block. */
+ else if ((e->flags & EDGE_ABNORMAL) == 0
+ && e->src->succ->succ_next == NULL
+ && e->src != ENTRY_BLOCK_PTR)
+ {
+ bb = e->src;
+
+ /* It is possible to have a non-simple jump here. Consider a target
+ where some forms of unconditional jumps clobber a register. This
+ happens on the fr30 for example.
+
+ We know this block has a single successor, so we can just emit
+ the queued insns before the jump. */
+ if (GET_CODE (bb->end) == JUMP_INSN)
+ for (before = bb->end;
+ GET_CODE (PREV_INSN (before)) == NOTE
+ && NOTE_LINE_NUMBER (PREV_INSN (before)) ==
+ NOTE_INSN_LOOP_BEG; before = PREV_INSN (before))
+ ;
+ else
+ {
+ /* We'd better be fallthru, or we've lost track of what's what. */
+ if ((e->flags & EDGE_FALLTHRU) == 0)
+ abort ();
+ after = bb->end;
+ }
+ }
+ /* Otherwise we must split the edge. */
+ else
+ {
+ bb = split_edge (e);
after = bb->end;
}
}
- /* Otherwise we must split the edge. */
- else
- {
- bb = split_edge (e);
- after = bb->end;
- }
-
/* Now that we've found the spot, do the insertion. */
if (before)
@@ -1352,13 +1412,12 @@ commit_one_edge_insertion (e)
{
/* ??? Remove all outgoing edges from BB and add one for EXIT.
This is not currently a problem because this only happens
- for the (single) epilogue, which already has a fallthru edge
- to EXIT. */
+ for the (single) epilogue, which already has a fallthru edge
+ to EXIT. */
e = bb->succ;
if (e->dest != EXIT_BLOCK_PTR
- || e->succ_next != NULL
- || (e->flags & EDGE_FALLTHRU) == 0)
+ || e->succ_next != NULL || (e->flags & EDGE_FALLTHRU) == 0)
abort ();
e->flags &= ~EDGE_FALLTHRU;
@@ -1395,7 +1454,39 @@ commit_edge_insertions ()
{
next = e->succ_next;
if (e->insns)
- commit_one_edge_insertion (e);
+ commit_one_edge_insertion (e, false);
+ }
+
+ if (++i >= n_basic_blocks)
+ break;
+ bb = BASIC_BLOCK (i);
+ }
+}
+
+/* Update the CFG for all queued instructions, taking special care of inserting
+ code on edges between call and storing its return value. */
+
+void
+commit_edge_insertions_watch_calls ()
+{
+ int i;
+ basic_block bb;
+
+#ifdef ENABLE_CHECKING
+ verify_flow_info ();
+#endif
+
+ i = -1;
+ bb = ENTRY_BLOCK_PTR;
+ while (1)
+ {
+ edge e, next;
+
+ for (e = bb->succ; e; e = next)
+ {
+ next = e->succ_next;
+ if (e->insns)
+ commit_one_edge_insertion (e, true);
}
if (++i >= n_basic_blocks)
@@ -1649,7 +1740,7 @@ verify_flow_info ()
for (i = n_basic_blocks - 1; i >= 0; i--)
{
basic_block bb = BASIC_BLOCK (i);
- int has_fallthru = 0;
+ int n_fallthru = 0, n_eh = 0, n_call = 0, n_abnormal = 0, n_branch = 0;
edge e;
rtx note;
@@ -1705,7 +1796,18 @@ verify_flow_info ()
last_visited [e->dest->index + 2] = bb;
if (e->flags & EDGE_FALLTHRU)
- has_fallthru = 1;
+ n_fallthru++;
+
+ if ((e->flags & ~EDGE_DFS_BACK) == 0)
+ n_branch++;
+
+ if (e->flags & EDGE_ABNORMAL_CALL)
+ n_call++;
+
+ if (e->flags & EDGE_EH)
+ n_eh++;
+ else if (e->flags & EDGE_ABNORMAL)
+ n_abnormal++;
if ((e->flags & EDGE_FALLTHRU)
&& e->src != ENTRY_BLOCK_PTR
@@ -1753,7 +1855,51 @@ verify_flow_info ()
edge_checksum[e->dest->index + 2] += (size_t) e;
}
- if (!has_fallthru)
+ if (n_eh && !find_reg_note (bb->end, REG_EH_REGION, NULL_RTX))
+ {
+ error ("Missing REG_EH_REGION note in the end of bb %i", bb->index);
+ err = 1;
+ }
+ if (n_branch
+ && (GET_CODE (bb->end) != JUMP_INSN
+ || (n_branch > 1 && (any_uncondjump_p (bb->end)
+ || any_condjump_p (bb->end)))))
+ {
+ error ("Too many outgoing branch edges from bb %i", bb->index);
+ err = 1;
+ }
+ if (n_fallthru && any_uncondjump_p (bb->end))
+ {
+ error ("Fallthru edge after unconditional jump %i", bb->index);
+ err = 1;
+ }
+ if (n_branch != 1 && any_uncondjump_p (bb->end))
+ {
+ error ("Wrong amount of branch edges after unconditional jump %i", bb->index);
+ err = 1;
+ }
+ if (n_branch != 1 && any_condjump_p (bb->end)
+ && JUMP_LABEL (bb->end) != BASIC_BLOCK (bb->index + 1)->head)
+ {
+ error ("Wrong amount of branch edges after conditional jump %i", bb->index);
+ err = 1;
+ }
+ if (n_call && GET_CODE (bb->end) != CALL_INSN)
+ {
+ error ("Call edges for non-call insn in bb %i", bb->index);
+ err = 1;
+ }
+ if (n_abnormal
+ && (GET_CODE (bb->end) != CALL_INSN && n_call != n_abnormal)
+ && (GET_CODE (bb->end) != JUMP_INSN
+ || any_condjump_p (bb->end)
+ || any_uncondjump_p (bb->end)))
+ {
+ error ("Abnormal edges for no purpose in bb %i", bb->index);
+ err = 1;
+ }
+
+ if (!n_fallthru)
{
rtx insn;