aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog14
-rw-r--r--gcc/config/arm/arm-protos.h4
-rw-r--r--gcc/config/arm/arm.c82
-rw-r--r--gcc/config/arm/arm.h2
-rw-r--r--gcc/config/arm/arm.md6
5 files changed, 92 insertions, 16 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 6c251cd..0dbbea4 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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.
diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
index 61c28be..471254e 100644
--- a/gcc/config/arm/arm-protos.h
+++ b/gcc/config/arm/arm-protos.h
@@ -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 *);
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index c0a0cd84..44a5fa0 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -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
diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h
index e73df85..5c4a9b8 100644
--- a/gcc/config/arm/arm.h
+++ b/gcc/config/arm/arm.h
@@ -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.
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index 289687f..17a3770 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -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 ();
"