On Fri, Apr 18, 2025 at 7:10 PM H.J. Lu <hjl.to...@gmail.com> wrote: > > Add preserve_none attribute which is similar to no_callee_saved_registers > attribute, except on x86-64, r12, r13, r14, r15, rdi and rsi registers are Could you split preserve_none into a separate patch, It looks like it's different from clang's preserve_none[1], can we make them align? [1] https://clang.llvm.org/docs/AttributeReference.html
> used for integer parameter passing. This can be used in an interpreter > to avoid saving/restoring the registers in functions which processing > byte codes. It improved the pystones benchmark by 6-7%: > > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119628#c15 > > Remove -mgeneral-regs-only restriction on no_caller_saved_registers > attribute. Only SSE is allowed since SSE XMM register load preserves > the upper bits in YMM/ZMM register while YMM register load zeros the > upper 256 bits of ZMM register, and preserving 32 ZMM registers can > be quite expensive. > > gcc/ > > PR target/119628 > * config/i386/i386-expand.cc (ix86_expand_call): Call > ix86_type_preserve_none_attribute_p instead of looking up > no_callee_saved_registers attribute. > * config/i386/i386-options.cc (ix86_set_func_type): Call > ix86_type_preserve_none_attribute_p instead of looking up > no_callee_saved_registers attribute. Check preserve_none > attribute for interrupt attribute. Don't check > no_caller_saved_registers and no_callee_saved_registers conflicts > here. > (ix86_set_current_function): Allow SSE with > no_caller_saved_registers attribute. > (ix86_handle_call_saved_registers_attribute): Check preserve_none, > no_callee_saved_registers and no_caller_saved_registers conflicts. > (ix86_gnu_attributes): Add preserve_none attribute. > * config/i386/i386-protos.h (ix86_type_preserve_none_attribute_p): > New. > * config/i386/i386.cc > (x86_64_preserve_none_int_parameter_registers): New. > (ix86_using_red_zone): Don't use red-zone when there are no > caller-saved registers with SSE. > (ix86_type_preserve_none_attribute_p): New. > (ix86_function_ok_for_sibcall): Call > ix86_type_preserve_none_attribute_p instead of looking up > no_callee_saved_registers attribute. > (ix86_comp_type_attributes): Call > ix86_type_preserve_none_attribute_p instead of looking up > no_callee_saved_registers attribute. Return 0 if preserve_none > attribute doesn't match in 64-bit mode. > (ix86_function_arg_regno_p): If preserve_none calling convention > is used, use x86_64_preserve_none_int_parameter_registers. > (ix86_call_abi_override): Also set the preserve_none_abi field. > (init_cumulative_args): Likewise. > (function_arg_64): Use x86_64_preserve_none_int_parameter_registers > with preserve_none attribute. > (setup_incoming_varargs_64): Use > x86_64_preserve_none_int_parameter_registers with preserve_none > attribute. > (ix86_nsaved_sseregs): Allow saving XMM registers for > no_caller_saved_registers attribute. > (ix86_compute_frame_layout): Likewise. > (x86_this_parameter): Use > x86_64_preserve_none_int_parameter_registers with preserve_none > attribute. > * config/i386/i386.h (ix86_args): Add preserve_none_abi. > (call_saved_registers_type): Update comments for > TYPE_NO_CALLEE_SAVED_REGISTERS. > (machine_function): Add preserve_none_abi. > * doc/extend.texi: Add preserve_none attribute. Update > no_caller_saved_registers attribute to remove -mgeneral-regs-only > restriction. > > gcc/testsuite/ > > PR target/119628 > * gcc.target/i386/no-callee-saved-19a.c: New test. > * gcc.target/i386/no-callee-saved-19b.c: Likewise. > * gcc.target/i386/no-callee-saved-19c.c: Likewise. > * gcc.target/i386/no-callee-saved-19d.c: Likewise. > * gcc.target/i386/no-callee-saved-19e.c: Likewise. > * gcc.target/i386/preserve-none-1.c: Likewise. > * gcc.target/i386/preserve-none-2.c: Likewise. > * gcc.target/i386/preserve-none-3.c: Likewise. > * gcc.target/i386/preserve-none-4.c: Likewise. > * gcc.target/i386/preserve-none-5.c: Likewise. > * gcc.target/i386/preserve-none-6.c: Likewise. > * gcc.target/i386/preserve-none-7.c: Likewise. > * gcc.target/i386/preserve-none-8.c: Likewise. > * gcc.target/i386/preserve-none-9.c: Likewise. > * gcc.target/i386/preserve-none-10.c: Likewise. > * gcc.target/i386/preserve-none-11.c: Likewise. > * gcc.target/i386/preserve-none-12.c: Likewise. > * gcc.target/i386/preserve-none-13.c: Likewise. > * gcc.target/i386/preserve-none-14.c: Likewise. > * gcc.target/i386/preserve-none-15.c: Likewise. > * gcc.target/i386/preserve-none-16.c: Likewise. > * gcc.target/i386/preserve-none-17.c: Likewise. > * gcc.target/i386/preserve-none-19.c: Likewise. > * gcc.target/i386/preserve-none-19.c: Likewise. > * gcc.target/i386/preserve-none-20.c: Likewise. > * gcc.target/i386/preserve-none-21.c: Likewise. > * gcc.target/i386/preserve-none-22.c: Likewise. > * gcc.target/i386/preserve-none-23.c: Likewise. > * gcc.target/i386/preserve-none-24.c: Likewise. > * gcc.target/i386/preserve-none-25.c: Likewise. > * gcc.target/i386/preserve-none-26.c: Likewise. > * gcc.target/i386/preserve-none-27.c: Likewise. > * gcc.target/i386/preserve-none-28.c: Likewise. > * gcc.target/i386/preserve-none-29.c: Likewise. > * gcc.target/i386/preserve-none-30a.c: Likewise. > * gcc.target/i386/preserve-none-30b.c: Likewise. > > Signed-off-by: H.J. Lu <hjl.to...@gmail.com> > --- > gcc/config/i386/i386-expand.cc | 6 +- > gcc/config/i386/i386-options.cc | 90 ++++++++-- > gcc/config/i386/i386-protos.h | 1 + > gcc/config/i386/i386.cc | 105 +++++++++-- > gcc/config/i386/i386.h | 7 +- > gcc/doc/extend.texi | 14 +- > .../gcc.target/i386/no-callee-saved-19a.c | 166 ++++++++++++++++++ > .../gcc.target/i386/no-callee-saved-19b.c | 129 ++++++++++++++ > .../gcc.target/i386/no-callee-saved-19c.c | 94 ++++++++++ > .../gcc.target/i386/no-callee-saved-19d.c | 159 +++++++++++++++++ > .../gcc.target/i386/no-callee-saved-19e.c | 162 +++++++++++++++++ > .../gcc.target/i386/no-callee-saved-3.c | 4 +- > .../gcc.target/i386/preserve-none-1.c | 17 ++ > .../gcc.target/i386/preserve-none-10.c | 11 ++ > .../gcc.target/i386/preserve-none-11.c | 12 ++ > .../gcc.target/i386/preserve-none-12.c | 49 ++++++ > .../gcc.target/i386/preserve-none-13.c | 50 ++++++ > .../gcc.target/i386/preserve-none-14.c | 49 ++++++ > .../gcc.target/i386/preserve-none-15.c | 46 +++++ > .../gcc.target/i386/preserve-none-16.c | 11 ++ > .../gcc.target/i386/preserve-none-17.c | 10 ++ > .../gcc.target/i386/preserve-none-18.c | 17 ++ > .../gcc.target/i386/preserve-none-19.c | 17 ++ > .../gcc.target/i386/preserve-none-2.c | 12 ++ > .../gcc.target/i386/preserve-none-20.c | 18 ++ > .../gcc.target/i386/preserve-none-21.c | 16 ++ > .../gcc.target/i386/preserve-none-22.c | 17 ++ > .../gcc.target/i386/preserve-none-23.c | 51 ++++++ > .../gcc.target/i386/preserve-none-24.c | 8 + > .../gcc.target/i386/preserve-none-25.c | 27 +++ > .../gcc.target/i386/preserve-none-26.c | 27 +++ > .../gcc.target/i386/preserve-none-27.c | 33 ++++ > .../gcc.target/i386/preserve-none-28.c | 48 +++++ > .../gcc.target/i386/preserve-none-29.c | 57 ++++++ > .../gcc.target/i386/preserve-none-3.c | 18 ++ > .../gcc.target/i386/preserve-none-30a.c | 31 ++++ > .../gcc.target/i386/preserve-none-30b.c | 21 +++ > .../gcc.target/i386/preserve-none-4.c | 19 ++ > .../gcc.target/i386/preserve-none-5.c | 18 ++ > .../gcc.target/i386/preserve-none-6.c | 30 ++++ > .../gcc.target/i386/preserve-none-7.c | 30 ++++ > .../gcc.target/i386/preserve-none-8.c | 8 + > .../gcc.target/i386/preserve-none-9.c | 8 + > 43 files changed, 1675 insertions(+), 48 deletions(-) > create mode 100644 gcc/testsuite/gcc.target/i386/no-callee-saved-19a.c > create mode 100644 gcc/testsuite/gcc.target/i386/no-callee-saved-19b.c > create mode 100644 gcc/testsuite/gcc.target/i386/no-callee-saved-19c.c > create mode 100644 gcc/testsuite/gcc.target/i386/no-callee-saved-19d.c > create mode 100644 gcc/testsuite/gcc.target/i386/no-callee-saved-19e.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-1.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-10.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-11.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-12.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-13.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-14.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-15.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-16.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-17.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-18.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-19.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-2.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-20.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-21.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-22.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-23.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-24.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-25.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-26.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-27.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-28.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-29.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-3.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-30a.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-30b.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-4.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-5.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-6.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-7.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-8.c > create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-9.c > > diff --git a/gcc/config/i386/i386-expand.cc b/gcc/config/i386/i386-expand.cc > index cdfd94d3c73..0f12b34db6c 100644 > --- a/gcc/config/i386/i386-expand.cc > +++ b/gcc/config/i386/i386-expand.cc > @@ -10108,8 +10108,7 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx > callarg1, > 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_preserve_none_attribute_p (TREE_TYPE (fndecl))) > call_no_callee_saved_registers = true; > } > } > @@ -10120,8 +10119,7 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx > callarg1, > 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_preserve_none_attribute_p (TREE_TYPE (mem_expr))) > call_no_callee_saved_registers = true; > } > > diff --git a/gcc/config/i386/i386-options.cc b/gcc/config/i386/i386-options.cc > index 964449fa8cd..2238fb904f8 100644 > --- a/gcc/config/i386/i386-options.cc > +++ b/gcc/config/i386/i386-options.cc > @@ -3420,8 +3420,7 @@ ix86_set_func_type (tree fndecl) > 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", > - TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))) > + if (ix86_type_preserve_none_attribute_p (TREE_TYPE (fndecl))) > no_callee_saved_registers = TYPE_NO_CALLEE_SAVED_REGISTERS; > else if (ix86_noreturn_no_callee_saved_registers > && TREE_THIS_VOLATILE (fndecl) > @@ -3444,9 +3443,17 @@ ix86_set_func_type (tree fndecl) > "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 (lookup_attribute ("preserve_none", > + TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))) > + 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); > @@ -3473,16 +3480,8 @@ ix86_set_func_type (tree 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 > + = no_callee_saved_registers; > } > } > } > @@ -3671,11 +3670,21 @@ ix86_set_current_function (tree fndecl) > || (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) > @@ -4100,9 +4109,50 @@ ix86_handle_fndecl_attribute (tree *node, tree name, > tree args, int, > } > > 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; > } > > @@ -4264,6 +4314,8 @@ static const attribute_spec ix86_gnu_attributes[] = > 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, > diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h > index bea3fd4b2e2..f4190945e69 100644 > --- a/gcc/config/i386/i386-protos.h > +++ b/gcc/config/i386/i386-protos.h > @@ -280,6 +280,7 @@ extern tree ix86_valid_target_attribute_tree (tree, tree, > struct gcc_options *, > struct gcc_options *, bool); > extern unsigned int ix86_get_callcvt (const_tree); > +extern bool ix86_type_preserve_none_attribute_p (const_tree); > > #endif > > diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc > index 38df84f7db2..5ccb52188c7 100644 > --- a/gcc/config/i386/i386.cc > +++ b/gcc/config/i386/i386.cc > @@ -334,6 +334,14 @@ static int const > x86_64_ms_abi_int_parameter_registers[4] = > CX_REG, DX_REG, R8_REG, R9_REG > }; > > +/* The same 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 > @@ -459,7 +467,8 @@ int ix86_arch_specified; > 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 > @@ -470,7 +479,7 @@ ix86_using_red_zone (void) > { > 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 > @@ -897,6 +906,18 @@ x86_64_elf_unique_section (tree decl, int reloc) > default_unique_section (decl, reloc); > } > > +/* Return true if TYPE has no_callee_saved_registers or preserve_none > + attribute. */ > + > +bool > +ix86_type_preserve_none_attribute_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 > @@ -1021,8 +1042,7 @@ ix86_function_ok_for_sibcall (tree decl, tree exp) > if (cfun->machine->call_saved_registers != TYPE_NO_CALLEE_SAVED_REGISTERS > && (cfun->machine->call_saved_registers > != TYPE_NO_CALLEE_SAVED_REGISTERS_EXCEPT_BP) > - && lookup_attribute ("no_callee_saved_registers", > - TYPE_ATTRIBUTES (type))) > + && ix86_type_preserve_none_attribute_p (type)) > return false; > > /* If outgoing reg parm stack space changes, we cannot do sibcall. */ > @@ -1187,10 +1207,16 @@ 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))) > + if (ix86_type_preserve_none_attribute_p (type1) > + != ix86_type_preserve_none_attribute_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; > @@ -1552,7 +1578,9 @@ ix86_function_arg_regno_p (int regno) > if (call_abi == SYSV_ABI && regno == AX_REG) > return true; > > - if (call_abi == MS_ABI) > + if (cfun && cfun->machine->preserve_none_abi) > + 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; > @@ -1722,6 +1750,11 @@ void > ix86_call_abi_override (const_tree fndecl) > { > cfun->machine->call_abi = ix86_function_abi (fndecl); > + cfun->machine->preserve_none_abi > + = (fndecl > + && (lookup_attribute ("preserve_none", > + TYPE_ATTRIBUTES (TREE_TYPE (fndecl))) > + != nullptr)); > } > > /* Return 1 if pseudo register should be created and used to hold > @@ -1822,6 +1855,7 @@ init_cumulative_args (CUMULATIVE_ARGS *cum, /* > Argument info to initialize */ > > memset (cum, 0, sizeof (*cum)); > > + tree preserve_none_type; > if (fndecl) > { > target = cgraph_node::get (fndecl); > @@ -1830,12 +1864,24 @@ init_cumulative_args (CUMULATIVE_ARGS *cum, /* > Argument info to initialize */ > 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; > > @@ -3409,9 +3455,15 @@ function_arg_64 (const CUMULATIVE_ARGS *cum, > machine_mode mode, > 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); > } > > @@ -4576,6 +4628,12 @@ setup_incoming_varargs_64 (CUMULATIVE_ARGS *cum) > 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, > @@ -4583,8 +4641,7 @@ setup_incoming_varargs_64 (CUMULATIVE_ARGS *cum) > 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) > @@ -6779,7 +6836,9 @@ ix86_nsaved_sseregs (void) > 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)) > @@ -6967,12 +7026,18 @@ ix86_compute_frame_layout (void) > 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. > @@ -22833,7 +22898,9 @@ x86_this_parameter (tree function) > { > 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; > diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h > index 8507243d726..3b5c9520ddf 100644 > --- a/gcc/config/i386/i386.h > +++ b/gcc/config/i386/i386.h > @@ -1682,6 +1682,8 @@ typedef struct ix86_args { > 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; > > @@ -2782,7 +2784,7 @@ enum call_saved_registers_type > or "no_caller_saved_registers" attribute. */ > TYPE_NO_CALLER_SAVED_REGISTERS, > /* The current function is a function specified with the > - "no_callee_saved_registers" attribute. */ > + "no_callee_saved_registers"/"preserve_none" attribute. */ > TYPE_NO_CALLEE_SAVED_REGISTERS, Can we introduce a new member TYPE_PRESERVE_NONE instead of combining it with TYPE_NO_CALLEE_SAVED_REGISTERS. TYPE_PRESERVE_{MOST, ALL} can also be introduced if we want, and they should conflict with each other.(preserve_none should conflict no_callee_saved_registers) > /* The current function is a function specified with the "noreturn" > attribute. */ > @@ -2816,6 +2818,9 @@ struct GTY(()) machine_function { > to be used. MS_ABI means ms abi. Otherwise SYSV_ABI means sysv abi. */ > ENUM_BITFIELD(calling_abi) call_abi : 8; > > + /* True if the preserve_none ABI is used. */ > + BOOL_BITFIELD preserve_none_abi : 1; > + > /* Nonzero if the function accesses a previous frame. */ > BOOL_BITFIELD accesses_prev_frame : 1; > > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi > index 0978c4c41b2..4be531bb4fc 100644 > --- a/gcc/doc/extend.texi > +++ b/gcc/doc/extend.texi > @@ -6117,6 +6117,13 @@ 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{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 > @@ -6124,9 +6131,10 @@ caller-saved registers. That is, all registers are > callee-saved. For > 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 > diff --git a/gcc/testsuite/gcc.target/i386/no-callee-saved-19a.c > b/gcc/testsuite/gcc.target/i386/no-callee-saved-19a.c > new file mode 100644 > index 00000000000..25ef8558a5d > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/no-callee-saved-19a.c > @@ -0,0 +1,166 @@ > +/* { 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); > +} > diff --git a/gcc/testsuite/gcc.target/i386/no-callee-saved-19b.c > b/gcc/testsuite/gcc.target/i386/no-callee-saved-19b.c > new file mode 100644 > index 00000000000..c9343a65470 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/no-callee-saved-19b.c > @@ -0,0 +1,129 @@ > +/* { 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" > diff --git a/gcc/testsuite/gcc.target/i386/no-callee-saved-19c.c > b/gcc/testsuite/gcc.target/i386/no-callee-saved-19c.c > new file mode 100644 > index 00000000000..2ad388d1a56 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/no-callee-saved-19c.c > @@ -0,0 +1,94 @@ > +/* { 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" > diff --git a/gcc/testsuite/gcc.target/i386/no-callee-saved-19d.c > b/gcc/testsuite/gcc.target/i386/no-callee-saved-19d.c > new file mode 100644 > index 00000000000..a18d12e5899 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/no-callee-saved-19d.c > @@ -0,0 +1,159 @@ > +/* { 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" > diff --git a/gcc/testsuite/gcc.target/i386/no-callee-saved-19e.c > b/gcc/testsuite/gcc.target/i386/no-callee-saved-19e.c > new file mode 100644 > index 00000000000..3fcb41ff196 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/no-callee-saved-19e.c > @@ -0,0 +1,162 @@ > +/* { 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" > diff --git a/gcc/testsuite/gcc.target/i386/no-callee-saved-3.c > b/gcc/testsuite/gcc.target/i386/no-callee-saved-3.c > index 453272e11c0..44ad0b2114e 100644 > --- a/gcc/testsuite/gcc.target/i386/no-callee-saved-3.c > +++ b/gcc/testsuite/gcc.target/i386/no-callee-saved-3.c > @@ -3,6 +3,6 @@ > > __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" } */ > } > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-1.c > b/gcc/testsuite/gcc.target/i386/preserve-none-1.c > new file mode 100644 > index 00000000000..850791872a4 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-1.c > @@ -0,0 +1,17 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-10.c > b/gcc/testsuite/gcc.target/i386/preserve-none-10.c > new file mode 100644 > index 00000000000..f22200a9ff1 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-10.c > @@ -0,0 +1,11 @@ > +/* { 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" } */ > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-11.c > b/gcc/testsuite/gcc.target/i386/preserve-none-11.c > new file mode 100644 > index 00000000000..3bc82ba671a > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-11.c > @@ -0,0 +1,12 @@ > +/* { 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; > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-12.c > b/gcc/testsuite/gcc.target/i386/preserve-none-12.c > new file mode 100644 > index 00000000000..6960f660797 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-12.c > @@ -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__ ((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 > } } } } */ > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-13.c > b/gcc/testsuite/gcc.target/i386/preserve-none-13.c > new file mode 100644 > index 00000000000..31b33201e85 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-13.c > @@ -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__ ((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 > } } } } */ > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-14.c > b/gcc/testsuite/gcc.target/i386/preserve-none-14.c > new file mode 100644 > index 00000000000..64a957ddf83 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-14.c > @@ -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__ ((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 > } } } } */ > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-15.c > b/gcc/testsuite/gcc.target/i386/preserve-none-15.c > new file mode 100644 > index 00000000000..8af930bd914 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-15.c > @@ -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__ ((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 > } } } } */ > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-16.c > b/gcc/testsuite/gcc.target/i386/preserve-none-16.c > new file mode 100644 > index 00000000000..680083646fc > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-16.c > @@ -0,0 +1,11 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2" } */ > + > +extern void foo (void); /* { dg-note "previous declaration" } */ > + > +__attribute__ ((preserve_none)) > +void > +foo (void) /* { dg-error "conflicting types" } */ > +{ > +} > + > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-17.c > b/gcc/testsuite/gcc.target/i386/preserve-none-17.c > new file mode 100644 > index 00000000000..e105da1b709 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-17.c > @@ -0,0 +1,10 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2" } */ > + > +extern void foo (void) __attribute__ ((preserve_none)); /* { dg-note > "previous declaration" } */ > + > +void > +foo (void) /* { dg-error "conflicting types" } */ > +{ > +} > + > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-18.c > b/gcc/testsuite/gcc.target/i386/preserve-none-18.c > new file mode 100644 > index 00000000000..a2ac5e32ab5 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-18.c > @@ -0,0 +1,17 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-19.c > b/gcc/testsuite/gcc.target/i386/preserve-none-19.c > new file mode 100644 > index 00000000000..5e9cbd26fda > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-19.c > @@ -0,0 +1,17 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-2.c > b/gcc/testsuite/gcc.target/i386/preserve-none-2.c > new file mode 100644 > index 00000000000..027f1816ca2 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-2.c > @@ -0,0 +1,12 @@ > +/* { 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 } } } */ > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-20.c > b/gcc/testsuite/gcc.target/i386/preserve-none-20.c > new file mode 100644 > index 00000000000..0070ee7253e > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-20.c > @@ -0,0 +1,18 @@ > +/* { 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 \]+" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-21.c > b/gcc/testsuite/gcc.target/i386/preserve-none-21.c > new file mode 100644 > index 00000000000..4550d22fe4b > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-21.c > @@ -0,0 +1,16 @@ > +/* { 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 \]+" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-22.c > b/gcc/testsuite/gcc.target/i386/preserve-none-22.c > new file mode 100644 > index 00000000000..6ec8d0cfe95 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-22.c > @@ -0,0 +1,17 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-23.c > b/gcc/testsuite/gcc.target/i386/preserve-none-23.c > new file mode 100644 > index 00000000000..8cc933f8aa8 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-23.c > @@ -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__ ((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 > } } } } */ > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-24.c > b/gcc/testsuite/gcc.target/i386/preserve-none-24.c > new file mode 100644 > index 00000000000..d7adfba8ddd > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-24.c > @@ -0,0 +1,8 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2" } */ > + > +__attribute__ ((preserve_none, no_callee_saved_registers)) > +void > +foo (void) > +{ /* { dg-error "attributes are not compatible" } */ > +} > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-25.c > b/gcc/testsuite/gcc.target/i386/preserve-none-25.c > new file mode 100644 > index 00000000000..e22da50427f > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-25.c > @@ -0,0 +1,27 @@ > +/* { 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); > +} > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-26.c > b/gcc/testsuite/gcc.target/i386/preserve-none-26.c > new file mode 100644 > index 00000000000..926d127b576 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-26.c > @@ -0,0 +1,27 @@ > +/* { 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); > +} > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-27.c > b/gcc/testsuite/gcc.target/i386/preserve-none-27.c > new file mode 100644 > index 00000000000..17aa57d60ed > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-27.c > @@ -0,0 +1,33 @@ > +/* { 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); > +} > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-28.c > b/gcc/testsuite/gcc.target/i386/preserve-none-28.c > new file mode 100644 > index 00000000000..7042b8db667 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-28.c > @@ -0,0 +1,48 @@ > +/* { 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; > +} > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-29.c > b/gcc/testsuite/gcc.target/i386/preserve-none-29.c > new file mode 100644 > index 00000000000..e6520fa380b > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-29.c > @@ -0,0 +1,57 @@ > +/* { 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; > +} > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-3.c > b/gcc/testsuite/gcc.target/i386/preserve-none-3.c > new file mode 100644 > index 00000000000..df484a5184c > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-3.c > @@ -0,0 +1,18 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-30a.c > b/gcc/testsuite/gcc.target/i386/preserve-none-30a.c > new file mode 100644 > index 00000000000..2a21ef52708 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-30a.c > @@ -0,0 +1,31 @@ > +/* { 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); > +} > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-30b.c > b/gcc/testsuite/gcc.target/i386/preserve-none-30b.c > new file mode 100644 > index 00000000000..425d0aa24a6 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-30b.c > @@ -0,0 +1,21 @@ > +/* { 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" > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-4.c > b/gcc/testsuite/gcc.target/i386/preserve-none-4.c > new file mode 100644 > index 00000000000..35c3501f6f0 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-4.c > @@ -0,0 +1,19 @@ > +/* { 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 \]+" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-5.c > b/gcc/testsuite/gcc.target/i386/preserve-none-5.c > new file mode 100644 > index 00000000000..1498886a986 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-5.c > @@ -0,0 +1,18 @@ > +/* { 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 \]+" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-6.c > b/gcc/testsuite/gcc.target/i386/preserve-none-6.c > new file mode 100644 > index 00000000000..2606ea3bc19 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-6.c > @@ -0,0 +1,30 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-7.c > b/gcc/testsuite/gcc.target/i386/preserve-none-7.c > new file mode 100644 > index 00000000000..79ce761eaf5 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-7.c > @@ -0,0 +1,30 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-8.c > b/gcc/testsuite/gcc.target/i386/preserve-none-8.c > new file mode 100644 > index 00000000000..9309ceba388 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-8.c > @@ -0,0 +1,8 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2" } */ > + > +__attribute__ ((preserve_none, no_caller_saved_registers)) > +void > +foo (void) > +{ /* { dg-error "attributes are not compatible" } */ > +} > diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-9.c > b/gcc/testsuite/gcc.target/i386/preserve-none-9.c > new file mode 100644 > index 00000000000..f28ddeb17ec > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/preserve-none-9.c > @@ -0,0 +1,8 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -mgeneral-regs-only" } */ > + > +__attribute__ ((preserve_none, interrupt)) > +void > +foo (void *frame) /* { dg-error "attributes are not compatible" } */ > +{ > +} > -- > 2.49.0 > -- BR, Hongtao