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

Reply via email to