diff options
-rw-r--r-- | gcc/ChangeLog | 14 | ||||
-rw-r--r-- | gcc/config/arm/arm-protos.h | 4 | ||||
-rw-r--r-- | gcc/config/arm/arm.c | 82 | ||||
-rw-r--r-- | gcc/config/arm/arm.h | 2 | ||||
-rw-r--r-- | gcc/config/arm/arm.md | 6 |
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 (); " |