The main function for epilogue RTL generation, used by expand epilogue

patterns.

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_expand_epilogue): New declaration.
        * config/arm/arm.c (arm_expand_epilogue): New function.
        * config/arm/arm.md (epilogue): Update condition and code.
        (sibcall_epilogue): Likewise.


Co-Authored-By: Greta Yorsh <greta.yorsh@arm.com>
Co-Authored-By: Sameera Deshpande <sameera.deshpande@arm.com>

From-SVN: r188742
This commit is contained in:
Ian Bolton 2012-06-18 17:47:40 +00:00 committed by Greta Yorsh
parent 0f9926f367
commit d461c88a25
4 changed files with 268 additions and 24 deletions

View File

@ -1,3 +1,12 @@
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_expand_epilogue): New declaration.
* config/arm/arm.c (arm_expand_epilogue): New function.
* config/arm/arm.md (epilogue): Update condition and code.
(sibcall_epilogue): Likewise.
2012-06-18 Ian Bolton <ian.bolton@arm.com>
Sameera Deshpande <sameera.deshpande@arm.com>
Greta Yorsh <greta.yorsh@arm.com>

View File

@ -30,6 +30,7 @@ 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 const char *arm_strip_name_encoding (const char *);
extern void arm_asm_output_labelref (FILE *, const char *);
extern void thumb2_asm_output_opcode (FILE *);

View File

@ -23473,6 +23473,241 @@ arm_expand_epilogue_apcs_frame (bool really_return)
emit_jump_insn (simple_return_rtx);
}
/* Generate RTL to represent ARM epilogue. Really_return is true if the
function is not a sibcall. */
void
arm_expand_epilogue (bool really_return)
{
unsigned long func_type;
unsigned long saved_regs_mask;
int num_regs = 0;
int i;
int amount;
int floats_from_frame = 0;
arm_stack_offsets *offsets;
func_type = arm_current_func_type ();
/* Naked functions don't have epilogue. Hence, generate return pattern, and
let output_return_instruction take care of instruction emition if any. */
if (IS_NAKED (func_type)
|| (IS_VOLATILE (func_type) && TARGET_ABORT_NORETURN))
{
emit_jump_insn (simple_return_rtx);
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);
if (TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM)
{
arm_expand_epilogue_apcs_frame (really_return);
return;
}
/* Get frame offsets for ARM. */
offsets = arm_get_frame_offsets ();
saved_regs_mask = offsets->saved_regs_mask;
/* Find offset of floating point register from frame pointer.
The initialization is done in this way to take care of frame pointer
and static-chain register, if stored. */
floats_from_frame = offsets->saved_args - offsets->frame;
/* Compute how many registers saved and how far away the floats will be. */
for (i = 0; i <= LAST_ARM_REGNUM; i++)
if (saved_regs_mask & (1 << i))
{
num_regs++;
floats_from_frame += 4;
}
if (frame_pointer_needed)
{
/* Restore stack pointer if necessary. */
if (TARGET_ARM)
{
/* In ARM mode, frame pointer points to first saved register.
Restore stack pointer to last saved register. */
amount = offsets->frame - offsets->saved_regs;
/* Force out any pending memory operations that reference stacked data
before stack de-allocation occurs. */
emit_insn (gen_blockage ());
emit_insn (gen_addsi3 (stack_pointer_rtx,
hard_frame_pointer_rtx,
GEN_INT (amount)));
/* Emit USE(stack_pointer_rtx) to ensure that stack adjustment is not
deleted. */
emit_insn (gen_prologue_use (stack_pointer_rtx));
}
else
{
/* In Thumb-2 mode, the frame pointer points to the last saved
register. */
amount = offsets->locals_base - offsets->saved_regs;
if (amount)
emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
hard_frame_pointer_rtx,
GEN_INT (amount)));
/* Force out any pending memory operations that reference stacked data
before stack de-allocation occurs. */
emit_insn (gen_blockage ());
emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx));
/* Emit USE(stack_pointer_rtx) to ensure that stack adjustment is not
deleted. */
emit_insn (gen_prologue_use (stack_pointer_rtx));
}
}
else
{
/* Pop off outgoing args and local frame to adjust stack pointer to
last saved register. */
amount = offsets->outgoing_args - offsets->saved_regs;
if (amount)
{
/* Force out any pending memory operations that reference stacked data
before stack de-allocation occurs. */
emit_insn (gen_blockage ());
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (amount)));
/* Emit USE(stack_pointer_rtx) to ensure that stack adjustment is
not deleted. */
emit_insn (gen_prologue_use (stack_pointer_rtx));
}
}
if (TARGET_HARD_FLOAT && TARGET_VFP)
{
/* Generate VFP register multi-pop. */
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
vldm operations. The need to match groups is because,
unlike pop, vldm can only do consecutive regs. */
for (i = LAST_VFP_REGNUM - 1; i >= FIRST_VFP_REGNUM; i -= 2)
/* Look for a case where a reg does not need restoring. */
if ((!df_regs_ever_live_p (i) || call_used_regs[i])
&& (!df_regs_ever_live_p (i + 1)
|| call_used_regs[i + 1]))
{
/* Restore the regs discovered so far (from reg+2 to
end_reg). */
if (end_reg > i + 2)
arm_emit_vfp_multi_reg_pop (i + 2,
(end_reg - (i + 2)) / 2,
stack_pointer_rtx);
end_reg = i;
}
/* Restore the remaining regs that we have discovered (or possibly
even all of them, if the conditional in the for loop never
fired). */
if (end_reg > i + 2)
arm_emit_vfp_multi_reg_pop (i + 2,
(end_reg - (i + 2)) / 2,
stack_pointer_rtx);
}
if (TARGET_IWMMXT)
for (i = FIRST_IWMMXT_REGNUM; i <= LAST_IWMMXT_REGNUM; i++)
if (df_regs_ever_live_p (i) && !call_used_regs[i])
{
rtx insn;
rtx addr = gen_rtx_MEM (V2SImode,
gen_rtx_POST_INC (SImode,
stack_pointer_rtx));
set_mem_alias_set (addr, get_frame_alias_set ());
insn = emit_insn (gen_movsi (gen_rtx_REG (V2SImode, i), addr));
REG_NOTES (insn) = alloc_reg_note (REG_CFA_RESTORE,
gen_rtx_REG (V2SImode, i),
NULL_RTX);
}
if (saved_regs_mask)
{
rtx insn;
bool return_in_pc = false;
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);
return_in_pc = true;
}
if (num_regs == 1 && (!IS_INTERRUPT (func_type) || !return_in_pc))
{
for (i = 0; i <= LAST_ARM_REGNUM; i++)
if (saved_regs_mask & (1 << i))
{
rtx addr = gen_rtx_MEM (SImode,
gen_rtx_POST_INC (SImode,
stack_pointer_rtx));
set_mem_alias_set (addr, get_frame_alias_set ());
if (i == PC_REGNUM)
{
insn = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (2));
XVECEXP (insn, 0, 0) = ret_rtx;
XVECEXP (insn, 0, 1) = gen_rtx_SET (SImode,
gen_rtx_REG (SImode, i),
addr);
RTX_FRAME_RELATED_P (XVECEXP (insn, 0, 1)) = 1;
insn = emit_jump_insn (insn);
}
else
{
insn = emit_insn (gen_movsi (gen_rtx_REG (SImode, i),
addr));
REG_NOTES (insn) = alloc_reg_note (REG_CFA_RESTORE,
gen_rtx_REG (SImode, i),
NULL_RTX);
}
}
}
else
{
arm_emit_multi_reg_pop (saved_regs_mask);
}
if (return_in_pc == true)
return;
}
if (crtl->args.pretend_args_size)
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (crtl->args.pretend_args_size)));
if (!really_return)
return;
if (crtl->calls_eh_return)
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
gen_rtx_REG (SImode, ARM_EH_STACKADJ_REGNUM)));
if (IS_STACKALIGN (func_type))
/* Restore the original stack pointer. Before prologue, the stack was
realigned and the original stack pointer saved in r0. For details,
see comment in arm_expand_prologue. */
emit_insn (gen_movsi (stack_pointer_rtx, gen_rtx_REG (SImode, 0)));
emit_jump_insn (simple_return_rtx);
}
/* Implementation of insn prologue_thumb1_interwork. This is the first
"instruction" of a function called in ARM mode. Swap to thumb mode. */

View File

@ -10547,14 +10547,21 @@
if (crtl->calls_eh_return)
emit_insn (gen_prologue_use (gen_rtx_REG (Pmode, 2)));
if (TARGET_THUMB1)
thumb1_expand_epilogue ();
else if (USE_RETURN_INSN (FALSE))
{
emit_jump_insn (gen_return ());
DONE;
}
emit_jump_insn (gen_rtx_UNSPEC_VOLATILE (VOIDmode,
gen_rtvec (1, ret_rtx), VUNSPEC_EPILOGUE));
{
thumb1_expand_epilogue ();
emit_jump_insn (gen_rtx_UNSPEC_VOLATILE (VOIDmode,
gen_rtvec (1, ret_rtx), VUNSPEC_EPILOGUE));
}
else if (HAVE_return)
{
/* HAVE_return is testing for USE_RETURN_INSN (FALSE). Hence,
no need for explicit testing again. */
emit_jump_insn (gen_return ());
}
else if (TARGET_32BIT)
{
arm_expand_epilogue (true);
}
DONE;
"
)
@ -10571,22 +10578,14 @@
;; to add an unspec of the link register to ensure that flow
;; does not think that it is unused by the sibcall branch that
;; will replace the standard function epilogue.
(define_insn "sibcall_epilogue"
[(parallel [(unspec:SI [(reg:SI LR_REGNUM)] UNSPEC_PROLOGUE_USE)
(unspec_volatile [(return)] VUNSPEC_EPILOGUE)])]
"TARGET_32BIT"
"*
if (use_return_insn (FALSE, next_nonnote_insn (insn)))
return output_return_instruction (const_true_rtx, FALSE, FALSE);
return arm_output_epilogue (next_nonnote_insn (insn));
"
;; Length is absolute worst case
[(set_attr "length" "44")
(set_attr "type" "block")
;; We don't clobber the conditions, but the potential length of this
;; operation is sufficient to make conditionalizing the sequence
;; unlikely to be profitable.
(set_attr "conds" "clob")]
(define_expand "sibcall_epilogue"
[(parallel [(unspec:SI [(reg:SI LR_REGNUM)] UNSPEC_PROLOGUE_USE)
(unspec_volatile [(return)] VUNSPEC_EPILOGUE)])]
"TARGET_32BIT"
"
arm_expand_epilogue (false);
DONE;
"
)
(define_insn "*epilogue_insns"