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:
H.J. Lu 2024-01-23 06:59:50 -08:00 committed by H.J. Lu
parent a12b0e9360
commit a96549dce7
25 changed files with 675 additions and 35 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View 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 ();
}

View 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;
}

View 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" } } */

View 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 } } } } */

View 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" } */
{
}

View 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" } */
{
}

View 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" } } */

View 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" } } */

View 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 \]+" } } */

View 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 \]+" } } */

View 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" } } */

View 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 } } } } */

View 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" } } */

View 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" } */
{
}

View 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" } */
{
}

View 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" } */

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

View 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 } } } } */

View 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 } } } } */

View 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 } } } } */