Index: gcc/gcc/cfgrtl.c |
diff --git a/gcc/gcc/cfgrtl.c b/gcc/gcc/cfgrtl.c |
index 7af4215a8452d401357aee98d28ab1e37b8f2f7b..5ad07f4d449ff9099889bed9d084547a0652eb10 100644 |
--- a/gcc/gcc/cfgrtl.c |
+++ b/gcc/gcc/cfgrtl.c |
@@ -1,6 +1,6 @@ |
/* 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, 2010 |
Free Software Foundation, Inc. |
This file is part of GCC. |
@@ -65,7 +65,6 @@ along with GCC; see the file COPYING3. If not see |
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); |
@@ -87,8 +86,16 @@ static void rtl_make_forwarder_block (edge); |
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. */ |
@@ -163,9 +170,7 @@ delete_insn (rtx insn) |
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; |
@@ -233,22 +238,6 @@ delete_insn_chain (rtx start, rtx finish, bool clear_bb) |
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)); |
-} |
/* Create a new basic block consisting of the instructions between HEAD and END |
inclusive. This function is designed to allow fast BB construction - reuses |
@@ -380,8 +369,6 @@ rtl_delete_block (basic_block b) |
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); |
@@ -446,13 +433,13 @@ struct rtl_opt_pass pass_free_cfg = |
{ |
{ |
RTL_PASS, |
- NULL, /* name */ |
+ "*free_cfg", /* name */ |
NULL, /* gate */ |
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 */ |
@@ -483,7 +470,7 @@ emit_insn_at_entry (rtx insn) |
} |
/* Update BLOCK_FOR_INSN of insns between BEGIN and END |
- (or BARRIER if found) and notify df of the bb change. |
+ (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. */ |
@@ -544,7 +531,26 @@ rtl_split_block (basic_block bb, void *insnp) |
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 (); |
} |
@@ -579,18 +585,18 @@ rtl_merge_blocks (basic_block a, basic_block b) |
{ |
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) |
@@ -653,9 +659,21 @@ rtl_merge_blocks (basic_block a, basic_block b) |
/* 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); |
@@ -893,31 +911,25 @@ try_redirect_by_replacing_jump (edge e, basic_block target, bool in_cfglayout) |
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 |
@@ -944,6 +956,48 @@ redirect_branch_edge (edge e, basic_block target) |
++LABEL_NUSES (new_label); |
} |
} |
+ else if ((tmp = extract_asm_operands (PATTERN (insn))) != NULL) |
+ { |
+ int i, n = ASM_OPERANDS_LABEL_LENGTH (tmp); |
+ rtx new_label, note; |
+ |
+ if (new_bb == EXIT_BLOCK_PTR) |
+ return false; |
+ new_label = block_label (new_bb); |
+ |
+ for (i = 0; i < n; ++i) |
+ { |
+ rtx old_ref = ASM_OPERANDS_LABEL (tmp, i); |
+ gcc_assert (GET_CODE (old_ref) == LABEL_REF); |
+ if (XEXP (old_ref, 0) == old_label) |
+ { |
+ ASM_OPERANDS_LABEL (tmp, i) |
+ = gen_rtx_LABEL_REF (Pmode, new_label); |
+ --LABEL_NUSES (old_label); |
+ ++LABEL_NUSES (new_label); |
+ } |
+ } |
+ |
+ if (JUMP_LABEL (insn) == old_label) |
+ { |
+ JUMP_LABEL (insn) = new_label; |
+ note = find_reg_note (insn, REG_LABEL_TARGET, new_label); |
+ if (note) |
+ remove_note (insn, note); |
+ } |
+ else |
+ { |
+ note = find_reg_note (insn, REG_LABEL_TARGET, old_label); |
+ if (note) |
+ remove_note (insn, note); |
+ if (JUMP_LABEL (insn) != new_label |
+ && !find_reg_note (insn, REG_LABEL_TARGET, new_label)) |
+ add_reg_note (insn, REG_LABEL_TARGET, new_label); |
+ } |
+ while ((note = find_reg_note (insn, REG_LABEL_OPERAND, old_label)) |
+ != NULL_RTX) |
+ XEXP (note, 0) = new_label; |
+ } |
else |
{ |
/* ?? We may play the games with moving the named labels from |
@@ -952,20 +1006,55 @@ redirect_branch_edge (edge e, basic_block target) |
if (computed_jump_p (insn) |
/* A return instruction can't be redirected. */ |
|| returnjump_p (insn)) |
- return NULL; |
+ return false; |
- /* 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 (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", |
@@ -1170,7 +1259,7 @@ force_nonfallthru_and_redirect (edge e, basic_block target) |
if (abnormal_edge_flags) |
make_edge (src, target, abnormal_edge_flags); |
- df_mark_solutions_dirty (); |
+ df_mark_solutions_dirty (); |
return new_bb; |
} |
@@ -1321,7 +1410,22 @@ rtl_split_edge (edge edge_in) |
gcc_assert (redirected); |
} |
else |
- redirect_edge_succ (edge_in, bb); |
+ { |
+ if (edge_in->src != ENTRY_BLOCK_PTR) |
+ { |
+ /* For asm goto even splitting of fallthru edge might |
+ need insn patching, as other labels might point to the |
+ old label. */ |
+ rtx last = BB_END (edge_in->src); |
+ if (last |
+ && JUMP_P (last) |
+ && edge_in->dest != EXIT_BLOCK_PTR |
+ && extract_asm_operands (PATTERN (last)) != NULL_RTX |
+ && patch_jump_insn (last, before, bb)) |
+ df_set_bb_dirty (edge_in->src); |
+ } |
+ redirect_edge_succ (edge_in, bb); |
+ } |
return bb; |
} |
@@ -1350,7 +1454,7 @@ insert_insn_on_edge (rtx pattern, edge e) |
/* 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; |
@@ -1418,24 +1522,11 @@ commit_one_edge_insertion (edge e) |
&& targetm.have_named_sections |
&& e->src != ENTRY_BLOCK_PTR |
&& BB_PARTITION (e->src) == BB_COLD_PARTITION |
- && !(e->flags & EDGE_CROSSING)) |
- { |
- rtx bb_note, cur_insn; |
- |
- bb_note = NULL_RTX; |
- for (cur_insn = BB_HEAD (bb); cur_insn != NEXT_INSN (BB_END (bb)); |
- cur_insn = NEXT_INSN (cur_insn)) |
- if (NOTE_INSN_BASIC_BLOCK_P (cur_insn)) |
- { |
- bb_note = cur_insn; |
- break; |
- } |
- |
- if (JUMP_P (BB_END (bb)) |
- && !any_condjump_p (BB_END (bb)) |
- && (single_succ_edge (bb)->flags & EDGE_CROSSING)) |
- add_reg_note (BB_END (bb), REG_CROSSING_JUMP, NULL_RTX); |
- } |
+ && !(e->flags & EDGE_CROSSING) |
+ && JUMP_P (after) |
+ && !any_condjump_p (after) |
+ && (single_succ_edge (bb)->flags & EDGE_CROSSING)) |
+ add_reg_note (after, REG_CROSSING_JUMP, NULL_RTX); |
} |
} |
@@ -1539,7 +1630,7 @@ rtl_dump_bb (basic_block bb, FILE *outf, int indent, int flags ATTRIBUTE_UNUSED) |
s_indent = (char *) alloca ((size_t) indent + 1); |
memset (s_indent, ' ', (size_t) indent); |
s_indent[indent] = '\0'; |
- |
+ |
if (df) |
{ |
df_dump_top (bb, outf); |
@@ -1606,7 +1697,7 @@ print_rtl_with_bb (FILE *outf, const_rtx rtx_first) |
{ |
edge e; |
edge_iterator ei; |
- |
+ |
fprintf (outf, ";; Start of basic block ("); |
FOR_EACH_EDGE (e, ei, bb->preds) |
fprintf (outf, " %d", e->src->index); |
@@ -1700,11 +1791,11 @@ get_last_bb_insn (basic_block bb) |
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; |
@@ -1826,12 +1917,16 @@ rtl_verify_flow_info_1 (void) |
n_abnormal++; |
} |
- if (n_eh && GET_CODE (PATTERN (BB_END (bb))) != RESX |
- && !find_reg_note (BB_END (bb), REG_EH_REGION, NULL_RTX)) |
+ if (n_eh && !find_reg_note (BB_END (bb), REG_EH_REGION, NULL_RTX)) |
{ |
error ("missing REG_EH_REGION note in the end of bb %i", bb->index); |
err = 1; |
} |
+ if (n_eh > 1) |
+ { |
+ error ("too many eh edges %i", bb->index); |
+ err = 1; |
+ } |
if (n_branch |
&& (!JUMP_P (BB_END (bb)) |
|| (n_branch > 1 && (any_uncondjump_p (BB_END (bb)) |
@@ -1847,7 +1942,8 @@ rtl_verify_flow_info_1 (void) |
} |
if (n_branch != 1 && any_uncondjump_p (BB_END (bb))) |
{ |
- error ("wrong amount of branch edges after unconditional jump %i", bb->index); |
+ error ("wrong number of branch edges after unconditional jump %i", |
+ bb->index); |
err = 1; |
} |
if (n_branch != 1 && any_condjump_p (BB_END (bb)) |
@@ -2032,15 +2128,17 @@ rtl_verify_flow_info (void) |
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) |
@@ -2108,9 +2206,7 @@ rtl_verify_flow_info (void) |
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. */ |
@@ -2150,6 +2246,11 @@ purge_dead_edges (basic_block bb) |
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))) |
@@ -2165,39 +2266,33 @@ purge_dead_edges (basic_block bb) |
/* Cleanup abnormal edges caused by exceptions or non-local gotos. */ |
for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); ) |
{ |
+ bool remove = false; |
+ |
/* There are three types of edges we need to handle correctly here: EH |
edges, abnormal call EH edges, and abnormal call non-EH edges. The |
latter can appear when nonlocal gotos are used. */ |
- if (e->flags & EDGE_EH) |
+ if (e->flags & EDGE_ABNORMAL_CALL) |
{ |
- if (can_throw_internal (BB_END (bb)) |
- /* If this is a call edge, verify that this is a call insn. */ |
- && (! (e->flags & EDGE_ABNORMAL_CALL) |
- || CALL_P (BB_END (bb)))) |
- { |
- ei_next (&ei); |
- continue; |
- } |
+ if (!CALL_P (insn)) |
+ remove = true; |
+ else if (can_nonlocal_goto (insn)) |
+ ; |
+ else if ((e->flags & EDGE_EH) && can_throw_internal (insn)) |
+ ; |
+ else |
+ remove = true; |
} |
- else if (e->flags & EDGE_ABNORMAL_CALL) |
+ else if (e->flags & EDGE_EH) |
+ remove = !can_throw_internal (insn); |
+ |
+ if (remove) |
{ |
- if (CALL_P (BB_END (bb)) |
- && (! (note = find_reg_note (insn, REG_EH_REGION, NULL)) |
- || INTVAL (XEXP (note, 0)) >= 0)) |
- { |
- ei_next (&ei); |
- continue; |
- } |
+ remove_edge (e); |
+ df_set_bb_dirty (bb); |
+ purged = true; |
} |
else |
- { |
- ei_next (&ei); |
- continue; |
- } |
- |
- remove_edge (e); |
- df_set_bb_dirty (bb); |
- purged = true; |
+ ei_next (&ei); |
} |
if (JUMP_P (insn)) |
@@ -2613,10 +2708,6 @@ cfg_layout_merge_blocks (basic_block a, basic_block b) |
/* 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)); |
} |
@@ -2763,7 +2854,8 @@ rtl_block_ends_with_call_p (basic_block bb) |
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)); |
} |
@@ -2951,7 +3043,7 @@ rtl_lv_add_condition_to_bb (basic_block first_head , |
op0 = force_operand (op0, NULL_RTX); |
op1 = force_operand (op1, NULL_RTX); |
do_compare_rtx_and_jump (op0, op1, comp, 0, |
- mode, NULL_RTX, NULL_RTX, label); |
+ mode, NULL_RTX, NULL_RTX, label, -1); |
jump = get_last_insn (); |
JUMP_LABEL (jump) = label; |
LABEL_NUSES (label)++; |