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:
parent
76fa6b3b73
commit
0616531fb5
@ -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.
|
||||
|
@ -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. */
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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++)
|
||||
|
Loading…
Reference in New Issue
Block a user