The first basic block contains insns to move incoming argument registers
to pseudos. When these pseudos live across calls, they get allocated to
call-saved registers. This in turns disables shrink-wrapping, since the
move instruction requires the prologue (saving the call-saved reg) to
occur before it.
This patch addresses the problem by moving such moves downwards through
the CFG until we find a place where the destination is used or the
incoming argument is clobbered.
Bernd
* function.c (prepare_shrink_wrap): New function.
(thread_prologue_and_epilogue_insns): Call it.
Index: gcc/function.c
===================================================================
--- gcc.orig/function.c
+++ gcc/function.c
@@ -5299,6 +5299,127 @@ requires_stack_frame_p (rtx insn)
return true;
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. */
+
+static void
+prepare_shrink_wrap (basic_block entry_block)
+{
+ rtx insn, curr;
+ FOR_BB_INSNS_SAFE (entry_block, insn, curr)
+ {
+ basic_block next_bb;
+ edge e, live_edge;
+ edge_iterator ei;
+ rtx set, scan;
+ unsigned destreg, srcreg;
+
+ if (!NONDEBUG_INSN_P (insn))
+ continue;
+ set = single_set (insn);
+ if (!set)
+ continue;
+
+ 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;
+
+ next_bb = entry_block;
+ scan = insn;
+
+ for (;;)
+ {
+ live_edge = NULL;
+ 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 (next_bb != entry_block)
+ {
+ 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);
+ }
+ }
+}
+
#endif
#ifdef HAVE_return
@@ -5499,6 +5620,8 @@ thread_prologue_and_epilogue_insns (void
bitmap_head bb_antic_flags;
bitmap_head bb_on_list;
+ prepare_shrink_wrap (entry_edge->dest);
+
bitmap_initialize (&bb_antic_flags, &bitmap_default_obstack);
bitmap_initialize (&bb_on_list, &bitmap_default_obstack);