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

Reply via email to