aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorRichard Earnshaw <rearnsha@arm.com>2000-05-06 18:13:35 +0000
committerRichard Earnshaw <rearnsha@gcc.gnu.org>2000-05-06 18:13:35 +0000
commit0616531fb5b99c6139eb0241067d000ab9b44960 (patch)
treed111ccb15b7572bbaf8d52dd2c4ab1c9a3146cf1 /gcc
parent76fa6b3b73f655f8739d6704a1769ad98abaaecf (diff)
downloadgcc-0616531fb5b99c6139eb0241067d000ab9b44960.zip
gcc-0616531fb5b99c6139eb0241067d000ab9b44960.tar.gz
gcc-0616531fb5b99c6139eb0241067d000ab9b44960.tar.bz2
Use new tail-calling mechanism on ARM.
* arm.md (sibcall, sibcall_value): New expands. (sibcall_insn, sibcall_value_insn, sibcall_epilogue): New insns. (tailcalling peepholes): Delete. (push_multi): Simplify. * arm.c (lr_save_eliminated): Delete definition. (pattern_really_clobbers_lr, function_really_clobbers_lr): Delete. (output_return_instruction): Remove checks on lr_save_eliminated. (output_arm_prologue): Remove old tail-calling code. (arm_output_epilogue): New parameter, really_return. All callers changed. Handle tail-calling epilogues. * arm.h (lr_save_eliminated): Delete declaration. (frame_pointer_needed): Delete declaration. * arm-protos.h (arm_output_epilogue): Adjust prototype. * arm.md (is_thumb): Examine symbol thumb_code, not expression TARGET_ARM. * arm.c (thumb_code): Define it. (arm_override_options): Set it. * arm.h (thumb_code): Declare it. From-SVN: r33731
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog23
-rw-r--r--gcc/config/arm/arm-protos.h2
-rw-r--r--gcc/config/arm/arm.c253
-rw-r--r--gcc/config/arm/arm.h6
-rw-r--r--gcc/config/arm/arm.md198
5 files changed, 166 insertions, 316 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 6e05a3f..be9337f3 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,5 +1,28 @@
2000-05-06 Richard Earnshaw (reanrsha@arm.com)
+ Use new tail-calling mechanism on ARM.
+ * arm.md (sibcall, sibcall_value): New expands.
+ (sibcall_insn, sibcall_value_insn, sibcall_epilogue): New insns.
+ (tailcalling peepholes): Delete.
+ (push_multi): Simplify.
+ * arm.c (lr_save_eliminated): Delete definition.
+ (pattern_really_clobbers_lr, function_really_clobbers_lr): Delete.
+ (output_return_instruction): Remove checks on lr_save_eliminated.
+ (output_arm_prologue): Remove old tail-calling code.
+ (arm_output_epilogue): New parameter, really_return. All callers
+ changed. Handle tail-calling epilogues.
+ * arm.h (lr_save_eliminated): Delete declaration.
+ (frame_pointer_needed): Delete declaration.
+ * arm-protos.h (arm_output_epilogue): Adjust prototype.
+
+ * arm.md (is_thumb): Examine symbol thumb_code, not expression
+ TARGET_ARM.
+ * arm.c (thumb_code): Define it.
+ (arm_override_options): Set it.
+ * arm.h (thumb_code): Declare it.
+
+2000-05-06 Richard Earnshaw (reanrsha@arm.com)
+
* arm-protos.h (arm_dllexport_name_p, arm_dllimport_name_p): Constify.
2000-05-06 Richard Earnshaw (reanrsha@arm.com)
diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
index 9c4d942..8a3cb5b 100644
--- a/gcc/config/arm/arm-protos.h
+++ b/gcc/config/arm/arm-protos.h
@@ -27,7 +27,7 @@ extern int arm_process_pragma PARAMS ((int (*)(void), void (*) (int),
char *));
extern void arm_finalize_pic PARAMS ((void));
extern int arm_volatile_func PARAMS ((void));
-extern char * arm_output_epilogue PARAMS ((void));
+extern char * arm_output_epilogue PARAMS ((int));
extern void output_func_epilogue PARAMS ((int));
extern void arm_expand_prologue PARAMS ((void));
/* Used in arm.md, but defined in output.c. */
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index ba5b38c..206224d 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -64,13 +64,11 @@ static int eliminate_lr2ip PARAMS ((rtx *));
static rtx emit_multi_reg_push PARAMS ((int));
static rtx emit_sfm PARAMS ((int, int));
static char * fp_const_from_val PARAMS ((REAL_VALUE_TYPE *));
-static int function_really_clobbers_lr PARAMS ((rtx));
static arm_cc get_arm_condition_code PARAMS ((rtx));
static void init_fpa_table PARAMS ((void));
static Hint int_log2 PARAMS ((Hint));
static rtx is_jump_table PARAMS ((rtx));
static char * output_multi_immediate PARAMS ((rtx *, char *, char *, int, Hint));
-static int pattern_really_clobbers_lr PARAMS ((rtx));
static void print_multi_reg PARAMS ((FILE *, char *, int, int, int));
static Mmode select_dominance_cc_mode PARAMS ((rtx, rtx, Hint));
static char * shift_op PARAMS ((rtx, Hint *));
@@ -171,6 +169,9 @@ int arm_is_strong = 0;
/* Nonzero if this chip is a an ARM6 or an ARM7. */
int arm_is_6_or_7 = 0;
+/* Nonzero if generating Thumb instructions. */
+int thumb_code = 0;
+
/* In case of a PRE_INC, POST_INC, PRE_DEC, POST_DEC memory reference, we
must report the mode of the memory reference from PRINT_OPERAND to
PRINT_OPERAND_ADDRESS. */
@@ -183,10 +184,6 @@ int current_function_anonymous_args;
const char * arm_pic_register_string = NULL;
int arm_pic_register = 9;
-/* Set to one if we think that lr is only saved because of subroutine calls,
- but all of these can be `put after' return insns. */
-int lr_save_eliminated;
-
/* Set to 1 when a return insn is output, this means that the epilogue
is not needed. */
int return_used_this_function;
@@ -569,6 +566,7 @@ arm_override_options ()
arm_ld_sched = (tune_flags & FL_LDSCHED) != 0;
arm_is_strong = (tune_flags & FL_STRONG) != 0;
+ thumb_code = (TARGET_ARM == 0);
arm_is_6_or_7 = (((tune_flags & (FL_MODE26 | FL_MODE32))
&& !(tune_flags & FL_ARCH4))) != 0;
@@ -6449,151 +6447,6 @@ output_ascii_pseudo_op (stream, p, len)
}
-/* Try to determine whether a pattern really clobbers the link register.
- This information is useful when peepholing, so that lr need not be pushed
- if we combine a call followed by a return.
- NOTE: This code does not check for side-effect expressions in a SET_SRC:
- such a check should not be needed because these only update an existing
- value within a register; the register must still be set elsewhere within
- the function. */
-static int
-pattern_really_clobbers_lr (x)
- rtx x;
-{
- int i;
-
- switch (GET_CODE (x))
- {
- case SET:
- switch (GET_CODE (SET_DEST (x)))
- {
- case REG:
- return REGNO (SET_DEST (x)) == LR_REGNUM;
-
- case SUBREG:
- if (GET_CODE (XEXP (SET_DEST (x), 0)) == REG)
- return REGNO (XEXP (SET_DEST (x), 0)) == LR_REGNUM;
-
- if (GET_CODE (XEXP (SET_DEST (x), 0)) == MEM)
- return 0;
- abort ();
-
- default:
- return 0;
- }
-
- case PARALLEL:
- for (i = 0; i < XVECLEN (x, 0); i++)
- if (pattern_really_clobbers_lr (XVECEXP (x, 0, i)))
- return 1;
- return 0;
-
- case CLOBBER:
- switch (GET_CODE (XEXP (x, 0)))
- {
- case REG:
- return REGNO (XEXP (x, 0)) == LR_REGNUM;
-
- case SUBREG:
- if (GET_CODE (XEXP (XEXP (x, 0), 0)) == REG)
- return REGNO (XEXP (XEXP (x, 0), 0)) == LR_REGNUM;
- abort ();
-
- default:
- return 0;
- }
-
- case UNSPEC:
- return 1;
-
- default:
- return 0;
- }
-}
-
-static int
-function_really_clobbers_lr (first)
- rtx first;
-{
- rtx insn, next;
-
- for (insn = first; insn; insn = next_nonnote_insn (insn))
- {
- switch (GET_CODE (insn))
- {
- case BARRIER:
- case NOTE:
- case CODE_LABEL:
- case JUMP_INSN: /* Jump insns only change the PC (and conds) */
- break;
-
- case INSN:
- if (pattern_really_clobbers_lr (PATTERN (insn)))
- return 1;
- break;
-
- case CALL_INSN:
- /* Don't yet know how to handle those calls that are not to a
- SYMBOL_REF. */
- if (GET_CODE (PATTERN (insn)) != PARALLEL)
- abort ();
-
- switch (GET_CODE (XVECEXP (PATTERN (insn), 0, 0)))
- {
- case CALL:
- if (GET_CODE (XEXP (XEXP (XVECEXP (PATTERN (insn), 0, 0), 0), 0))
- != SYMBOL_REF)
- return 1;
- break;
-
- case SET:
- if (GET_CODE (XEXP (XEXP (SET_SRC (XVECEXP (PATTERN (insn),
- 0, 0)), 0), 0))
- != SYMBOL_REF)
- return 1;
- break;
-
- default: /* Don't recognize it, be safe. */
- return 1;
- }
-
- /* A call can be made (by peepholing) not to clobber lr iff it is
- followed by a return. There may, however, be a use insn iff
- we are returning the result of the call.
- If we run off the end of the insn chain, then that means the
- call was at the end of the function. Unfortunately we don't
- have a return insn for the peephole to recognize, so we
- must reject this. (Can this be fixed by adding our own insn?) */
- if ((next = next_nonnote_insn (insn)) == NULL)
- return 1;
-
- /* No need to worry about lr if the call never returns. */
- if (GET_CODE (next) == BARRIER)
- break;
-
- if (GET_CODE (next) == INSN
- && GET_CODE (PATTERN (next)) == USE
- && (GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET)
- && (GET_CODE (XEXP (PATTERN (next), 0)) == REG)
- && (REGNO (SET_DEST (XVECEXP (PATTERN (insn), 0, 0)))
- == REGNO (XEXP (PATTERN (next), 0))))
- if ((next = next_nonnote_insn (next)) == NULL)
- return 1;
-
- if (GET_CODE (next) == JUMP_INSN
- && GET_CODE (PATTERN (next)) == RETURN)
- break;
- return 1;
-
- default:
- abort ();
- }
- }
-
- /* We have reached the end of the chain so lr was _not_ clobbered. */
- return 0;
-}
-
char *
output_return_instruction (operand, really_return, reverse)
rtx operand;
@@ -6646,7 +6499,7 @@ output_return_instruction (operand, really_return, reverse)
&& regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
live_regs++;
- if (live_regs || (regs_ever_live[LR_REGNUM] && ! lr_save_eliminated))
+ if (live_regs || regs_ever_live[LR_REGNUM])
live_regs++;
if (frame_pointer_needed)
@@ -6656,21 +6509,17 @@ output_return_instruction (operand, really_return, reverse)
load a single register. On other architectures, the cost is the same. */
if (live_regs == 1
&& regs_ever_live[LR_REGNUM]
- && ! lr_save_eliminated
- /* FIXME: We ought to handle the case TARGET_APCS_32 is true,
- really_return is true, and only the PC needs restoring. */
&& ! really_return)
output_asm_insn (reverse ? "ldr%?%D0\t%|lr, [%|sp], #4"
: "ldr%?%d0\t%|lr, [%|sp], #4", &operand);
else if (live_regs == 1
&& regs_ever_live[LR_REGNUM]
- && ! lr_save_eliminated
&& TARGET_APCS_32)
output_asm_insn (reverse ? "ldr%?%D0\t%|pc, [%|sp], #4"
: "ldr%?%d0\t%|pc, [%|sp], #4", &operand);
else if (live_regs)
{
- if (lr_save_eliminated || ! regs_ever_live[LR_REGNUM])
+ if (! regs_ever_live[LR_REGNUM])
live_regs++;
if (frame_pointer_needed)
@@ -6835,7 +6684,6 @@ output_arm_prologue (f, frame_size)
return;
return_used_this_function = 0;
- lr_save_eliminated = 0;
asm_fprintf (f, "\t%@ args = %d, pretend = %d, frame = %d\n",
current_function_args_size,
@@ -6868,26 +6716,15 @@ output_arm_prologue (f, frame_size)
live_regs_mask |= 0xD800;
else if (regs_ever_live[LR_REGNUM])
{
- if (! current_function_args_size
- && ! function_really_clobbers_lr (get_insns ()))
- lr_save_eliminated = 1;
- else
- live_regs_mask |= 1 << LR_REGNUM;
- }
-
- if (live_regs_mask)
- {
- /* If a di mode load/store multiple is used, and the base register
- is r3, then r4 can become an ever live register without lr
- doing so, in this case we need to push lr as well, or we
- will fail to get a proper return. */
live_regs_mask |= 1 << LR_REGNUM;
- lr_save_eliminated = 0;
-
}
- if (lr_save_eliminated)
- asm_fprintf (f,"\t%@ I don't think this function clobbers lr\n");
+ if (live_regs_mask)
+ /* If a di mode load/store multiple is used, and the base register
+ is r3, then r4 can become an ever live register without lr
+ doing so, in this case we need to push lr as well, or we
+ will fail to get a proper return. */
+ live_regs_mask |= 1 << LR_REGNUM;
#ifdef AOF_ASSEMBLER
if (flag_pic)
@@ -6896,7 +6733,8 @@ output_arm_prologue (f, frame_size)
}
char *
-arm_output_epilogue ()
+arm_output_epilogue (really_return)
+ int really_return;
{
int reg;
int live_regs_mask = 0;
@@ -6920,6 +6758,11 @@ arm_output_epilogue ()
R1; otherwise, it's in LR. */
return_regnum = eh_ofs ? 2 : LR_REGNUM;
+ /* If we are throwing an exception, then we really must be doing a return,
+ so we can't tail-call. */
+ if (eh_ofs && ! really_return)
+ abort();
+
/* A volatile function should never return. Call abort. */
if (TARGET_ABORT_NORETURN && volatile_func)
{
@@ -7010,17 +6853,22 @@ arm_output_epilogue ()
if (eh_ofs)
asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
REGNO (eh_ofs));
- asm_fprintf (f, "\tbx\t%r\n", return_regnum);
+ if (really_return)
+ asm_fprintf (f, "\tbx\t%r\n", return_regnum);
}
- else if (eh_ofs)
+ else if (eh_ofs || ! really_return)
{
live_regs_mask |= 0x6800;
print_multi_reg (f, "ldmea\t%r", FP_REGNUM, live_regs_mask, FALSE);
- asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
- REGNO (eh_ofs));
- /* Even in 26-bit mode we do a mov (rather than a movs) because
- we don't have the PSR bits set in the address. */
- asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, return_regnum);
+ if (eh_ofs)
+ {
+ asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
+ REGNO (eh_ofs));
+ /* Even in 26-bit mode we do a mov (rather than a movs)
+ because we don't have the PSR bits set in the
+ address. */
+ asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, return_regnum);
+ }
}
else
{
@@ -7083,8 +6931,7 @@ arm_output_epilogue ()
{
if (TARGET_INTERWORK)
{
- if (! lr_save_eliminated)
- live_regs_mask |= 1 << LR_REGNUM;
+ live_regs_mask |= 1 << LR_REGNUM;
/* Handle LR on its own. */
if (live_regs_mask == (1 << LR_REGNUM))
@@ -7104,12 +6951,9 @@ arm_output_epilogue ()
asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
REGNO (eh_ofs));
- asm_fprintf (f, "\tbx\t%r\n", return_regnum);
+ if (really_return)
+ asm_fprintf (f, "\tbx\t%r\n", return_regnum);
}
- else if (lr_save_eliminated)
- asm_fprintf (f,
- TARGET_APCS_32 ? "\tmov\t%r, %r\n" : "\tmovs\t%r, %r\n",
- PC_REGNUM, LR_REGNUM);
else if (eh_ofs)
{
if (live_regs_mask == 0)
@@ -7123,8 +6967,13 @@ arm_output_epilogue ()
/* Jump to the target; even in 26-bit mode. */
asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, return_regnum);
}
- else if (TARGET_APCS_32 && live_regs_mask == 0)
+ else if (TARGET_APCS_32 && live_regs_mask == 0 && ! really_return)
+ asm_fprintf (f, "\tldr\t%r, [%r], #4\n", LR_REGNUM, SP_REGNUM);
+ else if (TARGET_APCS_32 && live_regs_mask == 0 && really_return)
asm_fprintf (f, "\tldr\t%r, [%r], #4\n", PC_REGNUM, SP_REGNUM);
+ else if (! really_return)
+ print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM,
+ live_regs_mask | (1 << LR_REGNUM), FALSE);
else
print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM,
live_regs_mask | (1 << PC_REGNUM),
@@ -7135,8 +6984,7 @@ arm_output_epilogue ()
if (live_regs_mask || regs_ever_live[LR_REGNUM])
{
/* Restore the integer regs, and the return address into lr. */
- if (! lr_save_eliminated)
- live_regs_mask |= 1 << LR_REGNUM;
+ live_regs_mask |= 1 << LR_REGNUM;
if (live_regs_mask == (1 << LR_REGNUM))
{
@@ -7163,14 +7011,17 @@ arm_output_epilogue ()
if (eh_ofs)
asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
REGNO (eh_ofs));
-
- /* And finally, go home. */
- if (TARGET_INTERWORK)
- asm_fprintf (f, "\tbx\t%r\n", return_regnum);
- else if (TARGET_APCS_32 || eh_ofs)
- asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, return_regnum);
- else
- asm_fprintf (f, "\tmovs\t%r, %r\n", PC_REGNUM, return_regnum);
+
+ if (really_return)
+ {
+ /* And finally, go home. */
+ if (TARGET_INTERWORK)
+ asm_fprintf (f, "\tbx\t%r\n", return_regnum);
+ else if (TARGET_APCS_32 || eh_ofs)
+ asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, return_regnum);
+ else
+ asm_fprintf (f, "\tmovs\t%r, %r\n", PC_REGNUM, return_regnum);
+ }
}
}
diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h
index 0bbf627..d4c553f 100644
--- a/gcc/config/arm/arm.h
+++ b/gcc/config/arm/arm.h
@@ -66,9 +66,6 @@ extern char * arm_condition_codes[];
extern int arm_target_label;
extern int arm_ccfsm_state;
extern struct rtx_def * arm_target_insn;
-extern int lr_save_eliminated;
-/* This is needed by the tail-calling peepholes */
-extern int frame_pointer_needed;
/* Run-time compilation parameters selecting different hardware subsets. */
extern int target_flags;
/* The floating point instruction architecture, can be 2 or 3 */
@@ -540,6 +537,9 @@ extern int arm_arch5;
/* Nonzero if this chip can benefit from load scheduling. */
extern int arm_ld_sched;
+/* Nonzero if generating thumb code. */
+extern int thumb_code;
+
/* Nonzero if this chip is a StrongARM. */
extern int arm_is_strong;
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index 67e053b..69e7c9b 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -40,7 +40,7 @@
;; Attributes
-(define_attr "is_thumb" "no,yes" (const (symbol_ref "TARGET_ARM")))
+(define_attr "is_thumb" "no,yes" (const (symbol_ref "thumb_code")))
; PROG_MODE attribute is used to determine whether condition codes are
; clobbered by a call insn: they are if in prog32 mode. This is controlled
@@ -6024,7 +6024,8 @@
(match_operand:SI 1 "" ""))
(use (match_operand 2 "" ""))
(clobber (reg:SI 14))]
- "TARGET_THUMB && operands[2] == const0_rtx && (GET_CODE (operands[0]) == SYMBOL_REF)"
+ "TARGET_THUMB
+ && operands[2] == const0_rtx && (GET_CODE (operands[0]) == SYMBOL_REF)"
"bl\\t%a0"
[(set_attr "length" "4")
(set_attr "type" "call")]
@@ -6036,12 +6037,79 @@
(match_operand 2 "" "")))
(use (match_operand 3 "" ""))
(clobber (reg:SI 14))]
- "TARGET_THUMB && operands[3] == const0_rtx && (GET_CODE (operands[1]) == SYMBOL_REF)"
+ "TARGET_THUMB
+ && operands[3] == const0_rtx && (GET_CODE (operands[1]) == SYMBOL_REF)"
"bl\\t%a1"
[(set_attr "length" "4")
(set_attr "type" "call")]
)
+;; We may also be able to do sibcalls for Thumb, but it's much harder...
+(define_expand "sibcall"
+ [(parallel [(call (match_operand 0 "memory_operand" "")
+ (match_operand 1 "general_operand" ""))
+ (use (match_operand 2 "" ""))])]
+ "TARGET_ARM"
+ "
+ {
+ if (operands[2] == NULL_RTX)
+ operands[2] = const0_rtx;
+
+ /* If we need to emit a long-call, we can't do it as a sibling call,
+ so fail over to a normal call. */
+ if (arm_is_longcall_p (operands[0], INTVAL (operands[2]), 0))
+ {
+ emit_call_insn (gen_call (operands[0], operands[1], operands[2]));
+ DONE;
+ }
+ }"
+)
+
+(define_expand "sibcall_value"
+ [(parallel [(set (match_operand 0 "register_operand" "")
+ (call (match_operand 1 "memory_operand" "")
+ (match_operand 2 "general_operand" "")))
+ (use (match_operand 3 "" ""))])]
+ "TARGET_ARM"
+ "
+ {
+ if (operands[3] == NULL_RTX)
+ operands[3] = const0_rtx;
+
+ /* If we need to emit a long-call, we can't do it as a sibling call,
+ so fail over to a normal call. */
+ if (arm_is_longcall_p (operands[1], INTVAL (operands[3]), 0))
+ {
+ emit_call_insn (gen_call_value (operands[0], operands[1], operands[2],
+ operands[3]));
+ DONE;
+ }
+ }"
+)
+
+(define_insn "*sibcall_insn"
+ [(call (mem:SI (match_operand:SI 0 "" "X"))
+ (match_operand 1 "" ""))
+ (use (match_operand 2 "" ""))]
+ "TARGET_ARM && GET_CODE (operands[0]) == SYMBOL_REF"
+ "*
+ return NEED_PLT_RELOC ? \"b%?\\t%a0(PLT)\" : \"b%?\\t%a0\";
+ "
+ [(set_attr "type" "call")]
+)
+
+(define_insn "*sibcall_value_insn"
+ [(set (match_operand 0 "s_register_operand" "=rf")
+ (call (mem:SI (match_operand:SI 1 "" "X"))
+ (match_operand 2 "" "")))
+ (use (match_operand 3 "" ""))]
+ "TARGET_ARM && GET_CODE (operands[1]) == SYMBOL_REF"
+ "*
+ return NEED_PLT_RELOC ? \"b%?\\t%a1(PLT)\" : \"b%?\\t%a1\";
+ "
+ [(set_attr "type" "call")]
+)
+
;; Often the return insn will be the same as loading from memory, so set attr
(define_insn "return"
[(return)]
@@ -7791,103 +7859,6 @@
return emit_stm_seq (operands, 2);
")
-;; A call followed by return can be replaced by restoring the regs and
-;; jumping to the subroutine, provided we aren't passing the address of
-;; any of our local variables. If we call alloca then this is unsafe
-;; since restoring the frame frees the memory, which is not what we want.
-;; Sometimes the return might have been targeted by the final prescan:
-;; if so then emit a proper return insn as well.
-;; Unfortunately, if the frame pointer is required, we don't know if the
-;; current function has any implicit stack pointer adjustments that will
-;; be restored by the return: we can't therefore do a tail call.
-;; Another unfortunate that we can't handle is if current_function_args_size
-;; is non-zero: in this case elimination of the argument pointer assumed
-;; that lr was pushed onto the stack, so eliminating upsets the offset
-;; calculations.
-
-(define_peephole
- [(parallel [(call (mem:SI (match_operand:SI 0 "" "X"))
- (match_operand:SI 1 "general_operand" "g"))
- (use (match_operand 2 "" ""))
- (clobber (reg:SI 14))])
- (return)]
- "TARGET_ARM
- && (GET_CODE (operands[0]) == SYMBOL_REF && USE_RETURN_INSN (FALSE)
- && !get_frame_size () && !current_function_calls_alloca
- && !frame_pointer_needed && !current_function_args_size)"
- "*
-{
- if (arm_ccfsm_state && arm_target_insn && INSN_DELETED_P (arm_target_insn))
- {
- arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc);
- output_return_instruction (NULL, TRUE, FALSE);
- arm_ccfsm_state = 0;
- arm_target_insn = NULL;
- }
-
- output_return_instruction (NULL, FALSE, FALSE);
- return NEED_PLT_RELOC ? \"b%?\\t%a0(PLT)\" : \"b%?\\t%a0\";
-}"
-[(set_attr "type" "call")
- (set_attr "length" "8")])
-
-(define_peephole
- [(parallel [(set (match_operand 0 "s_register_operand" "=rf")
- (call (mem:SI (match_operand:SI 1 "" "X"))
- (match_operand:SI 2 "general_operand" "g")))
- (use (match_operand 3 "" ""))
- (clobber (reg:SI 14))])
- (return)]
- "TARGET_ARM && (GET_CODE (operands[1]) == SYMBOL_REF && USE_RETURN_INSN (FALSE)
- && !get_frame_size () && !current_function_calls_alloca
- && !frame_pointer_needed && !current_function_args_size)"
- "*
-{
- if (arm_ccfsm_state && arm_target_insn && INSN_DELETED_P (arm_target_insn))
- {
- arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc);
- output_return_instruction (NULL, TRUE, FALSE);
- arm_ccfsm_state = 0;
- arm_target_insn = NULL;
- }
-
- output_return_instruction (NULL, FALSE, FALSE);
- return NEED_PLT_RELOC ? \"b%?\\t%a1(PLT)\" : \"b%?\\t%a1\";
-}"
-[(set_attr "type" "call")
- (set_attr "length" "8")])
-
-;; As above but when this function is not void, we must be returning the
-;; result of the called subroutine.
-
-(define_peephole
- [(parallel [(set (match_operand 0 "s_register_operand" "=rf")
- (call (mem:SI (match_operand:SI 1 "" "X"))
- (match_operand:SI 2 "general_operand" "g")))
- (use (match_operand 3 "" ""))
- (clobber (reg:SI 14))])
- (use (match_dup 0))
- (return)]
- "TARGET_ARM
- && (GET_CODE (operands[1]) == SYMBOL_REF && USE_RETURN_INSN (FALSE)
- && !get_frame_size () && !current_function_calls_alloca
- && !frame_pointer_needed && !current_function_args_size)"
- "*
-{
- if (arm_ccfsm_state && arm_target_insn && INSN_DELETED_P (arm_target_insn))
- {
- arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc);
- output_return_instruction (NULL, TRUE, FALSE);
- arm_ccfsm_state = 0;
- arm_target_insn = NULL;
- }
-
- output_return_instruction (NULL, FALSE, FALSE);
- return \"b%?\\t%a1\";
-}"
-[(set_attr "type" "call")
- (set_attr "length" "8")])
-
(define_split
[(set (match_operand:SI 0 "s_register_operand" "")
(and:SI (ge:SI (match_operand:SI 1 "s_register_operand" "")
@@ -7952,12 +7923,26 @@
"
)
+(define_insn "sibcall_epilogue"
+ [(unspec_volatile [(const_int 0)] 1)]
+ "TARGET_ARM"
+ "*
+ output_asm_insn (\"%@ Sibcall epilogue\", operands);
+ if (USE_RETURN_INSN (FALSE))
+ return output_return_instruction (NULL, FALSE, FALSE);
+ return arm_output_epilogue (FALSE);
+ "
+;; Length is absolute worst case
+ [(set_attr "length" "44")
+ (set_attr "type" "block")]
+)
+
(define_insn "*epilogue_insns"
[(unspec_volatile [(return)] 1)]
"TARGET_EITHER"
"*
if (TARGET_ARM)
- return arm_output_epilogue ();
+ return arm_output_epilogue (TRUE);
else /* TARGET_THUMB */
return thumb_unexpanded_epilogue ();
"
@@ -8094,26 +8079,17 @@
"TARGET_ARM"
"*
{
- extern int lr_save_eliminated;
int num_saves = XVECLEN (operands[2], 0);
- if (lr_save_eliminated)
- {
- if (num_saves > 1)
- abort ();
- }
/* For the StrongARM at least it is faster to
use STR to store only a single register. */
- else if (num_saves == 1)
+ if (num_saves == 1)
output_asm_insn (\"str\\t%1, [%m0, #-4]!\", operands);
else
{
int i;
char pattern[100];
- if (lr_save_eliminated)
- abort ();
-
strcpy (pattern, \"stmfd\\t%m0!, {%1\");
for (i = 1; i < num_saves; i++)