On Fri, May 23, 2025 at 1:56 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 > 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_no_callee_saved_registers_p instead of looking up > no_callee_saved_registers attribute. > * config/i386/i386-options.cc (ix86_set_func_type): Look up > preserve_none attribute. Check preserve_none attribute for > interrupt attribute. Don't check no_caller_saved_registers nor > no_callee_saved_registers conflicts here. > (ix86_set_func_type): Check no_callee_saved_registers before > checking no_caller_saved_registers attribute. > (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_no_callee_saved_registers_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_no_callee_saved_registers_p): New. > (ix86_function_ok_for_sibcall): Also check TYPE_PRESERVE_NONE > and call ix86_type_no_callee_saved_registers_p instead of looking > up no_callee_saved_registers attribute. > (ix86_comp_type_attributes): Call > ix86_type_no_callee_saved_registers_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): For cfun with TYPE_PRESERVE_NONE, > use x86_64_preserve_none_int_parameter_registers. > (init_cumulative_args): Set preserve_none_abi. > (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_save_reg): Treat TYPE_PRESERVE_NONE like > TYPE_NO_CALLEE_SAVED_REGISTERS. > (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): Add TYPE_PRESERVE_NONE. > (machine_function): Change call_saved_registers to 3 bits. > * 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-3.c: Adjust error location. > * 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 | 97 +++++++--- > gcc/config/i386/i386-protos.h | 1 + > gcc/config/i386/i386.cc | 106 ++++++++--- > 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, 1680 insertions(+), 51 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 7fd03c88630..f44279848d9 100644 > --- a/gcc/config/i386/i386-expand.cc > +++ b/gcc/config/i386/i386-expand.cc > @@ -10119,8 +10119,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_no_callee_saved_registers_p (TREE_TYPE (fndecl))) > call_no_callee_saved_registers = true; > } > } > @@ -10131,8 +10130,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_no_callee_saved_registers_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 12438673503..137cdff3170 100644 > --- a/gcc/config/i386/i386-options.cc > +++ b/gcc/config/i386/i386-options.cc > @@ -3277,7 +3277,10 @@ 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", > + 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 > @@ -3301,9 +3304,16 @@ 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 (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); > @@ -3325,21 +3335,13 @@ ix86_set_func_type (tree 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; > - } > } > } > } > @@ -3528,11 +3530,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; We can't guarantee there's no mmx/x87 instructions, so need to add !TARGET_MMX && !TARGET_80387? Perhaps rewrite the conditions with if (TARGET_AVX512F) isa = "AVX512"; else if (TARGET_AVX) isa = "AVX"; else if (TARGET_MMX) isa = "MMX/3Dnow"; else if (TARGET_80387) isa = "80387"; else if (TARGET_SSE) isa = cfun->machine->func_type != TYPE_NORMAL ? "SSE" : NULL; else isa = NULL;
> + } > else if (TARGET_MMX) > isa = "MMX/3Dnow"; > else if (TARGET_80387) > @@ -3957,9 +3969,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; > } > > @@ -4121,6 +4174,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 e85b925704b..263852ceead 100644 > --- a/gcc/config/i386/i386-protos.h > +++ b/gcc/config/i386/i386-protos.h > @@ -281,6 +281,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_no_callee_saved_registers_p (const_tree); > > #endif > > diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc > index 5cb66dadb43..dc5214a4774 100644 > --- a/gcc/config/i386/i386.cc > +++ b/gcc/config/i386/i386.cc > @@ -335,6 +335,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. It's not exactly the same as Clang's preserve_none, So perhaps use Similar as Clang's .... > + 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 > @@ -460,7 +468,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 > @@ -471,7 +480,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 > @@ -898,6 +907,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_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 > @@ -1019,11 +1040,12 @@ ix86_function_ok_for_sibcall (tree decl, tree exp) > > /* 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. */ > @@ -1188,10 +1210,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_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; > @@ -1553,7 +1581,10 @@ 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->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; > @@ -1822,6 +1853,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 +1862,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; > > @@ -3408,9 +3452,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); > } > > @@ -4575,6 +4625,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, > @@ -4582,8 +4638,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) > @@ -6705,6 +6760,7 @@ ix86_save_reg (unsigned int regno, bool > maybe_eh_return, bool ignore_outlined) > || !frame_pointer_needed)); > > case TYPE_NO_CALLEE_SAVED_REGISTERS: > + case TYPE_PRESERVE_NONE: > return false; > > case TYPE_NO_CALLEE_SAVED_REGISTERS_EXCEPT_BP: > @@ -6784,7 +6840,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)) > @@ -6972,12 +7030,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. > @@ -23235,7 +23299,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 5aa056ff553..8e15b9a8494 100644 > --- a/gcc/config/i386/i386.h > +++ b/gcc/config/i386/i386.h > @@ -1694,6 +1694,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; > > @@ -2794,6 +2796,9 @@ enum call_saved_registers_type > /* 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 > @@ -2866,7 +2871,7 @@ struct GTY(()) machine_function { > 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 > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi > index 442fce653a4..0a1a1c20f9b 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 -- BR, Hongtao