"H.J. Lu" <[email protected]> writes:
> 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.

Like I mentioned in the PR trail for 125266, I don't think
the ix86_original_abi stuff, and the use of cfun in
ix86_conditional_register_usage, is correct.  Maybe the simplest
thing would be for me to come up with a counterproposal, based on
this patch.  It might be a few days before I can give it a go though.

Thanks,
Richard

>
> 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 } }  */

Reply via email to