/* 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
+ 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 basic_block rtl_redirect_edge_and_branch_force (edge, basic_block);
static edge rtl_redirect_edge_and_branch (edge, basic_block);
static basic_block rtl_split_block (basic_block, void *);
-static void rtl_dump_bb (basic_block, FILE *, int);
+static void rtl_dump_bb (basic_block, FILE *, int, int);
static int rtl_verify_flow_info_1 (void);
static void rtl_make_forwarder_block (edge);
\f
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. */
/* If deleting a jump, decrement the use count of the label. Deleting
the label itself should happen in the normal course of block merging. */
- if (JUMP_P (insn)
- && JUMP_LABEL (insn)
- && LABEL_P (JUMP_LABEL (insn)))
+ if (JUMP_P (insn))
{
- LABEL_NUSES (JUMP_LABEL (insn))--;
- JUMP_LABEL (insn) = NULL;
- }
+ if (JUMP_LABEL (insn)
+ && LABEL_P (JUMP_LABEL (insn)))
+ LABEL_NUSES (JUMP_LABEL (insn))--;
- /* Also if deleting an insn that references a label. */
- else
- {
- while ((note = find_reg_note (insn, REG_LABEL, NULL_RTX)) != NULL_RTX
+ /* If there are more targets, remove them too. */
+ while ((note
+ = find_reg_note (insn, REG_LABEL_TARGET, NULL_RTX)) != NULL_RTX
&& LABEL_P (XEXP (note, 0)))
{
LABEL_NUSES (XEXP (note, 0))--;
}
}
- if (JUMP_P (insn)
- && (GET_CODE (PATTERN (insn)) == ADDR_VEC
- || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC))
+ /* Also if deleting any insn that references a label as an operand. */
+ while ((note = find_reg_note (insn, REG_LABEL_OPERAND, NULL_RTX)) != NULL_RTX
+ && LABEL_P (XEXP (note, 0)))
+ {
+ LABEL_NUSES (XEXP (note, 0))--;
+ remove_note (insn, note);
+ }
+
+ if (JUMP_TABLE_DATA_P (insn))
{
rtx pat = PATTERN (insn);
int diff_vec_p = GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC;
}
/* Like delete_insn but also purge dead edges from BB. */
+
rtx
delete_insn_and_edges (rtx insn)
{
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;
}
-struct tree_opt_pass pass_free_cfg =
+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 */
0, /* todo_flags_start */
0, /* todo_flags_finish */
- 0 /* letter */
+ }
};
/* Return RTX to emit after when we want to emit code on the entry of function. */
commit_edge_insertions ();
}
-/* Update insns block within BB. */
+/* Update BLOCK_FOR_INSN of insns between BEGIN and END
+ (or BARRIER if found) and notify df of the bb change.
+ The insn chain range is inclusive
+ (i.e. both BEGIN and END will be updated. */
-void
-update_bb_for_insn (basic_block bb)
+static void
+update_bb_for_insn_chain (rtx begin, rtx end, basic_block bb)
{
rtx insn;
- for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn))
- {
- if (!BARRIER_P (insn))
- {
- set_block_for_insn (insn, bb);
- df_insn_change_bb (insn);
- }
- if (insn == BB_END (bb))
- break;
- }
+ end = NEXT_INSN (end);
+ for (insn = begin; insn != end; insn = NEXT_INSN (insn))
+ if (!BARRIER_P (insn))
+ df_insn_change_bb (insn, bb);
}
+
+/* Update BLOCK_FOR_INSN of insns in BB to BB,
+ and notify df of the change. */
+
+void
+update_bb_for_insn (basic_block bb)
+{
+ update_bb_for_insn_chain (BB_HEAD (bb), BB_END (bb), bb);
+}
+
\f
/* Return the INSN immediately following the NOTE_INSN_BASIC_BLOCK
note associated with the BLOCK. */
/* 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)
{
- rtx x;
-
- for (x = a_end; x != b_end; x = NEXT_INSN (x))
- {
- set_block_for_insn (x, a);
- df_insn_change_bb (x);
- }
-
- set_block_for_insn (b_end, a);
- df_insn_change_bb (b_end);
+ update_bb_for_insn_chain (a_end, b_end, a);
a_end = b_end;
}
/* Return true when block A and B can be merged. */
-static bool
-rtl_can_merge_blocks (const_basic_block a, const_basic_block b)
+static bool
+rtl_can_merge_blocks (basic_block a, basic_block b)
{
/* If we are partitioning hot/cold basic blocks, we don't want to
mess up unconditional or indirect jumps that cross between hot
{
rtx target_label = block_label (target);
rtx barrier, label, table;
- bool jump_p;
emit_jump_insn_after_noloc (gen_jump (target_label), insn);
JUMP_LABEL (BB_END (src)) = target_label;
INSN_UID (insn), INSN_UID (BB_END (src)));
+ delete_insn_chain (kill_from, insn, false);
+
/* Recognize a tablejump that we are converting to a
simple jump and remove its associated CODE_LABEL
and ADDR_VEC or ADDR_DIFF_VEC. */
- jump_p = tablejump_p (insn, &label, &table);
-
- delete_insn_chain (kill_from, insn, false);
- if (jump_p)
- delete_insn_chain (label, table, false);
+ if (tablejump_p (insn, &label, &table))
+ delete_insn_chain (label, table, false);
barrier = next_nonnote_insn (BB_END (src));
if (!barrier || !BARRIER_P (barrier))
which originally were or were created before jump table are
inside the basic block. */
rtx new_insn = BB_END (src);
- rtx tmp;
- for (tmp = NEXT_INSN (BB_END (src)); tmp != barrier;
- tmp = NEXT_INSN (tmp))
- {
- set_block_for_insn (tmp, src);
- df_insn_change_bb (tmp);
- }
+ update_bb_for_insn_chain (NEXT_INSN (BB_END (src)),
+ PREV_INSN (barrier), src);
NEXT_INSN (PREV_INSN (new_insn)) = NEXT_INSN (new_insn);
PREV_INSN (NEXT_INSN (new_insn)) = PREV_INSN (new_insn);
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
&& JUMP_P (BB_END (jump_block))
&& !any_condjump_p (BB_END (jump_block))
&& (EDGE_SUCC (jump_block, 0)->flags & EDGE_CROSSING))
- REG_NOTES (BB_END (jump_block)) = gen_rtx_EXPR_LIST (REG_CROSSING_JUMP,
- NULL_RTX,
- REG_NOTES
- (BB_END
- (jump_block)));
+ add_reg_note (BB_END (jump_block), REG_CROSSING_JUMP, NULL_RTX);
/* Wire edge in. */
new_edge = make_edge (e->src, jump_block, EDGE_FALLTHRU);
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;
if (JUMP_P (BB_END (bb))
&& !any_condjump_p (BB_END (bb))
&& (single_succ_edge (bb)->flags & EDGE_CROSSING))
- REG_NOTES (BB_END (bb)) = gen_rtx_EXPR_LIST
- (REG_CROSSING_JUMP, NULL_RTX, REG_NOTES (BB_END (bb)));
+ add_reg_note (BB_END (bb), REG_CROSSING_JUMP, NULL_RTX);
}
}
}
at start and end). */
static void
-rtl_dump_bb (basic_block bb, FILE *outf, int indent)
+rtl_dump_bb (basic_block bb, FILE *outf, int indent, int flags ATTRIBUTE_UNUSED)
{
rtx insn;
rtx last;
free (in_bb_p);
}
- if (current_function_epilogue_delay_list != 0)
+ if (crtl->epilogue_delay_list != 0)
{
fprintf (outf, "\n;; Insns in epilogue delay list:\n\n");
- for (tmp_rtx = current_function_epilogue_delay_list; tmp_rtx != 0;
+ for (tmp_rtx = crtl->epilogue_delay_list; tmp_rtx != 0;
tmp_rtx = XEXP (tmp_rtx, 1))
print_rtl_single (outf, XEXP (tmp_rtx, 0));
}
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. */
}
/* Return true when blocks A and B can be safely merged. */
+
static bool
-cfg_layout_can_merge_blocks_p (const_basic_block a, const_basic_block b)
+cfg_layout_can_merge_blocks_p (basic_block a, basic_block b)
{
/* If we are partitioning hot/cold basic blocks, we don't want to
mess up unconditional or indirect jumps that cross between hot
/* 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)
{
first = NEXT_INSN (first);
gcc_assert (NOTE_INSN_BASIC_BLOCK_P (first));
BB_HEAD (b) = NULL;
+
+ /* emit_insn_after_noloc doesn't call df_insn_change_bb.
+ We need to explicitly call. */
+ update_bb_for_insn_chain (NEXT_INSN (first),
+ BB_END (b),
+ a);
+
delete_insn (first);
}
/* Otherwise just re-associate the instructions. */
{
rtx insn;
- for (insn = BB_HEAD (b);
- insn != NEXT_INSN (BB_END (b));
- insn = NEXT_INSN (insn))
- {
- set_block_for_insn (insn, a);
- df_insn_change_bb (insn);
- }
+ update_bb_for_insn_chain (BB_HEAD (b), BB_END (b), a);
insn = BB_HEAD (b);
/* Skip possible DELETED_LABEL insn. */
? NEXT_INSN (BB_END (e->src)) : get_insns (),
NULL_RTX, e->src);
+ if (e->dest == EXIT_BLOCK_PTR)
+ BB_COPY_PARTITION (new_bb, e->src);
+ else
+ BB_COPY_PARTITION (new_bb, e->dest);
make_edge (new_bb, e->dest, EDGE_FALLTHRU);
redirect_edge_and_branch_force (e, new_bb);
instructions that must stay with the call, 0 otherwise. */
static bool
-rtl_block_ends_with_call_p (const_basic_block bb)
+rtl_block_ends_with_call_p (basic_block bb)
{
rtx insn = BB_END (bb);
while (!CALL_P (insn)
&& insn != BB_HEAD (bb)
- && keep_with_call_p (insn))
+ && (keep_with_call_p (insn)
+ || NOTE_P (insn)))
insn = PREV_INSN (insn);
return (CALL_P (insn));
}
if ((CALL_P (insn)
&& !SIBLING_CALL_P (insn)
&& !find_reg_note (insn, REG_NORETURN, NULL)
- && !CONST_OR_PURE_CALL_P (insn)))
+ && !(RTL_CONST_OR_PURE_CALL_P (insn))))
return true;
return ((GET_CODE (PATTERN (insn)) == ASM_OPERANDS
e = find_edge (bb, EXIT_BLOCK_PTR);
if (e)
{
- insert_insn_on_edge (gen_rtx_USE (VOIDmode, const0_rtx), e);
+ insert_insn_on_edge (gen_use (const0_rtx), e);
commit_edge_insertions ();
}
}