Implement TARGET_FNTYPE_ABI to avoid spills of callee-saved registers
when calling functions with no_caller_saved_registers attribute.

1. For functions with no_callee_saved_registers attribute, frame register
is preserved to mitigate PR target/114116.  MMX and x87 registers aren't
clobbered if MMX and x87 aren't enabled.
2. For functions with no_caller_saved_registers attribute, MMX and x87
registers are clobbered since saving and restoring registers doesn't
include MMX nor x87 registers.
3. Don't mark disabled registers as call used to avoid reg_to_stack
crashes when x87 registers are still accessed even with -mno-mmx
-mno-80387.
4. Add ABI_ORIGINAL which is the function ABI without attributes on
the current function.
5. Add ABI_ALTERNATE which is the alternate function ABI from ABI_DEFAULT.
If ix86_abi is SYSV_ABI, ABI_ALTERNATE is the function ABI for MS_ABI.
Otherwise, ABI_ALTERNATE is the function ABI for SYSV_ABI.

Tested on Linux/x86-64 and with CPython 3.14.4.

gcc/

PR target/124798
* config/i386/i386-expand.cc: Include "function-abi.h".
(ix86_expand_call): Add call clobbers only when callee is
no-callee-saved and caller isn't.  Use callee abi to get the
list of call clobbers.
* config/i386/i386-options.cc (ix86_init_machine_status): Call
ix86_init_original_abi.
* config/i386/i386-protos.h
(ix86_type_no_callee_saved_registers_p): Removed.
(ix86_init_original_abi): New.
* config/i386/i386.cc (ix86_conditional_register_usage): Mark
all, but frame pointer, are caller-saved in no-callee-saved and
preserve_none functions.  Clear disabled registers in
call_used_regs.
(ix86_type_no_callee_saved_registers_p): Make it static.
(ix86_no_callee_saved_abi): New function.
(ix86_no_caller_saved_abi_void): Likewise.
(ix86_no_caller_saved_abi_ax): Likewise.
(ix86_no_caller_saved_abi_ax_dx): Likewise.
(ix86_no_caller_saved_abi_xmm0): Likewise.
(ix86_no_caller_saved_abi_xmm0_xmm1): Likewise.
(ix86_original_abi): Likewise.
(ix86_alternate_abi): Likewise.
(ix86_init_original_abi): Likewise.
(ix86_function_abi_id): Likewise.
(ix86_fntype_abi): Likewise.
(ix86_hard_regno_call_part_clobbered): Handle newly added ABIs.
(TARGET_FNTYPE_ABI): New.
* config/i386/i386.md: Add ABI_ORIGINAL, ABI_ALTERNATE,
ABI_NO_CALLEE_SAVED, ABI_NO_CALLER_SAVED_RETURN_VOID,
ABI_NO_CALLER_SAVED_RETURN_AX, ABI_NO_CALLER_SAVED_RETURN_AX_DX,
ABI_NO_CALLER_SAVED_RETURN_XMM0 and
ABI_NO_CALLER_SAVED_RETURN_XMM0_XMM1.

gcc/testsuite/

PR target/124798
* gcc.target/i386/no-callee-saved-18.c: Don't check frame
register.
* gcc.target/i386/no-callee-saved-19b.c: Update the expected
instruction order.
* gcc.target/i386/no-callee-saved-19d.c: Likewise.
* gcc.target/i386/no-callee-saved-19e.c: Likewise.
* gcc.target/i386/no-callee-saved-2.c: Check frame register isn't
saved nor restored in 64-bit mode.
* gcc.target/i386/no-callee-saved-8.c: Expect no saving nor
restoring caller-saved registers.
* gcc.target/i386/no-callee-saved-9.c: Likewise.
* gcc.target/i386/preserve-none-14.c: Don't check frame register.
* gcc.target/i386/preserve-none-23.c: Likewise.
* gcc.target/i386/preserve-none-7.c: Check frame register isn't
saved nor restored in 64-bit mode
* gcc.target/i386/no-caller-saved-1-ms.c: New test.
* gcc.target/i386/no-caller-saved-1-sysv.c: Likewise.
* gcc.target/i386/no-caller-saved-1.c: Likewise.
* gcc.target/i386/no-caller-saved-2.c: Likewise.
* gcc.target/i386/no-caller-saved-3.c: Likewise.
* gcc.target/i386/no-caller-saved-4.c: Likewise.
* gcc.target/i386/no-caller-saved-5.c: Likewise.
* gcc.target/i386/no-caller-saved-6.c: Likewise.
* gcc.target/i386/no-caller-saved-7.c: Likewise.
* gcc.target/i386/stack-check-17.c: Also expect 1 pop in 64-bit
mode.


-- 
H.J.
From 8f6ebc95073e972b75796e9109ca1242f7bee94d Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <[email protected]>
Date: Tue, 14 Apr 2026 18:37:20 +0800
Subject: [PATCH v2] x86: Implement TARGET_FNTYPE_ABI

Implement TARGET_FNTYPE_ABI to avoid spills of callee-saved registers
when calling functions with no_caller_saved_registers attribute.

1. For functions with no_callee_saved_registers attribute, frame register
is preserved to mitigate PR target/114116.  MMX and x87 registers aren't
clobbered if MMX and x87 aren't enabled.
2. For functions with no_caller_saved_registers attribute, MMX and x87
registers are clobbered since saving and restoring registers doesn't
include MMX nor x87 registers.
3. Don't mark disabled registers as call used to avoid reg_to_stack
crashes when x87 registers are still accessed even with -mno-mmx
-mno-80387.
4. Add ABI_ORIGINAL which is the function ABI without attributes on
the current function.
5. Add ABI_ALTERNATE which is the alternate function ABI from ABI_DEFAULT.
If ix86_abi is SYSV_ABI, ABI_ALTERNATE is the function ABI for MS_ABI.
Otherwise, ABI_ALTERNATE is the function ABI for SYSV_ABI.

Tested on Linux/x86-64 and with CPython 3.14.4.

gcc/

	PR target/124798
	* config/i386/i386-expand.cc: Include "function-abi.h".
	(ix86_expand_call): Add call clobbers only when callee is
	no-callee-saved and caller isn't.  Use callee abi to get the
	list of call clobbers.
	* config/i386/i386-options.cc (ix86_init_machine_status): Call
	ix86_init_original_abi.
	* config/i386/i386-protos.h
	(ix86_type_no_callee_saved_registers_p): Removed.
	(ix86_init_original_abi): New.
	* config/i386/i386.cc (ix86_conditional_register_usage): Mark
	all, but frame pointer, are caller-saved in no-callee-saved and
	preserve_none functions.  Clear disabled registers in
	call_used_regs.
	(ix86_type_no_callee_saved_registers_p): Make it static.
	(ix86_no_callee_saved_abi): New function.
	(ix86_no_caller_saved_abi_void): Likewise.
	(ix86_no_caller_saved_abi_ax): Likewise.
	(ix86_no_caller_saved_abi_ax_dx): Likewise.
	(ix86_no_caller_saved_abi_xmm0): Likewise.
	(ix86_no_caller_saved_abi_xmm0_xmm1): Likewise.
	(ix86_original_abi): Likewise.
	(ix86_alternate_abi): Likewise.
	(ix86_init_original_abi): Likewise.
	(ix86_function_abi_id): Likewise.
	(ix86_fntype_abi): Likewise.
	(ix86_hard_regno_call_part_clobbered): Handle newly added ABIs.
	(TARGET_FNTYPE_ABI): New.
	* config/i386/i386.md: Add ABI_ORIGINAL, ABI_ALTERNATE,
	ABI_NO_CALLEE_SAVED, ABI_NO_CALLER_SAVED_RETURN_VOID,
	ABI_NO_CALLER_SAVED_RETURN_AX, ABI_NO_CALLER_SAVED_RETURN_AX_DX,
	ABI_NO_CALLER_SAVED_RETURN_XMM0 and
	ABI_NO_CALLER_SAVED_RETURN_XMM0_XMM1.

gcc/testsuite/

	PR target/124798
	* gcc.target/i386/no-callee-saved-18.c: Don't check frame
	register.
	* gcc.target/i386/no-callee-saved-19b.c: Update the expected
	instruction order.
	* gcc.target/i386/no-callee-saved-19d.c: Likewise.
	* gcc.target/i386/no-callee-saved-19e.c: Likewise.
	* gcc.target/i386/no-callee-saved-2.c: Check frame register isn't
	saved nor restored in 64-bit mode.
	* gcc.target/i386/no-callee-saved-8.c: Expect no saving nor
	restoring caller-saved registers.
	* gcc.target/i386/no-callee-saved-9.c: Likewise.
	* gcc.target/i386/preserve-none-14.c: Don't check frame register.
	* gcc.target/i386/preserve-none-23.c: Likewise.
	* gcc.target/i386/preserve-none-7.c: Check frame register isn't
	saved nor restored in 64-bit mode
	* gcc.target/i386/no-caller-saved-1-ms.c: New test.
	* gcc.target/i386/no-caller-saved-1-sysv.c: Likewise.
	* gcc.target/i386/no-caller-saved-1.c: Likewise.
	* gcc.target/i386/no-caller-saved-2.c: Likewise.
	* gcc.target/i386/no-caller-saved-3.c: Likewise.
	* gcc.target/i386/no-caller-saved-4.c: Likewise.
	* gcc.target/i386/no-caller-saved-5.c: Likewise.
	* gcc.target/i386/no-caller-saved-6.c: Likewise.
	* gcc.target/i386/no-caller-saved-7.c: Likewise.
	* gcc.target/i386/stack-check-17.c: Also expect 1 pop in 64-bit
	mode.

Signed-off-by: H.J. Lu <[email protected]>
---
 gcc/config/i386/i386-expand.cc                |  31 +-
 gcc/config/i386/i386-options.cc               |   4 +
 gcc/config/i386/i386-protos.h                 |   2 +-
 gcc/config/i386/i386.cc                       | 393 +++++++++++++++++-
 gcc/config/i386/i386.md                       |  30 +-
 .../gcc.target/i386/no-callee-saved-18.c      |   2 -
 .../gcc.target/i386/no-callee-saved-19b.c     |   8 +-
 .../gcc.target/i386/no-callee-saved-19d.c     |   6 +-
 .../gcc.target/i386/no-callee-saved-19e.c     |   8 +-
 .../gcc.target/i386/no-callee-saved-2.c       |  10 +-
 .../gcc.target/i386/no-callee-saved-8.c       |   8 +-
 .../gcc.target/i386/no-callee-saved-9.c       |  10 +-
 .../gcc.target/i386/no-caller-saved-1-ms.c    |  50 +++
 .../gcc.target/i386/no-caller-saved-1-sysv.c  |  46 ++
 .../gcc.target/i386/no-caller-saved-1.c       |  50 +++
 .../gcc.target/i386/no-caller-saved-2.c       |  49 +++
 .../gcc.target/i386/no-caller-saved-3.c       |  49 +++
 .../gcc.target/i386/no-caller-saved-4.c       |  44 ++
 .../gcc.target/i386/no-caller-saved-5.c       |  34 ++
 .../gcc.target/i386/no-caller-saved-6.c       |  34 ++
 .../gcc.target/i386/no-caller-saved-7.c       |  49 +++
 .../gcc.target/i386/preserve-none-14.c        |   2 -
 .../gcc.target/i386/preserve-none-23.c        |   2 -
 .../gcc.target/i386/preserve-none-7.c         |  10 +-
 .../gcc.target/i386/stack-check-17.c          |   3 +-
 25 files changed, 860 insertions(+), 74 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/no-caller-saved-1-ms.c
 create mode 100644 gcc/testsuite/gcc.target/i386/no-caller-saved-1-sysv.c
 create mode 100644 gcc/testsuite/gcc.target/i386/no-caller-saved-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/no-caller-saved-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/no-caller-saved-3.c
 create mode 100644 gcc/testsuite/gcc.target/i386/no-caller-saved-4.c
 create mode 100644 gcc/testsuite/gcc.target/i386/no-caller-saved-5.c
 create mode 100644 gcc/testsuite/gcc.target/i386/no-caller-saved-6.c
 create mode 100644 gcc/testsuite/gcc.target/i386/no-caller-saved-7.c

diff --git a/gcc/config/i386/i386-expand.cc b/gcc/config/i386/i386-expand.cc
index df44a4eb99d..443eacbfe62 100644
--- a/gcc/config/i386/i386-expand.cc
+++ b/gcc/config/i386/i386-expand.cc
@@ -94,6 +94,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "i386-builtins.h"
 #include "i386-expand.h"
 #include "asan.h"
+#include "function-abi.h"
 
 /* Split one or more double-mode RTL references into pairs of half-mode
    references.  The RTL can be REG, offsettable MEM, integer constant, or
@@ -11081,7 +11082,8 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
   rtx use = NULL, call;
   unsigned int vec_len = 0;
   tree fndecl;
-  bool call_no_callee_saved_registers = false;
+  function_abi caller_abi = fndecl_abi (current_function_decl);
+  function_abi callee_abi = caller_abi;
 
   if (SYMBOL_REF_P (XEXP (fnaddr, 0)))
     {
@@ -11091,8 +11093,8 @@ 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 (ix86_type_no_callee_saved_registers_p (TREE_TYPE (fndecl)))
-	    call_no_callee_saved_registers = true;
+	  else if (TREE_CODE (fndecl) == FUNCTION_DECL)
+	    callee_abi = fndecl_abi (fndecl);
 	  if (fndecl == current_function_decl
 	      && decl_binds_to_current_def_p (fndecl))
 	    cfun->machine->recursive_function = true;
@@ -11103,10 +11105,8 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
       if (MEM_P (fnaddr))
 	{
 	  tree mem_expr = MEM_EXPR (fnaddr);
-	  if (mem_expr != nullptr
-	      && TREE_CODE (mem_expr) == MEM_REF
-	      && ix86_type_no_callee_saved_registers_p (TREE_TYPE (mem_expr)))
-	    call_no_callee_saved_registers = true;
+	  if (mem_expr != nullptr && TREE_CODE (mem_expr) == MEM_REF)
+	    callee_abi = fntype_abi (TREE_TYPE (mem_expr));
 	}
 
       fndecl = NULL_TREE;
@@ -11320,21 +11320,14 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
       clobber_reg (&use, gen_rtx_REG (DImode, R10_REG));
     }
 
-  if (call_no_callee_saved_registers)
+  if (callee_abi.id () == ABI_NO_CALLEE_SAVED
+      && caller_abi.id () != ABI_NO_CALLEE_SAVED)
     {
-      /* After calling a no_callee_saved_registers function, all
-	 registers may be clobbered.  Clobber all registers that are
-	 not used by the callee.  */
-      bool is_64bit_ms_abi = (TARGET_64BIT
-			      && ix86_function_abi (fndecl) == MS_ABI);
-      char c_mask = CALL_USED_REGISTERS_MASK (is_64bit_ms_abi);
       for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-	if (!fixed_regs[i]
+	if (GENERAL_REGNO_P (i)
+	    && TEST_HARD_REG_BIT (accessible_reg_set, i)
 	    && i != HARD_FRAME_POINTER_REGNUM
-	    && !(ix86_call_used_regs[i] == 1
-		 || (ix86_call_used_regs[i] & c_mask))
-	    && !STACK_REGNO_P (i)
-	    && !MMX_REGNO_P (i))
+	    && callee_abi.clobbers_at_least_part_of_reg_p (i))
 	  clobber_reg (&use,
 		       gen_rtx_REG (GET_MODE (regno_reg_rtx[i]), i));
     }
diff --git a/gcc/config/i386/i386-options.cc b/gcc/config/i386/i386-options.cc
index 7ffe9cd2a38..890b36835dd 100644
--- a/gcc/config/i386/i386-options.cc
+++ b/gcc/config/i386/i386-options.cc
@@ -2004,6 +2004,10 @@ ix86_init_machine_status (void)
   f->stack_frame_required = true;
   f->silent_p = true;
 
+  /* NB: Call ix86_init_original_abi to make a copy of the function ABI
+     without attributes on the current function.  */
+  ix86_init_original_abi ();
+
   return f;
 }
 
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 4ba4fb08556..1664a1a06a3 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -283,10 +283,10 @@ 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
 
+extern void ix86_init_original_abi (void);
 extern rtx ix86_tls_module_base (void);
 extern bool ix86_gpr_tls_address_pattern_p (rtx);
 extern bool ix86_tls_address_pattern_p (rtx);
diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
index 2744c749578..ab30d03e998 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -503,16 +503,33 @@ ix86_conditional_register_usage (void)
 {
   int i, c_mask;
 
-  /* If there are no caller-saved registers, preserve all registers.
-     except fixed_regs and registers used for function return value
-     since aggregate_value_p checks call_used_regs[regno] on return
-     value.  */
-  if (cfun
-      && (cfun->machine->call_saved_registers
-	  == TYPE_NO_CALLER_SAVED_REGISTERS))
-    for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-      if (!fixed_regs[i] && !ix86_function_value_regno_p (i))
-	call_used_regs[i] = 0;
+  if (cfun)
+    switch (cfun->machine->call_saved_registers)
+      {
+      case TYPE_DEFAULT_CALL_SAVED_REGISTERS:
+	break;
+
+      case TYPE_NO_CALLER_SAVED_REGISTERS:
+	/* If there are no caller-saved registers, preserve all
+	   registers.  except fixed_regs and registers used for
+	   function return value since aggregate_value_p checks
+	   call_used_regs[regno] on return value.  */
+	for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+	  if (!fixed_regs[i] && !ix86_function_value_regno_p (i))
+	    call_used_regs[i] = 0;
+	break;
+
+      case TYPE_NO_CALLEE_SAVED_REGISTERS:
+      case TYPE_PRESERVE_NONE:
+	/* All, but frame pointer, are caller-saved.  */
+	for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+	  if (GENERAL_REGNO_P (i)
+	      || SSE_REGNO_P (i)
+	      || MASK_REGNO_P (i))
+	    call_used_regs[i] = 1;
+	call_used_regs[BP_REG] = 0;
+	break;
+      }
 
   /* For 32-bit targets, disable the REX registers.  */
   if (! TARGET_64BIT)
@@ -571,6 +588,13 @@ ix86_conditional_register_usage (void)
       for (i = FIRST_REX2_INT_REG; i <= LAST_REX2_INT_REG; i++)
 	CLEAR_HARD_REG_BIT (accessible_reg_set, i);
     }
+
+  /* If a register is disabled, it can't be used for call.  This avoids
+     reg_to_stack crashes when x87 registers are still accessed even
+     with -mno-mmx -mno-80387.  */
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    if (!TEST_HARD_REG_BIT (accessible_reg_set, i))
+      call_used_regs[i] = 0;
 }
 
 /* Canonicalize a comparison from one we don't have to one we do have.  */
@@ -932,7 +956,7 @@ x86_64_elf_unique_section (tree decl, int reloc)
 /* Return true if TYPE has no_callee_saved_registers or preserve_none
    attribute.  */
 
-bool
+static bool
 ix86_type_no_callee_saved_registers_p (const_tree type)
 {
   return (lookup_attribute ("no_callee_saved_registers",
@@ -21854,6 +21878,307 @@ ix86_hard_regno_mode_ok (unsigned int regno, machine_mode mode)
   return false;
 }
 
+/* Return the descriptor of no_callee_saved_registers function type.
+   None of registers are preserved, except for frame register to
+   mitigate PR target/114116.  MMX and x87 registers are preserved
+   if MMX and x87 aren't enabled.  */
+
+static const predefined_function_abi &
+ix86_no_callee_saved_abi (void)
+{
+  auto &no_callee_saved_abi = function_abis[ABI_NO_CALLEE_SAVED];
+  if (!no_callee_saved_abi.initialized_p ())
+    {
+      HARD_REG_SET full_reg_clobbers = reg_class_contents[ALL_REGS];
+      CLEAR_HARD_REG_BIT (full_reg_clobbers, HARD_FRAME_POINTER_REGNUM);
+      for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+	if ((!TARGET_80387 && STACK_REGNO_P (i))
+	    || (!TARGET_MMX && MMX_REGNO_P (i)))
+	  CLEAR_HARD_REG_BIT (full_reg_clobbers, i);
+      no_callee_saved_abi.initialize (ABI_NO_CALLEE_SAVED,
+				      full_reg_clobbers);
+    }
+  return no_callee_saved_abi;
+}
+
+/* Return the descriptor of no_caller_saved_registers function type.
+   All registers are preserved, except for MMX and x87 registers
+   which aren't supported when saving and restoring registers.  */
+
+static const predefined_function_abi &
+ix86_no_caller_saved_abi_void (void)
+{
+  auto &no_caller_saved_abi
+    = function_abis[ABI_NO_CALLER_SAVED_RETURN_VOID];
+  if (!no_caller_saved_abi.initialized_p ())
+    {
+      HARD_REG_SET full_reg_clobbers = {};
+      for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+	if ((TARGET_80387 && STACK_REGNO_P (i))
+	    || (TARGET_MMX && MMX_REGNO_P (i)))
+	  SET_HARD_REG_BIT (full_reg_clobbers, i);
+      no_caller_saved_abi.initialize
+	(ABI_NO_CALLER_SAVED_RETURN_VOID, full_reg_clobbers);
+    }
+  return no_caller_saved_abi;
+}
+
+/* Return the descriptor of no_caller_saved_registers function type.
+   All registers are preserved, except for AX used for return value,
+   MMX and x87 registers which aren't supported when saving and
+   restoring registers.  */
+
+static const predefined_function_abi &
+ix86_no_caller_saved_abi_ax (void)
+{
+  auto &no_caller_saved_abi
+    = function_abis[ABI_NO_CALLER_SAVED_RETURN_AX];
+  if (!no_caller_saved_abi.initialized_p ())
+    {
+      HARD_REG_SET full_reg_clobbers = {};
+      for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+	if (i == AX_REG
+	    || (TARGET_80387 && STACK_REGNO_P (i))
+	    || (TARGET_MMX && MMX_REGNO_P (i)))
+	  SET_HARD_REG_BIT (full_reg_clobbers, i);
+      no_caller_saved_abi.initialize
+	(ABI_NO_CALLER_SAVED_RETURN_AX, full_reg_clobbers);
+    }
+  return no_caller_saved_abi;
+}
+
+/* Return the descriptor of no_caller_saved_registers function type.
+   All registers are preserved, except for AX/DX used for return value,
+   MMX and x87 registers which aren't supported when saving and
+   restoring registers.  */
+
+static const predefined_function_abi &
+ix86_no_caller_saved_abi_ax_dx (void)
+{
+  auto &no_caller_saved_abi
+    = function_abis[ABI_NO_CALLER_SAVED_RETURN_AX_DX];
+  if (!no_caller_saved_abi.initialized_p ())
+    {
+      HARD_REG_SET full_reg_clobbers = {};
+      for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+	if (i == AX_REG
+	    || i == DX_REG
+	    || (TARGET_80387 && STACK_REGNO_P (i))
+	    || (TARGET_MMX && MMX_REGNO_P (i)))
+	  SET_HARD_REG_BIT (full_reg_clobbers, i);
+      no_caller_saved_abi.initialize
+	(ABI_NO_CALLER_SAVED_RETURN_AX_DX, full_reg_clobbers);
+    }
+  return no_caller_saved_abi;
+}
+
+/* Return the descriptor of no_caller_saved_registers function type.
+   All registers are preserved, except for XMM0 used for return value,
+   MMX and x87 registers which aren't supported when saving and
+   restoring registers.  */
+
+static const predefined_function_abi &
+ix86_no_caller_saved_abi_xmm0 (void)
+{
+  auto &no_caller_saved_abi
+    = function_abis[ABI_NO_CALLER_SAVED_RETURN_XMM0];
+  if (!no_caller_saved_abi.initialized_p ())
+    {
+      HARD_REG_SET full_reg_clobbers = {};
+      for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+	if (i == XMM0_REG
+	    || (TARGET_80387 && STACK_REGNO_P (i))
+	    || (TARGET_MMX && MMX_REGNO_P (i)))
+	  SET_HARD_REG_BIT (full_reg_clobbers, i);
+      no_caller_saved_abi.initialize
+	(ABI_NO_CALLER_SAVED_RETURN_XMM0, full_reg_clobbers);
+    }
+  return no_caller_saved_abi;
+}
+
+/* Return the descriptor of no_caller_saved_registers function type.
+   All registers are preserved, except for XMM0/XMM1 used for return
+   value, MMX and x87 registers which aren't supported when saving and
+   restoring registers.  */
+
+static const predefined_function_abi &
+ix86_no_caller_saved_abi_xmm0_xmm1 (void)
+{
+  auto &no_caller_saved_abi
+    = function_abis[ABI_NO_CALLER_SAVED_RETURN_XMM0_XMM1];
+  if (!no_caller_saved_abi.initialized_p ())
+    {
+      HARD_REG_SET full_reg_clobbers = {};
+      for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+	if (i == XMM0_REG
+	    || i == XMM1_REG
+	    || (TARGET_80387 && STACK_REGNO_P (i))
+	    || (TARGET_MMX && MMX_REGNO_P (i)))
+	  SET_HARD_REG_BIT (full_reg_clobbers, i);
+      no_caller_saved_abi.initialize
+	(ABI_NO_CALLER_SAVED_RETURN_XMM0_XMM1, full_reg_clobbers);
+    }
+  return no_caller_saved_abi;
+}
+
+/* Return the descriptor of the function ABI type without attributes
+   on the current function.  */
+
+static const predefined_function_abi &
+ix86_original_abi (void)
+{
+  auto &original_abi = function_abis[ABI_ORIGINAL];
+  if (!original_abi.initialized_p ())
+    {
+      HARD_REG_SET full_reg_clobbers
+	= default_function_abi.full_reg_clobbers ();
+      original_abi.initialize (ABI_ORIGINAL, full_reg_clobbers);
+    }
+  return original_abi;
+}
+
+/* Return the descriptor of the function alternate ABI type.  */
+
+static const predefined_function_abi &
+ix86_alternate_abi (void)
+{
+  static const char ix86_call_used_regs[] = CALL_USED_REGISTERS;
+  auto &alternate_abi = function_abis[ABI_ALTERNATE];
+  if (!alternate_abi.initialized_p ())
+    {
+      HARD_REG_SET full_reg_clobbers = {};
+
+      /* Add all registers that are clobbered by the call.  NB: If the
+	 current ABI is SYSV_ABI, the alternate ABI is MS_ABI.   */
+      bool is_64bit_ms_abi = TARGET_64BIT && ix86_abi == SYSV_ABI;
+      char c_mask = CALL_USED_REGISTERS_MASK (is_64bit_ms_abi);
+      for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+	if (!fixed_regs[i]
+	    && (ix86_call_used_regs[i] == 1
+		|| (ix86_call_used_regs[i] & c_mask)))
+	  SET_HARD_REG_BIT (full_reg_clobbers, i);
+      alternate_abi.initialize (ABI_ORIGINAL, full_reg_clobbers);
+    }
+  return alternate_abi;
+}
+
+/* Make ABI_ORIGINAL a copy of the function ABI without attributes on
+   the current function.  */
+
+void
+ix86_init_original_abi (void)
+{
+  gcc_assert (default_function_abi.initialized_p ());
+  ix86_original_abi ();
+}
+
+/* Return the function ABI ID based on FNTYPE.  */
+
+static int
+ix86_function_abi_id (const_tree fntype)
+{
+  if (ix86_type_no_callee_saved_registers_p (fntype))
+    return ABI_NO_CALLEE_SAVED;
+
+  if (lookup_attribute ("no_caller_saved_registers",
+			TYPE_ATTRIBUTES (fntype)))
+    {
+      tree type = TREE_TYPE (fntype);
+      if (VOID_TYPE_P (type))
+	return ABI_NO_CALLER_SAVED_RETURN_VOID;
+      /* AX register contains the address of the return value location
+	 passed in by the caller.  */
+      else if (ix86_return_in_memory (type, fntype))
+	return ABI_NO_CALLER_SAVED_RETURN_AX;
+      rtx ret = ix86_function_value (type, fntype, false);
+      unsigned int nregs;
+      if (REG_P (ret))
+	{
+	  unsigned int regno = REGNO (ret);
+	  if (STACK_REGNO_P (regno) || MMX_REGNO_P (regno))
+	    return ABI_NO_CALLER_SAVED_RETURN_VOID;
+	  else
+	    switch (regno)
+	      {
+	      case AX_REG:
+		nregs = REG_NREGS (ret);
+		if (nregs == 1)
+		  return ABI_NO_CALLER_SAVED_RETURN_AX;
+		else if (nregs == 2)
+		  return ABI_NO_CALLER_SAVED_RETURN_AX_DX;
+		break;
+	      case XMM0_REG:
+		return ABI_NO_CALLER_SAVED_RETURN_XMM0;
+	      default:
+		gcc_unreachable ();
+	      }
+	}
+      else if (GET_CODE (ret) == PARALLEL && XVECLEN (ret, 0) == 2)
+	{
+	  rtx x0 = XVECEXP (ret, 0, 0);
+	  rtx x1 = XVECEXP (ret, 0, 1);
+	  if (GET_CODE (x0) == EXPR_LIST
+	      && GET_CODE (x1) == EXPR_LIST)
+	    {
+	      x0 = XEXP (x0, 0);
+	      x1 = XEXP (x1, 0);
+	      if (REG_P (x0)
+		  && REGNO (x0) == XMM0_REG
+		  && REG_P (x1)
+		  && REGNO (x1) == XMM1_REG)
+		return ABI_NO_CALLER_SAVED_RETURN_XMM0_XMM1;
+	    }
+
+	  gcc_unreachable ();
+	}
+    }
+
+  /* NB: This must be the last since other attributes change the
+     function ABI.  */
+  if (ix86_function_type_abi (fntype) != ix86_abi)
+    return ABI_ALTERNATE;
+
+  return ABI_ORIGINAL;
+}
+
+/* Implement TARGET_FNTYPE_ABI.  */
+
+static const predefined_function_abi &
+ix86_fntype_abi (const_tree fntype)
+{
+  switch (ix86_function_abi_id (fntype))
+    {
+    case ABI_ORIGINAL:
+      return ix86_original_abi ();
+
+    case ABI_ALTERNATE:
+      return ix86_alternate_abi ();
+
+    case ABI_NO_CALLEE_SAVED:
+      return ix86_no_callee_saved_abi ();
+
+    case ABI_NO_CALLER_SAVED_RETURN_VOID:
+      return ix86_no_caller_saved_abi_void ();
+
+    case ABI_NO_CALLER_SAVED_RETURN_AX:
+      return ix86_no_caller_saved_abi_ax ();
+
+    case ABI_NO_CALLER_SAVED_RETURN_AX_DX:
+      return ix86_no_caller_saved_abi_ax_dx ();
+
+    case ABI_NO_CALLER_SAVED_RETURN_XMM0:
+      return ix86_no_caller_saved_abi_xmm0 ();
+
+    case ABI_NO_CALLER_SAVED_RETURN_XMM0_XMM1:
+      return ix86_no_caller_saved_abi_xmm0_xmm1 ();
+
+    default:
+      gcc_unreachable ();
+    }
+
+  return default_function_abi;
+}
+
 /* Implement TARGET_INSN_CALLEE_ABI.  */
 
 const predefined_function_abi &
@@ -21904,12 +22229,51 @@ static bool
 ix86_hard_regno_call_part_clobbered (unsigned int abi_id, unsigned int regno,
 				     machine_mode mode)
 {
-  /* Special ABI for vzeroupper which only clobber higher part of sse regs.  */
-  if (abi_id == ABI_VZEROUPPER)
+  if (abi_id == ABI_DEFAULT)
+    {
+      /* Get the ABI ID from the current function.  */
+      if (cfun)
+	abi_id = ix86_function_abi_id (TREE_TYPE (cfun->decl));
+      else
+	abi_id = ABI_ORIGINAL;
+    }
+
+  switch (abi_id)
+    {
+    case ABI_VZEROUPPER:
+      /* Special ABI for vzeroupper which only clobbers higher part of
+	 SSE registers.  */
       return (GET_MODE_SIZE (mode) > 16
 	      && ((TARGET_64BIT && REX_SSE_REGNO_P (regno))
 		  || LEGACY_SSE_REGNO_P (regno)));
 
+    case ABI_ORIGINAL:
+    case ABI_ALTERNATE:
+    case ABI_NO_CALLEE_SAVED:
+      break;
+
+    case ABI_NO_CALLER_SAVED_RETURN_VOID:
+    case ABI_NO_CALLER_SAVED_RETURN_AX:
+    case ABI_NO_CALLER_SAVED_RETURN_AX_DX:
+      /* These ABIs don't clobber SSE registers.  */
+      return false;
+
+    case ABI_NO_CALLER_SAVED_RETURN_XMM0:
+      /* This ABI only clobbers XMM0.  */
+      if (regno != XMM0_REG)
+	return false;
+      break;
+
+    case ABI_NO_CALLER_SAVED_RETURN_XMM0_XMM1:
+      /* This ABI only clobbers XMM0 and XMM1.  */
+      if (regno != XMM0_REG && regno != XMM1_REG)
+	return false;
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
   return SSE_REGNO_P (regno) && GET_MODE_SIZE (mode) > 16;
 }
 
@@ -28786,6 +29150,9 @@ ix86_libgcc_floating_mode_supported_p
 #define TARGET_HARD_REGNO_CALL_PART_CLOBBERED \
   ix86_hard_regno_call_part_clobbered
 
+#undef TARGET_FNTYPE_ABI
+#define TARGET_FNTYPE_ABI ix86_fntype_abi
+
 #undef TARGET_INSN_CALLEE_ABI
 #define TARGET_INSN_CALLEE_ABI ix86_insn_callee_abi
 
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index b4e397bc925..3ac7aca9461 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -511,11 +511,33 @@ (define_constants
    (FIRST_PSEUDO_REG		92)
   ])
 
-;; Insn callee abi index.
+;; Insn callee abi index.  ABI_DEFAULT is the funtion ABI for the
+;; current function.  ABI_ORIGINAL is the function ABI without
+;; attributes on the current function.  ABI_ALTERNATE is the Windows
+;; function ABI if ix86_abi == SYSV_ABI and is the SYSV function ABI
+;; if ix86_abi == MS_ABI.
 (define_constants
-  [(ABI_DEFAULT		0)
-   (ABI_VZEROUPPER	1)
-   (ABI_UNKNOWN		2)])
+  [(ABI_DEFAULT                             0)
+   (ABI_VZEROUPPER                          1)
+   (ABI_ORIGINAL                            2)
+   (ABI_ALTERNATE                           3)
+   (ABI_NO_CALLEE_SAVED                     4)
+   ;; Return void.
+   (ABI_NO_CALLER_SAVED_RETURN_VOID         5)
+   ;; Return char, short, int in 32-bit/64-bit.
+   ;; Return int64 and _Complex int in 64-bit.
+   ;; Return _Complex float in MS 32-bit/64-bit.
+   (ABI_NO_CALLER_SAVED_RETURN_AX           6)
+   ;; Return int64 and _Complex int in 32-bit.
+   ;; Return _Complex int64 in 64-bit.
+   (ABI_NO_CALLER_SAVED_RETURN_AX_DX        7)
+   ;; Return float and double in 64-bit.
+   ;; Return _Complex float in SYSV 64-bit.
+   ;; Return int28, _Complex double in MS 64-bit.
+   (ABI_NO_CALLER_SAVED_RETURN_XMM0         8)
+   ;; Return _Complex double in SYSV 64-bit.
+   (ABI_NO_CALLER_SAVED_RETURN_XMM0_XMM1    9)
+   (ABI_UNKNOWN                             10)])
 
 ;; Insns whose names begin with "x86_" are emitted by gen_FOO calls
 ;; from i386.cc.
diff --git a/gcc/testsuite/gcc.target/i386/no-callee-saved-18.c b/gcc/testsuite/gcc.target/i386/no-callee-saved-18.c
index 128b9c46e8e..5e228753d8a 100644
--- a/gcc/testsuite/gcc.target/i386/no-callee-saved-18.c
+++ b/gcc/testsuite/gcc.target/i386/no-callee-saved-18.c
@@ -19,7 +19,6 @@ foo (uintptr_t p)
 /* { 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-not "push(?:l|q)\[\\t \]*%(?:e|r)bp" } } */
 /* { 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 } } } */
@@ -36,7 +35,6 @@ foo (uintptr_t p)
 /* { 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-not "pop(?:l|q)\[\\t \]*%(?:e|r)bp" } } */
 /* { 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 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/no-callee-saved-19b.c b/gcc/testsuite/gcc.target/i386/no-callee-saved-19b.c
index dc38936a61a..d784065b456 100644
--- a/gcc/testsuite/gcc.target/i386/no-callee-saved-19b.c
+++ b/gcc/testsuite/gcc.target/i386/no-callee-saved-19b.c
@@ -52,15 +52,15 @@
 **	.cfi_startproc
 **	subl	\$376, %esp
 **...
+**	movq	%rdi, 296\(%rsp\)
+**...
+**	movl	\$code\+4, %edi
+**	movq	%rbp, 304\(%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\+4, %edi
-**	movq	%rbp, 304\(%rsp\)
 **	movq	%r8, 312\(%rsp\)
 **	movq	%r9, 320\(%rsp\)
 **	movq	%r10, 328\(%rsp\)
diff --git a/gcc/testsuite/gcc.target/i386/no-callee-saved-19d.c b/gcc/testsuite/gcc.target/i386/no-callee-saved-19d.c
index 4657e170350..bb9dce13350 100644
--- a/gcc/testsuite/gcc.target/i386/no-callee-saved-19d.c
+++ b/gcc/testsuite/gcc.target/i386/no-callee-saved-19d.c
@@ -50,14 +50,14 @@
 **	.cfi_startproc
 **	subq	\$504, %rsp
 **...
+**	movq	%rdi, 304\(%rsp\)
+**...
+**	movl	\$code\+8, %edi
 **	movq	%rax, 264\(%rsp\)
 **	movq	%rdx, 272\(%rsp\)
 **	movq	%rcx, 280\(%rsp\)
 **	movq	%rbx, 288\(%rsp\)
 **	movq	%rsi, 296\(%rsp\)
-**	movq	%rdi, 304\(%rsp\)
-**...
-**	movl	\$code\+8, %edi
 **	movq	%r8, 312\(%rsp\)
 **	movq	%r9, 320\(%rsp\)
 **	movq	%r10, 328\(%rsp\)
diff --git a/gcc/testsuite/gcc.target/i386/no-callee-saved-19e.c b/gcc/testsuite/gcc.target/i386/no-callee-saved-19e.c
index 8e0bbe82eae..617bb755f85 100644
--- a/gcc/testsuite/gcc.target/i386/no-callee-saved-19e.c
+++ b/gcc/testsuite/gcc.target/i386/no-callee-saved-19e.c
@@ -52,15 +52,15 @@
 **	.cfi_startproc
 **	subl	\$504, %esp
 **...
+**	movq	%rdi, 296\(%rsp\)
+**...
+**	movl	\$code\+4, %edi
+**	movq	%rbp, 304\(%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\+4, %edi
-**	movq	%rbp, 304\(%rsp\)
 **	movq	%r8, 312\(%rsp\)
 **	movq	%r9, 320\(%rsp\)
 **	movq	%r10, 328\(%rsp\)
diff --git a/gcc/testsuite/gcc.target/i386/no-callee-saved-2.c b/gcc/testsuite/gcc.target/i386/no-callee-saved-2.c
index e074ca51df4..86864ea9bff 100644
--- a/gcc/testsuite/gcc.target/i386/no-callee-saved-2.c
+++ b/gcc/testsuite/gcc.target/i386/no-callee-saved-2.c
@@ -26,7 +26,9 @@ foo (void *frame)
   }
 }
 
-/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)bp" 1 } } */
-/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)bp" 1 } } */
-/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*" 1 } } */
-/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)bp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)bp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-not "push(?:l|q)\[\\t \]*" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pop(?:l|q)\[\\t \]*" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/no-callee-saved-8.c b/gcc/testsuite/gcc.target/i386/no-callee-saved-8.c
index ed3d96bdca0..692166c98e9 100644
--- a/gcc/testsuite/gcc.target/i386/no-callee-saved-8.c
+++ b/gcc/testsuite/gcc.target/i386/no-callee-saved-8.c
@@ -44,7 +44,7 @@ foo (void)
 /* { 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 } } } } */
+/* { 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 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/no-callee-saved-9.c b/gcc/testsuite/gcc.target/i386/no-callee-saved-9.c
index 7730c5903d4..7acaff2ad35 100644
--- a/gcc/testsuite/gcc.target/i386/no-callee-saved-9.c
+++ b/gcc/testsuite/gcc.target/i386/no-callee-saved-9.c
@@ -17,7 +17,6 @@ foo (fn_t bar)
 /* { 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-not "push(?:l|q)\[\\t \]*%(?:e|r)bp" } } */
 /* { 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 } } } */
@@ -34,7 +33,6 @@ foo (fn_t bar)
 /* { 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-not "pop(?:l|q)\[\\t \]*%(?:e|r)bp" } } */
 /* { 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 } } } */
@@ -43,7 +41,7 @@ foo (fn_t bar)
 /* { 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 } } } } */
+/* { 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 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/no-caller-saved-1-ms.c b/gcc/testsuite/gcc.target/i386/no-caller-saved-1-ms.c
new file mode 100644
index 00000000000..9a834d49870
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/no-caller-saved-1-ms.c
@@ -0,0 +1,50 @@
+/* PR target/124798  */
+/* { dg-do compile } */
+/* { dg-options "-mabi=sysv -O2 -mtune=corei7 -mtune-ctrl=^prologue_using_move,^epilogue_using_move -fomit-frame-pointer" } */
+
+extern void foo (void) __attribute__ ((no_caller_saved_registers, ms_abi));
+
+void
+qux (void)
+{
+  int a, b, c, d, e, f;
+  asm volatile ("# %0 %1 %2 %3 %4 %5"
+		: "=r" (a), "=r" (b), "=r" (c), "=r" (d), "=r" (e), "=r" (f));
+#ifdef __x86_64__
+  int g, h, i, j, k, l, m, n, o, p;
+  asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7 %8 %9"
+		: "=r" (g), "=r" (h), "=r" (i), "=r" (j), "=r" (k), "=r" (l), "=r" (m), "=r" (n), "=r" (o), "=r" (p));
+#endif
+  foo ();
+  asm volatile ("# %0 %1 %2 %3 %4 %5"
+		:: "r" (a), "r" (b), "r" (c), "r" (d), "r" (e), "r" (f));
+#ifdef __x86_64__
+  asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7 %8 %9"
+		: : "r" (g), "r" (h), "r" (i), "r" (j), "r" (k), "r" (l), "r" (m), "r" (n), "r" (o), "r" (p));
+#endif
+}
+
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%edx, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%ecx, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%esi, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%edi, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %edx" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %ecx" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %esi" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %edi" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r8d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r9d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r10d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r11d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r12d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r13d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r14d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r15d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r8d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r9d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r10d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r11d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r12d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r13d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r14d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r15d" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/no-caller-saved-1-sysv.c b/gcc/testsuite/gcc.target/i386/no-caller-saved-1-sysv.c
new file mode 100644
index 00000000000..4bd3eecfcc6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/no-caller-saved-1-sysv.c
@@ -0,0 +1,46 @@
+/* PR target/124798  */
+/* { dg-do compile } */
+/* { dg-options "-mabi=ms -O2 -mtune=corei7 -mtune-ctrl=^prologue_using_move,^epilogue_using_move -fomit-frame-pointer" } */
+
+extern void foo (void) __attribute__ ((no_caller_saved_registers, sysv_abi));
+
+void
+qux (void)
+{
+  int a, b, c, d, e, f;
+  asm volatile ("# %0 %1 %2 %3 %4 %5"
+		: "=r" (a), "=r" (b), "=r" (c), "=r" (d), "=r" (e), "=r" (f));
+#ifdef __x86_64__
+  int g, h, i, j, k, l, m, n, o, p;
+  asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7 %8 %9"
+		: "=r" (g), "=r" (h), "=r" (i), "=r" (j), "=r" (k), "=r" (l), "=r" (m), "=r" (n), "=r" (o), "=r" (p));
+#endif
+  foo ();
+  asm volatile ("# %0 %1 %2 %3 %4 %5"
+		:: "r" (a), "r" (b), "r" (c), "r" (d), "r" (e), "r" (f));
+#ifdef __x86_64__
+  asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7 %8 %9"
+		: : "r" (g), "r" (h), "r" (i), "r" (j), "r" (k), "r" (l), "r" (m), "r" (n), "r" (o), "r" (p));
+#endif
+}
+
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%edx, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%ecx, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %edx" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %ecx" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r8d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r9d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r10d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r11d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r12d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r13d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r14d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r15d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r8d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r9d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r10d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r11d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r12d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r13d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r14d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r15d" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/no-caller-saved-1.c b/gcc/testsuite/gcc.target/i386/no-caller-saved-1.c
new file mode 100644
index 00000000000..fc8ab95c7e8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/no-caller-saved-1.c
@@ -0,0 +1,50 @@
+/* PR target/124798  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -mtune=corei7 -mtune-ctrl=^prologue_using_move,^epilogue_using_move -fomit-frame-pointer" } */
+
+[[gnu::no_caller_saved_registers]] extern void foo (void);
+
+void
+qux (void)
+{
+  int a, b, c, d, e, f;
+  asm volatile ("# %0 %1 %2 %3 %4 %5"
+		: "=r" (a), "=r" (b), "=r" (c), "=r" (d), "=r" (e), "=r" (f));
+#ifdef __x86_64__
+  int g, h, i, j, k, l, m, n, o, p;
+  asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7 %8 %9"
+		: "=r" (g), "=r" (h), "=r" (i), "=r" (j), "=r" (k), "=r" (l), "=r" (m), "=r" (n), "=r" (o), "=r" (p));
+#endif
+  foo ();
+  asm volatile ("# %0 %1 %2 %3 %4 %5"
+		:: "r" (a), "r" (b), "r" (c), "r" (d), "r" (e), "r" (f));
+#ifdef __x86_64__
+  asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7 %8 %9"
+		: : "r" (g), "r" (h), "r" (i), "r" (j), "r" (k), "r" (l), "r" (m), "r" (n), "r" (o), "r" (p));
+#endif
+}
+
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%edx, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%ecx, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%esi, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%edi, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %edx" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %ecx" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %esi" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %edi" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r8d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r9d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r10d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r11d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r12d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r13d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r14d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r15d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r8d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r9d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r10d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r11d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r12d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r13d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r14d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r15d" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/no-caller-saved-2.c b/gcc/testsuite/gcc.target/i386/no-caller-saved-2.c
new file mode 100644
index 00000000000..47b671dfa40
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/no-caller-saved-2.c
@@ -0,0 +1,49 @@
+/* PR target/124798  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -mtune=corei7 -mtune-ctrl=^prologue_using_move,^epilogue_using_move -fomit-frame-pointer" } */
+
+[[gnu::no_caller_saved_registers]] extern int foo (void);
+
+int
+qux (void)
+{
+  int a, b, c, d, e, f;
+  asm volatile ("# %0 %1 %2 %3 %4 %5"
+		: "=r" (a), "=r" (b), "=r" (c), "=r" (d), "=r" (e), "=r" (f));
+#ifdef __x86_64__
+  int g, h, i, j, k, l, m, n, o, p;
+  asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7 %8 %9"
+		: "=r" (g), "=r" (h), "=r" (i), "=r" (j), "=r" (k), "=r" (l), "=r" (m), "=r" (n), "=r" (o), "=r" (p));
+#endif
+  int ret = foo ();
+  asm volatile ("# %0 %1 %2 %3 %4 %5"
+		:: "r" (a), "r" (b), "r" (c), "r" (d), "r" (e), "r" (f));
+#ifdef __x86_64__
+  asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7 %8 %9"
+		: : "r" (g), "r" (h), "r" (i), "r" (j), "r" (k), "r" (l), "r" (m), "r" (n), "r" (o), "r" (p));
+#endif
+
+  return ret;
+}
+
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%ecx, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%esi, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%edi, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %ecx" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %esi" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %edi" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r8d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r9d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r10d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r11d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r12d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r13d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r14d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r15d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r8d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r9d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r10d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r11d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r12d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r13d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r15d" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/no-caller-saved-3.c b/gcc/testsuite/gcc.target/i386/no-caller-saved-3.c
new file mode 100644
index 00000000000..990b870c323
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/no-caller-saved-3.c
@@ -0,0 +1,49 @@
+/* PR target/124798  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -mtune=corei7 -mtune-ctrl=^prologue_using_move,^epilogue_using_move -fomit-frame-pointer" } */
+
+[[gnu::no_caller_saved_registers]] extern long long foo (void);
+
+long long
+qux (void)
+{
+  int a, b, c, d, e, f;
+  asm volatile ("# %0 %1 %2 %3 %4 %5"
+		: "=r" (a), "=r" (b), "=r" (c), "=r" (d), "=r" (e), "=r" (f));
+#ifdef __x86_64__
+  int g, h, i, j, k, l, m, n, o, p;
+  asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7 %8 %9"
+		: "=r" (g), "=r" (h), "=r" (i), "=r" (j), "=r" (k), "=r" (l), "=r" (m), "=r" (n), "=r" (o), "=r" (p));
+#endif
+  long long ret = foo ();
+  asm volatile ("# %0 %1 %2 %3 %4 %5"
+		:: "r" (a), "r" (b), "r" (c), "r" (d), "r" (e), "r" (f));
+#ifdef __x86_64__
+  asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7 %8 %9"
+		: : "r" (g), "r" (h), "r" (i), "r" (j), "r" (k), "r" (l), "r" (m), "r" (n), "r" (o), "r" (p));
+#endif
+
+  return ret;
+}
+
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%ecx, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%esi, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%edi, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %ecx" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %esi" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %edi" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r8d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r9d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r10d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r11d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r12d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r13d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r14d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r15d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r8d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r9d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r10d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r11d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r12d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r13d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r15d" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/no-caller-saved-4.c b/gcc/testsuite/gcc.target/i386/no-caller-saved-4.c
new file mode 100644
index 00000000000..9f6b494bdb7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/no-caller-saved-4.c
@@ -0,0 +1,44 @@
+/* PR target/124798  */
+/* { dg-do compile { target int128 } } */
+/* { dg-options "-O2 -mtune=corei7 -mtune-ctrl=^prologue_using_move,^epilogue_using_move -fomit-frame-pointer" } */
+
+[[gnu::no_caller_saved_registers]] extern __int128 foo (void);
+
+__int128
+qux (void)
+{
+  int a, b, c, d, e, f;
+  asm volatile ("# %0 %1 %2 %3 %4 %5"
+		: "=r" (a), "=r" (b), "=r" (c), "=r" (d), "=r" (e), "=r" (f));
+#ifdef __x86_64__
+  int g, h, i, j, k, l, m, n;
+  asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7"
+		: "=r" (g), "=r" (h), "=r" (i), "=r" (j), "=r" (k), "=r" (l), "=r" (m), "=r" (n));
+#endif
+  __int128 ret = foo ();
+  asm volatile ("# %0 %1 %2 %3 %4 %5"
+		:: "r" (a), "r" (b), "r" (c), "r" (d), "r" (e), "r" (f));
+#ifdef __x86_64__
+  asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7"
+		: : "r" (g), "r" (h), "r" (i), "r" (j), "r" (k), "r" (l), "r" (m), "r" (n));
+#endif
+
+  return ret;
+}
+
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%edx, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%ecx, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%esi, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%edi, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %edx" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %ecx" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %esi" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %edi" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r8d, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r9d, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r10d, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r11d, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r8d" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r9d" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r10d" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r11d" } } */
diff --git a/gcc/testsuite/gcc.target/i386/no-caller-saved-5.c b/gcc/testsuite/gcc.target/i386/no-caller-saved-5.c
new file mode 100644
index 00000000000..98f58fe92f5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/no-caller-saved-5.c
@@ -0,0 +1,34 @@
+/* PR target/124798  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -mtune=corei7 -msse2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move -fomit-frame-pointer" } */
+
+[[gnu::no_caller_saved_registers]] extern float foo (void);
+
+float
+qux (void)
+{
+  float a, b, c, d, e, f;
+  asm volatile ("# %0 %1 %2 %3 %4 %5"
+		: "=v" (a), "=v" (b), "=v" (c), "=v" (d), "=v" (e), "=v" (f));
+#ifdef __x86_64__
+  float g, h, i, j, k, l, m, n, o, p;
+  asm volatile ("# %0 %1"
+		: "=v" (g), "=v" (h));
+  asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7"
+		: "=v" (i), "=v" (j), "=v" (k), "=v" (l), "=v" (m), "=v" (n), "=v" (o), "=v" (p));
+#endif
+  float ret = foo ();
+  asm volatile ("# %0 %1 %2 %3 %4 %5"
+		:: "v" (a), "v" (b), "v" (c), "v" (d), "v" (e), "v" (f));
+#ifdef __x86_64__
+  asm volatile ("# %0 %1"
+		:: "v" (g), "v" (h));
+  asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7"
+		: : "v" (i), "v" (j), "v" (k), "v" (l), "v" (m), "v" (n), "v" (o), "v" (p));
+#endif
+
+  return ret;
+}
+
+/* { dg-final { scan-assembler-not "movss\[ \\t\]+%xmm\[0-9\]+, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %xmm\[0-9\]+" } } */
diff --git a/gcc/testsuite/gcc.target/i386/no-caller-saved-6.c b/gcc/testsuite/gcc.target/i386/no-caller-saved-6.c
new file mode 100644
index 00000000000..5eb5b102843
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/no-caller-saved-6.c
@@ -0,0 +1,34 @@
+/* PR target/124798  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -mtune=corei7 -msse2 -mtune-ctrl=^prologue_using_move,^epilogue_using_move -fomit-frame-pointer" } */
+
+[[gnu::no_caller_saved_registers]] extern _Complex double foo (void);
+
+_Complex double
+qux (void)
+{
+  double a, b, c, d, e, f;
+  asm volatile ("# %0 %1 %2 %3 %4 %5"
+		: "=v" (a), "=v" (b), "=v" (c), "=v" (d), "=v" (e), "=v" (f));
+#ifdef __x86_64__
+  double g, h, i, j, k, l, m, n, o, p;
+  asm volatile ("# %0 %1"
+		: "=v" (g), "=v" (h));
+  asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7"
+		: "=v" (i), "=v" (j), "=v" (k), "=v" (l), "=v" (m), "=v" (n), "=v" (o), "=v" (p));
+#endif
+  _Complex double ret = foo ();
+  asm volatile ("# %0 %1 %2 %3 %4 %5"
+		:: "v" (a), "v" (b), "v" (c), "v" (d), "v" (e), "v" (f));
+#ifdef __x86_64__
+  asm volatile ("# %0 %1"
+		:: "v" (g), "v" (h));
+  asm volatile ("# %0 %1 %2 %3 %4 %5 %6 %7"
+		: : "v" (i), "v" (j), "v" (k), "v" (l), "v" (m), "v" (n), "v" (o), "v" (p));
+#endif
+
+  return ret;
+}
+
+/* { dg-final { scan-assembler-not "movss\[ \\t\]+%xmm\[0-9\]+, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %xmm\[0-9\]+" } } */
diff --git a/gcc/testsuite/gcc.target/i386/no-caller-saved-7.c b/gcc/testsuite/gcc.target/i386/no-caller-saved-7.c
new file mode 100644
index 00000000000..c8a99e950da
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/no-caller-saved-7.c
@@ -0,0 +1,49 @@
+/* PR target/124798  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -mtune=corei7 -mtune-ctrl=^prologue_using_move,^epilogue_using_move -fomit-frame-pointer" } */
+
+typedef struct
+{
+  double d[16];
+} record;
+
+[[gnu::no_caller_saved_registers]] extern record foo (void);
+
+record
+qux (void)
+{
+  int a, b, c, d, e, f;
+  asm volatile ("# %0 %1 %2 %3 %4 %5"
+		: "=r" (a), "=r" (b), "=r" (c), "=r" (d), "=r" (e), "=r" (f));
+#ifdef __x86_64__
+  int g, h, i, j;
+  asm volatile ("# %0 %1 %2 %3"
+		: "=r" (g), "=r" (h), "=r" (i), "=r" (j));
+#endif
+  record ret = foo ();
+  asm volatile ("# %0 %1 %2 %3 %4 %5"
+		:: "r" (a), "r" (b), "r" (c), "r" (d), "r" (e), "r" (f));
+#ifdef __x86_64__
+  asm volatile ("# %0 %1 %2 %3"
+		:: "r" (g), "r" (h), "r" (i), "r" (j));
+#endif
+
+  return ret;
+}
+
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%edx, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%ecx, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%esi, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%edi, \[0-9\]*\\(%\[re\]?sp\\)" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %edx" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %ecx" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %esi" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %edi" } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r8d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r9d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r10d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+%r11d, \[0-9\]*\\(%\[re\]?sp\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r8d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r9d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r10d" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \\t\]+\[0-9\]*\\(%\[re\]?sp\\), %r11d" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-14.c b/gcc/testsuite/gcc.target/i386/preserve-none-14.c
index ca23b586fa1..175eb25acd6 100644
--- a/gcc/testsuite/gcc.target/i386/preserve-none-14.c
+++ b/gcc/testsuite/gcc.target/i386/preserve-none-14.c
@@ -17,7 +17,6 @@ foo (fn_t bar)
 /* { 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-not "push(?:l|q)\[\\t \]*%(?:e|r)bp" } } */
 /* { 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 } } } */
@@ -34,7 +33,6 @@ foo (fn_t bar)
 /* { 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-not "pop(?:l|q)\[\\t \]*%(?:e|r)bp" } } */
 /* { 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 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-23.c b/gcc/testsuite/gcc.target/i386/preserve-none-23.c
index 8e83879443f..629bd695374 100644
--- a/gcc/testsuite/gcc.target/i386/preserve-none-23.c
+++ b/gcc/testsuite/gcc.target/i386/preserve-none-23.c
@@ -19,7 +19,6 @@ foo (uintptr_t p)
 /* { 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-not "push(?:l|q)\[\\t \]*%(?:e|r)bp" } } */
 /* { 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 } } } */
@@ -36,7 +35,6 @@ foo (uintptr_t p)
 /* { 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-not "pop(?:l|q)\[\\t \]*%(?:e|r)bp" } } */
 /* { 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 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/preserve-none-7.c b/gcc/testsuite/gcc.target/i386/preserve-none-7.c
index 2c80560887c..6f252ee50a4 100644
--- a/gcc/testsuite/gcc.target/i386/preserve-none-7.c
+++ b/gcc/testsuite/gcc.target/i386/preserve-none-7.c
@@ -26,7 +26,9 @@ foo (void *frame)
   }
 }
 
-/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)bp" 1 } } */
-/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)bp" 1 } } */
-/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*" 1 } } */
-/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*" 1 } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*%(?:e|r)bp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*%(?:e|r)bp" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "push(?:l|q)\[\\t \]*" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pop(?:l|q)\[\\t \]*" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-not "push(?:l|q)\[\\t \]*" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "pop(?:l|q)\[\\t \]*" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/stack-check-17.c b/gcc/testsuite/gcc.target/i386/stack-check-17.c
index 924a459c4e2..ed2f341b106 100644
--- a/gcc/testsuite/gcc.target/i386/stack-check-17.c
+++ b/gcc/testsuite/gcc.target/i386/stack-check-17.c
@@ -32,5 +32,4 @@ f3 (void)
    register on ia32 for a noreturn function.  */
 /* { dg-final { scan-assembler-times "push\[ql\]" 1 { target { ! ia32 } } } }  */
 /* { dg-final { scan-assembler-times "push\[ql\]" 3 { target ia32 } } }  */
-/* { dg-final { scan-assembler-not "pop" { target { ! ia32 } } } } */
-/* { dg-final { scan-assembler-times "pop" 1 { target ia32 } } } */
+/* { dg-final { scan-assembler-times "pop\[ql\]" 1 } }  */
-- 
2.54.0

Reply via email to