x86: Add no_callee_saved_registers function attribute
When an interrupt handler is implemented by an assembly stub which does: 1. Save all registers. 2. Call a C function. 3. Restore all registers. 4. Return from interrupt. it is completely unnecessary to save and restore any registers in the C function called by the assembly stub, even if they would normally be callee-saved. Add no_callee_saved_registers function attribute, which is complementary to no_caller_saved_registers function attribute, to mark a function which doesn't have any callee-saved registers. Such a function won't save and restore any registers. Classify function call-saved register handling type with: 1. Default call-saved registers. 2. No caller-saved registers with no_caller_saved_registers attribute. 3. No callee-saved registers with no_callee_saved_registers attribute. Disallow sibcall if callee is a no_callee_saved_registers function and caller isn't a no_callee_saved_registers function. Otherwise, callee-saved registers won't be preserved. After a no_callee_saved_registers function is called, all registers may be clobbered. If the calling function isn't a no_callee_saved_registers function, we need to preserve all registers which aren't used by function calls. gcc/ PR target/103503 PR target/113312 * config/i386/i386-expand.cc (ix86_expand_call): Replace no_caller_saved_registers check with call_saved_registers check. Clobber all registers that are not used by the callee with no_callee_saved_registers attribute. * config/i386/i386-options.cc (ix86_set_func_type): Set call_saved_registers to TYPE_NO_CALLEE_SAVED_REGISTERS for noreturn function. Disallow no_callee_saved_registers with interrupt or no_caller_saved_registers attributes together. (ix86_set_current_function): Replace no_caller_saved_registers check with call_saved_registers check. (ix86_handle_no_caller_saved_registers_attribute): Renamed to ... (ix86_handle_call_saved_registers_attribute): This. (ix86_gnu_attributes): Add ix86_handle_call_saved_registers_attribute. * config/i386/i386.cc (ix86_conditional_register_usage): Replace no_caller_saved_registers check with call_saved_registers check. (ix86_function_ok_for_sibcall): Don't allow callee with no_callee_saved_registers attribute when the calling function has callee-saved registers. (ix86_comp_type_attributes): Also check no_callee_saved_registers. (ix86_epilogue_uses): Replace no_caller_saved_registers check with call_saved_registers check. (ix86_hard_regno_scratch_ok): Likewise. (ix86_save_reg): Replace no_caller_saved_registers check with call_saved_registers check. Don't save any registers for TYPE_NO_CALLEE_SAVED_REGISTERS. Save all registers with TYPE_DEFAULT_CALL_SAVED_REGISTERS if function with no_callee_saved_registers attribute is called. (find_drap_reg): Replace no_caller_saved_registers check with call_saved_registers check. * config/i386/i386.h (call_saved_registers_type): New enum. (machine_function): Replace no_caller_saved_registers with call_saved_registers. * doc/extend.texi: Document no_callee_saved_registers attribute. gcc/testsuite/ PR target/103503 PR target/113312 * gcc.dg/torture/no-callee-saved-run-1a.c: New file. * gcc.dg/torture/no-callee-saved-run-1b.c: Likewise. * gcc.target/i386/no-callee-saved-1.c: Likewise. * gcc.target/i386/no-callee-saved-2.c: Likewise. * gcc.target/i386/no-callee-saved-3.c: Likewise. * gcc.target/i386/no-callee-saved-4.c: Likewise. * gcc.target/i386/no-callee-saved-5.c: Likewise. * gcc.target/i386/no-callee-saved-6.c: Likewise. * gcc.target/i386/no-callee-saved-7.c: Likewise. * gcc.target/i386/no-callee-saved-8.c: Likewise. * gcc.target/i386/no-callee-saved-9.c: Likewise. * gcc.target/i386/no-callee-saved-10.c: Likewise. * gcc.target/i386/no-callee-saved-11.c: Likewise. * gcc.target/i386/no-callee-saved-12.c: Likewise. * gcc.target/i386/no-callee-saved-13.c: Likewise. * gcc.target/i386/no-callee-saved-14.c: Likewise. * gcc.target/i386/no-callee-saved-15.c: Likewise. * gcc.target/i386/no-callee-saved-16.c: Likewise. * gcc.target/i386/no-callee-saved-17.c: Likewise. * gcc.target/i386/no-callee-saved-18.c: Likewise.
This commit is contained in:
parent
a12b0e9360
commit
a96549dce7
25 changed files with 675 additions and 35 deletions
|
@ -9739,17 +9739,35 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
|
|||
rtx use = NULL, call;
|
||||
unsigned int vec_len = 0;
|
||||
tree fndecl;
|
||||
bool call_no_callee_saved_registers = false;
|
||||
|
||||
if (GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF)
|
||||
{
|
||||
fndecl = SYMBOL_REF_DECL (XEXP (fnaddr, 0));
|
||||
if (fndecl
|
||||
&& (lookup_attribute ("interrupt",
|
||||
TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))))
|
||||
error ("interrupt service routine cannot be called directly");
|
||||
if (fndecl)
|
||||
{
|
||||
if (lookup_attribute ("interrupt",
|
||||
TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
|
||||
error ("interrupt service routine cannot be called directly");
|
||||
else if (lookup_attribute ("no_callee_saved_registers",
|
||||
TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
|
||||
call_no_callee_saved_registers = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
fndecl = NULL_TREE;
|
||||
{
|
||||
if (MEM_P (fnaddr))
|
||||
{
|
||||
tree mem_expr = MEM_EXPR (fnaddr);
|
||||
if (mem_expr != nullptr
|
||||
&& TREE_CODE (mem_expr) == MEM_REF
|
||||
&& lookup_attribute ("no_callee_saved_registers",
|
||||
TYPE_ATTRIBUTES (TREE_TYPE (mem_expr))))
|
||||
call_no_callee_saved_registers = true;
|
||||
}
|
||||
|
||||
fndecl = NULL_TREE;
|
||||
}
|
||||
|
||||
if (pop == const0_rtx)
|
||||
pop = NULL;
|
||||
|
@ -9884,13 +9902,15 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
|
|||
vec[vec_len++] = pop;
|
||||
}
|
||||
|
||||
if (cfun->machine->no_caller_saved_registers
|
||||
static const char ix86_call_used_regs[] = CALL_USED_REGISTERS;
|
||||
|
||||
if ((cfun->machine->call_saved_registers
|
||||
== TYPE_NO_CALLER_SAVED_REGISTERS)
|
||||
&& (!fndecl
|
||||
|| (!TREE_THIS_VOLATILE (fndecl)
|
||||
&& !lookup_attribute ("no_caller_saved_registers",
|
||||
TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))))
|
||||
{
|
||||
static const char ix86_call_used_regs[] = CALL_USED_REGISTERS;
|
||||
bool is_64bit_ms_abi = (TARGET_64BIT
|
||||
&& ix86_function_abi (fndecl) == MS_ABI);
|
||||
char c_mask = CALL_USED_REGISTERS_MASK (is_64bit_ms_abi);
|
||||
|
@ -9955,6 +9975,24 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
|
|||
clobber_reg (&use, gen_rtx_REG (DImode, R10_REG));
|
||||
}
|
||||
|
||||
if (call_no_callee_saved_registers)
|
||||
{
|
||||
/* After calling a no_callee_saved_registers function, all
|
||||
registers may be clobbered. Clobber all registers that are
|
||||
not used by the callee. */
|
||||
bool is_64bit_ms_abi = (TARGET_64BIT
|
||||
&& ix86_function_abi (fndecl) == MS_ABI);
|
||||
char c_mask = CALL_USED_REGISTERS_MASK (is_64bit_ms_abi);
|
||||
for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
||||
if (!fixed_regs[i]
|
||||
&& !(ix86_call_used_regs[i] == 1
|
||||
|| (ix86_call_used_regs[i] & c_mask))
|
||||
&& !STACK_REGNO_P (i)
|
||||
&& !MMX_REGNO_P (i))
|
||||
clobber_reg (&use,
|
||||
gen_rtx_REG (GET_MODE (regno_reg_rtx[i]), i));
|
||||
}
|
||||
|
||||
if (vec_len > 1)
|
||||
call = gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (vec_len, vec));
|
||||
rtx_insn *call_insn = emit_call_insn (call);
|
||||
|
|
|
@ -3380,6 +3380,10 @@ ix86_simd_clone_adjust (struct cgraph_node *node)
|
|||
static void
|
||||
ix86_set_func_type (tree fndecl)
|
||||
{
|
||||
bool has_no_callee_saved_registers
|
||||
= lookup_attribute ("no_callee_saved_registers",
|
||||
TYPE_ATTRIBUTES (TREE_TYPE (fndecl)));
|
||||
|
||||
if (cfun->machine->func_type == TYPE_UNKNOWN)
|
||||
{
|
||||
if (lookup_attribute ("interrupt",
|
||||
|
@ -3389,12 +3393,18 @@ ix86_set_func_type (tree fndecl)
|
|||
error_at (DECL_SOURCE_LOCATION (fndecl),
|
||||
"interrupt and naked attributes are not compatible");
|
||||
|
||||
if (has_no_callee_saved_registers)
|
||||
error_at (DECL_SOURCE_LOCATION (fndecl),
|
||||
"%qs and %qs attributes are not compatible",
|
||||
"interrupt", "no_callee_saved_registers");
|
||||
|
||||
int nargs = 0;
|
||||
for (tree arg = DECL_ARGUMENTS (fndecl);
|
||||
arg;
|
||||
arg = TREE_CHAIN (arg))
|
||||
nargs++;
|
||||
cfun->machine->no_caller_saved_registers = true;
|
||||
cfun->machine->call_saved_registers
|
||||
= TYPE_NO_CALLER_SAVED_REGISTERS;
|
||||
cfun->machine->func_type
|
||||
= nargs == 2 ? TYPE_EXCEPTION : TYPE_INTERRUPT;
|
||||
|
||||
|
@ -3410,7 +3420,19 @@ ix86_set_func_type (tree fndecl)
|
|||
cfun->machine->func_type = TYPE_NORMAL;
|
||||
if (lookup_attribute ("no_caller_saved_registers",
|
||||
TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
|
||||
cfun->machine->no_caller_saved_registers = true;
|
||||
cfun->machine->call_saved_registers
|
||||
= TYPE_NO_CALLER_SAVED_REGISTERS;
|
||||
if (has_no_callee_saved_registers)
|
||||
{
|
||||
if (cfun->machine->call_saved_registers
|
||||
== TYPE_NO_CALLER_SAVED_REGISTERS)
|
||||
error_at (DECL_SOURCE_LOCATION (fndecl),
|
||||
"%qs and %qs attributes are not compatible",
|
||||
"no_caller_saved_registers",
|
||||
"no_callee_saved_registers");
|
||||
cfun->machine->call_saved_registers
|
||||
= TYPE_NO_CALLEE_SAVED_REGISTERS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3580,7 +3602,7 @@ ix86_set_current_function (tree fndecl)
|
|||
}
|
||||
ix86_previous_fndecl = fndecl;
|
||||
|
||||
static bool prev_no_caller_saved_registers;
|
||||
static call_saved_registers_type prev_call_saved_registers;
|
||||
|
||||
/* 64-bit MS and SYSV ABI have different set of call used registers.
|
||||
Avoid expensive re-initialization of init_regs each time we switch
|
||||
|
@ -3591,12 +3613,13 @@ ix86_set_current_function (tree fndecl)
|
|||
reinit_regs ();
|
||||
/* Need to re-initialize init_regs if caller-saved registers are
|
||||
changed. */
|
||||
else if (prev_no_caller_saved_registers
|
||||
!= cfun->machine->no_caller_saved_registers)
|
||||
else if (prev_call_saved_registers
|
||||
!= cfun->machine->call_saved_registers)
|
||||
reinit_regs ();
|
||||
|
||||
if (cfun->machine->func_type != TYPE_NORMAL
|
||||
|| cfun->machine->no_caller_saved_registers)
|
||||
|| (cfun->machine->call_saved_registers
|
||||
== TYPE_NO_CALLER_SAVED_REGISTERS))
|
||||
{
|
||||
/* Don't allow SSE, MMX nor x87 instructions since they
|
||||
may change processor state. */
|
||||
|
@ -3623,12 +3646,12 @@ ix86_set_current_function (tree fndecl)
|
|||
"the %<no_caller_saved_registers%> attribute", isa);
|
||||
/* Don't issue the same error twice. */
|
||||
cfun->machine->func_type = TYPE_NORMAL;
|
||||
cfun->machine->no_caller_saved_registers = false;
|
||||
cfun->machine->call_saved_registers
|
||||
= TYPE_DEFAULT_CALL_SAVED_REGISTERS;
|
||||
}
|
||||
}
|
||||
|
||||
prev_no_caller_saved_registers
|
||||
= cfun->machine->no_caller_saved_registers;
|
||||
prev_call_saved_registers = cfun->machine->call_saved_registers;
|
||||
}
|
||||
|
||||
/* Implement the TARGET_OFFLOAD_OPTIONS hook. */
|
||||
|
@ -4027,8 +4050,8 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree args, int,
|
|||
}
|
||||
|
||||
static tree
|
||||
ix86_handle_no_caller_saved_registers_attribute (tree *, tree, tree,
|
||||
int, bool *)
|
||||
ix86_handle_call_saved_registers_attribute (tree *, tree, tree,
|
||||
int, bool *)
|
||||
{
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
@ -4190,7 +4213,9 @@ static const attribute_spec ix86_gnu_attributes[] =
|
|||
{ "interrupt", 0, 0, false, true, true, false,
|
||||
ix86_handle_interrupt_attribute, NULL },
|
||||
{ "no_caller_saved_registers", 0, 0, false, true, true, false,
|
||||
ix86_handle_no_caller_saved_registers_attribute, NULL },
|
||||
ix86_handle_call_saved_registers_attribute, NULL },
|
||||
{ "no_callee_saved_registers", 0, 0, false, true, true, true,
|
||||
ix86_handle_call_saved_registers_attribute, NULL },
|
||||
{ "naked", 0, 0, true, false, false, false,
|
||||
ix86_handle_fndecl_attribute, NULL },
|
||||
{ "indirect_branch", 1, 1, true, false, false, false,
|
||||
|
|
|
@ -475,7 +475,9 @@ ix86_conditional_register_usage (void)
|
|||
except fixed_regs and registers used for function return value
|
||||
since aggregate_value_p checks call_used_regs[regno] on return
|
||||
value. */
|
||||
if (cfun && cfun->machine->no_caller_saved_registers)
|
||||
if (cfun
|
||||
&& (cfun->machine->call_saved_registers
|
||||
== TYPE_NO_CALLER_SAVED_REGISTERS))
|
||||
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
||||
if (!fixed_regs[i] && !ix86_function_value_regno_p (i))
|
||||
call_used_regs[i] = 0;
|
||||
|
@ -944,7 +946,8 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
|
|||
|
||||
/* Sibling call isn't OK if there are no caller-saved registers
|
||||
since all registers must be preserved before return. */
|
||||
if (cfun->machine->no_caller_saved_registers)
|
||||
if (cfun->machine->call_saved_registers
|
||||
== TYPE_NO_CALLER_SAVED_REGISTERS)
|
||||
return false;
|
||||
|
||||
/* If we are generating position-independent code, we cannot sibcall
|
||||
|
@ -977,6 +980,14 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
|
|||
decl_or_type = type;
|
||||
}
|
||||
|
||||
/* Sibling call isn't OK if callee has no callee-saved registers
|
||||
and the calling function has callee-saved registers. */
|
||||
if ((cfun->machine->call_saved_registers
|
||||
!= TYPE_NO_CALLEE_SAVED_REGISTERS)
|
||||
&& lookup_attribute ("no_callee_saved_registers",
|
||||
TYPE_ATTRIBUTES (type)))
|
||||
return false;
|
||||
|
||||
/* If outgoing reg parm stack space changes, we cannot do sibcall. */
|
||||
if ((OUTGOING_REG_PARM_STACK_SPACE (type)
|
||||
!= OUTGOING_REG_PARM_STACK_SPACE (TREE_TYPE (current_function_decl)))
|
||||
|
@ -1139,6 +1150,12 @@ ix86_comp_type_attributes (const_tree type1, const_tree type2)
|
|||
!= ix86_function_regparm (type2, NULL))
|
||||
return 0;
|
||||
|
||||
if (lookup_attribute ("no_callee_saved_registers",
|
||||
TYPE_ATTRIBUTES (type1))
|
||||
!= lookup_attribute ("no_callee_saved_registers",
|
||||
TYPE_ATTRIBUTES (type2)))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -6569,7 +6586,8 @@ ix86_epilogue_uses (int regno)
|
|||
and restoring registers. Don't explicitly save SP register since
|
||||
it is always preserved. */
|
||||
return (epilogue_completed
|
||||
&& cfun->machine->no_caller_saved_registers
|
||||
&& (cfun->machine->call_saved_registers
|
||||
== TYPE_NO_CALLER_SAVED_REGISTERS)
|
||||
&& !fixed_regs[regno]
|
||||
&& !STACK_REGNO_P (regno)
|
||||
&& !MMX_REGNO_P (regno));
|
||||
|
@ -6585,7 +6603,8 @@ ix86_hard_regno_scratch_ok (unsigned int regno)
|
|||
as a scratch register after epilogue and use REGNO as scratch
|
||||
register only if it has been used before to avoid saving and
|
||||
restoring it. */
|
||||
return (!cfun->machine->no_caller_saved_registers
|
||||
return ((cfun->machine->call_saved_registers
|
||||
!= TYPE_NO_CALLER_SAVED_REGISTERS)
|
||||
|| (!epilogue_completed
|
||||
&& df_regs_ever_live_p (regno)));
|
||||
}
|
||||
|
@ -6595,14 +6614,21 @@ ix86_hard_regno_scratch_ok (unsigned int regno)
|
|||
bool
|
||||
ix86_save_reg (unsigned int regno, bool maybe_eh_return, bool ignore_outlined)
|
||||
{
|
||||
/* If there are no caller-saved registers, we preserve all registers,
|
||||
except for MMX and x87 registers which aren't supported when saving
|
||||
and restoring registers. Don't explicitly save SP register since
|
||||
it is always preserved. */
|
||||
if (cfun->machine->no_caller_saved_registers)
|
||||
rtx reg;
|
||||
|
||||
switch (cfun->machine->call_saved_registers)
|
||||
{
|
||||
/* Don't preserve registers used for function return value. */
|
||||
rtx reg = crtl->return_rtx;
|
||||
case TYPE_DEFAULT_CALL_SAVED_REGISTERS:
|
||||
break;
|
||||
|
||||
case TYPE_NO_CALLER_SAVED_REGISTERS:
|
||||
/* If there are no caller-saved registers, we preserve all
|
||||
registers, except for MMX and x87 registers which aren't
|
||||
supported when saving and restoring registers. Don't
|
||||
explicitly save SP register since it is always preserved.
|
||||
|
||||
Don't preserve registers used for function return value. */
|
||||
reg = crtl->return_rtx;
|
||||
if (reg)
|
||||
{
|
||||
unsigned int i = REGNO (reg);
|
||||
|
@ -6618,6 +6644,9 @@ ix86_save_reg (unsigned int regno, bool maybe_eh_return, bool ignore_outlined)
|
|||
&& !MMX_REGNO_P (regno)
|
||||
&& (regno != HARD_FRAME_POINTER_REGNUM
|
||||
|| !frame_pointer_needed));
|
||||
|
||||
case TYPE_NO_CALLEE_SAVED_REGISTERS:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (regno == REAL_PIC_OFFSET_TABLE_REGNUM
|
||||
|
@ -7717,7 +7746,8 @@ find_drap_reg (void)
|
|||
registers in epilogue, DRAP must not use caller-saved
|
||||
register in such case. */
|
||||
if (DECL_STATIC_CHAIN (decl)
|
||||
|| cfun->machine->no_caller_saved_registers
|
||||
|| (cfun->machine->call_saved_registers
|
||||
== TYPE_NO_CALLER_SAVED_REGISTERS)
|
||||
|| crtl->tail_call_emit)
|
||||
return R13_REG;
|
||||
|
||||
|
@ -7730,7 +7760,8 @@ find_drap_reg (void)
|
|||
registers in epilogue, DRAP must not use caller-saved
|
||||
register in such case. */
|
||||
if (DECL_STATIC_CHAIN (decl)
|
||||
|| cfun->machine->no_caller_saved_registers
|
||||
|| (cfun->machine->call_saved_registers
|
||||
== TYPE_NO_CALLER_SAVED_REGISTERS)
|
||||
|| crtl->tail_call_emit
|
||||
|| crtl->calls_eh_return)
|
||||
return DI_REG;
|
||||
|
|
|
@ -2724,6 +2724,17 @@ enum function_type
|
|||
TYPE_EXCEPTION
|
||||
};
|
||||
|
||||
enum call_saved_registers_type
|
||||
{
|
||||
TYPE_DEFAULT_CALL_SAVED_REGISTERS = 0,
|
||||
/* The current function is a function specified with the "interrupt"
|
||||
or "no_caller_saved_registers" attribute. */
|
||||
TYPE_NO_CALLER_SAVED_REGISTERS,
|
||||
/* The current function is a function specified with the "noreturn"
|
||||
or "no_callee_saved_registers" attribute. */
|
||||
TYPE_NO_CALLEE_SAVED_REGISTERS
|
||||
};
|
||||
|
||||
enum queued_insn_type
|
||||
{
|
||||
TYPE_NONE = 0,
|
||||
|
@ -2793,9 +2804,8 @@ struct GTY(()) machine_function {
|
|||
/* How to generate function return. */
|
||||
ENUM_BITFIELD(indirect_branch) function_return_type : 3;
|
||||
|
||||
/* If true, the current function is a function specified with
|
||||
the "interrupt" or "no_caller_saved_registers" attribute. */
|
||||
BOOL_BITFIELD no_caller_saved_registers : 1;
|
||||
/* Call saved registers type. */
|
||||
ENUM_BITFIELD(call_saved_registers_type) call_saved_registers : 2;
|
||||
|
||||
/* If true, there is register available for argument passing. This
|
||||
is used only in ix86_function_ok_for_sibcall by 32-bit to determine
|
||||
|
|
|
@ -6767,6 +6767,14 @@ On x86-32 targets, the @code{stdcall} attribute causes the compiler to
|
|||
assume that the called function pops off the stack space used to
|
||||
pass arguments, unless it takes a variable number of arguments.
|
||||
|
||||
@cindex @code{no_callee_saved_registers} function attribute, x86
|
||||
@item no_callee_saved_registers
|
||||
Use this attribute to indicate that the specified function has no
|
||||
callee-saved registers. That is, all registers can be used as scratch
|
||||
registers. For example, this attribute can be used for a function
|
||||
called from the interrupt handler assembly stub which will preserve
|
||||
all registers and return from interrupt.
|
||||
|
||||
@cindex @code{no_caller_saved_registers} function attribute, x86
|
||||
@item no_caller_saved_registers
|
||||
Use this attribute to indicate that the specified function has no
|
||||
|
|
23
gcc/testsuite/gcc.dg/torture/no-callee-saved-run-1a.c
Normal file
23
gcc/testsuite/gcc.dg/torture/no-callee-saved-run-1a.c
Normal file
|
@ -0,0 +1,23 @@
|
|||
/* { dg-do run { target i?86-*-* x86_64-*-* } } */
|
||||
/* { dg-additional-sources no-callee-saved-run-1b.c } */
|
||||
|
||||
extern void bar0 (int, int, int, int, int, int, int, int, int)
|
||||
__attribute__ ((no_callee_saved_registers));
|
||||
|
||||
void
|
||||
foo (void)
|
||||
{
|
||||
bar0 (0, 1, 2, 3, 4, 5, 6, 7, 8);
|
||||
}
|
||||
|
||||
int
|
||||
bar (int x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
void
|
||||
bad (void)
|
||||
{
|
||||
__builtin_abort ();
|
||||
}
|
59
gcc/testsuite/gcc.dg/torture/no-callee-saved-run-1b.c
Normal file
59
gcc/testsuite/gcc.dg/torture/no-callee-saved-run-1b.c
Normal file
|
@ -0,0 +1,59 @@
|
|||
/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
|
||||
|
||||
extern void foo (void);
|
||||
extern void bad (void);
|
||||
extern int bar (int);
|
||||
|
||||
void
|
||||
__attribute__ ((no_callee_saved_registers))
|
||||
bar0 (int i0, int i1, int i2, int i3, int i4, int i5, int i6,
|
||||
int i7, int i8)
|
||||
{
|
||||
if (i0 != 0)
|
||||
bad ();
|
||||
|
||||
if (i1 != 1)
|
||||
bad ();
|
||||
|
||||
if (i2 != 2)
|
||||
bad ();
|
||||
|
||||
if (i3 != 3)
|
||||
bad ();
|
||||
|
||||
if (i4 != 4)
|
||||
bad ();
|
||||
|
||||
if (i5 != 5)
|
||||
bad ();
|
||||
|
||||
if (i6 != 6)
|
||||
bad ();
|
||||
|
||||
if (i7 != 7)
|
||||
bad ();
|
||||
|
||||
if (i8 != 8)
|
||||
bad ();
|
||||
|
||||
int a,b,c,d,e,f,i;
|
||||
a = bar (5);
|
||||
b = bar (a);
|
||||
c = bar (b);
|
||||
d = bar (c);
|
||||
e = bar (d);
|
||||
f = bar (e);
|
||||
for (i = 1; i < 10; i++)
|
||||
{
|
||||
a += bar (a + i) + bar (b + i) +
|
||||
bar (c + i) + bar (d + i) +
|
||||
bar (e + i) + bar (f + i);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
foo ();
|
||||
return 0;
|
||||
}
|
30
gcc/testsuite/gcc.target/i386/no-callee-saved-1.c
Normal file
30
gcc/testsuite/gcc.target/i386/no-callee-saved-1.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
|
||||
|
||||
extern int bar (int)
|
||||
#ifndef __x86_64__
|
||||
__attribute__ ((regparm(3)))
|
||||
#endif
|
||||
;
|
||||
|
||||
__attribute__ ((no_callee_saved_registers))
|
||||
void
|
||||
foo (void *frame)
|
||||
{
|
||||
int a,b,c,d,e,f,i;
|
||||
a = bar (5);
|
||||
b = bar (a);
|
||||
c = bar (b);
|
||||
d = bar (c);
|
||||
e = bar (d);
|
||||
f = bar (e);
|
||||
for (i = 1; i < 10; i++)
|
||||
{
|
||||
a += bar (a + i) + bar (b + i) +
|
||||
bar (c + i) + bar (d + i) +
|
||||
bar (e + i) + bar (f + i);
|
||||
}
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-not "push" } } */
|
||||
/* { dg-final { scan-assembler-not "pop" } } */
|
46
gcc/testsuite/gcc.target/i386/no-callee-saved-10.c
Normal file
46
gcc/testsuite/gcc.target/i386/no-callee-saved-10.c
Normal file
|
@ -0,0 +1,46 @@
|
|||
/* { dg-do compile { target *-*-linux* } } */
|
||||
/* { dg-options "-O2 -mgeneral-regs-only -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
|
||||
|
||||
extern void bar (void) __attribute__ ((no_callee_saved_registers));
|
||||
|
||||
__attribute__ ((no_caller_saved_registers))
|
||||
void
|
||||
foo (void)
|
||||
{
|
||||
bar ();
|
||||
}
|
||||
|
||||
/* foo must save and restore all caller saved registers since bar won't
|
||||
preserve any. */
|
||||
/* { dg-final { scan-assembler-not "jmp\[\\t \]+_?bar" } } */
|
||||
/* { dg-final { scan-assembler "call\[\\t \]+_?bar" } } */
|
||||
/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)bp" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)si" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)di" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)ax" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)cx" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)dx" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)bp" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)si" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)di" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r8" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r9" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r10" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r11" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */
|
11
gcc/testsuite/gcc.target/i386/no-callee-saved-11.c
Normal file
11
gcc/testsuite/gcc.target/i386/no-callee-saved-11.c
Normal file
|
@ -0,0 +1,11 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2" } */
|
||||
|
||||
extern void foo (void); /* { dg-note "previous declaration" } */
|
||||
|
||||
__attribute__ ((no_callee_saved_registers))
|
||||
void
|
||||
foo (void) /* { dg-error "conflicting types" } */
|
||||
{
|
||||
}
|
||||
|
10
gcc/testsuite/gcc.target/i386/no-callee-saved-12.c
Normal file
10
gcc/testsuite/gcc.target/i386/no-callee-saved-12.c
Normal file
|
@ -0,0 +1,10 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2" } */
|
||||
|
||||
extern void foo (void) __attribute__ ((no_callee_saved_registers)); /* { dg-note "previous declaration" } */
|
||||
|
||||
void
|
||||
foo (void) /* { dg-error "conflicting types" } */
|
||||
{
|
||||
}
|
||||
|
16
gcc/testsuite/gcc.target/i386/no-callee-saved-13.c
Normal file
16
gcc/testsuite/gcc.target/i386/no-callee-saved-13.c
Normal file
|
@ -0,0 +1,16 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
|
||||
|
||||
extern void foo (void);
|
||||
|
||||
__attribute__ ((no_callee_saved_registers))
|
||||
void
|
||||
bar (void)
|
||||
{
|
||||
foo ();
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-not "push" } } */
|
||||
/* { dg-final { scan-assembler-not "pop" } } */
|
||||
/* { dg-final { scan-assembler-not "call\[\\t \]+_?foo" } } */
|
||||
/* { dg-final { scan-assembler "jmp\[\\t \]+_?foo" } } */
|
16
gcc/testsuite/gcc.target/i386/no-callee-saved-14.c
Normal file
16
gcc/testsuite/gcc.target/i386/no-callee-saved-14.c
Normal file
|
@ -0,0 +1,16 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
|
||||
|
||||
extern void bar (void) __attribute__ ((no_callee_saved_registers));
|
||||
|
||||
__attribute__ ((no_callee_saved_registers))
|
||||
void
|
||||
foo (void)
|
||||
{
|
||||
bar ();
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-not "push" } } */
|
||||
/* { dg-final { scan-assembler-not "pop" } } */
|
||||
/* { dg-final { scan-assembler "jmp\[\\t \]+_?bar" } } */
|
||||
/* { dg-final { scan-assembler-not "call\[\\t \]+_?bar" } } */
|
17
gcc/testsuite/gcc.target/i386/no-callee-saved-15.c
Normal file
17
gcc/testsuite/gcc.target/i386/no-callee-saved-15.c
Normal file
|
@ -0,0 +1,17 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
|
||||
|
||||
typedef void (*fn_t) (void) __attribute__ ((no_callee_saved_registers));
|
||||
extern fn_t bar;
|
||||
|
||||
__attribute__ ((no_callee_saved_registers))
|
||||
void
|
||||
foo (void)
|
||||
{
|
||||
bar ();
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-not "push" } } */
|
||||
/* { dg-final { scan-assembler-not "pop" } } */
|
||||
/* { dg-final { scan-assembler "jmp" } } */
|
||||
/* { dg-final { scan-assembler-not "call\[\\t \]+" } } */
|
16
gcc/testsuite/gcc.target/i386/no-callee-saved-16.c
Normal file
16
gcc/testsuite/gcc.target/i386/no-callee-saved-16.c
Normal file
|
@ -0,0 +1,16 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
|
||||
|
||||
typedef void (*fn_t) (void) __attribute__ ((no_callee_saved_registers));
|
||||
|
||||
__attribute__ ((no_callee_saved_registers))
|
||||
void
|
||||
foo (fn_t bar)
|
||||
{
|
||||
bar ();
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-not "push" } } */
|
||||
/* { dg-final { scan-assembler-not "pop" } } */
|
||||
/* { dg-final { scan-assembler "jmp" } } */
|
||||
/* { dg-final { scan-assembler-not "call\[\\t \]+" } } */
|
16
gcc/testsuite/gcc.target/i386/no-callee-saved-17.c
Normal file
16
gcc/testsuite/gcc.target/i386/no-callee-saved-17.c
Normal file
|
@ -0,0 +1,16 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
|
||||
|
||||
extern void foo (void) __attribute__ ((no_caller_saved_registers));
|
||||
|
||||
__attribute__ ((no_callee_saved_registers))
|
||||
void
|
||||
bar (void)
|
||||
{
|
||||
foo ();
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-not "push" } } */
|
||||
/* { dg-final { scan-assembler-not "pop" } } */
|
||||
/* { dg-final { scan-assembler-not "call\[\\t \]+_?foo" } } */
|
||||
/* { dg-final { scan-assembler "jmp\[\\t \]+_?foo" } } */
|
51
gcc/testsuite/gcc.target/i386/no-callee-saved-18.c
Normal file
51
gcc/testsuite/gcc.target/i386/no-callee-saved-18.c
Normal file
|
@ -0,0 +1,51 @@
|
|||
/* { dg-do compile { target *-*-linux* } } */
|
||||
/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef void (*fn_t) (void) __attribute__ ((no_callee_saved_registers));
|
||||
|
||||
void
|
||||
foo (uintptr_t p)
|
||||
{
|
||||
((fn_t) p) ();
|
||||
}
|
||||
|
||||
/* foo must save and restore all caller saved registers since bar won't
|
||||
preserve any. */
|
||||
/* { dg-final { scan-assembler-not "jmp" } } */
|
||||
/* { dg-final { scan-assembler "call\[\\t \]+" } } */
|
||||
/* { dg-final { scan-assembler-not "push(?:l|q)\[\\t \]*%(?:e|r)ax" } } */
|
||||
/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */
|
||||
/* { dg-final { scan-assembler-not "push(?:l|q)\[\\t \]*%(?:e|r)cx" } } */
|
||||
/* { dg-final { scan-assembler-not "push(?:l|q)\[\\t \]*%(?:e|r)dx" } } */
|
||||
/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)bp" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "pushl\[\\t \]*%esi" 1 { target ia32 } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rsi" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edi" 1 { target ia32 } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rdi" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r8" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r9" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r10" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r11" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "pop(?:l|q)\[\\t \]*%(?:e|r)ax" } } */
|
||||
/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */
|
||||
/* { dg-final { scan-assembler-not "pop(?:l|q)\[\\t \]*%(?:e|r)cx" } } */
|
||||
/* { dg-final { scan-assembler-not "pop(?:l|q)\[\\t \]*%(?:e|r)dx" } } */
|
||||
/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)bp" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "popl\[\\t \]*%esi" 1 { target ia32 } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%rsi" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popl\[\\t \]*%edi" 1 { target ia32 } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%rdi" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%r8" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%r9" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%r10" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%r11" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */
|
30
gcc/testsuite/gcc.target/i386/no-callee-saved-2.c
Normal file
30
gcc/testsuite/gcc.target/i386/no-callee-saved-2.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
|
||||
|
||||
extern int bar (int) __attribute__ ((no_caller_saved_registers))
|
||||
#ifndef __x86_64__
|
||||
__attribute__ ((regparm(3)))
|
||||
#endif
|
||||
;
|
||||
|
||||
__attribute__ ((no_callee_saved_registers))
|
||||
void
|
||||
foo (void *frame)
|
||||
{
|
||||
int a,b,c,d,e,f,i;
|
||||
a = bar (5);
|
||||
b = bar (a);
|
||||
c = bar (b);
|
||||
d = bar (c);
|
||||
e = bar (d);
|
||||
f = bar (e);
|
||||
for (i = 1; i < 10; i++)
|
||||
{
|
||||
a += bar (a + i) + bar (b + i) +
|
||||
bar (c + i) + bar (d + i) +
|
||||
bar (e + i) + bar (f + i);
|
||||
}
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-not "push" } } */
|
||||
/* { dg-final { scan-assembler-not "pop" } } */
|
8
gcc/testsuite/gcc.target/i386/no-callee-saved-3.c
Normal file
8
gcc/testsuite/gcc.target/i386/no-callee-saved-3.c
Normal file
|
@ -0,0 +1,8 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2" } */
|
||||
|
||||
__attribute__ ((no_callee_saved_registers, no_caller_saved_registers))
|
||||
void
|
||||
foo (void) /* { dg-error "attributes are not compatible" } */
|
||||
{
|
||||
}
|
8
gcc/testsuite/gcc.target/i386/no-callee-saved-4.c
Normal file
8
gcc/testsuite/gcc.target/i386/no-callee-saved-4.c
Normal file
|
@ -0,0 +1,8 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -mgeneral-regs-only" } */
|
||||
|
||||
__attribute__ ((no_callee_saved_registers, interrupt))
|
||||
void
|
||||
foo (void *frame) /* { dg-error "attributes are not compatible" } */
|
||||
{
|
||||
}
|
11
gcc/testsuite/gcc.target/i386/no-callee-saved-5.c
Normal file
11
gcc/testsuite/gcc.target/i386/no-callee-saved-5.c
Normal file
|
@ -0,0 +1,11 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2" } */
|
||||
|
||||
typedef void (*fn_t) (void *) __attribute__ ((no_callee_saved_registers));
|
||||
|
||||
void
|
||||
foo (void *frame)
|
||||
{
|
||||
}
|
||||
|
||||
fn_t func = foo; /* { dg-error "incompatible pointer type" } */
|
12
gcc/testsuite/gcc.target/i386/no-callee-saved-6.c
Normal file
12
gcc/testsuite/gcc.target/i386/no-callee-saved-6.c
Normal file
|
@ -0,0 +1,12 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2" } */
|
||||
|
||||
typedef void (*fn_t) (void *) __attribute__ ((no_callee_saved_registers));
|
||||
|
||||
__attribute__ ((no_callee_saved_registers))
|
||||
void
|
||||
foo (void *frame)
|
||||
{
|
||||
}
|
||||
|
||||
fn_t func = foo;
|
49
gcc/testsuite/gcc.target/i386/no-callee-saved-7.c
Normal file
49
gcc/testsuite/gcc.target/i386/no-callee-saved-7.c
Normal file
|
@ -0,0 +1,49 @@
|
|||
/* { dg-do compile { target *-*-linux* } } */
|
||||
/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
|
||||
|
||||
extern void bar (void) __attribute__ ((no_callee_saved_registers));
|
||||
|
||||
void
|
||||
foo (void)
|
||||
{
|
||||
bar ();
|
||||
}
|
||||
|
||||
/* foo must save and restore all caller saved registers since bar won't
|
||||
preserve any. */
|
||||
/* { dg-final { scan-assembler-not "jmp\[\\t \]+_?bar" } } */
|
||||
/* { dg-final { scan-assembler "call\[\\t \]+_?bar" } } */
|
||||
/* { dg-final { scan-assembler-not "push(?:l|q)\[\\t \]*%(?:e|r)ax" } } */
|
||||
/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */
|
||||
/* { dg-final { scan-assembler-not "push(?:l|q)\[\\t \]*%(?:e|r)cx" } } */
|
||||
/* { dg-final { scan-assembler-not "push(?:l|q)\[\\t \]*%(?:e|r)dx" } } */
|
||||
/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)bp" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "pushl\[\\t \]*%esi" 1 { target ia32 } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rsi" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edi" 1 { target ia32 } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rdi" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r8" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r9" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r10" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r11" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "pop(?:l|q)\[\\t \]*%(?:e|r)ax" } } */
|
||||
/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */
|
||||
/* { dg-final { scan-assembler-not "pop(?:l|q)\[\\t \]*%(?:e|r)cx" } } */
|
||||
/* { dg-final { scan-assembler-not "pop(?:l|q)\[\\t \]*%(?:e|r)dx" } } */
|
||||
/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)bp" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "popl\[\\t \]*%esi" 1 { target ia32 } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%rsi" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popl\[\\t \]*%edi" 1 { target ia32 } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%rdi" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%r8" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%r9" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%r10" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%r11" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */
|
50
gcc/testsuite/gcc.target/i386/no-callee-saved-8.c
Normal file
50
gcc/testsuite/gcc.target/i386/no-callee-saved-8.c
Normal file
|
@ -0,0 +1,50 @@
|
|||
/* { dg-do compile { target *-*-linux* } } */
|
||||
/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
|
||||
|
||||
typedef void (*fn_t) (void) __attribute__ ((no_callee_saved_registers));
|
||||
extern fn_t bar;
|
||||
|
||||
void
|
||||
foo (void)
|
||||
{
|
||||
bar ();
|
||||
}
|
||||
|
||||
/* foo must save and restore all caller saved registers since bar won't
|
||||
preserve any. */
|
||||
/* { dg-final { scan-assembler-not "jmp" } } */
|
||||
/* { dg-final { scan-assembler "call\[\\t \]+" } } */
|
||||
/* { dg-final { scan-assembler-not "push(?:l|q)\[\\t \]*%(?:e|r)ax" } } */
|
||||
/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */
|
||||
/* { dg-final { scan-assembler-not "push(?:l|q)\[\\t \]*%(?:e|r)cx" } } */
|
||||
/* { dg-final { scan-assembler-not "push(?:l|q)\[\\t \]*%(?:e|r)dx" } } */
|
||||
/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)bp" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "pushl\[\\t \]*%esi" 1 { target ia32 } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rsi" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edi" 1 { target ia32 } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rdi" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r8" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r9" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r10" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r11" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "pop(?:l|q)\[\\t \]*%(?:e|r)ax" } } */
|
||||
/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */
|
||||
/* { dg-final { scan-assembler-not "pop(?:l|q)\[\\t \]*%(?:e|r)cx" } } */
|
||||
/* { dg-final { scan-assembler-not "pop(?:l|q)\[\\t \]*%(?:e|r)dx" } } */
|
||||
/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)bp" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "popl\[\\t \]*%esi" 1 { target ia32 } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%rsi" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popl\[\\t \]*%edi" 1 { target ia32 } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%rdi" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%r8" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%r9" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%r10" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%r11" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */
|
49
gcc/testsuite/gcc.target/i386/no-callee-saved-9.c
Normal file
49
gcc/testsuite/gcc.target/i386/no-callee-saved-9.c
Normal file
|
@ -0,0 +1,49 @@
|
|||
/* { dg-do compile { target *-*-linux* } } */
|
||||
/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
|
||||
|
||||
typedef void (*fn_t) (void) __attribute__ ((no_callee_saved_registers));
|
||||
|
||||
void
|
||||
foo (fn_t bar)
|
||||
{
|
||||
bar ();
|
||||
}
|
||||
|
||||
/* foo must save and restore all caller saved registers since bar won't
|
||||
preserve any. */
|
||||
/* { dg-final { scan-assembler-not "jmp" } } */
|
||||
/* { dg-final { scan-assembler "call\[\\t \]+" } } */
|
||||
/* { dg-final { scan-assembler-not "push(?:l|q)\[\\t \]*%(?:e|r)ax" } } */
|
||||
/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */
|
||||
/* { dg-final { scan-assembler-not "push(?:l|q)\[\\t \]*%(?:e|r)cx" } } */
|
||||
/* { dg-final { scan-assembler-not "push(?:l|q)\[\\t \]*%(?:e|r)dx" } } */
|
||||
/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)bp" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "pushl\[\\t \]*%esi" 1 { target ia32 } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rsi" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushl\[\\t \]*%edi" 1 { target ia32 } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%rdi" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r8" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r9" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r10" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "pushq\[\\t \]*%r11" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "pushq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "pop(?:l|q)\[\\t \]*%(?:e|r)ax" } } */
|
||||
/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)bx" 1 } } */
|
||||
/* { dg-final { scan-assembler-not "pop(?:l|q)\[\\t \]*%(?:e|r)cx" } } */
|
||||
/* { dg-final { scan-assembler-not "pop(?:l|q)\[\\t \]*%(?:e|r)dx" } } */
|
||||
/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)bp" 1 } } */
|
||||
/* { dg-final { scan-assembler-times "popl\[\\t \]*%esi" 1 { target ia32 } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%rsi" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popl\[\\t \]*%edi" 1 { target ia32 } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%rdi" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%r8" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%r9" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%r10" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-not "popq\[\\t \]*%r11" { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r12" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r13" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r14" 1 { target { ! ia32 } } } } */
|
||||
/* { dg-final { scan-assembler-times "popq\[\\t \]*%r15" 1 { target { ! ia32 } } } } */
|
Loading…
Add table
Reference in a new issue