From 018f0595d4f663d5729a4cf29567feb8f92695de Mon Sep 17 00:00:00 2001 From: rsandifo Date: Mon, 9 Jan 2012 19:43:10 +0000 Subject: [PATCH] gcc/ * sched-int.h (bb_note): Move to... * basic-block.h: ...here. * haifa-sched.c (bb_note): Move to... * cfgrtl.c: ...here. * function.c (next_block_for_reg): New function. (move_insn_for_shrink_wrap): Likewise. (prepare_shrink_wrap): Rewrite to use the above. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@183028 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 10 ++ gcc/basic-block.h | 1 + gcc/cfgrtl.c | 14 +++ gcc/function.c | 270 ++++++++++++++++++++++++++++++++---------------------- gcc/haifa-sched.c | 14 --- gcc/sched-int.h | 1 - 6 files changed, 188 insertions(+), 122 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 202042abf5a..6affd168e7c 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,13 @@ +2012-01-09 Richard Sandiford + + * sched-int.h (bb_note): Move to... + * basic-block.h: ...here. + * haifa-sched.c (bb_note): Move to... + * cfgrtl.c: ...here. + * function.c (next_block_for_reg): New function. + (move_insn_for_shrink_wrap): Likewise. + (prepare_shrink_wrap): Rewrite to use the above. + 2012-01-09 Aldy Hernandez * gimple.c (is_gimple_non_addressable): Remove. diff --git a/gcc/basic-block.h b/gcc/basic-block.h index e36b20a3892..3ff1cd645ea 100644 --- a/gcc/basic-block.h +++ b/gcc/basic-block.h @@ -801,6 +801,7 @@ extern void flow_edge_list_print (const char *, const edge *, int, FILE *); /* In cfgrtl.c */ extern rtx block_label (basic_block); +extern rtx bb_note (basic_block); extern bool purge_all_dead_edges (void); extern bool purge_dead_edges (basic_block); extern bool fixup_abnormal_edges (void); diff --git a/gcc/cfgrtl.c b/gcc/cfgrtl.c index 635955b7990..9312a4b69f1 100644 --- a/gcc/cfgrtl.c +++ b/gcc/cfgrtl.c @@ -500,6 +500,20 @@ update_bb_for_insn (basic_block bb) } +/* Return the NOTE_INSN_BASIC_BLOCK of BB. */ +rtx +bb_note (basic_block bb) +{ + rtx note; + + note = BB_HEAD (bb); + if (LABEL_P (note)) + note = NEXT_INSN (note); + + gcc_assert (NOTE_INSN_BASIC_BLOCK_P (note)); + return note; +} + /* Return the INSN immediately following the NOTE_INSN_BASIC_BLOCK note associated with the BLOCK. */ diff --git a/gcc/function.c b/gcc/function.c index 506ec030745..cd82da404d2 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -5329,126 +5329,182 @@ requires_stack_frame_p (rtx insn, HARD_REG_SET prologue_used, return false; } -/* Look for sets of call-saved registers in the first block of the - function, and move them down into successor blocks if the register - is used only on one path. This exposes more opportunities for - shrink-wrapping. - These kinds of sets often occur when incoming argument registers are - moved to call-saved registers because their values are live across - one or more calls during the function. */ +/* See whether BB has a single successor that uses [REGNO, END_REGNO), + and if BB is its only predecessor. Return that block if so, + otherwise return null. */ -static void -prepare_shrink_wrap (basic_block entry_block) +static basic_block +next_block_for_reg (basic_block bb, int regno, int end_regno) { - rtx insn, curr; - FOR_BB_INSNS_SAFE (entry_block, insn, curr) + edge e, live_edge; + edge_iterator ei; + bitmap live; + int i; + + live_edge = NULL; + FOR_EACH_EDGE (e, ei, bb->succs) { - basic_block next_bb; - edge e, live_edge; - edge_iterator ei; - rtx set, scan; - unsigned destreg, srcreg; + live = df_get_live_in (e->dest); + for (i = regno; i < end_regno; i++) + if (REGNO_REG_SET_P (live, i)) + { + if (live_edge && live_edge != e) + return NULL; + live_edge = e; + } + } - if (!NONDEBUG_INSN_P (insn)) - continue; - set = single_set (insn); - if (!set) - continue; + /* We can sometimes encounter dead code. Don't try to move it + into the exit block. */ + if (!live_edge || live_edge->dest == EXIT_BLOCK_PTR) + return NULL; - if (!REG_P (SET_SRC (set)) || !REG_P (SET_DEST (set))) - continue; - srcreg = REGNO (SET_SRC (set)); - destreg = REGNO (SET_DEST (set)); - if (hard_regno_nregs[srcreg][GET_MODE (SET_SRC (set))] > 1 - || hard_regno_nregs[destreg][GET_MODE (SET_DEST (set))] > 1) - continue; + /* Reject targets of abnormal edges. This is needed for correctness + on ports like Alpha and MIPS, whose pic_offset_table_rtx can die on + exception edges even though it is generally treated as call-saved + for the majority of the compilation. Moving across abnormal edges + isn't going to be interesting for shrink-wrap usage anyway. */ + if (live_edge->flags & EDGE_ABNORMAL) + return NULL; - next_bb = entry_block; - scan = insn; + if (EDGE_COUNT (live_edge->dest->preds) > 1) + return NULL; - for (;;) + return live_edge->dest; +} + +/* Try to move INSN from BB to a successor. Return true on success. + USES and DEFS are the set of registers that are used and defined + after INSN in BB. */ + +static bool +move_insn_for_shrink_wrap (basic_block bb, rtx insn, + const HARD_REG_SET uses, + const HARD_REG_SET defs) +{ + rtx set, src, dest; + bitmap live_out, live_in, bb_uses, bb_defs; + unsigned int i, dregno, end_dregno, sregno, end_sregno; + basic_block next_block; + + /* Look for a simple register copy. */ + set = single_set (insn); + if (!set) + return false; + src = SET_SRC (set); + dest = SET_DEST (set); + if (!REG_P (dest) || !REG_P (src)) + return false; + + /* Make sure that the source register isn't defined later in BB. */ + sregno = REGNO (src); + end_sregno = END_REGNO (src); + if (overlaps_hard_reg_set_p (defs, GET_MODE (src), sregno)) + return false; + + /* Make sure that the destination register isn't referenced later in BB. */ + dregno = REGNO (dest); + end_dregno = END_REGNO (dest); + if (overlaps_hard_reg_set_p (uses, GET_MODE (dest), dregno) + || overlaps_hard_reg_set_p (defs, GET_MODE (dest), dregno)) + return false; + + /* See whether there is a successor block to which we could move INSN. */ + next_block = next_block_for_reg (bb, dregno, end_dregno); + if (!next_block) + return false; + + /* At this point we are committed to moving INSN, but let's try to + move it as far as we can. */ + do + { + live_out = df_get_live_out (bb); + live_in = df_get_live_in (next_block); + bb = next_block; + + /* Check whether BB uses DEST or clobbers DEST. We need to add + INSN to BB if so. Either way, DEST is no longer live on entry, + except for any part that overlaps SRC (next loop). */ + bb_uses = &DF_LR_BB_INFO (bb)->use; + bb_defs = &DF_LR_BB_INFO (bb)->def; + for (i = dregno; i < end_dregno; i++) { - live_edge = NULL; - /* Try to find a single edge across which the register is live. - If we find one, we'll try to move the set across this edge. */ - FOR_EACH_EDGE (e, ei, next_bb->succs) - { - if (REGNO_REG_SET_P (df_get_live_in (e->dest), destreg)) - { - if (live_edge) - { - live_edge = NULL; - break; - } - live_edge = e; - } - } - if (!live_edge) - break; - /* We can sometimes encounter dead code. Don't try to move it - into the exit block. */ - if (live_edge->dest == EXIT_BLOCK_PTR) - break; - if (EDGE_COUNT (live_edge->dest->preds) > 1) - break; - while (scan != BB_END (next_bb)) - { - scan = NEXT_INSN (scan); - if (NONDEBUG_INSN_P (scan)) - { - rtx link; - HARD_REG_SET set_regs; - - CLEAR_HARD_REG_SET (set_regs); - note_stores (PATTERN (scan), record_hard_reg_sets, - &set_regs); - if (CALL_P (scan)) - IOR_HARD_REG_SET (set_regs, call_used_reg_set); - for (link = REG_NOTES (scan); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_INC) - record_hard_reg_sets (XEXP (link, 0), NULL, &set_regs); - - if (TEST_HARD_REG_BIT (set_regs, srcreg) - || reg_referenced_p (SET_DEST (set), - PATTERN (scan))) - { - scan = NULL_RTX; - break; - } - if (CALL_P (scan)) - { - rtx link = CALL_INSN_FUNCTION_USAGE (scan); - while (link) - { - rtx tmp = XEXP (link, 0); - if (GET_CODE (tmp) == USE - && reg_referenced_p (SET_DEST (set), tmp)) - break; - link = XEXP (link, 1); - } - if (link) - { - scan = NULL_RTX; - break; - } - } - } - } - if (!scan) - break; - next_bb = live_edge->dest; + if (REGNO_REG_SET_P (bb_uses, i) || REGNO_REG_SET_P (bb_defs, i)) + next_block = NULL; + CLEAR_REGNO_REG_SET (live_out, i); + CLEAR_REGNO_REG_SET (live_in, i); } - if (next_bb != entry_block) + /* Check whether BB clobbers SRC. We need to add INSN to BB if so. + Either way, SRC is now live on entry. */ + for (i = sregno; i < end_sregno; i++) { - rtx after = BB_HEAD (next_bb); - while (!NOTE_P (after) - || NOTE_KIND (after) != NOTE_INSN_BASIC_BLOCK) - after = NEXT_INSN (after); - emit_insn_after (PATTERN (insn), after); - delete_insn (insn); + if (REGNO_REG_SET_P (bb_defs, i)) + next_block = NULL; + SET_REGNO_REG_SET (live_out, i); + SET_REGNO_REG_SET (live_in, i); } + + /* If we don't need to add the move to BB, look for a single + successor block. */ + if (next_block) + next_block = next_block_for_reg (next_block, dregno, end_dregno); } + while (next_block); + + /* BB now defines DEST. It only uses the parts of DEST that overlap SRC + (next loop). */ + for (i = dregno; i < end_dregno; i++) + { + CLEAR_REGNO_REG_SET (bb_uses, i); + SET_REGNO_REG_SET (bb_defs, i); + } + + /* BB now uses SRC. */ + for (i = sregno; i < end_sregno; i++) + SET_REGNO_REG_SET (bb_uses, i); + + emit_insn_after (PATTERN (insn), bb_note (bb)); + delete_insn (insn); + return true; +} + +/* Look for register copies in the first block of the function, and move + them down into successor blocks if the register is used only on one + path. This exposes more opportunities for shrink-wrapping. These + kinds of sets often occur when incoming argument registers are moved + to call-saved registers because their values are live across one or + more calls during the function. */ + +static void +prepare_shrink_wrap (basic_block entry_block) +{ + rtx insn, curr, x; + HARD_REG_SET uses, defs; + df_ref *ref; + + CLEAR_HARD_REG_SET (uses); + CLEAR_HARD_REG_SET (defs); + FOR_BB_INSNS_REVERSE_SAFE (entry_block, insn, curr) + if (NONDEBUG_INSN_P (insn) + && !move_insn_for_shrink_wrap (entry_block, insn, uses, defs)) + { + /* Add all defined registers to DEFs. */ + for (ref = DF_INSN_DEFS (insn); *ref; ref++) + { + x = DF_REF_REG (*ref); + if (REG_P (x) && HARD_REGISTER_P (x)) + SET_HARD_REG_BIT (defs, REGNO (x)); + } + + /* Add all used registers to USESs. */ + for (ref = DF_INSN_USES (insn); *ref; ref++) + { + x = DF_REF_REG (*ref); + if (REG_P (x) && HARD_REGISTER_P (x)) + SET_HARD_REG_BIT (uses, REGNO (x)); + } + } } #endif diff --git a/gcc/haifa-sched.c b/gcc/haifa-sched.c index 4db23139b10..4e1909a535f 100644 --- a/gcc/haifa-sched.c +++ b/gcc/haifa-sched.c @@ -6489,20 +6489,6 @@ add_jump_dependencies (rtx insn, rtx jump) gcc_assert (!sd_lists_empty_p (jump, SD_LIST_BACK)); } -/* Return the NOTE_INSN_BASIC_BLOCK of BB. */ -rtx -bb_note (basic_block bb) -{ - rtx note; - - note = BB_HEAD (bb); - if (LABEL_P (note)) - note = NEXT_INSN (note); - - gcc_assert (NOTE_INSN_BASIC_BLOCK_P (note)); - return note; -} - /* Extend data structures for logical insn UID. */ void sched_extend_luids (void) diff --git a/gcc/sched-int.h b/gcc/sched-int.h index 9b29ea158cb..2fa5f759e85 100644 --- a/gcc/sched-int.h +++ b/gcc/sched-int.h @@ -130,7 +130,6 @@ extern void sched_insns_init (rtx); extern void sched_insns_finish (void); extern void *xrecalloc (void *, size_t, size_t, size_t); -extern rtx bb_note (basic_block); extern void reemit_notes (rtx); -- 2.11.4.GIT