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
This commit is contained in:
Richard Earnshaw 2000-05-06 18:13:35 +00:00 committed by Richard Earnshaw
parent 76fa6b3b73
commit 0616531fb5
5 changed files with 166 additions and 316 deletions

View File

@ -1,3 +1,26 @@
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.

View File

@ -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. */

View File

@ -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;
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 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);
}
}
}

View File

@ -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;

View File

@ -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++)