On Thu, Jun 25, 2026 at 7:54 AM H.J. Lu <[email protected]> wrote:
>
> Implement TARGET_FNTYPE_ABI to avoid spills of callee-saved registers
> when calling functions with no_caller_saved_registers attribute.
>
> After
>
> commit 754c6600fe058f1026098fde57860bbc38cc28fe
> Author: Richard Sandiford <[email protected]>
> Date: Wed Jun 3 13:11:17 2026 +0100
>
> Add CALL_INSN_ABI_ID
>
> there is no need for ix86_expand_call to add clobbers for when one ABI
> calls another ABI.
>
> ABI_ALTERNATE is the alternate function ABI from the system ABI. 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.
>
> This target hook also properly marks the implicit __tls_get_addr call:
>
> (call_insn/u 9 8 10 (parallel [
> (set (reg:DI 0 ax)
> (call:DI (mem:QI (symbol_ref:DI ("__tls_get_addr")) [0 S1
> A8])
> (const_int 0 [0])))
> (unspec:DI [
> (reg/f:DI 7 sp)
> ] UNSPEC_TLS_LD_BASE)
> (clobber (reg:DI 5 di))
> ]) "x.c":7:16 -1
> (expr_list:REG_EH_REGION (const_int -2147483648 [0xffffffff80000000])
> (nil))
> (nil) 0)
>
> with ABI_DEFAULT function ABI.
>
> Tested on Linux/x86-64 and with CPython 3.14.4.
>
> gcc/
>
> PR target/124798
> PR target/125436
> * config/i386/i386-expand.cc: Include "function-abi.h".
> (x86_64_ms_sysv_extra_clobbered_registers): Removed.
> (ix86_expand_call): Don't add explicit call and ABI-switch
> clobbers.
> * config/i386/i386-features.cc (ix86_place_single_tls_call):
> Set CALL_INSN_ABI_ID on TLS calls.
> * config/i386/i386-options.cc (ix86_set_current_function): Don't
> call reinit_regs.
> * config/i386/i386-protos.h
> (ix86_type_no_callee_saved_registers_p): Removed.
> (ix86_tls_get_addr_abi): New.
> * config/i386/i386.cc (ix86_alternate_abi): New prototype.
> (ix86_conditional_register_usage): Changed to describe the
> system ABI.
> (ix86_type_no_callee_saved_registers_p): Make it static.
> (ix86_function_value_regno_p): Replace ix86_cfun_abi () with
> ix86_abi.
> (ix86_save_reg): Only check TYPE_NO_CALLEE_SAVED_REGISTERS as
> a special case.
> (ix86_tls_get_addr_abi): New.
> (legitimize_tls_address): Set CALL_INSN_ABI_ID on TLS calls.
> Call ix86_alternate_abi to initialize the alternate function ABI.
> (ix86_initialize_abi): New function.
> (ix86_no_callee_saved_abi): Likewise.
> (ix86_no_caller_saved_abi): Likewise.
> (ix86_standard_abi): Likewise.
> (ix86_alternate_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_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
> PR target/125436
> * gcc.target/i386/abi-mix-1.c: New test.
> * gcc.target/i386/abi-mix-2.c: Likewise.
> * gcc.target/i386/no-callee-saved-20.c: Likewise.
> * gcc.target/i386/no-caller-saved-1-ms.c: Likewise.
> * 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/pr125436-1a.c: Likewise.
> * gcc.target/i386/pr125436-1b.c: Likewise.
> * gcc.target/i386/pr125436-2a.c: Likewise.
> * gcc.target/i386/pr125436-2b.c: Likewise.
> * gcc.target/i386/pr125436-3.c: Likewise.
> * gcc.target/i386/pr125436-4.c: Likewise.
> * 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.
>
> Signed-off-by: H.J. Lu <[email protected]>
> Co-Authored by: Richard Sandiford <[email protected]>
> ---
> gcc/config/i386/i386-expand.cc | 113 +-----
> gcc/config/i386/i386-features.cc | 2 +
> gcc/config/i386/i386-options.cc | 17 -
> gcc/config/i386/i386-protos.h | 3 +-
> gcc/config/i386/i386.cc | 336 +++++++++++++++---
> gcc/config/i386/i386.md | 30 +-
> gcc/testsuite/gcc.target/i386/abi-mix-1.c | 20 ++
> gcc/testsuite/gcc.target/i386/abi-mix-2.c | 6 +
> .../gcc.target/i386/no-callee-saved-18.c | 2 -
> .../gcc.target/i386/no-callee-saved-19b.c | 32 +-
> .../gcc.target/i386/no-callee-saved-19d.c | 6 +-
> .../gcc.target/i386/no-callee-saved-19e.c | 33 +-
> .../gcc.target/i386/no-callee-saved-2.c | 10 +-
> .../gcc.target/i386/no-callee-saved-20.c | 92 +++++
> .../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/testsuite/gcc.target/i386/pr125436-1a.c | 130 +++++++
> gcc/testsuite/gcc.target/i386/pr125436-1b.c | 81 +++++
> gcc/testsuite/gcc.target/i386/pr125436-2a.c | 132 +++++++
> gcc/testsuite/gcc.target/i386/pr125436-2b.c | 83 +++++
> gcc/testsuite/gcc.target/i386/pr125436-3.c | 88 +++++
> gcc/testsuite/gcc.target/i386/pr125436-4.c | 88 +++++
> .../gcc.target/i386/preserve-none-14.c | 2 -
> .../gcc.target/i386/preserve-none-23.c | 2 -
> .../gcc.target/i386/preserve-none-7.c | 10 +-
> 34 files changed, 1503 insertions(+), 238 deletions(-)
> create mode 100644 gcc/testsuite/gcc.target/i386/abi-mix-1.c
> create mode 100644 gcc/testsuite/gcc.target/i386/abi-mix-2.c
> create mode 100644 gcc/testsuite/gcc.target/i386/no-callee-saved-20.c
> 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
> create mode 100644 gcc/testsuite/gcc.target/i386/pr125436-1a.c
> create mode 100644 gcc/testsuite/gcc.target/i386/pr125436-1b.c
> create mode 100644 gcc/testsuite/gcc.target/i386/pr125436-2a.c
> create mode 100644 gcc/testsuite/gcc.target/i386/pr125436-2b.c
> create mode 100644 gcc/testsuite/gcc.target/i386/pr125436-3.c
> create mode 100644 gcc/testsuite/gcc.target/i386/pr125436-4.c
>
> diff --git a/gcc/config/i386/i386-expand.cc b/gcc/config/i386/i386-expand.cc
> index 07756897fad..18b259a660d 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
> @@ -11094,17 +11095,6 @@ construct_plt_address (rtx symbol)
> return tmp;
> }
>
> -/* Additional registers that are clobbered by SYSV calls. */
> -
> -static int const x86_64_ms_sysv_extra_clobbered_registers
> - [NUM_X86_64_MS_CLOBBERED_REGS] =
> -{
> - SI_REG, DI_REG,
> - XMM6_REG, XMM7_REG,
> - XMM8_REG, XMM9_REG, XMM10_REG, XMM11_REG,
> - XMM12_REG, XMM13_REG, XMM14_REG, XMM15_REG
> -};
> -
> rtx_insn *
> ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
> rtx callarg2,
> @@ -11114,7 +11104,6 @@ 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;
>
> if (SYMBOL_REF_P (XEXP (fnaddr, 0)))
> {
> @@ -11124,26 +11113,13 @@ 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;
> if (fndecl == current_function_decl
> && decl_binds_to_current_def_p (fndecl))
> cfun->machine->recursive_function = true;
> }
> }
> else
> - {
> - 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;
> - }
> -
> - fndecl = NULL_TREE;
> - }
> + fndecl = NULL_TREE;
>
> if (pop == const0_rtx)
> pop = NULL;
> @@ -11280,62 +11256,26 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx
> callarg1,
> vec[vec_len++] = pop;
> }
>
> - static const char ix86_call_used_regs[] = CALL_USED_REGISTERS;
> -
> - if ((cfun->machine->call_saved_registers
> - == TYPE_NO_CALLER_SAVED_REGISTERS)
> - && (!fndecl
> - || (!TREE_THIS_VOLATILE (fndecl)
> - && !lookup_attribute ("no_caller_saved_registers",
> - TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))))
> + /* Set here, but it may get cleared later. */
> + if (TARGET_64BIT_MS_ABI
> + && (!callarg2 || INTVAL (callarg2) != -2)
> + && TARGET_CALL_MS2SYSV_XLOGUES)
> {
> - bool is_64bit_ms_abi = (TARGET_64BIT
> - && ix86_function_abi (fndecl) == MS_ABI);
> - char c_mask = CALL_USED_REGISTERS_MASK (is_64bit_ms_abi);
> -
> - /* If there are no caller-saved registers, add all registers
> - that are clobbered by the call which returns. */
> - 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))
> - && !STACK_REGNO_P (i)
> - && !MMX_REGNO_P (i))
> - clobber_reg (&use,
> - gen_rtx_REG (GET_MODE (regno_reg_rtx[i]), i));
> - }
> - else if (TARGET_64BIT_MS_ABI
> - && (!callarg2 || INTVAL (callarg2) != -2))
> - {
> - unsigned i;
> + if (!TARGET_SSE)
> + ;
>
> - for (i = 0; i < NUM_X86_64_MS_CLOBBERED_REGS; i++)
> - {
> - int regno = x86_64_ms_sysv_extra_clobbered_registers[i];
> - machine_mode mode = SSE_REGNO_P (regno) ? TImode : DImode;
> + /* Don't break hot-patched functions. */
> + else if (ix86_function_ms_hook_prologue (current_function_decl))
> + ;
>
> - clobber_reg (&use, gen_rtx_REG (mode, regno));
> - }
> + /* TODO: Cases not yet examined. */
> + else if (flag_split_stack)
> + warn_once_call_ms2sysv_xlogues ("-fsplit-stack");
>
> - /* Set here, but it may get cleared later. */
> - if (TARGET_CALL_MS2SYSV_XLOGUES)
> + else
> {
> - if (!TARGET_SSE)
> - ;
> -
> - /* Don't break hot-patched functions. */
> - else if (ix86_function_ms_hook_prologue (current_function_decl))
> - ;
> -
> - /* TODO: Cases not yet examined. */
> - else if (flag_split_stack)
> - warn_once_call_ms2sysv_xlogues ("-fsplit-stack");
> -
> - else
> - {
> - gcc_assert (!reload_completed);
> - cfun->machine->call_ms2sysv = true;
> - }
> + gcc_assert (!reload_completed);
> + cfun->machine->call_ms2sysv = true;
> }
> }
>
> @@ -11353,25 +11293,6 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx
> callarg1,
> clobber_reg (&use, gen_rtx_REG (DImode, R10_REG));
> }
>
> - if (call_no_callee_saved_registers)
> - {
> - /* 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]
> - && 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))
> - clobber_reg (&use,
> - gen_rtx_REG (GET_MODE (regno_reg_rtx[i]), i));
> - }
> -
> if (vec_len > 1)
> call = gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (vec_len, vec));
> rtx_insn *call_insn = emit_call_insn (call);
> diff --git a/gcc/config/i386/i386-features.cc
> b/gcc/config/i386/i386-features.cc
> index f5caef29c6e..aef364ba8b7 100644
> --- a/gcc/config/i386/i386-features.cc
> +++ b/gcc/config/i386/i386-features.cc
> @@ -4505,6 +4505,7 @@ ix86_place_single_tls_call (rtx dest, rtx val,
> x86_cse_kind kind,
>
> symbol = XVECEXP (val, 0, 0);
> tls = gen_tls_global_dynamic_64 (Pmode, rax, symbol, caddr, rdi);
> + CALL_INSN_ABI_ID (tls) = ix86_tls_get_addr_abi ().id ();
>
> if (GET_MODE (symbol) != Pmode)
> symbol = gen_rtx_ZERO_EXTEND (Pmode, symbol);
> @@ -4517,6 +4518,7 @@ ix86_place_single_tls_call (rtx dest, rtx val,
> x86_cse_kind kind,
> caddr = ix86_tls_get_addr ();
>
> tls = gen_tls_local_dynamic_base_64 (Pmode, rax, caddr, rdi);
> + CALL_INSN_ABI_ID (tls) = ix86_tls_get_addr_abi ().id ();
>
> /* Attach a unique REG_EQUAL to DEST, to allow the RTL optimizers
> to share the LD_BASE result with other LD model accesses. */
> diff --git a/gcc/config/i386/i386-options.cc b/gcc/config/i386/i386-options.cc
> index 626e1eab929..7aa9daf9ae1 100644
> --- a/gcc/config/i386/i386-options.cc
> +++ b/gcc/config/i386/i386-options.cc
> @@ -3527,21 +3527,6 @@ ix86_set_current_function (tree fndecl)
> }
> ix86_previous_fndecl = fndecl;
>
> - static call_saved_registers_type prev_call_saved_registers;
> -
> - /* 64-bit MS and SYSV ABI have different set of call used registers.
> - Avoid expensive re-initialization of init_regs each time we switch
> - function context. */
> - if (TARGET_64BIT
> - && (call_used_or_fixed_reg_p (SI_REG)
> - == (cfun->machine->call_abi == MS_ABI)))
> - reinit_regs ();
> - /* Need to re-initialize init_regs if caller-saved registers are
> - changed. */
> - else if (prev_call_saved_registers
> - != cfun->machine->call_saved_registers)
> - reinit_regs ();
> -
> if (cfun->machine->func_type != TYPE_NORMAL
> || (cfun->machine->call_saved_registers
> == TYPE_NO_CALLER_SAVED_REGISTERS))
> @@ -3589,8 +3574,6 @@ ix86_set_current_function (tree fndecl)
> = TYPE_DEFAULT_CALL_SAVED_REGISTERS;
> }
> }
> -
> - prev_call_saved_registers = cfun->machine->call_saved_registers;
> }
>
> /* Implement the TARGET_OFFLOAD_OPTIONS hook. */
> diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
> index 7dac3ed8e6c..ea23f75804a 100644
> --- a/gcc/config/i386/i386-protos.h
> +++ b/gcc/config/i386/i386-protos.h
> @@ -286,7 +286,6 @@ 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
>
> @@ -295,6 +294,8 @@ extern bool ix86_gpr_tls_address_pattern_p (rtx);
> extern bool ix86_tls_address_pattern_p (rtx);
> extern rtx ix86_rewrite_tls_address (rtx);
> extern rtx ix86_tls_get_addr (void);
> +class predefined_function_abi;
> +extern const predefined_function_abi & ix86_tls_get_addr_abi (void);
>
> extern void ix86_expand_vector_init (bool, rtx, rtx);
> extern void ix86_expand_vector_set (bool, rtx, rtx, int);
> diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
> index 5bcf7254ab2..86bd6caf59f 100644
> --- a/gcc/config/i386/i386.cc
> +++ b/gcc/config/i386/i386.cc
> @@ -107,6 +107,7 @@ along with GCC; see the file COPYING3. If not see
>
> static void ix86_print_operand_address_as (FILE *, rtx, addr_space_t, bool);
> static void ix86_emit_restore_reg_using_pop (rtx, bool = false);
> +static const predefined_function_abi & ix86_alternate_abi (void);
>
>
> #ifndef CHECK_STACK_LIMIT
> @@ -503,17 +504,6 @@ 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;
> -
> /* For 32-bit targets, disable the REX registers. */
> if (! TARGET_64BIT)
> {
> @@ -525,8 +515,10 @@ ix86_conditional_register_usage (void)
> CLEAR_HARD_REG_BIT (accessible_reg_set, i);
> }
>
> - /* See the definition of CALL_USED_REGISTERS in i386.h. */
> - c_mask = CALL_USED_REGISTERS_MASK (TARGET_64BIT_MS_ABI);
> + /* Set up the call-used registers based on the system ABI (ix86_abi).
> +
> + See the definition of CALL_USED_REGISTERS in i386.h. */
> + c_mask = CALL_USED_REGISTERS_MASK (TARGET_64BIT && ix86_abi == MS_ABI);
>
> CLEAR_HARD_REG_SET (reg_class_contents[(int)CLOBBERED_REGS]);
>
> @@ -932,7 +924,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 fntype)
> {
> auto type = ix86_fntype_call_saved_registers (fntype);
> @@ -3871,10 +3863,10 @@ ix86_function_value_regno_p (const unsigned int regno)
> /* Complex values are returned in %st(0)/%st(1) pair. */
> case ST0_REG:
> case ST1_REG:
> - /* TODO: The function should depend on current function ABI but
> - builtins.cc would need updating then. Therefore we use the
> - default ABI. */
> - if (TARGET_64BIT && ix86_cfun_abi () == MS_ABI)
> + /* TODO: An ABI identifier should be passed as a parameter.
> + For now, most callers, including those in builtins.cc,
> + expect us to use the default ABI. */
> + if (TARGET_64BIT && ix86_abi == MS_ABI)
> return false;
> return TARGET_FLOAT_RETURNS_IN_80387;
>
> @@ -6796,8 +6788,6 @@ ix86_hard_regno_scratch_ok (unsigned int regno)
> bool
> ix86_save_reg (unsigned int regno, bool maybe_eh_return, bool
> ignore_outlined)
> {
> - rtx reg;
> -
> /* Save and restore DRAP register between prologue and epilogue so
> that stack pointer can be restored. */
> if (crtl->drap_reg
> @@ -6805,41 +6795,12 @@ ix86_save_reg (unsigned int regno, bool
> maybe_eh_return, bool ignore_outlined)
> && !cfun->machine->no_drap_save_restore)
> return true;
>
> - 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, we preserve all
> - registers, except for MMX and x87 registers which aren't
> - supported when saving and restoring registers. Don't
> - explicitly save SP register since it is always preserved.
> -
> - Don't preserve registers used for function return value. */
> - reg = crtl->return_rtx;
> - if (reg)
> - {
> - unsigned int i = REGNO (reg);
> - unsigned int nregs = REG_NREGS (reg);
> - while (nregs-- > 0)
> - if ((i + nregs) == regno)
> - return false;
> - }
> -
> - return (df_regs_ever_live_p (regno)
> - && !fixed_regs[regno]
> - && !STACK_REGNO_P (regno)
> - && !MMX_REGNO_P (regno)
> - && (regno != HARD_FRAME_POINTER_REGNUM
> - || !frame_pointer_needed));
1) Original no_caller_saved_register won't preserve mmx/x86 register,
and it's documented in gcc/doc/extend.texi:8909
But the patch changes that, mmx/x86 registers are preserved for
no_caller_saved_register, so either we need to change the document or
the patch should be fixed.
--------------------------The testcase to reproduce the
issue.------------------------------
/* Verify that no_caller_saved_registers calls still clobber x87 and MMX
registers. */
/* { dg-do compile } */
/* { dg-options "-O2 -masm=att -mfpmath=387 -m80387 -mlong-double-80
-mmmx -mno-sse -mno-iamcu -fomit-frame-pointer" } */
typedef int v2si __attribute__ ((vector_size (8)));
extern void foo (void) __attribute__ ((no_caller_saved_registers));
long double
bar_x87 (long double x, long double y)
{
long double z = x + y;
foo ();
return z + 1.0L;
}
void
bar_mmx (void)
{
v2si x;
__asm__ volatile ("# %0" : "=y" (x));
foo ();
__asm__ volatile ("# %0" :: "y" (x));
}
/* { dg-final { scan-assembler-times "fstpt\[ \t\]+" 1 } } */
/* { dg-final { scan-assembler-times "movq\[ \t\]+%mm\[0-7\],
\[0-9\]*\\(%\[re\]?sp\\)" 1 } } */
/* { dg-final { scan-assembler-times "movq\[
\t\]+\[0-9\]*\\(%\[re\]?sp\\), %mm\[0-7\]" 1 } } */
----------------------------testcase ends
-----------------------------------------------------
2) Also it ICE for the mixed aggregate return
struct pair { long i; double d; };
extern struct pair foo (void)
__attribute__ ((no_caller_saved_registers, sysv_abi));
struct pair bar (void) { return foo (); }
with dg-do compile { target lp64 } and -O2 -msse2 -mno-avx.
> @@ -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 }
> } } } */
> --
> 2.54.0
>
--
BR,
Hongtao