aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/arm/arm.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/arm/arm.c')
-rw-r--r--gcc/config/arm/arm.c86
1 files changed, 68 insertions, 18 deletions
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 8cc1be2..86c897e 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -19216,7 +19216,36 @@ arm_compute_save_reg_mask (void)
return save_reg_mask;
}
-/* Compute a bit mask of which registers need to be
+/* Return a mask for the call-clobbered low registers that are unused
+ at the end of the prologue. */
+static unsigned long
+thumb1_prologue_unused_call_clobbered_lo_regs (void)
+{
+ unsigned long mask = 0;
+
+ for (int reg = 0; reg <= LAST_LO_REGNUM; reg++)
+ if (!callee_saved_reg_p (reg)
+ && !REGNO_REG_SET_P (df_get_live_out (ENTRY_BLOCK_PTR_FOR_FN (cfun)),
+ reg))
+ mask |= 1 << reg;
+ return mask;
+}
+
+/* Similarly for the start of the epilogue. */
+static unsigned long
+thumb1_epilogue_unused_call_clobbered_lo_regs (void)
+{
+ unsigned long mask = 0;
+
+ for (int reg = 0; reg <= LAST_LO_REGNUM; reg++)
+ if (!callee_saved_reg_p (reg)
+ && !REGNO_REG_SET_P (df_get_live_in (EXIT_BLOCK_PTR_FOR_FN (cfun)),
+ reg))
+ mask |= 1 << reg;
+ return mask;
+}
+
+/* Compute a bit mask of which core registers need to be
saved on the stack for the current function. */
static unsigned long
thumb1_compute_save_reg_mask (void)
@@ -19247,10 +19276,19 @@ thumb1_compute_save_reg_mask (void)
if (mask & 0xff || thumb_force_lr_save ())
mask |= (1 << LR_REGNUM);
- /* Make sure we have a low work register if we need one.
- We will need one if we are going to push a high register,
- but we are not currently intending to push a low register. */
+ bool call_clobbered_scratch
+ = (thumb1_prologue_unused_call_clobbered_lo_regs ()
+ && thumb1_epilogue_unused_call_clobbered_lo_regs ());
+
+ /* Make sure we have a low work register if we need one. We will
+ need one if we are going to push a high register, but we are not
+ currently intending to push a low register. However if both the
+ prologue and epilogue have a spare call-clobbered low register,
+ then we won't need to find an additional work register. It does
+ not need to be the same register in the prologue and
+ epilogue. */
if ((mask & 0xff) == 0
+ && !call_clobbered_scratch
&& ((mask & 0x0f00) || TARGET_BACKTRACE))
{
/* Use thumb_find_work_register to choose which register
@@ -24439,12 +24477,7 @@ thumb1_unexpanded_epilogue (void)
unsigned long mask = live_regs_mask & 0xff;
int next_hi_reg;
- /* The available low registers depend on the size of the value we are
- returning. */
- if (size <= 12)
- mask |= 1 << 3;
- if (size <= 8)
- mask |= 1 << 2;
+ mask |= thumb1_epilogue_unused_call_clobbered_lo_regs ();
if (mask == 0)
/* Oh dear! We have no low registers into which we can pop
@@ -24452,7 +24485,7 @@ thumb1_unexpanded_epilogue (void)
internal_error
("no low registers available for popping high registers");
- for (next_hi_reg = 8; next_hi_reg < 13; next_hi_reg++)
+ for (next_hi_reg = 12; next_hi_reg > LAST_LO_REGNUM; next_hi_reg--)
if (live_regs_mask & (1 << next_hi_reg))
break;
@@ -24460,7 +24493,7 @@ thumb1_unexpanded_epilogue (void)
{
/* Find lo register(s) into which the high register(s) can
be popped. */
- for (regno = 0; regno <= LAST_LO_REGNUM; regno++)
+ for (regno = LAST_LO_REGNUM; regno >= 0; regno--)
{
if (mask & (1 << regno))
high_regs_pushed--;
@@ -24468,20 +24501,22 @@ thumb1_unexpanded_epilogue (void)
break;
}
- mask &= (2 << regno) - 1; /* A noop if regno == 8 */
+ if (high_regs_pushed == 0 && regno >= 0)
+ mask &= ~((1 << regno) - 1);
/* Pop the values into the low register(s). */
thumb_pop (asm_out_file, mask);
/* Move the value(s) into the high registers. */
- for (regno = 0; regno <= LAST_LO_REGNUM; regno++)
+ for (regno = LAST_LO_REGNUM; regno >= 0; regno--)
{
if (mask & (1 << regno))
{
asm_fprintf (asm_out_file, "\tmov\t%r, %r\n", next_hi_reg,
regno);
- for (next_hi_reg++; next_hi_reg < 13; next_hi_reg++)
+ for (next_hi_reg--; next_hi_reg > LAST_LO_REGNUM;
+ next_hi_reg--)
if (live_regs_mask & (1 << next_hi_reg))
break;
}
@@ -24863,10 +24898,20 @@ thumb1_expand_prologue (void)
break;
/* Here we need to mask out registers used for passing arguments
- even if they can be pushed. This is to avoid using them to stash the high
- registers. Such kind of stash may clobber the use of arguments. */
+ even if they can be pushed. This is to avoid using them to
+ stash the high registers. Such kind of stash may clobber the
+ use of arguments. */
pushable_regs = l_mask & (~arg_regs_mask);
- if (lr_needs_saving)
+ pushable_regs |= thumb1_prologue_unused_call_clobbered_lo_regs ();
+
+ /* Normally, LR can be used as a scratch register once it has been
+ saved; but if the function examines its own return address then
+ the value is still live and we need to avoid using it. */
+ bool return_addr_live
+ = REGNO_REG_SET_P (df_get_live_out (ENTRY_BLOCK_PTR_FOR_FN (cfun)),
+ LR_REGNUM);
+
+ if (lr_needs_saving || return_addr_live)
pushable_regs &= ~(1 << LR_REGNUM);
if (pushable_regs == 0)
@@ -24907,6 +24952,11 @@ thumb1_expand_prologue (void)
push_mask |= 1 << LR_REGNUM;
real_regs_mask |= 1 << LR_REGNUM;
lr_needs_saving = false;
+ /* If the return address is not live at this point, we
+ can add LR to the list of registers that we can use
+ for pushes. */
+ if (!return_addr_live)
+ pushable_regs |= 1 << LR_REGNUM;
}
insn = thumb1_emit_multi_reg_push (push_mask, real_regs_mask);