mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-02-11 15:10:12 +08:00
arm.c (use_return_insn): New argument, SIBLING.
* arm.c (use_return_insn): New argument, SIBLING. Support returning with a single instruction if the stack has been decremented by 4 and we have a frame pointer. Update all callers. (output_return_instruction): Likewise. (arm_output_epilogue): Change argument to SIBLING. Calculate really_return from the new argument. Update all callers. * arm.h (USE_RETURN_INSN): Pass NULL for the sibling. * arm.md (sibcall_epilogue): Call use_return_insn directly, and pass the sibling call. * arm-protos.h (use_return_insn, arm_output_epilogue): Update prototypes. From-SVN: r73761
This commit is contained in:
parent
8d98c44cc3
commit
a72d4945d3
@ -1,3 +1,17 @@
|
||||
2003-11-20 Richard Earnshaw <rearnsha@arm.com>
|
||||
|
||||
* arm.c (use_return_insn): New argument, SIBLING. Support returning
|
||||
with a single instruction if the stack has been decremented by 4
|
||||
and we have a frame pointer. Update all callers.
|
||||
(output_return_instruction): Likewise.
|
||||
(arm_output_epilogue): Change argument to SIBLING. Calculate
|
||||
really_return from the new argument. Update all callers.
|
||||
* arm.h (USE_RETURN_INSN): Pass NULL for the sibling.
|
||||
* arm.md (sibcall_epilogue): Call use_return_insn directly, and
|
||||
pass the sibling call.
|
||||
* arm-protos.h (use_return_insn, arm_output_epilogue): Update
|
||||
prototypes.
|
||||
|
||||
2003-11-20 Joseph S. Myers <jsm@polyomino.org.uk>
|
||||
|
||||
* Makefile.in (extraclean): Delete.
|
||||
|
@ -24,11 +24,11 @@
|
||||
#define GCC_ARM_PROTOS_H
|
||||
|
||||
extern void arm_override_options (void);
|
||||
extern int use_return_insn (int);
|
||||
extern int use_return_insn (int, rtx);
|
||||
extern int arm_regno_class (int);
|
||||
extern void arm_finalize_pic (int);
|
||||
extern int arm_volatile_func (void);
|
||||
extern const char *arm_output_epilogue (int);
|
||||
extern const char *arm_output_epilogue (rtx);
|
||||
extern void arm_expand_prologue (void);
|
||||
extern HOST_WIDE_INT arm_get_frame_size (void);
|
||||
extern const char *arm_strip_name_encoding (const char *);
|
||||
|
@ -1002,14 +1002,17 @@ arm_current_func_type (void)
|
||||
return cfun->machine->func_type;
|
||||
}
|
||||
|
||||
/* Return 1 if it is possible to return using a single instruction. */
|
||||
/* Return 1 if it is possible to return using a single instruction.
|
||||
If SIBLING is non-null, this is a test for a return before a sibling
|
||||
call. SIBLING is the call insn, so we can examine its register usage. */
|
||||
|
||||
int
|
||||
use_return_insn (int iscond)
|
||||
use_return_insn (int iscond, rtx sibling)
|
||||
{
|
||||
int regno;
|
||||
unsigned int func_type;
|
||||
unsigned long saved_int_regs;
|
||||
unsigned HOST_WIDE_INT stack_adjust;
|
||||
|
||||
/* Never use a return instruction before reload has run. */
|
||||
if (!reload_completed)
|
||||
@ -1025,7 +1028,9 @@ use_return_insn (int iscond)
|
||||
/* So do interrupt functions that use the frame pointer. */
|
||||
if (IS_INTERRUPT (func_type) && frame_pointer_needed)
|
||||
return 0;
|
||||
|
||||
|
||||
stack_adjust = arm_get_frame_size () + current_function_outgoing_args_size;
|
||||
|
||||
/* As do variadic functions. */
|
||||
if (current_function_pretend_args_size
|
||||
|| cfun->machine->uses_anonymous_args
|
||||
@ -1033,12 +1038,51 @@ use_return_insn (int iscond)
|
||||
|| ARM_FUNC_TYPE (func_type) == ARM_FT_EXCEPTION_HANDLER
|
||||
/* Or if the function calls alloca */
|
||||
|| current_function_calls_alloca
|
||||
/* Or if there is a stack adjustment. */
|
||||
|| (arm_get_frame_size () + current_function_outgoing_args_size != 0))
|
||||
/* Or if there is a stack adjustment. However, if the stack pointer
|
||||
is saved on the stack, we can use a pre-incrementing stack load. */
|
||||
|| !(stack_adjust == 0 || (frame_pointer_needed && stack_adjust == 4)))
|
||||
return 0;
|
||||
|
||||
saved_int_regs = arm_compute_save_reg_mask ();
|
||||
|
||||
/* Unfortunately, the insn
|
||||
|
||||
ldmib sp, {..., sp, ...}
|
||||
|
||||
triggers a bug on most SA-110 based devices, such that the stack
|
||||
pointer won't be correctly restored if the instruction takes a
|
||||
page fault. We work around this problem by poping r3 along with
|
||||
the other registers, since that is never slower than executing
|
||||
another instruction.
|
||||
|
||||
We test for !arm_arch5 here, because code for any architecture
|
||||
less than this could potentially be run on one of the buggy
|
||||
chips. */
|
||||
if (stack_adjust == 4 && !arm_arch5)
|
||||
{
|
||||
/* Validate that r3 is a call-clobbered register (always true in
|
||||
the default abi) ... */
|
||||
if (!call_used_regs[3])
|
||||
return 0;
|
||||
|
||||
/* ... that it isn't being used for a return value (always true
|
||||
until we implement return-in-regs), or for a tail-call
|
||||
argument ... */
|
||||
if (sibling)
|
||||
{
|
||||
if (GET_CODE (sibling) != CALL_INSN)
|
||||
abort ();
|
||||
|
||||
if (find_regno_fusage (sibling, USE, 3))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ... and that there are no call-saved registers in r0-r2
|
||||
(always true in the default ABI). */
|
||||
if (saved_int_regs & 0x7)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Can't be done if interworking with Thumb, and any registers have been
|
||||
stacked. */
|
||||
if (TARGET_INTERWORK && saved_int_regs != 0)
|
||||
@ -8194,7 +8238,24 @@ output_return_instruction (rtx operand, int really_return, int reverse)
|
||||
frame_pointer_needed is true, but only if sp already
|
||||
points to the base of the saved core registers. */
|
||||
if (live_regs_mask & (1 << SP_REGNUM))
|
||||
sprintf (instr, "ldm%sfd\t%%|sp, {", conditional);
|
||||
{
|
||||
unsigned HOST_WIDE_INT stack_adjust =
|
||||
arm_get_frame_size () + current_function_outgoing_args_size;
|
||||
|
||||
if (stack_adjust != 0 && stack_adjust != 4)
|
||||
abort ();
|
||||
|
||||
if (stack_adjust && arm_arch5)
|
||||
sprintf (instr, "ldm%sib\t%%|sp, {", conditional);
|
||||
else
|
||||
{
|
||||
/* If we can't use ldmib (SA110 bug), then try to pop r3
|
||||
instead. */
|
||||
if (stack_adjust)
|
||||
live_regs_mask |= 1 << 3;
|
||||
sprintf (instr, "ldm%sfd\t%%|sp, {", conditional);
|
||||
}
|
||||
}
|
||||
else
|
||||
sprintf (instr, "ldm%sfd\t%%|sp!, {", conditional);
|
||||
|
||||
@ -8401,7 +8462,7 @@ arm_output_function_prologue (FILE *f, HOST_WIDE_INT frame_size)
|
||||
}
|
||||
|
||||
const char *
|
||||
arm_output_epilogue (int really_return)
|
||||
arm_output_epilogue (rtx sibling)
|
||||
{
|
||||
int reg;
|
||||
unsigned long saved_regs_mask;
|
||||
@ -8414,10 +8475,11 @@ arm_output_epilogue (int really_return)
|
||||
FILE * f = asm_out_file;
|
||||
rtx eh_ofs = cfun->machine->eh_epilogue_sp_ofs;
|
||||
unsigned int lrm_count = 0;
|
||||
int really_return = (sibling == NULL);
|
||||
|
||||
/* If we have already generated the return instruction
|
||||
then it is futile to generate anything else. */
|
||||
if (use_return_insn (FALSE) && return_used_this_function)
|
||||
if (use_return_insn (FALSE, sibling) && return_used_this_function)
|
||||
return "";
|
||||
|
||||
func_type = arm_current_func_type ();
|
||||
@ -8730,7 +8792,7 @@ arm_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
|
||||
/* We need to take into account any stack-frame rounding. */
|
||||
frame_size = arm_get_frame_size ();
|
||||
|
||||
if (use_return_insn (FALSE)
|
||||
if (use_return_insn (FALSE, NULL)
|
||||
&& return_used_this_function
|
||||
&& (frame_size + current_function_outgoing_args_size) != 0
|
||||
&& !frame_pointer_needed)
|
||||
@ -10187,7 +10249,7 @@ arm_final_prescan_insn (rtx insn)
|
||||
/* Fail if a conditional return is undesirable (eg on a
|
||||
StrongARM), but still allow this if optimizing for size. */
|
||||
else if (GET_CODE (scanbody) == RETURN
|
||||
&& !use_return_insn (TRUE)
|
||||
&& !use_return_insn (TRUE, NULL)
|
||||
&& !optimize_size)
|
||||
fail = TRUE;
|
||||
else if (GET_CODE (scanbody) == RETURN
|
||||
|
@ -1870,7 +1870,7 @@ typedef struct
|
||||
/* Determine if the epilogue should be output as RTL.
|
||||
You should override this if you define FUNCTION_EXTRA_EPILOGUE. */
|
||||
#define USE_RETURN_INSN(ISCOND) \
|
||||
(TARGET_ARM ? use_return_insn (ISCOND) : 0)
|
||||
(TARGET_ARM ? use_return_insn (ISCOND, NULL) : 0)
|
||||
|
||||
/* Definitions for register eliminations.
|
||||
|
||||
|
@ -9338,9 +9338,9 @@
|
||||
(unspec_volatile [(return)] VUNSPEC_EPILOGUE)])]
|
||||
"TARGET_ARM"
|
||||
"*
|
||||
if (USE_RETURN_INSN (FALSE))
|
||||
if (use_return_insn (FALSE, next_nonnote_insn (insn)))
|
||||
return output_return_instruction (const_true_rtx, FALSE, FALSE);
|
||||
return arm_output_epilogue (FALSE);
|
||||
return arm_output_epilogue (next_nonnote_insn (insn));
|
||||
"
|
||||
;; Length is absolute worst case
|
||||
[(set_attr "length" "44")
|
||||
@ -9356,7 +9356,7 @@
|
||||
"TARGET_EITHER"
|
||||
"*
|
||||
if (TARGET_ARM)
|
||||
return arm_output_epilogue (TRUE);
|
||||
return arm_output_epilogue (NULL);
|
||||
else /* TARGET_THUMB */
|
||||
return thumb_unexpanded_epilogue ();
|
||||
"
|
||||
|
Loading…
Reference in New Issue
Block a user