Add preserve_none attribute which is similar to no_callee_saved_registers
attribute, except on x86-64, r12, r13, r14, r15, rdi and rsi registers are
used for integer parameter passing.  This can be used in an interpreter
to avoid saving/restoring the registers in functions which processing
byte codes.  It improved the pystones benchmark by 6-7%:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119628#c15

Remove -mgeneral-regs-only restriction on no_caller_saved_registers
attribute.  Only SSE is allowed since SSE XMM register load preserves
the upper bits in YMM/ZMM register while YMM register load zeros the
upper 256 bits of ZMM register, and preserving 32 ZMM registers can
be quite expensive.

gcc/

        PR target/119628
        * config/i386/i386-expand.cc (ix86_expand_call): Call
        ix86_type_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,
   /* 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

Reply via email to