As a result of the previous changes, epilogue_insns pattern can only be generated in Thumb1.
As a result of the previous changes, epilogue_insns pattern can only be generated in Thumb1. After removing other cases in define_insn for epilogue_insns, the function arm_output_epilogue becomes dead code and can be eliminated, along with all its helper functions. gcc/ 2012-06-18 Ian Bolton <ian.bolton@arm.com> Sameera Deshpande <sameera.deshpande@arm.com> Greta Yorsh <greta.yorsh@arm.com> * config/arm/arm-protos.h (arm_output_epilogue): Remove. * config/arm/arm.c (print_multi_reg): Remove. (vfp_output_fldmd): Likewise. (arm_output_epilogue): Likewise. * config/arm/arm.md (epilogue_insns): Update condition and code. Co-Authored-By: Greta Yorsh <greta.yorsh@arm.com> Co-Authored-By: Sameera Deshpande <sameera.deshpande@arm.com> From-SVN: r188745
This commit is contained in:
parent
482baa63f3
commit
c59e1214f7
|
@ -1,3 +1,13 @@
|
|||
2012-06-18 Ian Bolton <ian.bolton@arm.com>
|
||||
Sameera Deshpande <sameera.deshpande@arm.com>
|
||||
Greta Yorsh <greta.yorsh@arm.com>
|
||||
|
||||
* config/arm/arm-protos.h (arm_output_epilogue): Remove.
|
||||
* config/arm/arm.c (print_multi_reg): Remove.
|
||||
(vfp_output_fldmd): Likewise.
|
||||
(arm_output_epilogue): Likewise.
|
||||
* config/arm/arm.md (epilogue_insns): Update condition and code.
|
||||
|
||||
2012-06-18 Ian Bolton <ian.bolton@arm.com>
|
||||
Sameera Deshpande <sameera.deshpande@arm.com>
|
||||
Greta Yorsh <greta.yorsh@arm.com>
|
||||
|
|
|
@ -28,7 +28,6 @@ extern int use_return_insn (int, rtx);
|
|||
extern enum reg_class arm_regno_class (int);
|
||||
extern void arm_load_pic_register (unsigned long);
|
||||
extern int arm_volatile_func (void);
|
||||
extern const char *arm_output_epilogue (rtx);
|
||||
extern void arm_expand_prologue (void);
|
||||
extern void arm_expand_epilogue (bool);
|
||||
extern void thumb2_expand_return (void);
|
||||
|
|
|
@ -13659,86 +13659,6 @@ fp_const_from_val (REAL_VALUE_TYPE *r)
|
|||
return "0";
|
||||
}
|
||||
|
||||
/* Output the operands of a LDM/STM instruction to STREAM.
|
||||
MASK is the ARM register set mask of which only bits 0-15 are important.
|
||||
REG is the base register, either the frame pointer or the stack pointer,
|
||||
INSTR is the possibly suffixed load or store instruction.
|
||||
RFE is nonzero if the instruction should also copy spsr to cpsr. */
|
||||
|
||||
static void
|
||||
print_multi_reg (FILE *stream, const char *instr, unsigned reg,
|
||||
unsigned long mask, int rfe)
|
||||
{
|
||||
unsigned i;
|
||||
bool not_first = FALSE;
|
||||
|
||||
gcc_assert (!rfe || (mask & (1 << PC_REGNUM)));
|
||||
fputc ('\t', stream);
|
||||
asm_fprintf (stream, instr, reg);
|
||||
fputc ('{', stream);
|
||||
|
||||
for (i = 0; i <= LAST_ARM_REGNUM; i++)
|
||||
if (mask & (1 << i))
|
||||
{
|
||||
if (not_first)
|
||||
fprintf (stream, ", ");
|
||||
|
||||
asm_fprintf (stream, "%r", i);
|
||||
not_first = TRUE;
|
||||
}
|
||||
|
||||
if (rfe)
|
||||
fprintf (stream, "}^\n");
|
||||
else
|
||||
fprintf (stream, "}\n");
|
||||
}
|
||||
|
||||
|
||||
/* Output a FLDMD instruction to STREAM.
|
||||
BASE if the register containing the address.
|
||||
REG and COUNT specify the register range.
|
||||
Extra registers may be added to avoid hardware bugs.
|
||||
|
||||
We output FLDMD even for ARMv5 VFP implementations. Although
|
||||
FLDMD is technically not supported until ARMv6, it is believed
|
||||
that all VFP implementations support its use in this context. */
|
||||
|
||||
static void
|
||||
vfp_output_fldmd (FILE * stream, unsigned int base, int reg, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Workaround ARM10 VFPr1 bug. */
|
||||
if (count == 2 && !arm_arch6)
|
||||
{
|
||||
if (reg == 15)
|
||||
reg--;
|
||||
count++;
|
||||
}
|
||||
|
||||
/* FLDMD may not load more than 16 doubleword registers at a time. Split the
|
||||
load into multiple parts if we have to handle more than 16 registers. */
|
||||
if (count > 16)
|
||||
{
|
||||
vfp_output_fldmd (stream, base, reg, 16);
|
||||
vfp_output_fldmd (stream, base, reg + 16, count - 16);
|
||||
return;
|
||||
}
|
||||
|
||||
fputc ('\t', stream);
|
||||
asm_fprintf (stream, "fldmfdd\t%r!, {", base);
|
||||
|
||||
for (i = reg; i < reg + count; i++)
|
||||
{
|
||||
if (i > reg)
|
||||
fputs (", ", stream);
|
||||
asm_fprintf (stream, "d%d", i);
|
||||
}
|
||||
fputs ("}\n", stream);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* OPERANDS[0] is the entire list of insns that constitute pop,
|
||||
OPERANDS[1] is the base register, RETURN_PC is true iff return insn
|
||||
is in the list, UPDATE is true iff the list contains explicit
|
||||
|
@ -15833,451 +15753,6 @@ arm_output_function_prologue (FILE *f, HOST_WIDE_INT frame_size)
|
|||
|
||||
}
|
||||
|
||||
const char *
|
||||
arm_output_epilogue (rtx sibling)
|
||||
{
|
||||
int reg;
|
||||
unsigned long saved_regs_mask;
|
||||
unsigned long func_type;
|
||||
/* Floats_offset is the offset from the "virtual" frame. In an APCS
|
||||
frame that is $fp + 4 for a non-variadic function. */
|
||||
int floats_offset = 0;
|
||||
rtx operands[3];
|
||||
FILE * f = asm_out_file;
|
||||
unsigned int lrm_count = 0;
|
||||
int really_return = (sibling == NULL);
|
||||
int start_reg;
|
||||
arm_stack_offsets *offsets;
|
||||
|
||||
/* If we have already generated the return instruction
|
||||
then it is futile to generate anything else. */
|
||||
if (use_return_insn (FALSE, sibling) &&
|
||||
(cfun->machine->return_used_this_function != 0))
|
||||
return "";
|
||||
|
||||
func_type = arm_current_func_type ();
|
||||
|
||||
if (IS_NAKED (func_type))
|
||||
/* Naked functions don't have epilogues. */
|
||||
return "";
|
||||
|
||||
if (IS_VOLATILE (func_type) && TARGET_ABORT_NORETURN)
|
||||
{
|
||||
rtx op;
|
||||
|
||||
/* A volatile function should never return. Call abort. */
|
||||
op = gen_rtx_SYMBOL_REF (Pmode, NEED_PLT_RELOC ? "abort(PLT)" : "abort");
|
||||
assemble_external_libcall (op);
|
||||
output_asm_insn ("bl\t%a0", &op);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/* If we are throwing an exception, then we really must be doing a
|
||||
return, so we can't tail-call. */
|
||||
gcc_assert (!crtl->calls_eh_return || really_return);
|
||||
|
||||
offsets = arm_get_frame_offsets ();
|
||||
saved_regs_mask = offsets->saved_regs_mask;
|
||||
|
||||
if (TARGET_IWMMXT)
|
||||
lrm_count = bit_count (saved_regs_mask);
|
||||
|
||||
floats_offset = offsets->saved_args;
|
||||
/* Compute how far away the floats will be. */
|
||||
for (reg = 0; reg <= LAST_ARM_REGNUM; reg++)
|
||||
if (saved_regs_mask & (1 << reg))
|
||||
floats_offset += 4;
|
||||
|
||||
if (TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM)
|
||||
{
|
||||
/* This variable is for the Virtual Frame Pointer, not VFP regs. */
|
||||
int vfp_offset = offsets->frame;
|
||||
|
||||
if (TARGET_FPA_EMU2)
|
||||
{
|
||||
for (reg = LAST_FPA_REGNUM; reg >= FIRST_FPA_REGNUM; reg--)
|
||||
if (df_regs_ever_live_p (reg) && !call_used_regs[reg])
|
||||
{
|
||||
floats_offset += 12;
|
||||
asm_fprintf (f, "\tldfe\t%r, [%r, #-%d]\n",
|
||||
reg, FP_REGNUM, floats_offset - vfp_offset);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
start_reg = LAST_FPA_REGNUM;
|
||||
|
||||
for (reg = LAST_FPA_REGNUM; reg >= FIRST_FPA_REGNUM; reg--)
|
||||
{
|
||||
if (df_regs_ever_live_p (reg) && !call_used_regs[reg])
|
||||
{
|
||||
floats_offset += 12;
|
||||
|
||||
/* We can't unstack more than four registers at once. */
|
||||
if (start_reg - reg == 3)
|
||||
{
|
||||
asm_fprintf (f, "\tlfm\t%r, 4, [%r, #-%d]\n",
|
||||
reg, FP_REGNUM, floats_offset - vfp_offset);
|
||||
start_reg = reg - 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (reg != start_reg)
|
||||
asm_fprintf (f, "\tlfm\t%r, %d, [%r, #-%d]\n",
|
||||
reg + 1, start_reg - reg,
|
||||
FP_REGNUM, floats_offset - vfp_offset);
|
||||
start_reg = reg - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Just in case the last register checked also needs unstacking. */
|
||||
if (reg != start_reg)
|
||||
asm_fprintf (f, "\tlfm\t%r, %d, [%r, #-%d]\n",
|
||||
reg + 1, start_reg - reg,
|
||||
FP_REGNUM, floats_offset - vfp_offset);
|
||||
}
|
||||
|
||||
if (TARGET_HARD_FLOAT && TARGET_VFP)
|
||||
{
|
||||
int saved_size;
|
||||
|
||||
/* The fldmd insns do not have base+offset addressing
|
||||
modes, so we use IP to hold the address. */
|
||||
saved_size = arm_get_vfp_saved_size ();
|
||||
|
||||
if (saved_size > 0)
|
||||
{
|
||||
floats_offset += saved_size;
|
||||
asm_fprintf (f, "\tsub\t%r, %r, #%d\n", IP_REGNUM,
|
||||
FP_REGNUM, floats_offset - vfp_offset);
|
||||
}
|
||||
start_reg = FIRST_VFP_REGNUM;
|
||||
for (reg = FIRST_VFP_REGNUM; reg < LAST_VFP_REGNUM; reg += 2)
|
||||
{
|
||||
if ((!df_regs_ever_live_p (reg) || call_used_regs[reg])
|
||||
&& (!df_regs_ever_live_p (reg + 1) || call_used_regs[reg + 1]))
|
||||
{
|
||||
if (start_reg != reg)
|
||||
vfp_output_fldmd (f, IP_REGNUM,
|
||||
(start_reg - FIRST_VFP_REGNUM) / 2,
|
||||
(reg - start_reg) / 2);
|
||||
start_reg = reg + 2;
|
||||
}
|
||||
}
|
||||
if (start_reg != reg)
|
||||
vfp_output_fldmd (f, IP_REGNUM,
|
||||
(start_reg - FIRST_VFP_REGNUM) / 2,
|
||||
(reg - start_reg) / 2);
|
||||
}
|
||||
|
||||
if (TARGET_IWMMXT)
|
||||
{
|
||||
/* The frame pointer is guaranteed to be non-double-word aligned.
|
||||
This is because it is set to (old_stack_pointer - 4) and the
|
||||
old_stack_pointer was double word aligned. Thus the offset to
|
||||
the iWMMXt registers to be loaded must also be non-double-word
|
||||
sized, so that the resultant address *is* double-word aligned.
|
||||
We can ignore floats_offset since that was already included in
|
||||
the live_regs_mask. */
|
||||
lrm_count += (lrm_count % 2 ? 2 : 1);
|
||||
|
||||
for (reg = LAST_IWMMXT_REGNUM; reg >= FIRST_IWMMXT_REGNUM; reg--)
|
||||
if (df_regs_ever_live_p (reg) && !call_used_regs[reg])
|
||||
{
|
||||
asm_fprintf (f, "\twldrd\t%r, [%r, #-%d]\n",
|
||||
reg, FP_REGNUM, lrm_count * 4);
|
||||
lrm_count += 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* saved_regs_mask should contain the IP, which at the time of stack
|
||||
frame generation actually contains the old stack pointer. So a
|
||||
quick way to unwind the stack is just pop the IP register directly
|
||||
into the stack pointer. */
|
||||
gcc_assert (saved_regs_mask & (1 << IP_REGNUM));
|
||||
saved_regs_mask &= ~ (1 << IP_REGNUM);
|
||||
saved_regs_mask |= (1 << SP_REGNUM);
|
||||
|
||||
/* There are two registers left in saved_regs_mask - LR and PC. We
|
||||
only need to restore the LR register (the return address), but to
|
||||
save time we can load it directly into the PC, unless we need a
|
||||
special function exit sequence, or we are not really returning. */
|
||||
if (really_return
|
||||
&& ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL
|
||||
&& !crtl->calls_eh_return)
|
||||
/* Delete the LR from the register mask, so that the LR on
|
||||
the stack is loaded into the PC in the register mask. */
|
||||
saved_regs_mask &= ~ (1 << LR_REGNUM);
|
||||
else
|
||||
saved_regs_mask &= ~ (1 << PC_REGNUM);
|
||||
|
||||
/* We must use SP as the base register, because SP is one of the
|
||||
registers being restored. If an interrupt or page fault
|
||||
happens in the ldm instruction, the SP might or might not
|
||||
have been restored. That would be bad, as then SP will no
|
||||
longer indicate the safe area of stack, and we can get stack
|
||||
corruption. Using SP as the base register means that it will
|
||||
be reset correctly to the original value, should an interrupt
|
||||
occur. If the stack pointer already points at the right
|
||||
place, then omit the subtraction. */
|
||||
if (offsets->outgoing_args != (1 + (int) bit_count (saved_regs_mask))
|
||||
|| cfun->calls_alloca)
|
||||
asm_fprintf (f, "\tsub\t%r, %r, #%d\n", SP_REGNUM, FP_REGNUM,
|
||||
4 * bit_count (saved_regs_mask));
|
||||
print_multi_reg (f, "ldmfd\t%r, ", SP_REGNUM, saved_regs_mask, 0);
|
||||
|
||||
if (IS_INTERRUPT (func_type))
|
||||
/* Interrupt handlers will have pushed the
|
||||
IP onto the stack, so restore it now. */
|
||||
print_multi_reg (f, "ldmfd\t%r!, ", SP_REGNUM, 1 << IP_REGNUM, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This branch is executed for ARM mode (non-apcs frames) and
|
||||
Thumb-2 mode. Frame layout is essentially the same for those
|
||||
cases, except that in ARM mode frame pointer points to the
|
||||
first saved register, while in Thumb-2 mode the frame pointer points
|
||||
to the last saved register.
|
||||
|
||||
It is possible to make frame pointer point to last saved
|
||||
register in both cases, and remove some conditionals below.
|
||||
That means that fp setup in prologue would be just "mov fp, sp"
|
||||
and sp restore in epilogue would be just "mov sp, fp", whereas
|
||||
now we have to use add/sub in those cases. However, the value
|
||||
of that would be marginal, as both mov and add/sub are 32-bit
|
||||
in ARM mode, and it would require extra conditionals
|
||||
in arm_expand_prologue to distinguish ARM-apcs-frame case
|
||||
(where frame pointer is required to point at first register)
|
||||
and ARM-non-apcs-frame. Therefore, such change is postponed
|
||||
until real need arise. */
|
||||
unsigned HOST_WIDE_INT amount;
|
||||
int rfe;
|
||||
/* Restore stack pointer if necessary. */
|
||||
if (TARGET_ARM && frame_pointer_needed)
|
||||
{
|
||||
operands[0] = stack_pointer_rtx;
|
||||
operands[1] = hard_frame_pointer_rtx;
|
||||
|
||||
operands[2] = GEN_INT (offsets->frame - offsets->saved_regs);
|
||||
output_add_immediate (operands);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (frame_pointer_needed)
|
||||
{
|
||||
/* For Thumb-2 restore sp from the frame pointer.
|
||||
Operand restrictions mean we have to incrememnt FP, then copy
|
||||
to SP. */
|
||||
amount = offsets->locals_base - offsets->saved_regs;
|
||||
operands[0] = hard_frame_pointer_rtx;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned long count;
|
||||
operands[0] = stack_pointer_rtx;
|
||||
amount = offsets->outgoing_args - offsets->saved_regs;
|
||||
/* pop call clobbered registers if it avoids a
|
||||
separate stack adjustment. */
|
||||
count = offsets->saved_regs - offsets->saved_args;
|
||||
if (optimize_size
|
||||
&& count != 0
|
||||
&& !crtl->calls_eh_return
|
||||
&& bit_count(saved_regs_mask) * 4 == count
|
||||
&& !IS_INTERRUPT (func_type)
|
||||
&& !IS_STACKALIGN (func_type)
|
||||
&& !crtl->tail_call_emit)
|
||||
{
|
||||
unsigned long mask;
|
||||
/* Preserve return values, of any size. */
|
||||
mask = (1 << ((arm_size_return_regs() + 3) / 4)) - 1;
|
||||
mask ^= 0xf;
|
||||
mask &= ~saved_regs_mask;
|
||||
reg = 0;
|
||||
while (bit_count (mask) * 4 > amount)
|
||||
{
|
||||
while ((mask & (1 << reg)) == 0)
|
||||
reg++;
|
||||
mask &= ~(1 << reg);
|
||||
}
|
||||
if (bit_count (mask) * 4 == amount) {
|
||||
amount = 0;
|
||||
saved_regs_mask |= mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (amount)
|
||||
{
|
||||
operands[1] = operands[0];
|
||||
operands[2] = GEN_INT (amount);
|
||||
output_add_immediate (operands);
|
||||
}
|
||||
if (frame_pointer_needed)
|
||||
asm_fprintf (f, "\tmov\t%r, %r\n",
|
||||
SP_REGNUM, HARD_FRAME_POINTER_REGNUM);
|
||||
}
|
||||
|
||||
if (TARGET_FPA_EMU2)
|
||||
{
|
||||
for (reg = FIRST_FPA_REGNUM; reg <= LAST_FPA_REGNUM; reg++)
|
||||
if (df_regs_ever_live_p (reg) && !call_used_regs[reg])
|
||||
asm_fprintf (f, "\tldfe\t%r, [%r], #12\n",
|
||||
reg, SP_REGNUM);
|
||||
}
|
||||
else
|
||||
{
|
||||
start_reg = FIRST_FPA_REGNUM;
|
||||
|
||||
for (reg = FIRST_FPA_REGNUM; reg <= LAST_FPA_REGNUM; reg++)
|
||||
{
|
||||
if (df_regs_ever_live_p (reg) && !call_used_regs[reg])
|
||||
{
|
||||
if (reg - start_reg == 3)
|
||||
{
|
||||
asm_fprintf (f, "\tlfmfd\t%r, 4, [%r]!\n",
|
||||
start_reg, SP_REGNUM);
|
||||
start_reg = reg + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (reg != start_reg)
|
||||
asm_fprintf (f, "\tlfmfd\t%r, %d, [%r]!\n",
|
||||
start_reg, reg - start_reg,
|
||||
SP_REGNUM);
|
||||
|
||||
start_reg = reg + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Just in case the last register checked also needs unstacking. */
|
||||
if (reg != start_reg)
|
||||
asm_fprintf (f, "\tlfmfd\t%r, %d, [%r]!\n",
|
||||
start_reg, reg - start_reg, SP_REGNUM);
|
||||
}
|
||||
|
||||
if (TARGET_HARD_FLOAT && TARGET_VFP)
|
||||
{
|
||||
int end_reg = LAST_VFP_REGNUM + 1;
|
||||
|
||||
/* Scan the registers in reverse order. We need to match
|
||||
any groupings made in the prologue and generate matching
|
||||
pop operations. */
|
||||
for (reg = LAST_VFP_REGNUM - 1; reg >= FIRST_VFP_REGNUM; reg -= 2)
|
||||
{
|
||||
if ((!df_regs_ever_live_p (reg) || call_used_regs[reg])
|
||||
&& (!df_regs_ever_live_p (reg + 1)
|
||||
|| call_used_regs[reg + 1]))
|
||||
{
|
||||
if (end_reg > reg + 2)
|
||||
vfp_output_fldmd (f, SP_REGNUM,
|
||||
(reg + 2 - FIRST_VFP_REGNUM) / 2,
|
||||
(end_reg - (reg + 2)) / 2);
|
||||
end_reg = reg;
|
||||
}
|
||||
}
|
||||
if (end_reg > reg + 2)
|
||||
vfp_output_fldmd (f, SP_REGNUM, 0,
|
||||
(end_reg - (reg + 2)) / 2);
|
||||
}
|
||||
|
||||
if (TARGET_IWMMXT)
|
||||
for (reg = FIRST_IWMMXT_REGNUM; reg <= LAST_IWMMXT_REGNUM; reg++)
|
||||
if (df_regs_ever_live_p (reg) && !call_used_regs[reg])
|
||||
asm_fprintf (f, "\twldrd\t%r, [%r], #8\n", reg, SP_REGNUM);
|
||||
|
||||
/* If we can, restore the LR into the PC. */
|
||||
if (ARM_FUNC_TYPE (func_type) != ARM_FT_INTERWORKED
|
||||
&& (TARGET_ARM || ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL)
|
||||
&& !IS_STACKALIGN (func_type)
|
||||
&& really_return
|
||||
&& crtl->args.pretend_args_size == 0
|
||||
&& saved_regs_mask & (1 << LR_REGNUM)
|
||||
&& !crtl->calls_eh_return)
|
||||
{
|
||||
saved_regs_mask &= ~ (1 << LR_REGNUM);
|
||||
saved_regs_mask |= (1 << PC_REGNUM);
|
||||
rfe = IS_INTERRUPT (func_type);
|
||||
}
|
||||
else
|
||||
rfe = 0;
|
||||
|
||||
/* Load the registers off the stack. If we only have one register
|
||||
to load use the LDR instruction - it is faster. For Thumb-2
|
||||
always use pop and the assembler will pick the best instruction.*/
|
||||
if (TARGET_ARM && saved_regs_mask == (1 << LR_REGNUM)
|
||||
&& !IS_INTERRUPT(func_type))
|
||||
{
|
||||
asm_fprintf (f, "\tldr\t%r, [%r], #4\n", LR_REGNUM, SP_REGNUM);
|
||||
}
|
||||
else if (saved_regs_mask)
|
||||
{
|
||||
if (saved_regs_mask & (1 << SP_REGNUM))
|
||||
/* Note - write back to the stack register is not enabled
|
||||
(i.e. "ldmfd sp!..."). We know that the stack pointer is
|
||||
in the list of registers and if we add writeback the
|
||||
instruction becomes UNPREDICTABLE. */
|
||||
print_multi_reg (f, "ldmfd\t%r, ", SP_REGNUM, saved_regs_mask,
|
||||
rfe);
|
||||
else if (TARGET_ARM)
|
||||
print_multi_reg (f, "ldmfd\t%r!, ", SP_REGNUM, saved_regs_mask,
|
||||
rfe);
|
||||
else
|
||||
print_multi_reg (f, "pop\t", SP_REGNUM, saved_regs_mask, 0);
|
||||
}
|
||||
|
||||
if (crtl->args.pretend_args_size)
|
||||
{
|
||||
/* Unwind the pre-pushed regs. */
|
||||
operands[0] = operands[1] = stack_pointer_rtx;
|
||||
operands[2] = GEN_INT (crtl->args.pretend_args_size);
|
||||
output_add_immediate (operands);
|
||||
}
|
||||
}
|
||||
|
||||
/* We may have already restored PC directly from the stack. */
|
||||
if (!really_return || saved_regs_mask & (1 << PC_REGNUM))
|
||||
return "";
|
||||
|
||||
/* Stack adjustment for exception handler. */
|
||||
if (crtl->calls_eh_return)
|
||||
asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
|
||||
ARM_EH_STACKADJ_REGNUM);
|
||||
|
||||
/* Generate the return instruction. */
|
||||
switch ((int) ARM_FUNC_TYPE (func_type))
|
||||
{
|
||||
case ARM_FT_ISR:
|
||||
case ARM_FT_FIQ:
|
||||
asm_fprintf (f, "\tsubs\t%r, %r, #4\n", PC_REGNUM, LR_REGNUM);
|
||||
break;
|
||||
|
||||
case ARM_FT_EXCEPTION:
|
||||
asm_fprintf (f, "\tmovs\t%r, %r\n", PC_REGNUM, LR_REGNUM);
|
||||
break;
|
||||
|
||||
case ARM_FT_INTERWORKED:
|
||||
asm_fprintf (f, "\tbx\t%r\n", LR_REGNUM);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (IS_STACKALIGN (func_type))
|
||||
{
|
||||
/* See comment in arm_expand_prologue. */
|
||||
asm_fprintf (f, "\tmov\t%r, %r\n", SP_REGNUM, 0);
|
||||
}
|
||||
if (arm_arch5 || arm_arch4t)
|
||||
asm_fprintf (f, "\tbx\t%r\n", LR_REGNUM);
|
||||
else
|
||||
asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, LR_REGNUM);
|
||||
break;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
static void
|
||||
arm_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
|
||||
HOST_WIDE_INT frame_size ATTRIBUTE_UNUSED)
|
||||
|
|
|
@ -10619,11 +10619,8 @@
|
|||
|
||||
(define_insn "*epilogue_insns"
|
||||
[(unspec_volatile [(return)] VUNSPEC_EPILOGUE)]
|
||||
"TARGET_EITHER"
|
||||
"TARGET_THUMB1"
|
||||
"*
|
||||
if (TARGET_32BIT)
|
||||
return arm_output_epilogue (NULL);
|
||||
else /* TARGET_THUMB1 */
|
||||
return thumb1_unexpanded_epilogue ();
|
||||
"
|
||||
; Length is absolute worst case
|
||||
|
|
Loading…
Reference in New Issue