bb-reorder: Split EH edges crossing partitions.

From-SVN: r176696
This commit is contained in:
Richard Henderson 2011-07-23 12:44:19 -07:00 committed by Richard Henderson
parent e01705517a
commit 0be7e7a6db
9 changed files with 277 additions and 175 deletions

View File

@ -1,3 +1,23 @@
2011-07-23 Richard Henderson <rth@redhat.com>
* basic-block.h (EDGE_PRESERVE): New.
(EDGE_ALL_FLAGS, EDGE_COMPLEX): Include it.
* bb-reorder.c: Include except.h.
(fix_up_crossing_landing_pad): New.
(find_rarely_executed_basic_blocks_and_crossing_edges): Place
landing pads in the right partition. Duplicate as necessary.
(partition_hot_cold_basic_blocks): Fix up DF info after
duplicating landing pads.
* cfg.c (dump_edge_info): Add crossing and preserve to bitnames.
* cfgrtl.c (rtl_verify_flow_info_1): Validate that EDGE_CROSSING
is set properly. Validate that EH edges are not CROSSING.
* except.c (expand_dw2_landing_pad_for_region): Split out from ...
(dw2_build_landing_pads): ... here.
(convert_to_eh_region_ranges): Remove code to fixup crossing
landing pads.
* except.h (expand_dw2_landing_pad_for_region): Declare.
* tree-cfg.c (gimple_can_merge_blocks_p): Don't merge PRESERVE edges.
2011-07-23 Richard Earnshaw <rearnsha@arm.com>
PR target/49816

View File

@ -3464,7 +3464,7 @@ bb-reorder.o : bb-reorder.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
$(RTL_H) $(FLAGS_H) $(TIMEVAR_H) output.h $(CFGLAYOUT_H) $(FIBHEAP_H) \
$(TARGET_H) $(FUNCTION_H) $(TM_P_H) $(OBSTACK_H) $(EXPR_H) $(REGS_H) \
$(PARAMS_H) toplev.h $(DIAGNOSTIC_CORE_H) $(TREE_PASS_H) $(DF_H) \
bb-reorder.h
$(EXCEPT_H) bb-reorder.h
tracer.o : tracer.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
$(TREE_H) $(BASIC_BLOCK_H) hard-reg-set.h output.h $(CFGLAYOUT_H) \
$(FLAGS_H) $(TIMEVAR_H) $(PARAMS_H) $(COVERAGE_H) $(FIBHEAP_H) \

View File

@ -65,31 +65,34 @@ DEF_VEC_P(edge);
DEF_VEC_ALLOC_P(edge,gc);
DEF_VEC_ALLOC_P(edge,heap);
#define EDGE_FALLTHRU 1 /* 'Straight line' flow */
#define EDGE_ABNORMAL 2 /* Strange flow, like computed
/* Always update the table in cfg.c dump_edge_info. */
#define EDGE_FALLTHRU 0x0001 /* 'Straight line' flow */
#define EDGE_ABNORMAL 0x0002 /* Strange flow, like computed
label, or eh */
#define EDGE_ABNORMAL_CALL 4 /* Call with abnormal exit
#define EDGE_ABNORMAL_CALL 0x0004 /* Call with abnormal exit
like an exception, or sibcall */
#define EDGE_EH 8 /* Exception throw */
#define EDGE_FAKE 16 /* Not a real edge (profile.c) */
#define EDGE_DFS_BACK 32 /* A backwards edge */
#define EDGE_CAN_FALLTHRU 64 /* Candidate for straight line
#define EDGE_EH 0x0008 /* Exception throw */
#define EDGE_FAKE 0x0010 /* Not a real edge (profile.c) */
#define EDGE_DFS_BACK 0x0020 /* A backwards edge */
#define EDGE_CAN_FALLTHRU 0x0040 /* Candidate for straight line
flow. */
#define EDGE_IRREDUCIBLE_LOOP 128 /* Part of irreducible loop. */
#define EDGE_SIBCALL 256 /* Edge from sibcall to exit. */
#define EDGE_LOOP_EXIT 512 /* Exit of a loop. */
#define EDGE_TRUE_VALUE 1024 /* Edge taken when controlling
#define EDGE_IRREDUCIBLE_LOOP 0x0080 /* Part of irreducible loop. */
#define EDGE_SIBCALL 0x0100 /* Edge from sibcall to exit. */
#define EDGE_LOOP_EXIT 0x0200 /* Exit of a loop. */
#define EDGE_TRUE_VALUE 0x0400 /* Edge taken when controlling
predicate is nonzero. */
#define EDGE_FALSE_VALUE 2048 /* Edge taken when controlling
#define EDGE_FALSE_VALUE 0x0800 /* Edge taken when controlling
predicate is zero. */
#define EDGE_EXECUTABLE 4096 /* Edge is executable. Only
#define EDGE_EXECUTABLE 0x1000 /* Edge is executable. Only
valid during SSA-CCP. */
#define EDGE_CROSSING 8192 /* Edge crosses between hot
#define EDGE_CROSSING 0x2000 /* Edge crosses between hot
and cold sections, when we
do partitioning. */
#define EDGE_ALL_FLAGS 16383
#define EDGE_PRESERVE 0x4000 /* Never merge blocks via this edge. */
#define EDGE_ALL_FLAGS 0x7fff
#define EDGE_COMPLEX (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL | EDGE_EH)
#define EDGE_COMPLEX \
(EDGE_ABNORMAL | EDGE_ABNORMAL_CALL | EDGE_EH | EDGE_PRESERVE)
/* Counter summary from the last set of coverage counts read by
profile.c. */
@ -203,7 +206,9 @@ DEF_VEC_ALLOC_P(basic_block,heap);
the compilation, so they are never cleared.
All other flags may be cleared by clear_bb_flags(). It is generally
a bad idea to rely on any flags being up-to-date. */
a bad idea to rely on any flags being up-to-date.
Always update the table in cfg.c dump_bb_info. */
enum bb_flags
{

View File

@ -87,6 +87,7 @@
#include "tree-pass.h"
#include "df.h"
#include "bb-reorder.h"
#include "except.h"
/* The number of rounds. In most cases there will only be 4 rounds, but
when partitioning hot and cold basic blocks into separate sections of
@ -1208,6 +1209,79 @@ get_uncond_jump_length (void)
return length;
}
/* Emit a barrier into the footer of BB. */
static void
emit_barrier_after_bb (basic_block bb)
{
rtx barrier = emit_barrier_after (BB_END (bb));
bb->il.rtl->footer = unlink_insn_chain (barrier, barrier);
}
/* The landing pad OLD_LP, in block OLD_BB, has edges from both partitions.
Duplicate the landing pad and split the edges so that no EH edge
crosses partitions. */
static void
fix_up_crossing_landing_pad (eh_landing_pad old_lp, basic_block old_bb)
{
eh_landing_pad new_lp;
basic_block new_bb, last_bb, post_bb;
rtx new_label, jump, post_label;
unsigned new_partition;
edge_iterator ei;
edge e;
/* Generate the new landing-pad structure. */
new_lp = gen_eh_landing_pad (old_lp->region);
new_lp->post_landing_pad = old_lp->post_landing_pad;
new_lp->landing_pad = gen_label_rtx ();
LABEL_PRESERVE_P (new_lp->landing_pad) = 1;
/* Put appropriate instructions in new bb. */
new_label = emit_label (new_lp->landing_pad);
expand_dw2_landing_pad_for_region (old_lp->region);
post_bb = BLOCK_FOR_INSN (old_lp->landing_pad);
post_bb = single_succ (post_bb);
post_label = block_label (post_bb);
jump = emit_jump_insn (gen_jump (post_label));
JUMP_LABEL (jump) = post_label;
/* Create new basic block to be dest for lp. */
last_bb = EXIT_BLOCK_PTR->prev_bb;
new_bb = create_basic_block (new_label, jump, last_bb);
new_bb->aux = last_bb->aux;
last_bb->aux = new_bb;
emit_barrier_after_bb (new_bb);
make_edge (new_bb, post_bb, 0);
/* Make sure new bb is in the other partition. */
new_partition = BB_PARTITION (old_bb);
new_partition ^= BB_HOT_PARTITION | BB_COLD_PARTITION;
BB_SET_PARTITION (new_bb, new_partition);
/* Fix up the edges. */
for (ei = ei_start (old_bb->preds); (e = ei_safe_edge (ei)) != NULL; )
if (BB_PARTITION (e->src) == new_partition)
{
rtx insn = BB_END (e->src);
rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
gcc_assert (note != NULL);
gcc_checking_assert (INTVAL (XEXP (note, 0)) == old_lp->index);
XEXP (note, 0) = GEN_INT (new_lp->index);
/* Adjust the edge to the new destination. */
redirect_edge_succ (e, new_bb);
}
else
ei_next (&ei);
}
/* Find the basic blocks that are rarely executed and need to be moved to
a separate section of the .o file (to cut down on paging and improve
cache locality). Return a vector of all edges that cross. */
@ -1221,7 +1295,6 @@ find_rarely_executed_basic_blocks_and_crossing_edges (void)
edge_iterator ei;
/* Mark which partition (hot/cold) each basic block belongs in. */
FOR_EACH_BB (bb)
{
if (probably_never_executed_bb_p (bb))
@ -1230,34 +1303,73 @@ find_rarely_executed_basic_blocks_and_crossing_edges (void)
BB_SET_PARTITION (bb, BB_HOT_PARTITION);
}
/* The format of .gcc_except_table does not allow landing pads to
be in a different partition as the throw. Fix this by either
moving or duplicating the landing pads. */
if (cfun->eh->lp_array)
{
unsigned i;
eh_landing_pad lp;
FOR_EACH_VEC_ELT (eh_landing_pad, cfun->eh->lp_array, i, lp)
{
bool all_same, all_diff;
if (lp == NULL)
continue;
all_same = all_diff = true;
bb = BLOCK_FOR_INSN (lp->landing_pad);
FOR_EACH_EDGE (e, ei, bb->preds)
{
gcc_assert (e->flags & EDGE_EH);
if (BB_PARTITION (bb) == BB_PARTITION (e->src))
all_diff = false;
else
all_same = false;
}
if (all_same)
;
else if (all_diff)
{
int which = BB_PARTITION (bb);
which ^= BB_HOT_PARTITION | BB_COLD_PARTITION;
BB_SET_PARTITION (bb, which);
}
else
fix_up_crossing_landing_pad (lp, bb);
}
}
/* Mark every edge that crosses between sections. */
FOR_EACH_BB (bb)
FOR_EACH_EDGE (e, ei, bb->succs)
{
if (e->src != ENTRY_BLOCK_PTR
&& e->dest != EXIT_BLOCK_PTR
&& BB_PARTITION (e->src) != BB_PARTITION (e->dest))
{
e->flags |= EDGE_CROSSING;
VEC_safe_push (edge, heap, crossing_edges, e);
}
else
e->flags &= ~EDGE_CROSSING;
}
{
unsigned int flags = e->flags;
/* We should never have EDGE_CROSSING set yet. */
gcc_checking_assert ((flags & EDGE_CROSSING) == 0);
if (e->src != ENTRY_BLOCK_PTR
&& e->dest != EXIT_BLOCK_PTR
&& BB_PARTITION (e->src) != BB_PARTITION (e->dest))
{
VEC_safe_push (edge, heap, crossing_edges, e);
flags |= EDGE_CROSSING;
}
/* Now that we've split eh edges as appropriate, allow landing pads
to be merged with the post-landing pads. */
flags &= ~EDGE_PRESERVE;
e->flags = flags;
}
return crossing_edges;
}
/* Emit a barrier into the footer of BB. */
static void
emit_barrier_after_bb (basic_block bb)
{
rtx barrier = emit_barrier_after (BB_END (bb));
bb->il.rtl->footer = unlink_insn_chain (barrier, barrier);
}
/* If any destination of a crossing edge does not have a label, add label;
Convert any easy fall-through crossing edges to unconditional jumps. */
@ -2108,6 +2220,8 @@ partition_hot_cold_basic_blocks (void)
if (n_basic_blocks <= NUM_FIXED_BLOCKS + 1)
return 0;
df_set_flags (DF_DEFER_INSN_RESCAN);
crossing_edges = find_rarely_executed_basic_blocks_and_crossing_edges ();
if (crossing_edges == NULL)
return 0;
@ -2139,6 +2253,38 @@ partition_hot_cold_basic_blocks (void)
VEC_free (edge, heap, crossing_edges);
/* ??? FIXME: DF generates the bb info for a block immediately.
And by immediately, I mean *during* creation of the block.
#0 df_bb_refs_collect
#1 in df_bb_refs_record
#2 in create_basic_block_structure
Which means that the bb_has_eh_pred test in df_bb_refs_collect
will *always* fail, because no edges can have been added to the
block yet. Which of course means we don't add the right
artificial refs, which means we fail df_verify (much) later.
Cleanest solution would seem to make DF_DEFER_INSN_RESCAN imply
that we also shouldn't grab data from the new blocks those new
insns are in either. In this way one can create the block, link
it up properly, and have everything Just Work later, when deferred
insns are processed.
In the meantime, we have no other option but to throw away all
of the DF data and recompute it all. */
if (cfun->eh->lp_array)
{
df_finish_pass (true);
df_scan_alloc (NULL);
df_scan_blocks ();
/* Not all post-landing pads use all of the EH_RETURN_DATA_REGNO
data. We blindly generated all of them when creating the new
landing pad. Delete those assignments we don't use. */
df_set_flags (DF_LR_RUN_DCE);
df_analyze ();
}
return TODO_verify_flow | TODO_verify_rtl_sharing;
}

View File

@ -720,7 +720,7 @@ dump_edge_info (FILE *file, edge e, int do_succ)
static const char * const bitnames[] = {
"fallthru", "ab", "abcall", "eh", "fake", "dfs_back",
"can_fallthru", "irreducible", "sibcall", "loop_exit",
"true", "false", "exec"
"true", "false", "exec", "crossing", "preserve"
};
int comma = 0;
int i, flags = e->flags;

View File

@ -1820,18 +1820,38 @@ rtl_verify_flow_info_1 (void)
}
FOR_EACH_EDGE (e, ei, bb->succs)
{
bool is_crossing;
if (e->flags & EDGE_FALLTHRU)
n_fallthru++, fallthru = e;
is_crossing = (BB_PARTITION (e->src) != BB_PARTITION (e->dest)
&& e->src != ENTRY_BLOCK_PTR
&& e->dest != EXIT_BLOCK_PTR);
if (e->flags & EDGE_CROSSING)
{
n_fallthru++, fallthru = e;
if ((e->flags & EDGE_CROSSING)
|| (BB_PARTITION (e->src) != BB_PARTITION (e->dest)
&& e->src != ENTRY_BLOCK_PTR
&& e->dest != EXIT_BLOCK_PTR))
{
if (!is_crossing)
{
error ("EDGE_CROSSING incorrectly set across same section");
err = 1;
}
if (e->flags & EDGE_FALLTHRU)
{
error ("fallthru edge crosses section boundary (bb %i)",
e->src->index);
err = 1;
}
if (e->flags & EDGE_EH)
{
error ("EH edge crosses section boundary (bb %i)",
e->src->index);
err = 1;
}
}
else if (is_crossing)
{
error ("EDGE_CROSSING missing across section boundary");
err = 1;
}
if ((e->flags & ~(EDGE_DFS_BACK

View File

@ -919,6 +919,34 @@ emit_to_new_bb_before (rtx seq, rtx insn)
return bb;
}
/* A subroutine of dw2_build_landing_pads, also used for edge splitting
at the rtl level. Emit the code required by the target at a landing
pad for the given region. */
void
expand_dw2_landing_pad_for_region (eh_region region)
{
#ifdef HAVE_exception_receiver
if (HAVE_exception_receiver)
emit_insn (gen_exception_receiver ());
else
#endif
#ifdef HAVE_nonlocal_goto_receiver
if (HAVE_nonlocal_goto_receiver)
emit_insn (gen_nonlocal_goto_receiver ());
else
#endif
{ /* Nothing */ }
if (region->exc_ptr_reg)
emit_move_insn (region->exc_ptr_reg,
gen_rtx_REG (ptr_mode, EH_RETURN_DATA_REGNO (0)));
if (region->filter_reg)
emit_move_insn (region->filter_reg,
gen_rtx_REG (targetm.eh_return_filter_mode (),
EH_RETURN_DATA_REGNO (1)));
}
/* Expand the extra code needed at landing pads for dwarf2 unwinding. */
static void
@ -926,10 +954,17 @@ dw2_build_landing_pads (void)
{
int i;
eh_landing_pad lp;
int e_flags = EDGE_FALLTHRU;
/* If we're going to partition blocks, we need to be able to add
new landing pads later, which means that we need to hold on to
the post-landing-pad block. Prevent it from being merged away.
We'll remove this bit after partitioning. */
if (flag_reorder_blocks_and_partition)
e_flags |= EDGE_PRESERVE;
for (i = 1; VEC_iterate (eh_landing_pad, cfun->eh->lp_array, i, lp); ++i)
{
eh_region region;
basic_block bb;
rtx seq;
edge e;
@ -943,32 +978,13 @@ dw2_build_landing_pads (void)
emit_label (lp->landing_pad);
LABEL_PRESERVE_P (lp->landing_pad) = 1;
#ifdef HAVE_exception_receiver
if (HAVE_exception_receiver)
emit_insn (gen_exception_receiver ());
else
#endif
#ifdef HAVE_nonlocal_goto_receiver
if (HAVE_nonlocal_goto_receiver)
emit_insn (gen_nonlocal_goto_receiver ());
else
#endif
{ /* Nothing */ }
region = lp->region;
if (region->exc_ptr_reg)
emit_move_insn (region->exc_ptr_reg,
gen_rtx_REG (ptr_mode, EH_RETURN_DATA_REGNO (0)));
if (region->filter_reg)
emit_move_insn (region->filter_reg,
gen_rtx_REG (targetm.eh_return_filter_mode (),
EH_RETURN_DATA_REGNO (1)));
expand_dw2_landing_pad_for_region (lp->region);
seq = get_insns ();
end_sequence ();
bb = emit_to_new_bb_before (seq, label_rtx (lp->post_landing_pad));
e = make_edge (bb, bb->next_bb, EDGE_FALLTHRU);
e = make_edge (bb, bb->next_bb, e_flags);
e->count = bb->count;
e->probability = REG_BR_PROB_BASE;
}
@ -2388,9 +2404,6 @@ convert_to_eh_region_ranges (void)
rtx section_switch_note = NULL_RTX;
rtx first_no_action_insn_before_switch = NULL_RTX;
rtx last_no_action_insn_before_switch = NULL_RTX;
rtx *pad_map = NULL;
sbitmap pad_loc = NULL;
int min_labelno = 0, max_labelno = 0;
int saved_call_site_base = call_site_base;
crtl->eh.action_record_data = VEC_alloc (uchar, gc, 64);
@ -2523,13 +2536,7 @@ convert_to_eh_region_ranges (void)
gcc_assert (crtl->eh.call_site_record[cur_sec] == NULL);
crtl->eh.call_site_record[cur_sec]
= VEC_alloc (call_site_record, gc, 10);
max_labelno = max_label_num ();
min_labelno = get_first_label_num ();
pad_map = XCNEWVEC (rtx, max_labelno - min_labelno + 1);
pad_loc = sbitmap_alloc (max_labelno - min_labelno + 1);
}
else if (LABEL_P (iter) && pad_map)
SET_BIT (pad_loc, CODE_LABEL_NUMBER (iter) - min_labelno);
if (last_action >= -1 && ! first_no_action_insn)
{
@ -2539,103 +2546,6 @@ convert_to_eh_region_ranges (void)
call_site_base = saved_call_site_base;
if (pad_map)
{
/* When doing hot/cold partitioning, ensure landing pads are
always in the same section as the EH region, .gcc_except_table
can't express it otherwise. */
for (cur_sec = 0; cur_sec < 2; cur_sec++)
{
int i, idx;
int n = VEC_length (call_site_record,
crtl->eh.call_site_record[cur_sec]);
basic_block prev_bb = NULL, padbb;
for (i = 0; i < n; ++i)
{
struct call_site_record_d *cs =
VEC_index (call_site_record,
crtl->eh.call_site_record[cur_sec], i);
rtx jump, note;
if (cs->landing_pad == NULL_RTX)
continue;
idx = CODE_LABEL_NUMBER (cs->landing_pad) - min_labelno;
/* If the landing pad is in the correct section, nothing
is needed. */
if (TEST_BIT (pad_loc, idx) ^ (cur_sec == 0))
continue;
/* Otherwise, if we haven't seen this pad yet, we need to
add a new label and jump to the correct section. */
if (pad_map[idx] == NULL_RTX)
{
pad_map[idx] = gen_label_rtx ();
if (prev_bb == NULL)
for (iter = section_switch_note;
iter; iter = PREV_INSN (iter))
if (NOTE_INSN_BASIC_BLOCK_P (iter))
{
prev_bb = NOTE_BASIC_BLOCK (iter);
break;
}
if (cur_sec == 0)
{
note = emit_label_before (pad_map[idx],
section_switch_note);
jump = emit_jump_insn_before (gen_jump (cs->landing_pad),
section_switch_note);
}
else
{
jump = emit_jump_insn_after (gen_jump (cs->landing_pad),
section_switch_note);
note = emit_label_after (pad_map[idx],
section_switch_note);
}
JUMP_LABEL (jump) = cs->landing_pad;
add_reg_note (jump, REG_CROSSING_JUMP, NULL_RTX);
iter = NEXT_INSN (cs->landing_pad);
if (iter && NOTE_INSN_BASIC_BLOCK_P (iter))
padbb = NOTE_BASIC_BLOCK (iter);
else
padbb = NULL;
if (padbb && prev_bb
&& BB_PARTITION (padbb) != BB_UNPARTITIONED)
{
basic_block bb;
int part
= BB_PARTITION (padbb) == BB_COLD_PARTITION
? BB_HOT_PARTITION : BB_COLD_PARTITION;
edge_iterator ei;
edge e;
bb = create_basic_block (note, jump, prev_bb);
make_single_succ_edge (bb, padbb, EDGE_CROSSING);
BB_SET_PARTITION (bb, part);
for (ei = ei_start (padbb->preds);
(e = ei_safe_edge (ei)); )
{
if ((e->flags & (EDGE_EH|EDGE_CROSSING))
== (EDGE_EH|EDGE_CROSSING))
{
redirect_edge_succ (e, bb);
e->flags &= ~EDGE_CROSSING;
}
else
ei_next (&ei);
}
if (cur_sec == 0)
prev_bb = bb;
}
}
cs->landing_pad = pad_map[idx];
}
}
sbitmap_free (pad_loc);
XDELETEVEC (pad_map);
}
htab_delete (ar_hash);
return 0;
}

View File

@ -253,6 +253,7 @@ extern rtx expand_builtin_dwarf_sp_column (void);
extern void expand_builtin_eh_return (tree, tree);
extern void expand_eh_return (void);
extern rtx expand_builtin_extend_pointer (tree);
extern void expand_dw2_landing_pad_for_region (eh_region);
typedef tree (*duplicate_eh_regions_map) (tree, void *);
extern struct pointer_map_t *duplicate_eh_regions

View File

@ -1421,7 +1421,7 @@ gimple_can_merge_blocks_p (basic_block a, basic_block b)
if (!single_succ_p (a))
return false;
if (single_succ_edge (a)->flags & (EDGE_ABNORMAL | EDGE_EH))
if (single_succ_edge (a)->flags & (EDGE_ABNORMAL | EDGE_EH | EDGE_PRESERVE))
return false;
if (single_succ (a) != b)