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:
Ian Bolton 2012-06-18 18:06:54 +00:00 committed by Greta Yorsh
parent 482baa63f3
commit c59e1214f7
4 changed files with 11 additions and 530 deletions

View File

@ -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>

View File

@ -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);

View File

@ -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)

View File

@ -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