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)
|
2000-05-06 Richard Earnshaw (reanrsha@arm.com)
|
||||||
|
|
||||||
* arm-protos.h (arm_dllexport_name_p, arm_dllimport_name_p): Constify.
|
* 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 *));
|
char *));
|
||||||
extern void arm_finalize_pic PARAMS ((void));
|
extern void arm_finalize_pic PARAMS ((void));
|
||||||
extern int arm_volatile_func 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 output_func_epilogue PARAMS ((int));
|
||||||
extern void arm_expand_prologue PARAMS ((void));
|
extern void arm_expand_prologue PARAMS ((void));
|
||||||
/* Used in arm.md, but defined in output.c. */
|
/* 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_multi_reg_push PARAMS ((int));
|
||||||
static rtx emit_sfm PARAMS ((int, int));
|
static rtx emit_sfm PARAMS ((int, int));
|
||||||
static char * fp_const_from_val PARAMS ((REAL_VALUE_TYPE *));
|
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 arm_cc get_arm_condition_code PARAMS ((rtx));
|
||||||
static void init_fpa_table PARAMS ((void));
|
static void init_fpa_table PARAMS ((void));
|
||||||
static Hint int_log2 PARAMS ((Hint));
|
static Hint int_log2 PARAMS ((Hint));
|
||||||
static rtx is_jump_table PARAMS ((rtx));
|
static rtx is_jump_table PARAMS ((rtx));
|
||||||
static char * output_multi_immediate PARAMS ((rtx *, char *, char *, int, Hint));
|
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 void print_multi_reg PARAMS ((FILE *, char *, int, int, int));
|
||||||
static Mmode select_dominance_cc_mode PARAMS ((rtx, rtx, Hint));
|
static Mmode select_dominance_cc_mode PARAMS ((rtx, rtx, Hint));
|
||||||
static char * shift_op PARAMS ((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. */
|
/* Nonzero if this chip is a an ARM6 or an ARM7. */
|
||||||
int arm_is_6_or_7 = 0;
|
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
|
/* 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
|
must report the mode of the memory reference from PRINT_OPERAND to
|
||||||
PRINT_OPERAND_ADDRESS. */
|
PRINT_OPERAND_ADDRESS. */
|
||||||
@ -183,10 +184,6 @@ int current_function_anonymous_args;
|
|||||||
const char * arm_pic_register_string = NULL;
|
const char * arm_pic_register_string = NULL;
|
||||||
int arm_pic_register = 9;
|
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
|
/* Set to 1 when a return insn is output, this means that the epilogue
|
||||||
is not needed. */
|
is not needed. */
|
||||||
int return_used_this_function;
|
int return_used_this_function;
|
||||||
@ -569,6 +566,7 @@ arm_override_options ()
|
|||||||
|
|
||||||
arm_ld_sched = (tune_flags & FL_LDSCHED) != 0;
|
arm_ld_sched = (tune_flags & FL_LDSCHED) != 0;
|
||||||
arm_is_strong = (tune_flags & FL_STRONG) != 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))
|
arm_is_6_or_7 = (((tune_flags & (FL_MODE26 | FL_MODE32))
|
||||||
&& !(tune_flags & FL_ARCH4))) != 0;
|
&& !(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 *
|
char *
|
||||||
output_return_instruction (operand, really_return, reverse)
|
output_return_instruction (operand, really_return, reverse)
|
||||||
rtx operand;
|
rtx operand;
|
||||||
@ -6646,7 +6499,7 @@ output_return_instruction (operand, really_return, reverse)
|
|||||||
&& regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
|
&& regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
|
||||||
live_regs++;
|
live_regs++;
|
||||||
|
|
||||||
if (live_regs || (regs_ever_live[LR_REGNUM] && ! lr_save_eliminated))
|
if (live_regs || regs_ever_live[LR_REGNUM])
|
||||||
live_regs++;
|
live_regs++;
|
||||||
|
|
||||||
if (frame_pointer_needed)
|
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. */
|
load a single register. On other architectures, the cost is the same. */
|
||||||
if (live_regs == 1
|
if (live_regs == 1
|
||||||
&& regs_ever_live[LR_REGNUM]
|
&& 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)
|
&& ! really_return)
|
||||||
output_asm_insn (reverse ? "ldr%?%D0\t%|lr, [%|sp], #4"
|
output_asm_insn (reverse ? "ldr%?%D0\t%|lr, [%|sp], #4"
|
||||||
: "ldr%?%d0\t%|lr, [%|sp], #4", &operand);
|
: "ldr%?%d0\t%|lr, [%|sp], #4", &operand);
|
||||||
else if (live_regs == 1
|
else if (live_regs == 1
|
||||||
&& regs_ever_live[LR_REGNUM]
|
&& regs_ever_live[LR_REGNUM]
|
||||||
&& ! lr_save_eliminated
|
|
||||||
&& TARGET_APCS_32)
|
&& TARGET_APCS_32)
|
||||||
output_asm_insn (reverse ? "ldr%?%D0\t%|pc, [%|sp], #4"
|
output_asm_insn (reverse ? "ldr%?%D0\t%|pc, [%|sp], #4"
|
||||||
: "ldr%?%d0\t%|pc, [%|sp], #4", &operand);
|
: "ldr%?%d0\t%|pc, [%|sp], #4", &operand);
|
||||||
else if (live_regs)
|
else if (live_regs)
|
||||||
{
|
{
|
||||||
if (lr_save_eliminated || ! regs_ever_live[LR_REGNUM])
|
if (! regs_ever_live[LR_REGNUM])
|
||||||
live_regs++;
|
live_regs++;
|
||||||
|
|
||||||
if (frame_pointer_needed)
|
if (frame_pointer_needed)
|
||||||
@ -6835,7 +6684,6 @@ output_arm_prologue (f, frame_size)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
return_used_this_function = 0;
|
return_used_this_function = 0;
|
||||||
lr_save_eliminated = 0;
|
|
||||||
|
|
||||||
asm_fprintf (f, "\t%@ args = %d, pretend = %d, frame = %d\n",
|
asm_fprintf (f, "\t%@ args = %d, pretend = %d, frame = %d\n",
|
||||||
current_function_args_size,
|
current_function_args_size,
|
||||||
@ -6868,26 +6716,15 @@ output_arm_prologue (f, frame_size)
|
|||||||
live_regs_mask |= 0xD800;
|
live_regs_mask |= 0xD800;
|
||||||
else if (regs_ever_live[LR_REGNUM])
|
else if (regs_ever_live[LR_REGNUM])
|
||||||
{
|
{
|
||||||
if (! current_function_args_size
|
live_regs_mask |= 1 << LR_REGNUM;
|
||||||
&& ! function_really_clobbers_lr (get_insns ()))
|
|
||||||
lr_save_eliminated = 1;
|
|
||||||
else
|
|
||||||
live_regs_mask |= 1 << LR_REGNUM;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (live_regs_mask)
|
if (live_regs_mask)
|
||||||
{
|
/* If a di mode load/store multiple is used, and the base register
|
||||||
/* 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
|
||||||
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
|
||||||
doing so, in this case we need to push lr as well, or we
|
will fail to get a proper return. */
|
||||||
will fail to get a proper return. */
|
live_regs_mask |= 1 << LR_REGNUM;
|
||||||
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");
|
|
||||||
|
|
||||||
#ifdef AOF_ASSEMBLER
|
#ifdef AOF_ASSEMBLER
|
||||||
if (flag_pic)
|
if (flag_pic)
|
||||||
@ -6896,7 +6733,8 @@ output_arm_prologue (f, frame_size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
arm_output_epilogue ()
|
arm_output_epilogue (really_return)
|
||||||
|
int really_return;
|
||||||
{
|
{
|
||||||
int reg;
|
int reg;
|
||||||
int live_regs_mask = 0;
|
int live_regs_mask = 0;
|
||||||
@ -6920,6 +6758,11 @@ arm_output_epilogue ()
|
|||||||
R1; otherwise, it's in LR. */
|
R1; otherwise, it's in LR. */
|
||||||
return_regnum = eh_ofs ? 2 : LR_REGNUM;
|
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. */
|
/* A volatile function should never return. Call abort. */
|
||||||
if (TARGET_ABORT_NORETURN && volatile_func)
|
if (TARGET_ABORT_NORETURN && volatile_func)
|
||||||
{
|
{
|
||||||
@ -7010,17 +6853,22 @@ arm_output_epilogue ()
|
|||||||
if (eh_ofs)
|
if (eh_ofs)
|
||||||
asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
|
asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
|
||||||
REGNO (eh_ofs));
|
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;
|
live_regs_mask |= 0x6800;
|
||||||
print_multi_reg (f, "ldmea\t%r", FP_REGNUM, live_regs_mask, FALSE);
|
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,
|
if (eh_ofs)
|
||||||
REGNO (eh_ofs));
|
{
|
||||||
/* Even in 26-bit mode we do a mov (rather than a movs) because
|
asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
|
||||||
we don't have the PSR bits set in the address. */
|
REGNO (eh_ofs));
|
||||||
asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, return_regnum);
|
/* 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
|
else
|
||||||
{
|
{
|
||||||
@ -7083,8 +6931,7 @@ arm_output_epilogue ()
|
|||||||
{
|
{
|
||||||
if (TARGET_INTERWORK)
|
if (TARGET_INTERWORK)
|
||||||
{
|
{
|
||||||
if (! lr_save_eliminated)
|
live_regs_mask |= 1 << LR_REGNUM;
|
||||||
live_regs_mask |= 1 << LR_REGNUM;
|
|
||||||
|
|
||||||
/* Handle LR on its own. */
|
/* Handle LR on its own. */
|
||||||
if (live_regs_mask == (1 << LR_REGNUM))
|
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,
|
asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
|
||||||
REGNO (eh_ofs));
|
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)
|
else if (eh_ofs)
|
||||||
{
|
{
|
||||||
if (live_regs_mask == 0)
|
if (live_regs_mask == 0)
|
||||||
@ -7123,8 +6967,13 @@ arm_output_epilogue ()
|
|||||||
/* Jump to the target; even in 26-bit mode. */
|
/* Jump to the target; even in 26-bit mode. */
|
||||||
asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, return_regnum);
|
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);
|
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
|
else
|
||||||
print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM,
|
print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM,
|
||||||
live_regs_mask | (1 << PC_REGNUM),
|
live_regs_mask | (1 << PC_REGNUM),
|
||||||
@ -7135,8 +6984,7 @@ arm_output_epilogue ()
|
|||||||
if (live_regs_mask || regs_ever_live[LR_REGNUM])
|
if (live_regs_mask || regs_ever_live[LR_REGNUM])
|
||||||
{
|
{
|
||||||
/* Restore the integer regs, and the return address into lr. */
|
/* 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))
|
if (live_regs_mask == (1 << LR_REGNUM))
|
||||||
{
|
{
|
||||||
@ -7163,14 +7011,17 @@ arm_output_epilogue ()
|
|||||||
if (eh_ofs)
|
if (eh_ofs)
|
||||||
asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
|
asm_fprintf (f, "\tadd\t%r, %r, %r\n", SP_REGNUM, SP_REGNUM,
|
||||||
REGNO (eh_ofs));
|
REGNO (eh_ofs));
|
||||||
|
|
||||||
/* And finally, go home. */
|
if (really_return)
|
||||||
if (TARGET_INTERWORK)
|
{
|
||||||
asm_fprintf (f, "\tbx\t%r\n", return_regnum);
|
/* And finally, go home. */
|
||||||
else if (TARGET_APCS_32 || eh_ofs)
|
if (TARGET_INTERWORK)
|
||||||
asm_fprintf (f, "\tmov\t%r, %r\n", PC_REGNUM, return_regnum);
|
asm_fprintf (f, "\tbx\t%r\n", return_regnum);
|
||||||
else
|
else if (TARGET_APCS_32 || eh_ofs)
|
||||||
asm_fprintf (f, "\tmovs\t%r, %r\n", PC_REGNUM, return_regnum);
|
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_target_label;
|
||||||
extern int arm_ccfsm_state;
|
extern int arm_ccfsm_state;
|
||||||
extern struct rtx_def * arm_target_insn;
|
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. */
|
/* Run-time compilation parameters selecting different hardware subsets. */
|
||||||
extern int target_flags;
|
extern int target_flags;
|
||||||
/* The floating point instruction architecture, can be 2 or 3 */
|
/* 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. */
|
/* Nonzero if this chip can benefit from load scheduling. */
|
||||||
extern int arm_ld_sched;
|
extern int arm_ld_sched;
|
||||||
|
|
||||||
|
/* Nonzero if generating thumb code. */
|
||||||
|
extern int thumb_code;
|
||||||
|
|
||||||
/* Nonzero if this chip is a StrongARM. */
|
/* Nonzero if this chip is a StrongARM. */
|
||||||
extern int arm_is_strong;
|
extern int arm_is_strong;
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
;; Attributes
|
;; 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
|
; 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
|
; clobbered by a call insn: they are if in prog32 mode. This is controlled
|
||||||
@ -6024,7 +6024,8 @@
|
|||||||
(match_operand:SI 1 "" ""))
|
(match_operand:SI 1 "" ""))
|
||||||
(use (match_operand 2 "" ""))
|
(use (match_operand 2 "" ""))
|
||||||
(clobber (reg:SI 14))]
|
(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"
|
"bl\\t%a0"
|
||||||
[(set_attr "length" "4")
|
[(set_attr "length" "4")
|
||||||
(set_attr "type" "call")]
|
(set_attr "type" "call")]
|
||||||
@ -6036,12 +6037,79 @@
|
|||||||
(match_operand 2 "" "")))
|
(match_operand 2 "" "")))
|
||||||
(use (match_operand 3 "" ""))
|
(use (match_operand 3 "" ""))
|
||||||
(clobber (reg:SI 14))]
|
(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"
|
"bl\\t%a1"
|
||||||
[(set_attr "length" "4")
|
[(set_attr "length" "4")
|
||||||
(set_attr "type" "call")]
|
(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
|
;; Often the return insn will be the same as loading from memory, so set attr
|
||||||
(define_insn "return"
|
(define_insn "return"
|
||||||
[(return)]
|
[(return)]
|
||||||
@ -7791,103 +7859,6 @@
|
|||||||
return emit_stm_seq (operands, 2);
|
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
|
(define_split
|
||||||
[(set (match_operand:SI 0 "s_register_operand" "")
|
[(set (match_operand:SI 0 "s_register_operand" "")
|
||||||
(and:SI (ge:SI (match_operand:SI 1 "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"
|
(define_insn "*epilogue_insns"
|
||||||
[(unspec_volatile [(return)] 1)]
|
[(unspec_volatile [(return)] 1)]
|
||||||
"TARGET_EITHER"
|
"TARGET_EITHER"
|
||||||
"*
|
"*
|
||||||
if (TARGET_ARM)
|
if (TARGET_ARM)
|
||||||
return arm_output_epilogue ();
|
return arm_output_epilogue (TRUE);
|
||||||
else /* TARGET_THUMB */
|
else /* TARGET_THUMB */
|
||||||
return thumb_unexpanded_epilogue ();
|
return thumb_unexpanded_epilogue ();
|
||||||
"
|
"
|
||||||
@ -8094,26 +8079,17 @@
|
|||||||
"TARGET_ARM"
|
"TARGET_ARM"
|
||||||
"*
|
"*
|
||||||
{
|
{
|
||||||
extern int lr_save_eliminated;
|
|
||||||
int num_saves = XVECLEN (operands[2], 0);
|
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
|
/* For the StrongARM at least it is faster to
|
||||||
use STR to store only a single register. */
|
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);
|
output_asm_insn (\"str\\t%1, [%m0, #-4]!\", operands);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
char pattern[100];
|
char pattern[100];
|
||||||
|
|
||||||
if (lr_save_eliminated)
|
|
||||||
abort ();
|
|
||||||
|
|
||||||
strcpy (pattern, \"stmfd\\t%m0!, {%1\");
|
strcpy (pattern, \"stmfd\\t%m0!, {%1\");
|
||||||
|
|
||||||
for (i = 1; i < num_saves; i++)
|
for (i = 1; i < num_saves; i++)
|
||||||
|
Loading…
Reference in New Issue
Block a user