/* Control flow graph manipulation code for GNU compiler.
Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
Free Software Foundation, Inc.
This file is part of GCC.
#include "toplev.h"
#include "tm_p.h"
#include "obstack.h"
+#include "insn-attr.h"
#include "insn-config.h"
#include "cfglayout.h"
#include "expr.h"
static int can_delete_note_p (const_rtx);
static int can_delete_label_p (const_rtx);
-static void commit_one_edge_insertion (edge);
static basic_block rtl_split_edge (edge);
static bool rtl_move_block_after (basic_block, basic_block);
static int rtl_verify_flow_info (void);
static int
can_delete_note_p (const_rtx note)
{
- return (NOTE_KIND (note) == NOTE_INSN_DELETED
- || NOTE_KIND (note) == NOTE_INSN_BASIC_BLOCK);
+ switch (NOTE_KIND (note))
+ {
+ case NOTE_INSN_DELETED:
+ case NOTE_INSN_BASIC_BLOCK:
+ case NOTE_INSN_EPILOGUE_BEG:
+ return true;
+
+ default:
+ return false;
+ }
}
/* True if a given label can be deleted. */
remove_note (insn, note);
}
- if (JUMP_P (insn)
- && (GET_CODE (PATTERN (insn)) == ADDR_VEC
- || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC))
+ if (JUMP_TABLE_DATA_P (insn))
{
rtx pat = PATTERN (insn);
int diff_vec_p = GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC;
start = next;
}
}
-
-/* Like delete_insn_chain but also purge dead edges from BB. */
-
-void
-delete_insn_chain_and_edges (rtx first, rtx last)
-{
- bool purge = false;
-
- if (INSN_P (last)
- && BLOCK_FOR_INSN (last)
- && BB_END (BLOCK_FOR_INSN (last)) == last)
- purge = true;
- delete_insn_chain (first, last, false);
- if (purge)
- purge_dead_edges (BLOCK_FOR_INSN (last));
-}
\f
/* Create a new basic block consisting of the instructions between HEAD and END
inclusive. This function is designed to allow fast BB construction - reuses
label for an exception handler which can't be reached. We need
to remove the label from the exception_handler_label list. */
insn = BB_HEAD (b);
- if (LABEL_P (insn))
- maybe_remove_eh_handler (insn);
end = get_last_bb_insn (b);
return 0;
}
+static unsigned int
+rest_of_pass_free_cfg (void)
+{
+#ifdef DELAY_SLOTS
+ /* The resource.c machinery uses DF but the CFG isn't guaranteed to be
+ valid at that point so it would be too late to call df_analyze. */
+ if (optimize > 0 && flag_delayed_branch)
+ df_analyze ();
+#endif
+
+ free_bb_for_insn ();
+ return 0;
+}
+
struct rtl_opt_pass pass_free_cfg =
{
{
RTL_PASS,
NULL, /* name */
NULL, /* gate */
- free_bb_for_insn, /* execute */
+ rest_of_pass_free_cfg, /* execute */
NULL, /* sub */
NULL, /* next */
0, /* static_pass_number */
- 0, /* tv_id */
+ TV_NONE, /* tv_id */
0, /* properties_required */
0, /* properties_provided */
PROP_cfg, /* properties_destroyed */
insn = first_insn_after_basic_block_note (bb);
if (insn)
- insn = PREV_INSN (insn);
+ {
+ rtx next = insn;
+
+ insn = PREV_INSN (insn);
+
+ /* If the block contains only debug insns, insn would have
+ been NULL in a non-debug compilation, and then we'd end
+ up emitting a DELETED note. For -fcompare-debug
+ stability, emit the note too. */
+ if (insn != BB_END (bb)
+ && DEBUG_INSN_P (next)
+ && DEBUG_INSN_P (BB_END (bb)))
+ {
+ while (next != BB_END (bb) && DEBUG_INSN_P (next))
+ next = NEXT_INSN (next);
+
+ if (next == BB_END (bb))
+ emit_note_after (NOTE_INSN_DELETED, next);
+ }
+ }
else
insn = get_last_insn ();
}
{
rtx b_head = BB_HEAD (b), b_end = BB_END (b), a_end = BB_END (a);
rtx del_first = NULL_RTX, del_last = NULL_RTX;
+ rtx b_debug_start = b_end, b_debug_end = b_end;
int b_empty = 0;
if (dump_file)
fprintf (dump_file, "merging block %d into block %d\n", b->index, a->index);
+ while (DEBUG_INSN_P (b_end))
+ b_end = PREV_INSN (b_debug_start = b_end);
+
/* If there was a CODE_LABEL beginning B, delete it. */
if (LABEL_P (b_head))
{
- /* This might have been an EH label that no longer has incoming
- EH edges. Update data structures to match. */
- maybe_remove_eh_handler (b_head);
-
/* Detect basic blocks with nothing but a label. This can happen
in particular at the end of a function. */
if (b_head == b_end)
/* Reassociate the insns of B with A. */
if (!b_empty)
{
- update_bb_for_insn_chain (a_end, b_end, a);
+ update_bb_for_insn_chain (a_end, b_debug_end, a);
- a_end = b_end;
+ a_end = b_debug_end;
+ }
+ else if (b_end != b_debug_end)
+ {
+ /* Move any deleted labels and other notes between the end of A
+ and the debug insns that make up B after the debug insns,
+ bringing the debug insns into A while keeping the notes after
+ the end of A. */
+ if (NEXT_INSN (a_end) != b_debug_start)
+ reorder_insns_nobb (NEXT_INSN (a_end), PREV_INSN (b_debug_start),
+ b_debug_end);
+ update_bb_for_insn_chain (b_debug_start, b_debug_end, a);
+ a_end = b_debug_end;
}
df_bb_delete (b->index);
return e;
}
-/* Redirect edge representing branch of (un)conditional jump or tablejump,
- NULL on failure */
-static edge
-redirect_branch_edge (edge e, basic_block target)
+/* Subroutine of redirect_branch_edge that tries to patch the jump
+ instruction INSN so that it reaches block NEW. Do this
+ only when it originally reached block OLD. Return true if this
+ worked or the original target wasn't OLD, return false if redirection
+ doesn't work. */
+
+static bool
+patch_jump_insn (rtx insn, rtx old_label, basic_block new_bb)
{
rtx tmp;
- rtx old_label = BB_HEAD (e->dest);
- basic_block src = e->src;
- rtx insn = BB_END (src);
-
- /* We can only redirect non-fallthru edges of jump insn. */
- if (e->flags & EDGE_FALLTHRU)
- return NULL;
- else if (!JUMP_P (insn))
- return NULL;
-
/* Recognize a tablejump and adjust all matching cases. */
if (tablejump_p (insn, NULL, &tmp))
{
rtvec vec;
int j;
- rtx new_label = block_label (target);
+ rtx new_label = block_label (new_bb);
- if (target == EXIT_BLOCK_PTR)
- return NULL;
+ if (new_bb == EXIT_BLOCK_PTR)
+ return false;
if (GET_CODE (PATTERN (tmp)) == ADDR_VEC)
vec = XVEC (PATTERN (tmp), 0);
else
if (computed_jump_p (insn)
/* A return instruction can't be redirected. */
|| returnjump_p (insn))
- return NULL;
-
- /* If the insn doesn't go where we think, we're confused. */
- gcc_assert (JUMP_LABEL (insn) == old_label);
+ return false;
- /* If the substitution doesn't succeed, die. This can happen
- if the back end emitted unrecognizable instructions or if
- target is exit block on some arches. */
- if (!redirect_jump (insn, block_label (target), 0))
+ if (!currently_expanding_to_rtl || JUMP_LABEL (insn) == old_label)
{
- gcc_assert (target == EXIT_BLOCK_PTR);
- return NULL;
+ /* If the insn doesn't go where we think, we're confused. */
+ gcc_assert (JUMP_LABEL (insn) == old_label);
+
+ /* If the substitution doesn't succeed, die. This can happen
+ if the back end emitted unrecognizable instructions or if
+ target is exit block on some arches. */
+ if (!redirect_jump (insn, block_label (new_bb), 0))
+ {
+ gcc_assert (new_bb == EXIT_BLOCK_PTR);
+ return false;
+ }
}
}
+ return true;
+}
+
+
+/* Redirect edge representing branch of (un)conditional jump or tablejump,
+ NULL on failure */
+static edge
+redirect_branch_edge (edge e, basic_block target)
+{
+ rtx old_label = BB_HEAD (e->dest);
+ basic_block src = e->src;
+ rtx insn = BB_END (src);
+
+ /* We can only redirect non-fallthru edges of jump insn. */
+ if (e->flags & EDGE_FALLTHRU)
+ return NULL;
+ else if (!JUMP_P (insn) && !currently_expanding_to_rtl)
+ return NULL;
+
+ if (!currently_expanding_to_rtl)
+ {
+ if (!patch_jump_insn (insn, old_label, target))
+ return NULL;
+ }
+ else
+ /* When expanding this BB might actually contain multiple
+ jumps (i.e. not yet split by find_many_sub_basic_blocks).
+ Redirect all of those that match our label. */
+ for (insn = BB_HEAD (src); insn != NEXT_INSN (BB_END (src));
+ insn = NEXT_INSN (insn))
+ if (JUMP_P (insn) && !patch_jump_insn (insn, old_label, target))
+ return NULL;
if (dump_file)
fprintf (dump_file, "Edge %i->%i redirected to %i\n",
rtx note;
edge new_edge;
int abnormal_edge_flags = 0;
+ int loc;
/* In the case the last instruction is conditional jump to the next
instruction, first redirect the jump itself and then continue
else
jump_block = e->src;
+ if (e->goto_locus && e->goto_block == NULL)
+ loc = e->goto_locus;
+ else
+ loc = 0;
e->flags &= ~EDGE_FALLTHRU;
if (target == EXIT_BLOCK_PTR)
{
#ifdef HAVE_return
- emit_jump_insn_after_noloc (gen_return (), BB_END (jump_block));
+ emit_jump_insn_after_setloc (gen_return (), BB_END (jump_block), loc);
#else
gcc_unreachable ();
#endif
else
{
rtx label = block_label (target);
- emit_jump_insn_after_noloc (gen_jump (label), BB_END (jump_block));
+ emit_jump_insn_after_setloc (gen_jump (label), BB_END (jump_block), loc);
JUMP_LABEL (BB_END (jump_block)) = label;
LABEL_NUSES (label)++;
}
/* Update the CFG for the instructions queued on edge E. */
-static void
+void
commit_one_edge_insertion (edge e)
{
rtx before = NULL_RTX, after = NULL_RTX, insns, tmp, last;
end = tmp;
/* Include any barriers that may follow the basic block. */
- tmp = next_nonnote_insn (end);
+ tmp = next_nonnote_insn_bb (end);
while (tmp && BARRIER_P (tmp))
{
end = tmp;
- tmp = next_nonnote_insn (end);
+ tmp = next_nonnote_insn_bb (end);
}
return end;
rtx insn;
/* Ensure existence of barrier in BB with no fallthru edges. */
- for (insn = BB_END (bb); !insn || !BARRIER_P (insn);
- insn = NEXT_INSN (insn))
- if (!insn
- || NOTE_INSN_BASIC_BLOCK_P (insn))
+ for (insn = NEXT_INSN (BB_END (bb)); ; insn = NEXT_INSN (insn))
+ {
+ if (!insn || NOTE_INSN_BASIC_BLOCK_P (insn))
{
error ("missing barrier after block %i", bb->index);
err = 1;
break;
}
+ if (BARRIER_P (insn))
+ break;
+ }
}
else if (e->src != ENTRY_BLOCK_PTR
&& e->dest != EXIT_BLOCK_PTR)
case CODE_LABEL:
/* An addr_vec is placed outside any basic block. */
if (NEXT_INSN (x)
- && JUMP_P (NEXT_INSN (x))
- && (GET_CODE (PATTERN (NEXT_INSN (x))) == ADDR_DIFF_VEC
- || GET_CODE (PATTERN (NEXT_INSN (x))) == ADDR_VEC))
+ && JUMP_TABLE_DATA_P (NEXT_INSN (x)))
x = NEXT_INSN (x);
/* But in any case, non-deletable labels can appear anywhere. */
bool found;
edge_iterator ei;
+ if (DEBUG_INSN_P (insn) && insn != BB_HEAD (bb))
+ do
+ insn = PREV_INSN (insn);
+ while ((DEBUG_INSN_P (insn) || NOTE_P (insn)) && insn != BB_HEAD (bb));
+
/* If this instruction cannot trap, remove REG_EH_REGION notes. */
if (NONJUMP_INSN_P (insn)
&& (note = find_reg_note (insn, REG_EH_REGION, NULL)))
latter can appear when nonlocal gotos are used. */
if (e->flags & EDGE_EH)
{
- if (can_throw_internal (BB_END (bb))
+ if (can_throw_internal (insn)
/* If this is a call edge, verify that this is a call insn. */
&& (! (e->flags & EDGE_ABNORMAL_CALL)
- || CALL_P (BB_END (bb))))
+ || CALL_P (insn)))
{
ei_next (&ei);
continue;
}
else if (e->flags & EDGE_ABNORMAL_CALL)
{
- if (CALL_P (BB_END (bb))
+ if (CALL_P (insn)
&& (! (note = find_reg_note (insn, REG_EH_REGION, NULL))
|| INTVAL (XEXP (note, 0)) >= 0))
{
/* If there was a CODE_LABEL beginning B, delete it. */
if (LABEL_P (BB_HEAD (b)))
{
- /* This might have been an EH label that no longer has incoming
- EH edges. Update data structures to match. */
- maybe_remove_eh_handler (BB_HEAD (b));
-
delete_insn (BB_HEAD (b));
}
try_redirect_by_replacing_jump (EDGE_SUCC (a, 0), b, true);
gcc_assert (!JUMP_P (BB_END (a)));
+ /* When not optimizing and the edge is the only place in RTL which holds
+ some unique locus, emit a nop with that locus in between. */
+ if (!optimize && EDGE_SUCC (a, 0)->goto_locus)
+ {
+ rtx insn = BB_END (a), end = PREV_INSN (BB_HEAD (a));
+ int goto_locus = EDGE_SUCC (a, 0)->goto_locus;
+
+ while (insn != end && (!INSN_P (insn) || INSN_LOCATOR (insn) == 0))
+ insn = PREV_INSN (insn);
+ if (insn != end && locator_eq (INSN_LOCATOR (insn), goto_locus))
+ goto_locus = 0;
+ else
+ {
+ insn = BB_HEAD (b);
+ end = NEXT_INSN (BB_END (b));
+ while (insn != end && !INSN_P (insn))
+ insn = NEXT_INSN (insn);
+ if (insn != end && INSN_LOCATOR (insn) != 0
+ && locator_eq (INSN_LOCATOR (insn), goto_locus))
+ goto_locus = 0;
+ }
+ if (goto_locus)
+ {
+ BB_END (a) = emit_insn_after_noloc (gen_nop (), BB_END (a), a);
+ INSN_LOCATOR (BB_END (a)) = goto_locus;
+ }
+ }
+
/* Possible line number notes should appear in between. */
if (b->il.rtl->header)
{
while (!CALL_P (insn)
&& insn != BB_HEAD (bb)
&& (keep_with_call_p (insn)
- || NOTE_P (insn)))
+ || NOTE_P (insn)
+ || DEBUG_INSN_P (insn)))
insn = PREV_INSN (insn);
return (CALL_P (insn));
}