On Fri, May 23, 2025 at 1:56 PM H.J. Lu <[email protected]> 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 <[email protected]>
> ---
> 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