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 process byte
codes.  It improved the pystones benchmark by 6-7%:

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

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

gcc/

PR target/119628
* config/i386/i386-expand.cc (ix86_expand_call): Call
ix86_type_no_callee_saved_registers_p instead of looking up
no_callee_saved_registers attribute.
* config/i386/i386-options.cc (ix86_set_func_type): Look up
preserve_none attribute.  Check preserve_none attribute for
interrupt attribute.  Don't check no_caller_saved_registers nor
no_callee_saved_registers conflicts here.
(ix86_set_func_type): Check no_callee_saved_registers before
checking no_caller_saved_registers attribute.
(ix86_set_current_function): Allow SSE with
no_caller_saved_registers attribute.
(ix86_handle_call_saved_registers_attribute): Check preserve_none,
no_callee_saved_registers and no_caller_saved_registers conflicts.
(ix86_gnu_attributes): Add preserve_none attribute.
* config/i386/i386-protos.h (ix86_type_no_callee_saved_registers_p):
New.
* config/i386/i386.cc
(x86_64_preserve_none_int_parameter_registers): New.
(ix86_using_red_zone): Don't use red-zone when there are no
caller-saved registers with SSE.
(ix86_type_no_callee_saved_registers_p): New.
(ix86_function_ok_for_sibcall): Also check TYPE_PRESERVE_NONE
and call ix86_type_no_callee_saved_registers_p instead of looking
up no_callee_saved_registers attribute.
(ix86_comp_type_attributes): Call
ix86_type_no_callee_saved_registers_p instead of looking up
no_callee_saved_registers attribute.  Return 0 if preserve_none
attribute doesn't match in 64-bit mode.
(ix86_function_arg_regno_p): For cfun with TYPE_PRESERVE_NONE,
use x86_64_preserve_none_int_parameter_registers.
(init_cumulative_args): Set preserve_none_abi.
(function_arg_64): Use x86_64_preserve_none_int_parameter_registers
with preserve_none attribute.
(setup_incoming_varargs_64): Use
x86_64_preserve_none_int_parameter_registers with preserve_none
attribute.
(ix86_save_reg): Treat TYPE_PRESERVE_NONE like
TYPE_NO_CALLEE_SAVED_REGISTERS.
(ix86_nsaved_sseregs): Allow saving XMM registers for
no_caller_saved_registers attribute.
(ix86_compute_frame_layout): Likewise.
(x86_this_parameter): Use
x86_64_preserve_none_int_parameter_registers with preserve_none
attribute.
* config/i386/i386.h (ix86_args): Add preserve_none_abi.
(call_saved_registers_type): Add TYPE_PRESERVE_NONE.
(machine_function): Change call_saved_registers to 3 bits.
* doc/extend.texi: Add preserve_none attribute.  Update
no_caller_saved_registers attribute to remove -mgeneral-regs-only
restriction.

gcc/testsuite/

PR target/119628
* gcc.target/i386/no-callee-saved-3.c: Adjust error location.
* gcc.target/i386/no-callee-saved-19a.c: New test.
* gcc.target/i386/no-callee-saved-19b.c: Likewise.
* gcc.target/i386/no-callee-saved-19c.c: Likewise.
* gcc.target/i386/no-callee-saved-19d.c: Likewise.
* gcc.target/i386/no-callee-saved-19e.c: Likewise.
* gcc.target/i386/preserve-none-1.c: Likewise.
* gcc.target/i386/preserve-none-2.c: Likewise.
* gcc.target/i386/preserve-none-3.c: Likewise.
* gcc.target/i386/preserve-none-4.c: Likewise.
* gcc.target/i386/preserve-none-5.c: Likewise.
* gcc.target/i386/preserve-none-6.c: Likewise.
* gcc.target/i386/preserve-none-7.c: Likewise.
* gcc.target/i386/preserve-none-8.c: Likewise.
* gcc.target/i386/preserve-none-9.c: Likewise.
* gcc.target/i386/preserve-none-10.c: Likewise.
* gcc.target/i386/preserve-none-11.c: Likewise.
* gcc.target/i386/preserve-none-12.c: Likewise.
* gcc.target/i386/preserve-none-13.c: Likewise.
* gcc.target/i386/preserve-none-14.c: Likewise.
* gcc.target/i386/preserve-none-15.c: Likewise.
* gcc.target/i386/preserve-none-16.c: Likewise.
* gcc.target/i386/preserve-none-17.c: Likewise.
* gcc.target/i386/preserve-none-19.c: Likewise.
* gcc.target/i386/preserve-none-19.c: Likewise.
* gcc.target/i386/preserve-none-20.c: Likewise.
* gcc.target/i386/preserve-none-21.c: Likewise.
* gcc.target/i386/preserve-none-22.c: Likewise.
* gcc.target/i386/preserve-none-23.c: Likewise.
* gcc.target/i386/preserve-none-24.c: Likewise.
* gcc.target/i386/preserve-none-25.c: Likewise.
* gcc.target/i386/preserve-none-26.c: Likewise.
* gcc.target/i386/preserve-none-27.c: Likewise.
* gcc.target/i386/preserve-none-28.c: Likewise.
* gcc.target/i386/preserve-none-29.c: Likewise.
* gcc.target/i386/preserve-none-30a.c: Likewise.
* gcc.target/i386/preserve-none-30b.c: Likewise.

-- 
H.J.
From e8929476ee4e1499a631d569914e4f0c54881fd9 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.to...@gmail.com>
Date: Sun, 13 Apr 2025 11:38:24 -0700
Subject: [PATCH v3] x86: Add preserve_none and update
 no_caller_saved_registers attributes

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 process byte
codes.  It improved the pystones benchmark by 6-7%:

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

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

gcc/

	PR target/119628
	* config/i386/i386-expand.cc (ix86_expand_call): Call
	ix86_type_no_callee_saved_registers_p instead of looking up
	no_callee_saved_registers attribute.
	* config/i386/i386-options.cc (ix86_set_func_type): Look up
	preserve_none attribute.  Check preserve_none attribute for
	interrupt attribute.  Don't check no_caller_saved_registers nor
	no_callee_saved_registers conflicts here.
	(ix86_set_func_type): Check no_callee_saved_registers before
	checking no_caller_saved_registers attribute.
	(ix86_set_current_function): Allow SSE with
	no_caller_saved_registers attribute.
	(ix86_handle_call_saved_registers_attribute): Check preserve_none,
	no_callee_saved_registers and no_caller_saved_registers conflicts.
	(ix86_gnu_attributes): Add preserve_none attribute.
	* config/i386/i386-protos.h (ix86_type_no_callee_saved_registers_p):
	New.
	* config/i386/i386.cc
	(x86_64_preserve_none_int_parameter_registers): New.
	(ix86_using_red_zone): Don't use red-zone when there are no
	caller-saved registers with SSE.
	(ix86_type_no_callee_saved_registers_p): New.
	(ix86_function_ok_for_sibcall): Also check TYPE_PRESERVE_NONE
	and call ix86_type_no_callee_saved_registers_p instead of looking
	up no_callee_saved_registers attribute.
	(ix86_comp_type_attributes): Call
	ix86_type_no_callee_saved_registers_p instead of looking up
	no_callee_saved_registers attribute.  Return 0 if preserve_none
	attribute doesn't match in 64-bit mode.
	(ix86_function_arg_regno_p): For cfun with TYPE_PRESERVE_NONE,
	use x86_64_preserve_none_int_parameter_registers.
	(init_cumulative_args): Set preserve_none_abi.
	(function_arg_64): Use x86_64_preserve_none_int_parameter_registers
	with preserve_none attribute.
	(setup_incoming_varargs_64): Use
	x86_64_preserve_none_int_parameter_registers with preserve_none
	attribute.
	(ix86_save_reg): Treat TYPE_PRESERVE_NONE like
	TYPE_NO_CALLEE_SAVED_REGISTERS.
	(ix86_nsaved_sseregs): Allow saving XMM registers for
	no_caller_saved_registers attribute.
	(ix86_compute_frame_layout): Likewise.
	(x86_this_parameter): Use
	x86_64_preserve_none_int_parameter_registers with preserve_none
	attribute.
	* config/i386/i386.h (ix86_args): Add preserve_none_abi.
	(call_saved_registers_type): Add TYPE_PRESERVE_NONE.
	(machine_function): Change call_saved_registers to 3 bits.
	* doc/extend.texi: Add preserve_none attribute.  Update
	no_caller_saved_registers attribute to remove -mgeneral-regs-only
	restriction.

gcc/testsuite/

	PR target/119628
	* gcc.target/i386/no-callee-saved-3.c: Adjust error location.
	* gcc.target/i386/no-callee-saved-19a.c: New test.
	* gcc.target/i386/no-callee-saved-19b.c: Likewise.
	* gcc.target/i386/no-callee-saved-19c.c: Likewise.
	* gcc.target/i386/no-callee-saved-19d.c: Likewise.
	* gcc.target/i386/no-callee-saved-19e.c: Likewise.
	* gcc.target/i386/preserve-none-1.c: Likewise.
	* gcc.target/i386/preserve-none-2.c: Likewise.
	* gcc.target/i386/preserve-none-3.c: Likewise.
	* gcc.target/i386/preserve-none-4.c: Likewise.
	* gcc.target/i386/preserve-none-5.c: Likewise.
	* gcc.target/i386/preserve-none-6.c: Likewise.
	* gcc.target/i386/preserve-none-7.c: Likewise.
	* gcc.target/i386/preserve-none-8.c: Likewise.
	* gcc.target/i386/preserve-none-9.c: Likewise.
	* gcc.target/i386/preserve-none-10.c: Likewise.
	* gcc.target/i386/preserve-none-11.c: Likewise.
	* gcc.target/i386/preserve-none-12.c: Likewise.
	* gcc.target/i386/preserve-none-13.c: Likewise.
	* gcc.target/i386/preserve-none-14.c: Likewise.
	* gcc.target/i386/preserve-none-15.c: Likewise.
	* gcc.target/i386/preserve-none-16.c: Likewise.
	* gcc.target/i386/preserve-none-17.c: Likewise.
	* gcc.target/i386/preserve-none-19.c: Likewise.
	* gcc.target/i386/preserve-none-19.c: Likewise.
	* gcc.target/i386/preserve-none-20.c: Likewise.
	* gcc.target/i386/preserve-none-21.c: Likewise.
	* gcc.target/i386/preserve-none-22.c: Likewise.
	* gcc.target/i386/preserve-none-23.c: Likewise.
	* gcc.target/i386/preserve-none-24.c: Likewise.
	* gcc.target/i386/preserve-none-25.c: Likewise.
	* gcc.target/i386/preserve-none-26.c: Likewise.
	* gcc.target/i386/preserve-none-27.c: Likewise.
	* gcc.target/i386/preserve-none-28.c: Likewise.
	* gcc.target/i386/preserve-none-29.c: Likewise.
	* gcc.target/i386/preserve-none-30a.c: Likewise.
	* gcc.target/i386/preserve-none-30b.c: Likewise.

Signed-off-by: H.J. Lu <hjl.to...@gmail.com>
---
 gcc/config/i386/i386-expand.cc                |   6 +-
 gcc/config/i386/i386-options.cc               |  97 +++++++---
 gcc/config/i386/i386-protos.h                 |   1 +
 gcc/config/i386/i386.cc                       | 106 ++++++++---
 gcc/config/i386/i386.h                        |   7 +-
 gcc/doc/extend.texi                           |  14 +-
 .../gcc.target/i386/no-callee-saved-19a.c     | 166 ++++++++++++++++++
 .../gcc.target/i386/no-callee-saved-19b.c     | 129 ++++++++++++++
 .../gcc.target/i386/no-callee-saved-19c.c     |  94 ++++++++++
 .../gcc.target/i386/no-callee-saved-19d.c     | 159 +++++++++++++++++
 .../gcc.target/i386/no-callee-saved-19e.c     | 162 +++++++++++++++++
 .../gcc.target/i386/no-callee-saved-3.c       |   4 +-
 .../gcc.target/i386/preserve-none-1.c         |  17 ++
 .../gcc.target/i386/preserve-none-10.c        |  11 ++
 .../gcc.target/i386/preserve-none-11.c        |  12 ++
 .../gcc.target/i386/preserve-none-12.c        |  49 ++++++
 .../gcc.target/i386/preserve-none-13.c        |  50 ++++++
 .../gcc.target/i386/preserve-none-14.c        |  49 ++++++
 .../gcc.target/i386/preserve-none-15.c        |  46 +++++
 .../gcc.target/i386/preserve-none-16.c        |  11 ++
 .../gcc.target/i386/preserve-none-17.c        |  10 ++
 .../gcc.target/i386/preserve-none-18.c        |  17 ++
 .../gcc.target/i386/preserve-none-19.c        |  17 ++
 .../gcc.target/i386/preserve-none-2.c         |  12 ++
 .../gcc.target/i386/preserve-none-20.c        |  18 ++
 .../gcc.target/i386/preserve-none-21.c        |  16 ++
 .../gcc.target/i386/preserve-none-22.c        |  17 ++
 .../gcc.target/i386/preserve-none-23.c        |  51 ++++++
 .../gcc.target/i386/preserve-none-24.c        |   8 +
 .../gcc.target/i386/preserve-none-25.c        |  27 +++
 .../gcc.target/i386/preserve-none-26.c        |  27 +++
 .../gcc.target/i386/preserve-none-27.c        |  33 ++++
 .../gcc.target/i386/preserve-none-28.c        |  48 +++++
 .../gcc.target/i386/preserve-none-29.c        |  57 ++++++
 .../gcc.target/i386/preserve-none-3.c         |  18 ++
 .../gcc.target/i386/preserve-none-30a.c       |  31 ++++
 .../gcc.target/i386/preserve-none-30b.c       |  21 +++
 .../gcc.target/i386/preserve-none-4.c         |  19 ++
 .../gcc.target/i386/preserve-none-5.c         |  18 ++
 .../gcc.target/i386/preserve-none-6.c         |  30 ++++
 .../gcc.target/i386/preserve-none-7.c         |  30 ++++
 .../gcc.target/i386/preserve-none-8.c         |   8 +
 .../gcc.target/i386/preserve-none-9.c         |   8 +
 43 files changed, 1680 insertions(+), 51 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/no-callee-saved-19a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/no-callee-saved-19b.c
 create mode 100644 gcc/testsuite/gcc.target/i386/no-callee-saved-19c.c
 create mode 100644 gcc/testsuite/gcc.target/i386/no-callee-saved-19d.c
 create mode 100644 gcc/testsuite/gcc.target/i386/no-callee-saved-19e.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-10.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-11.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-12.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-13.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-14.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-15.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-16.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-17.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-18.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-19.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-20.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-21.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-22.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-23.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-24.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-25.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-26.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-27.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-28.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-29.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-3.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-30a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-30b.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-4.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-5.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-6.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-7.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-8.c
 create mode 100644 gcc/testsuite/gcc.target/i386/preserve-none-9.c

diff --git a/gcc/config/i386/i386-expand.cc b/gcc/config/i386/i386-expand.cc
index 8e556f1b9c1..c1c0cf9ff80 100644
--- a/gcc/config/i386/i386-expand.cc
+++ b/gcc/config/i386/i386-expand.cc
@@ -10138,8 +10138,7 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
 	  if (lookup_attribute ("interrupt",
 				TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
 	    error ("interrupt service routine cannot be called directly");
-	  else if (lookup_attribute ("no_callee_saved_registers",
-				     TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
+	  else if (ix86_type_no_callee_saved_registers_p (TREE_TYPE (fndecl)))
 	    call_no_callee_saved_registers = true;
 	  if (fndecl == current_function_decl
 	      && decl_binds_to_current_def_p (fndecl))
@@ -10153,8 +10152,7 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
 	  tree mem_expr = MEM_EXPR (fnaddr);
 	  if (mem_expr != nullptr
 	      && TREE_CODE (mem_expr) == MEM_REF
-	      && lookup_attribute ("no_callee_saved_registers",
-				   TYPE_ATTRIBUTES (TREE_TYPE (mem_expr))))
+	      && ix86_type_no_callee_saved_registers_p (TREE_TYPE (mem_expr)))
 	    call_no_callee_saved_registers = true;
 	}
 
diff --git a/gcc/config/i386/i386-options.cc b/gcc/config/i386/i386-options.cc
index 27feeddaf81..60c04f65d9f 100644
--- a/gcc/config/i386/i386-options.cc
+++ b/gcc/config/i386/i386-options.cc
@@ -3277,7 +3277,10 @@ ix86_set_func_type (tree fndecl)
      interrupt function in this case.  */
   enum call_saved_registers_type no_callee_saved_registers
     = TYPE_DEFAULT_CALL_SAVED_REGISTERS;
-  if (lookup_attribute ("no_callee_saved_registers",
+  if (lookup_attribute ("preserve_none",
+			     TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
+    no_callee_saved_registers = TYPE_PRESERVE_NONE;
+  else if (lookup_attribute ("no_callee_saved_registers",
 			TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
     no_callee_saved_registers = TYPE_NO_CALLEE_SAVED_REGISTERS;
   else if (ix86_noreturn_no_callee_saved_registers
@@ -3301,9 +3304,16 @@ ix86_set_func_type (tree fndecl)
 		      "interrupt and naked attributes are not compatible");
 
 	  if (no_callee_saved_registers)
-	    error_at (DECL_SOURCE_LOCATION (fndecl),
-		      "%qs and %qs attributes are not compatible",
-		      "interrupt", "no_callee_saved_registers");
+	    {
+	      const char *attr;
+	      if (no_callee_saved_registers == TYPE_PRESERVE_NONE)
+		attr = "preserve_none";
+	      else
+		attr = "no_callee_saved_registers";
+	      error_at (DECL_SOURCE_LOCATION (fndecl),
+			"%qs and %qs attributes are not compatible",
+			"interrupt", attr);
+	    }
 
 	  int nargs = 0;
 	  for (tree arg = DECL_ARGUMENTS (fndecl);
@@ -3325,21 +3335,13 @@ ix86_set_func_type (tree fndecl)
       else
 	{
 	  cfun->machine->func_type = TYPE_NORMAL;
-	  if (lookup_attribute ("no_caller_saved_registers",
-				TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
+	  if (no_callee_saved_registers)
+	    cfun->machine->call_saved_registers
+	      = no_callee_saved_registers;
+	  else if (lookup_attribute ("no_caller_saved_registers",
+				     TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
 	    cfun->machine->call_saved_registers
 	      = TYPE_NO_CALLER_SAVED_REGISTERS;
-	  if (no_callee_saved_registers)
-	    {
-	      if (cfun->machine->call_saved_registers
-		  == TYPE_NO_CALLER_SAVED_REGISTERS)
-		error_at (DECL_SOURCE_LOCATION (fndecl),
-			  "%qs and %qs attributes are not compatible",
-			  "no_caller_saved_registers",
-			  "no_callee_saved_registers");
-	      cfun->machine->call_saved_registers
-		= no_callee_saved_registers;
-	    }
 	}
     }
 }
@@ -3528,11 +3530,21 @@ ix86_set_current_function (tree fndecl)
       || (cfun->machine->call_saved_registers
 	  == TYPE_NO_CALLER_SAVED_REGISTERS))
     {
-      /* Don't allow SSE, MMX nor x87 instructions since they
-	 may change processor state.  */
+      /* Don't allow AVX, AVX512, MMX nor x87 instructions since they
+	 may change processor state.  Don't allow SSE instructions in
+	 exception/interrupt service routines.  */
       const char *isa;
       if (TARGET_SSE)
-	isa = "SSE";
+	{
+	  if (TARGET_AVX512F)
+	    isa = "AVX512";
+	  else if (TARGET_AVX)
+	    isa = "AVX";
+	  else if (cfun->machine->func_type != TYPE_NORMAL)
+	    isa = "SSE";
+	  else
+	    isa = NULL;
+	}
       else if (TARGET_MMX)
 	isa = "MMX/3Dnow";
       else if (TARGET_80387)
@@ -3957,9 +3969,50 @@ ix86_handle_fndecl_attribute (tree *node, tree name, tree args, int,
 }
 
 static tree
-ix86_handle_call_saved_registers_attribute (tree *, tree, tree,
+ix86_handle_call_saved_registers_attribute (tree *node, tree name, tree,
 					    int, bool *)
 {
+  const char *attr1 = nullptr;
+  const char *attr2 = nullptr;
+
+  if (is_attribute_p ("no_callee_saved_registers", name))
+    {
+      /* Disallow preserve_none and no_caller_saved_registers
+	 attributes.  */
+      attr1 = "no_callee_saved_registers";
+      if (lookup_attribute ("preserve_none", TYPE_ATTRIBUTES (*node)))
+	attr2 = "preserve_none";
+      else if (lookup_attribute ("no_caller_saved_registers",
+				 TYPE_ATTRIBUTES (*node)))
+	attr2 = "no_caller_saved_registers";
+    }
+  else if (is_attribute_p ("no_caller_saved_registers", name))
+    {
+      /* Disallow preserve_none and no_callee_saved_registers
+	 attributes.  */
+      attr1 = "no_caller_saved_registers";
+      if (lookup_attribute ("preserve_none", TYPE_ATTRIBUTES (*node)))
+	attr2 = "preserve_none";
+      else if (lookup_attribute ("no_callee_saved_registers",
+				 TYPE_ATTRIBUTES (*node)))
+	attr2 = "no_callee_saved_registers";
+    }
+  else if (is_attribute_p ("preserve_none", name))
+    {
+      /* Disallow no_callee_saved_registers and no_caller_saved_registers
+	 attributes.  */
+      attr1 = "preserve_none";
+      if (lookup_attribute ("no_callee_saved_registers",
+			    TYPE_ATTRIBUTES (*node)))
+	attr2 = "no_caller_saved_registers";
+      else if (lookup_attribute ("no_callee_saved_registers",
+				 TYPE_ATTRIBUTES (*node)))
+	attr2 = "no_callee_saved_registers";
+    }
+
+  if (attr2)
+    error ("%qs and %qs attributes are not compatible", attr1, attr2);
+
   return NULL_TREE;
 }
 
@@ -4121,6 +4174,8 @@ static const attribute_spec ix86_gnu_attributes[] =
     ix86_handle_interrupt_attribute, NULL },
   { "no_caller_saved_registers", 0, 0, false, true, true, false,
     ix86_handle_call_saved_registers_attribute, NULL },
+  { "preserve_none", 0, 0, false, true, true, true,
+    ix86_handle_call_saved_registers_attribute, NULL },
   { "no_callee_saved_registers", 0, 0, false, true, true, true,
     ix86_handle_call_saved_registers_attribute, NULL },
   { "naked", 0, 0, true, false, false, false,
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 86194b3d319..69bc0ee570d 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -282,6 +282,7 @@ extern tree ix86_valid_target_attribute_tree (tree, tree,
 					      struct gcc_options *,
 					      struct gcc_options *, bool);
 extern unsigned int ix86_get_callcvt (const_tree);
+extern bool ix86_type_no_callee_saved_registers_p (const_tree);
 
 #endif
 
diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
index 84081ab1267..5e9559f5c05 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -335,6 +335,14 @@ static int const x86_64_ms_abi_int_parameter_registers[4] =
   CX_REG, DX_REG, R8_REG, R9_REG
 };
 
+/* Similar 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
@@ -460,7 +468,8 @@ int ix86_arch_specified;
    red-zone.
 
    NB: Don't use red-zone for functions with no_caller_saved_registers
-   and 32 GPRs since 128-byte red-zone is too small for 31 GPRs.
+   and 32 GPRs or 16 XMM registers since 128-byte red-zone is too small
+   for 31 GPRs or 15 GPRs + 16 XMM registers.
 
    TODO: If we can reserve the first 2 WORDs, for PUSH and, another
    for CALL, in red-zone, we can allow local indirect jumps with
@@ -471,7 +480,7 @@ ix86_using_red_zone (void)
 {
   return (TARGET_RED_ZONE
 	  && !TARGET_64BIT_MS_ABI
-	  && (!TARGET_APX_EGPR
+	  && ((!TARGET_APX_EGPR && !TARGET_SSE)
 	      || (cfun->machine->call_saved_registers
 		  != TYPE_NO_CALLER_SAVED_REGISTERS))
 	  && (!cfun->machine->has_local_indirect_jump
@@ -898,6 +907,18 @@ x86_64_elf_unique_section (tree decl, int reloc)
   default_unique_section (decl, reloc);
 }
 
+/* Return true if TYPE has no_callee_saved_registers or preserve_none
+   attribute.  */
+
+bool
+ix86_type_no_callee_saved_registers_p (const_tree type)
+{
+  return (lookup_attribute ("no_callee_saved_registers",
+			    TYPE_ATTRIBUTES (type)) != NULL
+	  || lookup_attribute ("preserve_none",
+			       TYPE_ATTRIBUTES (type)) != NULL);
+}
+
 #ifdef COMMON_ASM_OP
 
 #ifndef LARGECOMM_SECTION_ASM_OP
@@ -1019,11 +1040,12 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
 
   /* Sibling call isn't OK if callee has no callee-saved registers
      and the calling function has callee-saved registers.  */
-  if (cfun->machine->call_saved_registers != TYPE_NO_CALLEE_SAVED_REGISTERS
+  if ((cfun->machine->call_saved_registers
+       != TYPE_NO_CALLEE_SAVED_REGISTERS)
+      && cfun->machine->call_saved_registers != TYPE_PRESERVE_NONE
       && (cfun->machine->call_saved_registers
 	  != TYPE_NO_CALLEE_SAVED_REGISTERS_EXCEPT_BP)
-      && lookup_attribute ("no_callee_saved_registers",
-			   TYPE_ATTRIBUTES (type)))
+      && ix86_type_no_callee_saved_registers_p (type))
     return false;
 
   /* If outgoing reg parm stack space changes, we cannot do sibcall.  */
@@ -1188,10 +1210,16 @@ ix86_comp_type_attributes (const_tree type1, const_tree type2)
       != ix86_function_regparm (type2, NULL))
     return 0;
 
-  if (lookup_attribute ("no_callee_saved_registers",
-			TYPE_ATTRIBUTES (type1))
-      != lookup_attribute ("no_callee_saved_registers",
-			   TYPE_ATTRIBUTES (type2)))
+  if (ix86_type_no_callee_saved_registers_p (type1)
+      != ix86_type_no_callee_saved_registers_p (type2))
+    return 0;
+
+  /* preserve_none attribute uses a different calling convention is
+     only for 64-bit.  */
+  if (TARGET_64BIT
+      && (lookup_attribute ("preserve_none", TYPE_ATTRIBUTES (type1))
+	  != lookup_attribute ("preserve_none",
+			       TYPE_ATTRIBUTES (type2))))
     return 0;
 
   return 1;
@@ -1553,7 +1581,10 @@ ix86_function_arg_regno_p (int regno)
   if (call_abi == SYSV_ABI && regno == AX_REG)
     return true;
 
-  if (call_abi == MS_ABI)
+  if (cfun
+      && cfun->machine->call_saved_registers == TYPE_PRESERVE_NONE)
+    parm_regs = x86_64_preserve_none_int_parameter_registers;
+  else if (call_abi == MS_ABI)
     parm_regs = x86_64_ms_abi_int_parameter_registers;
   else
     parm_regs = x86_64_int_parameter_registers;
@@ -1835,6 +1866,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);
@@ -1843,12 +1875,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;
 
@@ -3421,9 +3465,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);
 }
 
@@ -4588,6 +4638,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,
@@ -4595,8 +4651,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)
@@ -6718,6 +6773,7 @@ ix86_save_reg (unsigned int regno, bool maybe_eh_return, bool ignore_outlined)
 		  || !frame_pointer_needed));
 
     case TYPE_NO_CALLEE_SAVED_REGISTERS:
+    case TYPE_PRESERVE_NONE:
       return false;
 
     case TYPE_NO_CALLEE_SAVED_REGISTERS_EXCEPT_BP:
@@ -6797,7 +6853,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))
@@ -7005,12 +7063,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.
@@ -23297,7 +23361,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 661fb8e7e52..a275a32682e 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -1696,6 +1696,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;
 
@@ -2805,6 +2807,9 @@ enum call_saved_registers_type
   /* The current function is a function specified with the "noreturn"
      attribute.  */
   TYPE_NO_CALLEE_SAVED_REGISTERS_EXCEPT_BP,
+  /* The current function is a function specified with the
+     "preserve_none" attribute.  */
+  TYPE_PRESERVE_NONE,
 };
 
 enum queued_insn_type
@@ -2881,7 +2886,7 @@ struct GTY(()) machine_function {
   ENUM_BITFIELD(indirect_branch) function_return_type : 3;
 
   /* Call saved registers type.  */
-  ENUM_BITFIELD(call_saved_registers_type) call_saved_registers : 2;
+  ENUM_BITFIELD(call_saved_registers_type) call_saved_registers : 3;
 
   /* If true, there is register available for argument passing.  This
      is used only in ix86_function_ok_for_sibcall by 32-bit to determine
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 55adf649acf..323a2dc1fc5 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -6129,6 +6129,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
@@ -6136,9 +6143,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