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))))
+ else if (ix86_type_no_callee_saved_registers_p (TREE_TYPE (fndecl)))
call_no_callee_saved_registers = true;
if (fndecl == current_function_decl
&& decl_binds_to_current_def_p (fndecl))
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))))
+ && ix86_type_no_callee_saved_registers_p (TREE_TYPE (mem_expr)))
call_no_callee_saved_registers = true;
}
interrupt function in this case. */
enum call_saved_registers_type no_callee_saved_registers
= TYPE_DEFAULT_CALL_SAVED_REGISTERS;
- if (lookup_attribute ("no_callee_saved_registers",
+ if (lookup_attribute ("preserve_none",
+ TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
+ no_callee_saved_registers = TYPE_PRESERVE_NONE;
+ else if (lookup_attribute ("no_callee_saved_registers",
TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
no_callee_saved_registers = TYPE_NO_CALLEE_SAVED_REGISTERS;
else if (ix86_noreturn_no_callee_saved_registers
"interrupt and naked attributes are not compatible");
if (no_callee_saved_registers)
- error_at (DECL_SOURCE_LOCATION (fndecl),
- "%qs and %qs attributes are not compatible",
- "interrupt", "no_callee_saved_registers");
+ {
+ const char *attr;
+ if (no_callee_saved_registers == TYPE_PRESERVE_NONE)
+ attr = "preserve_none";
+ else
+ attr = "no_callee_saved_registers";
+ error_at (DECL_SOURCE_LOCATION (fndecl),
+ "%qs and %qs attributes are not compatible",
+ "interrupt", attr);
+ }
int nargs = 0;
for (tree arg = DECL_ARGUMENTS (fndecl);
else
{
cfun->machine->func_type = TYPE_NORMAL;
- if (lookup_attribute ("no_caller_saved_registers",
- TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
+ if (no_callee_saved_registers)
+ cfun->machine->call_saved_registers
+ = no_callee_saved_registers;
+ else if (lookup_attribute ("no_caller_saved_registers",
+ TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
cfun->machine->call_saved_registers
= TYPE_NO_CALLER_SAVED_REGISTERS;
- if (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
- = no_callee_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. */
+ /* Don't allow AVX, AVX512, MMX nor x87 instructions since they
+ may change processor state. Don't allow SSE instructions in
+ exception/interrupt service routines. */
const char *isa;
if (TARGET_SSE)
- isa = "SSE";
+ {
+ if (TARGET_AVX512F)
+ isa = "AVX512";
+ else if (TARGET_AVX)
+ isa = "AVX";
+ else if (cfun->machine->func_type != TYPE_NORMAL)
+ isa = "SSE";
+ else
+ isa = NULL;
+ }
else if (TARGET_MMX)
isa = "MMX/3Dnow";
else if (TARGET_80387)
}
static tree
-ix86_handle_call_saved_registers_attribute (tree *, tree, tree,
+ix86_handle_call_saved_registers_attribute (tree *node, tree name, tree,
int, bool *)
{
+ const char *attr1 = nullptr;
+ const char *attr2 = nullptr;
+
+ if (is_attribute_p ("no_callee_saved_registers", name))
+ {
+ /* Disallow preserve_none and no_caller_saved_registers
+ attributes. */
+ attr1 = "no_callee_saved_registers";
+ if (lookup_attribute ("preserve_none", TYPE_ATTRIBUTES (*node)))
+ attr2 = "preserve_none";
+ else if (lookup_attribute ("no_caller_saved_registers",
+ TYPE_ATTRIBUTES (*node)))
+ attr2 = "no_caller_saved_registers";
+ }
+ else if (is_attribute_p ("no_caller_saved_registers", name))
+ {
+ /* Disallow preserve_none and no_callee_saved_registers
+ attributes. */
+ attr1 = "no_caller_saved_registers";
+ if (lookup_attribute ("preserve_none", TYPE_ATTRIBUTES (*node)))
+ attr2 = "preserve_none";
+ else if (lookup_attribute ("no_callee_saved_registers",
+ TYPE_ATTRIBUTES (*node)))
+ attr2 = "no_callee_saved_registers";
+ }
+ else if (is_attribute_p ("preserve_none", name))
+ {
+ /* Disallow no_callee_saved_registers and no_caller_saved_registers
+ attributes. */
+ attr1 = "preserve_none";
+ if (lookup_attribute ("no_callee_saved_registers",
+ TYPE_ATTRIBUTES (*node)))
+ attr2 = "no_caller_saved_registers";
+ else if (lookup_attribute ("no_callee_saved_registers",
+ TYPE_ATTRIBUTES (*node)))
+ attr2 = "no_callee_saved_registers";
+ }
+
+ if (attr2)
+ error ("%qs and %qs attributes are not compatible", attr1, attr2);
+
return NULL_TREE;
}
ix86_handle_interrupt_attribute, NULL },
{ "no_caller_saved_registers", 0, 0, false, true, true, false,
ix86_handle_call_saved_registers_attribute, NULL },
+ { "preserve_none", 0, 0, false, true, true, true,
+ 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,
struct gcc_options *,
struct gcc_options *, bool);
extern unsigned int ix86_get_callcvt (const_tree);
+extern bool ix86_type_no_callee_saved_registers_p (const_tree);
#endif
CX_REG, DX_REG, R8_REG, R9_REG
};
+/* Similar as Clang's preserve_none function parameter passing.
+ NB: Use DI_REG and SI_REG, see ix86_function_value_regno_p. */
+
+static int const x86_64_preserve_none_int_parameter_registers[6] =
+{
+ R12_REG, R13_REG, R14_REG, R15_REG, DI_REG, SI_REG
+};
+
static int const x86_64_int_return_registers[4] =
{
AX_REG, DX_REG, DI_REG, SI_REG
red-zone.
NB: Don't use red-zone for functions with no_caller_saved_registers
- and 32 GPRs since 128-byte red-zone is too small for 31 GPRs.
+ and 32 GPRs or 16 XMM registers since 128-byte red-zone is too small
+ for 31 GPRs or 15 GPRs + 16 XMM registers.
TODO: If we can reserve the first 2 WORDs, for PUSH and, another
for CALL, in red-zone, we can allow local indirect jumps with
{
return (TARGET_RED_ZONE
&& !TARGET_64BIT_MS_ABI
- && (!TARGET_APX_EGPR
+ && ((!TARGET_APX_EGPR && !TARGET_SSE)
|| (cfun->machine->call_saved_registers
!= TYPE_NO_CALLER_SAVED_REGISTERS))
&& (!cfun->machine->has_local_indirect_jump
default_unique_section (decl, reloc);
}
+/* Return true if TYPE has no_callee_saved_registers or preserve_none
+ attribute. */
+
+bool
+ix86_type_no_callee_saved_registers_p (const_tree type)
+{
+ return (lookup_attribute ("no_callee_saved_registers",
+ TYPE_ATTRIBUTES (type)) != NULL
+ || lookup_attribute ("preserve_none",
+ TYPE_ATTRIBUTES (type)) != NULL);
+}
+
#ifdef COMMON_ASM_OP
#ifndef LARGECOMM_SECTION_ASM_OP
/* 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
+ if ((cfun->machine->call_saved_registers
+ != TYPE_NO_CALLEE_SAVED_REGISTERS)
+ && cfun->machine->call_saved_registers != TYPE_PRESERVE_NONE
&& (cfun->machine->call_saved_registers
!= TYPE_NO_CALLEE_SAVED_REGISTERS_EXCEPT_BP)
- && lookup_attribute ("no_callee_saved_registers",
- TYPE_ATTRIBUTES (type)))
+ && ix86_type_no_callee_saved_registers_p (type))
return false;
/* If outgoing reg parm stack space changes, we cannot do sibcall. */
!= 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)))
+ if (ix86_type_no_callee_saved_registers_p (type1)
+ != ix86_type_no_callee_saved_registers_p (type2))
+ return 0;
+
+ /* preserve_none attribute uses a different calling convention is
+ only for 64-bit. */
+ if (TARGET_64BIT
+ && (lookup_attribute ("preserve_none", TYPE_ATTRIBUTES (type1))
+ != lookup_attribute ("preserve_none",
+ TYPE_ATTRIBUTES (type2))))
return 0;
return 1;
if (call_abi == SYSV_ABI && regno == AX_REG)
return true;
- if (call_abi == MS_ABI)
+ if (cfun
+ && cfun->machine->call_saved_registers == TYPE_PRESERVE_NONE)
+ parm_regs = x86_64_preserve_none_int_parameter_registers;
+ else if (call_abi == MS_ABI)
parm_regs = x86_64_ms_abi_int_parameter_registers;
else
parm_regs = x86_64_int_parameter_registers;
memset (cum, 0, sizeof (*cum));
+ tree preserve_none_type;
if (fndecl)
{
target = cgraph_node::get (fndecl);
target = target->function_symbol ();
local_info_node = cgraph_node::local_info_node (target->decl);
cum->call_abi = ix86_function_abi (target->decl);
+ preserve_none_type = TREE_TYPE (target->decl);
}
else
- cum->call_abi = ix86_function_abi (fndecl);
+ {
+ cum->call_abi = ix86_function_abi (fndecl);
+ preserve_none_type = TREE_TYPE (fndecl);
+ }
}
else
- cum->call_abi = ix86_function_type_abi (fntype);
+ {
+ cum->call_abi = ix86_function_type_abi (fntype);
+ preserve_none_type = fntype;
+ }
+ cum->preserve_none_abi
+ = (preserve_none_type
+ && (lookup_attribute ("preserve_none",
+ TYPE_ATTRIBUTES (preserve_none_type))
+ != nullptr));
cum->caller = caller;
break;
}
+ const int *parm_regs;
+ if (cum->preserve_none_abi)
+ parm_regs = x86_64_preserve_none_int_parameter_registers;
+ else
+ parm_regs = x86_64_int_parameter_registers;
+
return construct_container (mode, orig_mode, type, 0, cum->nregs,
cum->sse_nregs,
- &x86_64_int_parameter_registers [cum->regno],
+ &parm_regs[cum->regno],
cum->sse_regno);
}
if (max > X86_64_REGPARM_MAX)
max = X86_64_REGPARM_MAX;
+ const int *parm_regs;
+ if (cum->preserve_none_abi)
+ parm_regs = x86_64_preserve_none_int_parameter_registers;
+ else
+ parm_regs = x86_64_int_parameter_registers;
+
for (i = cum->regno; i < max; i++)
{
mem = gen_rtx_MEM (word_mode,
MEM_NOTRAP_P (mem) = 1;
set_mem_alias_set (mem, set);
emit_move_insn (mem,
- gen_rtx_REG (word_mode,
- x86_64_int_parameter_registers[i]));
+ gen_rtx_REG (word_mode, parm_regs[i]));
}
if (ix86_varargs_fpr_size)
|| !frame_pointer_needed));
case TYPE_NO_CALLEE_SAVED_REGISTERS:
+ case TYPE_PRESERVE_NONE:
return false;
case TYPE_NO_CALLEE_SAVED_REGISTERS_EXCEPT_BP:
int nregs = 0;
int regno;
- if (!TARGET_64BIT_MS_ABI)
+ if (!TARGET_64BIT_MS_ABI
+ && (cfun->machine->call_saved_registers
+ != TYPE_NO_CALLER_SAVED_REGISTERS))
return 0;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (SSE_REGNO_P (regno) && ix86_save_reg (regno, true, true))
gcc_assert (preferred_alignment >= STACK_BOUNDARY / BITS_PER_UNIT);
gcc_assert (preferred_alignment <= stack_alignment_needed);
- /* The only ABI saving SSE regs should be 64-bit ms_abi. */
- gcc_assert (TARGET_64BIT || !frame->nsseregs);
+ /* The only ABI saving SSE regs should be 64-bit ms_abi or with
+ no_caller_saved_registers attribue. */
+ gcc_assert (TARGET_64BIT
+ || (cfun->machine->call_saved_registers
+ == TYPE_NO_CALLER_SAVED_REGISTERS)
+ || !frame->nsseregs);
if (TARGET_64BIT && m->call_ms2sysv)
{
gcc_assert (stack_alignment_needed >= 16);
- gcc_assert (!frame->nsseregs);
+ gcc_assert ((cfun->machine->call_saved_registers
+ == TYPE_NO_CALLER_SAVED_REGISTERS)
+ || !frame->nsseregs);
}
/* For SEH we have to limit the amount of code movement into the prologue.
{
const int *parm_regs;
- if (ix86_function_type_abi (type) == MS_ABI)
+ if (lookup_attribute ("preserve_none", TYPE_ATTRIBUTES (type)))
+ parm_regs = x86_64_preserve_none_int_parameter_registers;
+ else if (ix86_function_type_abi (type) == MS_ABI)
parm_regs = x86_64_ms_abi_int_parameter_registers;
else
parm_regs = x86_64_int_parameter_registers;
int stdarg; /* Set to 1 if function is stdarg. */
enum calling_abi call_abi; /* Set to SYSV_ABI for sysv abi. Otherwise
MS_ABI for ms abi. */
+ bool preserve_none_abi; /* Set to true if the preserve_none ABI is
+ used. */
tree decl; /* Callee decl. */
} CUMULATIVE_ARGS;
/* The current function is a function specified with the "noreturn"
attribute. */
TYPE_NO_CALLEE_SAVED_REGISTERS_EXCEPT_BP,
+ /* The current function is a function specified with the
+ "preserve_none" attribute. */
+ TYPE_PRESERVE_NONE,
};
enum queued_insn_type
ENUM_BITFIELD(indirect_branch) function_return_type : 3;
/* Call saved registers type. */
- ENUM_BITFIELD(call_saved_registers_type) call_saved_registers : 2;
+ ENUM_BITFIELD(call_saved_registers_type) call_saved_registers : 3;
/* If true, there is register available for argument passing. This
is used only in ix86_function_ok_for_sibcall by 32-bit to determine
called from the interrupt handler assembly stub which will preserve
all registers and return from interrupt.
+@cindex @code{preserve_none} function attribute, x86
+@item preserve_none
+This attribute is similar to @code{no_callee_saved_registers}, except
+on x86-64, r12, r13, r14, r15, rdi and rsi registers are used for
+integer parameter passing and this calling convention is subject to
+change.
+
@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
example, this attribute can be used for a function called from an
interrupt handler. The compiler generates proper function entry and
exit sequences to save and restore any modified registers, except for
-the EFLAGS register. Since GCC doesn't preserve SSE, MMX nor x87
-states, the GCC option @option{-mgeneral-regs-only} should be used to
-compile functions with @code{no_caller_saved_registers} attribute.
+the EFLAGS register. Since GCC doesn't preserve YMM nor ZMM registers,
+@code{no_caller_saved_registers} attribute can't be used on functions
+with AVX enabled. Note that MMX and x87 registers aren't preserved by
+@code{no_caller_saved_registers} attribute.
@cindex @code{interrupt} function attribute, x86
@item interrupt
--- /dev/null
+/* { dg-do compile { target { *-*-linux* && lp64 } } } */
+/* { dg-options "-O2 -fno-pic -mtune=generic -msse2 -mno-apxf -mtune-ctrl=prologue_using_move,epilogue_using_move" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.} } } */
+
+/* end must be empty. */
+
+/*
+**end:
+**.LFB[0-9]+:
+** .cfi_startproc
+** ret
+** .cfi_endproc
+**...
+*/
+
+#define NEXT { op_t *op = next; [[gnu::musttail]] return (*op)(op + 1); }
+#ifdef __x86_64__
+# define CLOBBER asm("" ::: "r12","r13","r14","r15","rbp","rbx")
+#else
+# define CLOBBER asm("" ::: "ebp","ebx")
+#endif
+#define DONT_SAVE_REGS __attribute__((no_callee_saved_registers))
+#define SAVE_REGS __attribute__((no_caller_saved_registers))
+
+typedef DONT_SAVE_REGS void (*op_t)(void *next);
+
+extern int accumulator;
+
+static DONT_SAVE_REGS void end(void *next)
+{
+}
+
+/* inc doesn't have any callee saved registers. */
+
+/*
+**inc:
+**.LFB[0-9]+:
+** .cfi_startproc
+** addl \$1, accumulator\(%rip\)
+** movq \(%rdi\), %rax
+** addq \$8, %rdi
+** jmp \*%rax
+** .cfi_endproc
+**...
+*/
+
+static DONT_SAVE_REGS void inc(void *next)
+{
+ accumulator += 1;
+ CLOBBER;
+ NEXT;
+}
+
+/* dec doesn't have any callee saved registers. */
+
+/*
+**dec:
+**.LFB[0-9]+:
+** .cfi_startproc
+** subl \$1, accumulator\(%rip\)
+** movq \(%rdi\), %rax
+** addq \$8, %rdi
+** jmp \*%rax
+** .cfi_endproc
+**...
+*/
+
+static DONT_SAVE_REGS void dec(void *next)
+{
+ accumulator -= 1;
+ CLOBBER;
+ NEXT;
+}
+
+op_t code[] = { inc, inc, dec, end, };
+
+/* start must save and restore all caller saved registers. */
+
+/*
+**start:
+**.LFB[0-9]+:
+** .cfi_startproc
+** subq \$376, %rsp
+**...
+** movq %rax, 256\(%rsp\)
+** movq %rdx, 264\(%rsp\)
+** movq %rcx, 272\(%rsp\)
+** movq %rbx, 280\(%rsp\)
+** movq %rsi, 288\(%rsp\)
+** movq %rdi, 296\(%rsp\)
+**...
+** movl \$code\+8, %edi
+** movq %rbp, 304\(%rsp\)
+** movq %r8, 312\(%rsp\)
+** movq %r9, 320\(%rsp\)
+** movq %r10, 328\(%rsp\)
+** movq %r11, 336\(%rsp\)
+** movq %r12, 344\(%rsp\)
+** movq %r13, 352\(%rsp\)
+** movq %r14, 360\(%rsp\)
+** movq %r15, 368\(%rsp\)
+** movaps %xmm0, \(%rsp\)
+** movaps %xmm1, 16\(%rsp\)
+** movaps %xmm2, 32\(%rsp\)
+** movaps %xmm3, 48\(%rsp\)
+** movaps %xmm4, 64\(%rsp\)
+** movaps %xmm5, 80\(%rsp\)
+** movaps %xmm6, 96\(%rsp\)
+** movaps %xmm7, 112\(%rsp\)
+** movaps %xmm8, 128\(%rsp\)
+** movaps %xmm9, 144\(%rsp\)
+** movaps %xmm10, 160\(%rsp\)
+** movaps %xmm11, 176\(%rsp\)
+** movaps %xmm12, 192\(%rsp\)
+** movaps %xmm13, 208\(%rsp\)
+** movaps %xmm14, 224\(%rsp\)
+** movaps %xmm15, 240\(%rsp\)
+**...
+** call \*code\(%rip\)
+** movaps \(%rsp\), %xmm0
+** movaps 16\(%rsp\), %xmm1
+** movaps 32\(%rsp\), %xmm2
+** movaps 48\(%rsp\), %xmm3
+** movaps 64\(%rsp\), %xmm4
+** movaps 80\(%rsp\), %xmm5
+** movaps 96\(%rsp\), %xmm6
+** movaps 112\(%rsp\), %xmm7
+** movaps 128\(%rsp\), %xmm8
+** movaps 144\(%rsp\), %xmm9
+** movaps 160\(%rsp\), %xmm10
+** movaps 176\(%rsp\), %xmm11
+** movaps 192\(%rsp\), %xmm12
+** movaps 208\(%rsp\), %xmm13
+** movq 256\(%rsp\), %rax
+** movq 264\(%rsp\), %rdx
+** movq 272\(%rsp\), %rcx
+** movq 280\(%rsp\), %rbx
+** movq 288\(%rsp\), %rsi
+** movq 296\(%rsp\), %rdi
+** movq 304\(%rsp\), %rbp
+** movq 312\(%rsp\), %r8
+** movq 320\(%rsp\), %r9
+** movq 328\(%rsp\), %r10
+** movq 336\(%rsp\), %r11
+** movq 344\(%rsp\), %r12
+** movq 352\(%rsp\), %r13
+** movq 360\(%rsp\), %r14
+** movaps 224\(%rsp\), %xmm14
+** movq 368\(%rsp\), %r15
+** movaps 240\(%rsp\), %xmm15
+** addq \$376, %rsp
+**...
+** ret
+** .cfi_endproc
+**...
+*/
+
+/* This function should have normal ABI to interoperate with others */
+SAVE_REGS void start()
+{
+ void *next = code;
+
+ // musttail doesn't work here because the registers need to be restored
+ code[0](code + 1);
+}
--- /dev/null
+/* { dg-do compile { target { *-*-linux* && maybe_x32 } } } */
+/* { dg-options "-O2 -mx32 -fno-pic -mtune=generic -msse2 -mno-apxf -mtune-ctrl=prologue_using_move,epilogue_using_move" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.} } } */
+
+/* end must be empty. */
+
+/*
+**end:
+**.LFB[0-9]+:
+** .cfi_startproc
+** ret
+** .cfi_endproc
+**...
+*/
+
+/* inc doesn't have any callee saved registers. */
+
+/*
+**inc:
+**.LFB[0-9]+:
+** .cfi_startproc
+** addl \$1, accumulator\(%rip\)
+** movq %rdi, %rax
+** movl \(%eax\), %eax
+** leal 4\(%rdi\), %edi
+** jmp \*%rax
+** .cfi_endproc
+**...
+*/
+
+/* dec doesn't have any callee saved registers. */
+
+/*
+**dec:
+**.LFB[0-9]+:
+** .cfi_startproc
+** subl \$1, accumulator\(%rip\)
+** movq %rdi, %rax
+** movl \(%eax\), %eax
+** leal 4\(%rdi\), %edi
+** jmp \*%rax
+** .cfi_endproc
+**...
+*/
+
+/* start must save and restore all caller saved registers. */
+
+/*
+**start:
+**.LFB[0-9]+:
+** .cfi_startproc
+** subl \$376, %esp
+**...
+** movq %rax, 256\(%rsp\)
+** movq %rdx, 264\(%rsp\)
+** movq %rcx, 272\(%rsp\)
+** movq %rbx, 280\(%rsp\)
+** movq %rsi, 288\(%rsp\)
+** movq %rdi, 296\(%rsp\)
+**...
+** movl \$code\+4, %edi
+** movq %rbp, 304\(%rsp\)
+** movq %r8, 312\(%rsp\)
+** movq %r9, 320\(%rsp\)
+** movq %r10, 328\(%rsp\)
+** movq %r11, 336\(%rsp\)
+** movq %r12, 344\(%rsp\)
+** movq %r13, 352\(%rsp\)
+** movq %r14, 360\(%rsp\)
+** movq %r15, 368\(%rsp\)
+** movaps %xmm0, \(%rsp\)
+** movaps %xmm1, 16\(%rsp\)
+** movaps %xmm2, 32\(%rsp\)
+** movaps %xmm3, 48\(%rsp\)
+** movaps %xmm4, 64\(%rsp\)
+** movaps %xmm5, 80\(%rsp\)
+** movaps %xmm6, 96\(%rsp\)
+** movaps %xmm7, 112\(%rsp\)
+** movaps %xmm8, 128\(%rsp\)
+** movaps %xmm9, 144\(%rsp\)
+** movaps %xmm10, 160\(%rsp\)
+** movaps %xmm11, 176\(%rsp\)
+** movaps %xmm12, 192\(%rsp\)
+** movaps %xmm13, 208\(%rsp\)
+** movaps %xmm14, 224\(%rsp\)
+** movaps %xmm15, 240\(%rsp\)
+**...
+** movl code\(%rip\), %ebp
+** call \*%rbp
+** movaps \(%rsp\), %xmm0
+** movaps 16\(%rsp\), %xmm1
+** movaps 32\(%rsp\), %xmm2
+** movaps 48\(%rsp\), %xmm3
+** movaps 64\(%rsp\), %xmm4
+** movaps 80\(%rsp\), %xmm5
+** movaps 96\(%rsp\), %xmm6
+** movaps 112\(%rsp\), %xmm7
+** movaps 128\(%rsp\), %xmm8
+** movaps 144\(%rsp\), %xmm9
+** movaps 160\(%rsp\), %xmm10
+** movaps 176\(%rsp\), %xmm11
+** movaps 192\(%rsp\), %xmm12
+** movaps 208\(%rsp\), %xmm13
+** movaps 224\(%rsp\), %xmm14
+** movaps 240\(%rsp\), %xmm15
+** movq 256\(%rsp\), %rax
+** movq 264\(%rsp\), %rdx
+** movq 272\(%rsp\), %rcx
+** movq 280\(%rsp\), %rbx
+** movq 288\(%rsp\), %rsi
+** movq 296\(%rsp\), %rdi
+** movq 304\(%rsp\), %rbp
+** movq 312\(%rsp\), %r8
+** movq 320\(%rsp\), %r9
+** movq 328\(%rsp\), %r10
+** movq 336\(%rsp\), %r11
+** movq 344\(%rsp\), %r12
+** movq 352\(%rsp\), %r13
+** movq 360\(%rsp\), %r14
+** movq 368\(%rsp\), %r15
+** addl \$376, %esp
+**...
+** ret
+** .cfi_endproc
+**...
+*/
+
+#include "no-callee-saved-19a.c"
--- /dev/null
+/* { dg-do compile { target { *-*-linux* && ia32 } } } */
+/* { dg-options "-O2 -fno-pic -mtune=generic -msse2 -mtune-ctrl=prologue_using_move,epilogue_using_move" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.} } } */
+
+/* end must be empty. */
+
+/*
+**end:
+**.LFB[0-9]+:
+** .cfi_startproc
+** ret
+** .cfi_endproc
+**...
+*/
+
+/* inc doesn't have any callee saved registers. */
+
+/*
+**inc:
+**.LFB[0-9]+:
+** .cfi_startproc
+** addl \$1, accumulator
+** movl 4\(%esp\), %eax
+** leal 4\(%eax\), %edx
+** movl %edx, 4\(%esp\)
+** jmp \*\(%eax\)
+** .cfi_endproc
+**...
+*/
+
+/* dec doesn't have any callee saved registers. */
+
+/*
+**dec:
+**.LFB[0-9]+:
+** .cfi_startproc
+** subl \$1, accumulator
+** movl 4\(%esp\), %eax
+** leal 4\(%eax\), %edx
+** movl %edx, 4\(%esp\)
+** jmp \*\(%eax\)
+** .cfi_endproc
+**...
+*/
+
+/* start must save and restore all caller saved registers. */
+
+/*
+**start:
+**.LFB[0-9]+:
+** .cfi_startproc
+**...
+** movl %eax, 140\(%esp\)
+** movl %edx, 144\(%esp\)
+** movl %ecx, 148\(%esp\)
+** movl %ebx, 152\(%esp\)
+** movl %esi, 156\(%esp\)
+** movl %edi, 160\(%esp\)
+** movl %ebp, 164\(%esp\)
+** movaps %xmm0, 12\(%esp\)
+** movaps %xmm1, 28\(%esp\)
+** movaps %xmm2, 44\(%esp\)
+** movaps %xmm3, 60\(%esp\)
+** movaps %xmm4, 76\(%esp\)
+** movaps %xmm5, 92\(%esp\)
+** movaps %xmm6, 108\(%esp\)
+** movaps %xmm7, 124\(%esp\)
+**...
+** pushl \$code\+4
+**...
+** call \*code
+** movaps 16\(%esp\), %xmm0
+** movaps 32\(%esp\), %xmm1
+** movaps 48\(%esp\), %xmm2
+** movaps 64\(%esp\), %xmm3
+** movaps 80\(%esp\), %xmm4
+** movaps 96\(%esp\), %xmm5
+** movaps 112\(%esp\), %xmm6
+** movaps 128\(%esp\), %xmm7
+** movl 144\(%esp\), %eax
+** movl 148\(%esp\), %edx
+** movl 152\(%esp\), %ecx
+** movl 156\(%esp\), %ebx
+** movl 160\(%esp\), %esi
+** movl 164\(%esp\), %edi
+** movl 168\(%esp\), %ebp
+**...
+** ret
+** .cfi_endproc
+**...
+*/
+
+#include "no-callee-saved-19a.c"
--- /dev/null
+/* { dg-do compile { target { *-*-linux* && lp64 } } } */
+/* { dg-options "-O2 -fno-pic -mtune=generic -msse2 -mapxf -mtune-ctrl=prologue_using_move,epilogue_using_move" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.} } } */
+
+/* end must be empty. */
+
+/*
+**end:
+**.LFB[0-9]+:
+** .cfi_startproc
+** ret
+** .cfi_endproc
+**...
+*/
+
+/* inc doesn't have any callee saved registers. */
+
+/*
+**inc:
+**.LFB[0-9]+:
+** .cfi_startproc
+** addl \$1, accumulator\(%rip\)
+** movq \(%rdi\), %rax
+** addq \$8, %rdi
+** jmp \*%rax
+** .cfi_endproc
+**...
+*/
+
+/* dec doesn't have any callee saved registers. */
+
+/*
+**dec:
+**.LFB[0-9]+:
+** .cfi_startproc
+** subl \$1, accumulator\(%rip\)
+** movq \(%rdi\), %rax
+** addq \$8, %rdi
+** jmp \*%rax
+** .cfi_endproc
+**...
+*/
+
+/* start must save and restore all caller saved registers. */
+
+/*
+**start:
+**.LFB[0-9]+:
+** .cfi_startproc
+** subq \$504, %rsp
+**...
+** movq %rax, 256\(%rsp\)
+** movq %rdx, 264\(%rsp\)
+** movq %rcx, 272\(%rsp\)
+** movq %rbx, 280\(%rsp\)
+** movq %rsi, 288\(%rsp\)
+** movq %rdi, 296\(%rsp\)
+**...
+** movl \$code\+8, %edi
+** movq %rbp, 304\(%rsp\)
+** movq %r8, 312\(%rsp\)
+** movq %r9, 320\(%rsp\)
+** movq %r10, 328\(%rsp\)
+** movq %r11, 336\(%rsp\)
+** movq %r12, 344\(%rsp\)
+** movq %r13, 352\(%rsp\)
+** movq %r14, 360\(%rsp\)
+** movq %r15, 368\(%rsp\)
+** movq %r16, 376\(%rsp\)
+** movq %r17, 384\(%rsp\)
+** movq %r18, 392\(%rsp\)
+** movq %r19, 400\(%rsp\)
+** movq %r20, 408\(%rsp\)
+** movq %r21, 416\(%rsp\)
+** movq %r22, 424\(%rsp\)
+** movq %r23, 432\(%rsp\)
+** movq %r24, 440\(%rsp\)
+** movq %r25, 448\(%rsp\)
+** movq %r26, 456\(%rsp\)
+** movq %r27, 464\(%rsp\)
+** movq %r28, 472\(%rsp\)
+** movq %r29, 480\(%rsp\)
+** movq %r30, 488\(%rsp\)
+** movq %r31, 496\(%rsp\)
+**...
+** movaps %xmm0, \(%rsp\)
+** movaps %xmm1, 16\(%rsp\)
+** movaps %xmm2, 32\(%rsp\)
+** movaps %xmm3, 48\(%rsp\)
+** movaps %xmm4, 64\(%rsp\)
+** movaps %xmm5, 80\(%rsp\)
+** movaps %xmm6, 96\(%rsp\)
+** movaps %xmm7, 112\(%rsp\)
+** movaps %xmm8, 128\(%rsp\)
+** movaps %xmm9, 144\(%rsp\)
+** movaps %xmm10, 160\(%rsp\)
+** movaps %xmm11, 176\(%rsp\)
+** movaps %xmm12, 192\(%rsp\)
+** movaps %xmm13, 208\(%rsp\)
+** movaps %xmm14, 224\(%rsp\)
+** movaps %xmm15, 240\(%rsp\)
+**...
+** call \*code\(%rip\)
+** movaps \(%rsp\), %xmm0
+** movaps 16\(%rsp\), %xmm1
+** movaps 32\(%rsp\), %xmm2
+** movaps 48\(%rsp\), %xmm3
+** movaps 64\(%rsp\), %xmm4
+** movaps 80\(%rsp\), %xmm5
+** movaps 96\(%rsp\), %xmm6
+** movaps 112\(%rsp\), %xmm7
+** movaps 128\(%rsp\), %xmm8
+** movaps 144\(%rsp\), %xmm9
+** movaps 160\(%rsp\), %xmm10
+** movaps 176\(%rsp\), %xmm11
+** movaps 192\(%rsp\), %xmm12
+** movaps 208\(%rsp\), %xmm13
+** movq 256\(%rsp\), %rax
+** movq 264\(%rsp\), %rdx
+** movq 272\(%rsp\), %rcx
+** movq 280\(%rsp\), %rbx
+** movq 288\(%rsp\), %rsi
+** movq 296\(%rsp\), %rdi
+** movq 304\(%rsp\), %rbp
+** movq 312\(%rsp\), %r8
+** movq 320\(%rsp\), %r9
+** movq 328\(%rsp\), %r10
+** movq 336\(%rsp\), %r11
+** movq 344\(%rsp\), %r12
+** movq 352\(%rsp\), %r13
+** movq 360\(%rsp\), %r14
+** movq 368\(%rsp\), %r15
+** movq 376\(%rsp\), %r16
+** movaps 224\(%rsp\), %xmm14
+** movaps 240\(%rsp\), %xmm15
+** movq 384\(%rsp\), %r17
+** movq 392\(%rsp\), %r18
+** movq 400\(%rsp\), %r19
+** movq 408\(%rsp\), %r20
+** movq 416\(%rsp\), %r21
+** movq 424\(%rsp\), %r22
+** movq 432\(%rsp\), %r23
+** movq 440\(%rsp\), %r24
+** movq 448\(%rsp\), %r25
+** movq 456\(%rsp\), %r26
+** movq 464\(%rsp\), %r27
+** movq 472\(%rsp\), %r28
+** movq 480\(%rsp\), %r29
+** movq 488\(%rsp\), %r30
+** movq 496\(%rsp\), %r31
+** addq \$504, %rsp
+**...
+** ret
+** .cfi_endproc
+**...
+*/
+
+#include "no-callee-saved-19a.c"
--- /dev/null
+/* { dg-do compile { target { *-*-linux* && maybe_x32 } } } */
+/* { dg-options "-O2 -mx32 -fno-pic -mtune=generic -msse2 -mapxf -mtune-ctrl=prologue_using_move,epilogue_using_move" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.} } } */
+
+/* end must be empty. */
+
+/*
+**end:
+**.LFB[0-9]+:
+** .cfi_startproc
+** ret
+** .cfi_endproc
+**...
+*/
+
+/* inc doesn't have any callee saved registers. */
+
+/*
+**inc:
+**.LFB[0-9]+:
+** .cfi_startproc
+** addl \$1, accumulator\(%rip\)
+** movq %rdi, %rax
+** movl \(%eax\), %eax
+** leal 4\(%rdi\), %edi
+** jmp \*%rax
+** .cfi_endproc
+**...
+*/
+
+/* dec doesn't have any callee saved registers. */
+
+/*
+**dec:
+**.LFB[0-9]+:
+** .cfi_startproc
+** subl \$1, accumulator\(%rip\)
+** movq %rdi, %rax
+** movl \(%eax\), %eax
+** leal 4\(%rdi\), %edi
+** jmp \*%rax
+** .cfi_endproc
+**...
+*/
+
+/* start must save and restore all caller saved registers. */
+
+/*
+**start:
+**.LFB[0-9]+:
+** .cfi_startproc
+** subl \$504, %esp
+**...
+** movq %rax, 256\(%rsp\)
+** movq %rdx, 264\(%rsp\)
+** movq %rcx, 272\(%rsp\)
+** movq %rbx, 280\(%rsp\)
+** movq %rsi, 288\(%rsp\)
+** movq %rdi, 296\(%rsp\)
+**...
+** movl \$code\+4, %edi
+** movq %rbp, 304\(%rsp\)
+** movq %r8, 312\(%rsp\)
+** movq %r9, 320\(%rsp\)
+** movq %r10, 328\(%rsp\)
+** movq %r11, 336\(%rsp\)
+** movq %r12, 344\(%rsp\)
+** movq %r13, 352\(%rsp\)
+** movq %r14, 360\(%rsp\)
+** movq %r15, 368\(%rsp\)
+** movq %r16, 376\(%rsp\)
+** movq %r17, 384\(%rsp\)
+** movq %r18, 392\(%rsp\)
+** movq %r19, 400\(%rsp\)
+** movq %r20, 408\(%rsp\)
+** movq %r21, 416\(%rsp\)
+** movq %r22, 424\(%rsp\)
+** movq %r23, 432\(%rsp\)
+** movq %r24, 440\(%rsp\)
+** movq %r25, 448\(%rsp\)
+** movq %r26, 456\(%rsp\)
+** movq %r27, 464\(%rsp\)
+** movq %r28, 472\(%rsp\)
+** movq %r29, 480\(%rsp\)
+** movq %r30, 488\(%rsp\)
+** movq %r31, 496\(%rsp\)
+**...
+** movl code\(%rip\), %ebp
+** movaps %xmm0, \(%rsp\)
+** movaps %xmm1, 16\(%rsp\)
+** movaps %xmm2, 32\(%rsp\)
+** movaps %xmm3, 48\(%rsp\)
+** movaps %xmm4, 64\(%rsp\)
+** movaps %xmm5, 80\(%rsp\)
+** movaps %xmm6, 96\(%rsp\)
+** movaps %xmm7, 112\(%rsp\)
+** movaps %xmm8, 128\(%rsp\)
+** movaps %xmm9, 144\(%rsp\)
+** movaps %xmm10, 160\(%rsp\)
+** movaps %xmm11, 176\(%rsp\)
+** movaps %xmm12, 192\(%rsp\)
+** movaps %xmm13, 208\(%rsp\)
+** movaps %xmm14, 224\(%rsp\)
+** movaps %xmm15, 240\(%rsp\)
+**...
+** call \*%rbp
+** movaps \(%rsp\), %xmm0
+** movaps 16\(%rsp\), %xmm1
+** movaps 32\(%rsp\), %xmm2
+** movaps 48\(%rsp\), %xmm3
+** movaps 64\(%rsp\), %xmm4
+** movaps 80\(%rsp\), %xmm5
+** movaps 96\(%rsp\), %xmm6
+** movaps 112\(%rsp\), %xmm7
+** movaps 128\(%rsp\), %xmm8
+** movaps 144\(%rsp\), %xmm9
+** movaps 160\(%rsp\), %xmm10
+** movaps 176\(%rsp\), %xmm11
+** movaps 192\(%rsp\), %xmm12
+** movaps 208\(%rsp\), %xmm13
+** movaps 224\(%rsp\), %xmm14
+** movaps 240\(%rsp\), %xmm15
+** movq 256\(%rsp\), %rax
+** movq 264\(%rsp\), %rdx
+** movq 272\(%rsp\), %rcx
+** movq 280\(%rsp\), %rbx
+** movq 288\(%rsp\), %rsi
+** movq 296\(%rsp\), %rdi
+** movq 304\(%rsp\), %rbp
+** movq 312\(%rsp\), %r8
+** movq 320\(%rsp\), %r9
+** movq 328\(%rsp\), %r10
+** movq 336\(%rsp\), %r11
+** movq 344\(%rsp\), %r12
+** movq 352\(%rsp\), %r13
+** movq 360\(%rsp\), %r14
+** movq 368\(%rsp\), %r15
+** movq 376\(%rsp\), %r16
+** movq 384\(%rsp\), %r17
+** movq 392\(%rsp\), %r18
+** movq 400\(%rsp\), %r19
+** movq 408\(%rsp\), %r20
+** movq 416\(%rsp\), %r21
+** movq 424\(%rsp\), %r22
+** movq 432\(%rsp\), %r23
+** movq 440\(%rsp\), %r24
+** movq 448\(%rsp\), %r25
+** movq 456\(%rsp\), %r26
+** movq 464\(%rsp\), %r27
+** movq 472\(%rsp\), %r28
+** movq 480\(%rsp\), %r29
+** movq 488\(%rsp\), %r30
+** movq 496\(%rsp\), %r31
+** addl \$504, %esp
+**...
+** ret
+** .cfi_endproc
+**...
+*/
+
+#include "no-callee-saved-19a.c"
__attribute__ ((no_callee_saved_registers, no_caller_saved_registers))
void
-foo (void) /* { dg-error "attributes are not compatible" } */
-{
+foo (void)
+{ /* { dg-error "attributes are not compatible" } */
}
--- /dev/null
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2" } */
+
+extern void boring(void);
+
+extern void continuation(void *, void *, void *, void *)
+ __attribute__((preserve_none));
+
+__attribute__((preserve_none))
+void entry(void *a, void *b, void *c, void *d)
+{
+ boring();
+ continuation(a, b, c, d);
+}
+
+/* { dg-final { scan-assembler-not "movq" } } */
+/* { dg-final { scan-assembler "jmp\[\\t \]+_?continuation" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef void (*fn_t) (void *) __attribute__ ((preserve_none));
+
+void
+foo (void *frame)
+{
+}
+
+fn_t func = foo; /* { dg-error "incompatible pointer type" } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef void (*fn_t) (void *) __attribute__ ((preserve_none));
+
+__attribute__ ((preserve_none))
+void
+foo (void *frame)
+{
+}
+
+fn_t func = foo;
--- /dev/null
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
+
+extern void bar (void) __attribute__ ((preserve_none));
+
+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 } } } } */
--- /dev/null
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
+
+typedef void (*fn_t) (void) __attribute__ ((preserve_none));
+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 } } } } */
--- /dev/null
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
+
+typedef void (*fn_t) (void) __attribute__ ((preserve_none));
+
+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 } } } } */
--- /dev/null
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mgeneral-regs-only -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
+
+extern void bar (void) __attribute__ ((preserve_none));
+
+__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 } } } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern void foo (void); /* { dg-note "previous declaration" } */
+
+__attribute__ ((preserve_none))
+void
+foo (void) /* { dg-error "conflicting types" } */
+{
+}
+
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern void foo (void) __attribute__ ((preserve_none)); /* { dg-note "previous declaration" } */
+
+void
+foo (void) /* { dg-error "conflicting types" } */
+{
+}
+
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
+/* { dg-additional-options "-fno-PIE" { target ia32 } } */
+
+extern void foo (void);
+
+__attribute__ ((preserve_none))
+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" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
+/* { dg-additional-options "-fno-PIE" { target ia32 } } */
+
+extern void bar (void) __attribute__ ((preserve_none));
+
+__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" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+typedef void (*fn_t) (void *) __attribute__ ((preserve_none));
+
+__attribute__ ((no_callee_saved_registers))
+void
+foo (void *frame)
+{
+}
+
+fn_t func = foo; /* { dg-error "incompatible pointer type" "" { target { ! ia32 } } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
+/* { dg-additional-options "-fno-PIE" { target ia32 } } */
+
+typedef void (*fn_t) (void) __attribute__ ((no_callee_saved_registers));
+extern fn_t bar;
+
+__attribute__ ((preserve_none))
+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 \]+" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
+
+typedef void (*fn_t) (void) __attribute__ ((preserve_none));
+
+__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 \]+" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move" } */
+/* { dg-additional-options "-fno-PIE" { target ia32 } } */
+
+extern void foo (void) __attribute__ ((no_caller_saved_registers));
+
+__attribute__ ((preserve_none))
+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" } } */
--- /dev/null
+/* { 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__ ((preserve_none));
+
+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 } } } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+__attribute__ ((preserve_none, no_callee_saved_registers))
+void
+foo (void)
+{ /* { dg-error "attributes are not compatible" } */
+}
--- /dev/null
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -fno-pic -mtune=generic -msse2 -mno-apxf -mtune-ctrl=prologue_using_move,epilogue_using_move" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.} } } */
+
+/*
+**entry:
+**.LFB[0-9]+:
+** .cfi_startproc
+** movq %rdi, %r12
+** movq %rsi, %r13
+** movq %rdx, %r14
+** movq %rcx, %r15
+** jmp continuation
+** .cfi_endproc
+**...
+*/
+
+extern void continuation (void *, void *, void *, void *)
+ __attribute__ ((preserve_none));
+
+__attribute__ ((no_callee_saved_registers))
+void
+entry (void *a, void *b, void *c, void *d)
+{
+ continuation (a, b, c, d);
+}
--- /dev/null
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -fno-pic -mtune=generic -msse2 -mno-apxf -mtune-ctrl=prologue_using_move,epilogue_using_move" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.} } } */
+
+/*
+**entry:
+**.LFB[0-9]+:
+** .cfi_startproc
+** movq %r15, %rcx
+** movq %r14, %rdx
+** movq %r13, %rsi
+** movq %r12, %rdi
+** jmp continuation
+** .cfi_endproc
+**...
+*/
+
+extern void continuation (void *, void *, void *, void *)
+ __attribute__ ((no_callee_saved_registers));
+
+__attribute__ ((preserve_none))
+void
+entry (void *a, void *b, void *c, void *d)
+{
+ continuation(a, b, c, d);
+}
--- /dev/null
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -fno-pic -mtune=generic -msse2 -mno-apxf -mtune-ctrl=prologue_using_move,epilogue_using_move" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.} } } */
+
+/*
+**entry:
+**.LFB[0-9]+:
+** .cfi_startproc
+**...
+** movl %edi, %r12d
+** movl %esi, %r13d
+** movl %edx, %r14d
+** pushq \$-559038737
+**...
+** movl %ecx, %r15d
+** movl %r9d, %esi
+** movl %r8d, %edi
+** xorl %eax, %eax
+**...
+** call continuation
+**...
+*/
+
+extern void continuation (int, int, int, int, int, int, ...)
+ __attribute__ ((preserve_none));
+
+__attribute__ ((no_callee_saved_registers))
+void
+entry (int arg1, int arg2, int arg3, int arg4, int arg5, int arg6)
+{
+ continuation (arg1, arg2, arg3, arg4, arg5, arg6, 0xdeadbeef);
+}
--- /dev/null
+/* { dg-do run { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -fno-pic -mtune=generic -msse2 -mno-apxf -mtune-ctrl=prologue_using_move,epilogue_using_move" } */
+
+#include <stdlib.h>
+
+__attribute__ ((preserve_none, weak))
+void
+continuation (int arg1, int arg2, int arg3, int arg4, int arg5, int arg6)
+{
+ if (arg1 != 17)
+ abort ();
+ if (arg2 != 8)
+ abort ();
+ if (arg3 != 20)
+ abort ();
+ if (arg4 != -3)
+ abort ();
+ if (arg5 != -4)
+ abort ();
+ if (arg6 != 26)
+ abort ();
+}
+
+__attribute__ ((no_callee_saved_registers, weak))
+void
+entry (int arg1, int arg2, int arg3, int arg4, int arg5, int arg6)
+{
+ if (arg1 != 17)
+ abort ();
+ if (arg2 != 8)
+ abort ();
+ if (arg3 != 20)
+ abort ();
+ if (arg4 != -3)
+ abort ();
+ if (arg5 != -4)
+ abort ();
+ if (arg6 != 26)
+ abort ();
+ continuation (arg1, arg2, arg3, arg4, arg5, arg6);
+}
+
+int
+main (void)
+{
+ entry (17, 8, 20, -3, -4, 26);
+ return 0;
+}
--- /dev/null
+/* { dg-do run { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -fno-pic -mtune=generic -msse2 -mno-apxf -mtune-ctrl=prologue_using_move,epilogue_using_move" } */
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+__attribute__ ((preserve_none, weak))
+void
+continuation (int arg1, int arg2, int arg3, int arg4, int arg5, int arg6,
+ ...)
+{
+ int a;
+ va_list va_arglist;
+ va_start (va_arglist, arg6);
+ if (arg1 != 17)
+ abort ();
+ if (arg2 != 8)
+ abort ();
+ if (arg3 != 20)
+ abort ();
+ if (arg4 != -3)
+ abort ();
+ if (arg5 != -4)
+ abort ();
+ if (arg6 != 26)
+ abort ();
+ a = va_arg (va_arglist, int);
+ if (a != 0xdeadbeef)
+ abort ();
+ va_end (va_arglist);
+}
+
+__attribute__ ((no_callee_saved_registers, weak))
+void
+entry (int arg1, int arg2, int arg3, int arg4, int arg5, int arg6)
+{
+ if (arg1 != 17)
+ abort ();
+ if (arg2 != 8)
+ abort ();
+ if (arg3 != 20)
+ abort ();
+ if (arg4 != -3)
+ abort ();
+ if (arg5 != -4)
+ abort ();
+ if (arg6 != 26)
+ abort ();
+ continuation (arg1, arg2, arg3, arg4, arg5, arg6, 0xdeadbeef);
+}
+
+int
+main (void)
+{
+ entry (17, 8, 20, -3, -4, 26);
+ return 0;
+}
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move -fomit-frame-pointer -mnoreturn-no-callee-saved-registers" } */
+
+extern void bar (void) __attribute__ ((preserve_none));
+extern void fn (void) __attribute__ ((noreturn));
+
+__attribute__ ((noreturn))
+void
+foo (void)
+{
+ bar ();
+ fn ();
+}
+
+/* { dg-final { scan-assembler-not "push\[^\n\r\]*(?:\[abcd\]x|\[sd\]i|sp|r\[0-9\]|\[xyz\]mm)" } } */
+/* { dg-final { scan-assembler-not "pop\[^\n\r\]*(?:\[abcd\]x|\[sd\]i|sp|r\[0-9\]|\[xyz\]mm)" } } */
+/* { dg-final { scan-assembler-not "jmp\[\\t \]+_?bar" } } */
+/* { dg-final { scan-assembler "call\[\\t \]+_?bar" } } */
--- /dev/null
+/* { dg-do compile { target { *-*-linux* && lp64 } } } */
+/* { dg-options "-O2 -fno-pic -mtune=generic -msse2 -mno-apxf -mtune-ctrl=prologue_using_move,epilogue_using_move" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.} } } */
+
+/*
+**entry:
+**.LFB[0-9]+:
+** .cfi_startproc
+** subq \$8, %rsp
+** .cfi_def_cfa_offset 16
+** call boring
+** addq \$8, %rsp
+** .cfi_def_cfa_offset 8
+** jmp \*continuation\(%rip\)
+** .cfi_endproc
+**...
+*/
+
+extern void boring (void);
+
+extern void (*continuation) (void *, void *, void *, void *)
+ __attribute__ ((preserve_none));
+
+__attribute__ ((preserve_none))
+void
+entry (void *a, void *b, void *c, void *d)
+{
+ boring ();
+ continuation (a, b, c, d);
+}
--- /dev/null
+/* { dg-do compile { target { *-*-linux* && maybe_x32 } } } */
+/* { dg-options "-O2 -mx32 -fno-pic -mtune=generic -msse2 -mno-apxf -mtune-ctrl=prologue_using_move,epilogue_using_move" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.} } } */
+
+/*
+**entry:
+**.LFB[0-9]+:
+** .cfi_startproc
+** subl \$8, %esp
+** .cfi_def_cfa_offset 16
+** call boring
+** movl continuation\(%rip\), %eax
+** addl \$8, %esp
+** .cfi_def_cfa_offset 8
+** jmp \*%rax
+** .cfi_endproc
+**...
+*/
+
+#include "preserve-none-30a.c"
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move -fomit-frame-pointer -mnoreturn-no-callee-saved-registers" } */
+
+typedef void (*fn_t) (void) __attribute__ ((preserve_none));
+extern fn_t bar;
+extern void fn (void) __attribute__ ((noreturn));
+
+__attribute__ ((noreturn))
+void
+foo (void)
+{
+ bar ();
+ fn ();
+}
+
+/* { dg-final { scan-assembler-not "push\[^\n\r\]*(?:\[abcd\]x|\[sd\]i|sp|r\[0-9\]|\[xyz\]mm)" } } */
+/* { dg-final { scan-assembler-not "pop\[^\n\r\]*(?:\[abcd\]x|\[sd\]i|sp|r\[0-9\]|\[xyz\]mm)" } } */
+/* { dg-final { scan-assembler-not "jmp" } } */
+/* { dg-final { scan-assembler "call\[\\t \]+" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move -fomit-frame-pointer -mnoreturn-no-callee-saved-registers" } */
+
+typedef void (*fn_t) (void) __attribute__ ((preserve_none));
+extern void fn (void) __attribute__ ((noreturn));
+
+__attribute__ ((noreturn))
+void
+foo (fn_t bar)
+{
+ bar ();
+ fn ();
+}
+
+/* { dg-final { scan-assembler-not "push\[^\n\r\]*(?:\[abcd\]x|\[sd\]i|sp|r\[0-9\]|\[xyz\]mm)" } } */
+/* { dg-final { scan-assembler-not "pop\[^\n\r\]*(?:\[abcd\]x|\[sd\]i|sp|r\[0-9\]|\[xyz\]mm)" } } */
+/* { dg-final { scan-assembler-not "jmp" } } */
+/* { dg-final { scan-assembler "call\[\\t \]+" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move -fomit-frame-pointer" } */
+
+extern int bar (int)
+#ifndef __x86_64__
+__attribute__ ((regparm(3)))
+#endif
+;
+
+__attribute__ ((preserve_none))
+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" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move -fomit-frame-pointer" } */
+
+extern int bar (int) __attribute__ ((no_caller_saved_registers))
+#ifndef __x86_64__
+__attribute__ ((regparm(3)))
+#endif
+;
+
+__attribute__ ((preserve_none))
+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" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+__attribute__ ((preserve_none, no_caller_saved_registers))
+void
+foo (void)
+{ /* { dg-error "attributes are not compatible" } */
+}
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -mgeneral-regs-only" } */
+
+__attribute__ ((preserve_none, interrupt))
+void
+foo (void *frame) /* { dg-error "attributes are not compatible" } */
+{
+}