DRAP register is used to restore stack pointer in epilogue for stack
realignment.  In no-callee-saved and preserve_none functions, although
DRAP register isn't preserved, it must be unchanged between prologue
and epilogue so that stack pointer can be restored.  In no-callee-saved
and preserve_none functions, mark BX_REG as fixed and use it for DRAP
register.

NB: r12-r15 aren't made available as the local general purpose registers
since it causes:

FAIL: gcc.target/i386/preserve-none-1.c scan-assembler-not movq
FAIL: gcc.target/i386/preserve-none-30a.c check-function-bodies entry
FAIL: gcc.target/i386/preserve-none-30b.c check-function-bodies entry

which have the same cause as

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124798

gcc/

PR target/120870
* config/i386/i386.cc (X86_NO_CALLEE_DRAP_REG): New.
(ix86_conditional_register_usage): Mark X86_NO_CALLEE_DRAP_REG
as fixed.
(find_drap_reg): Return X86_PRESERVE_NONE_DRAP_REG for
no-callee-saved and preserve_none functions

gcc/testsuite/

PR target/120870
* gcc.target/i386/pr120870-1.c: New test.
* gcc.target/i386/pr120870-2.c: Likewise.



H.J.
From f7bc8a980a9b08d5659f8e6204fefb6166f30685 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <[email protected]>
Date: Sun, 10 May 2026 18:36:26 +0800
Subject: [PATCH] x86: Use BX_REG for DRAP register in preserve_none function

DRAP register is used to restore stack pointer in epilogue for stack
realignment.  In no-callee-saved and preserve_none functions, although
DRAP register isn't preserved, it must be unchanged between prologue
and epilogue so that stack pointer can be restored.  In no-callee-saved
and preserve_none functions, mark BX_REG as fixed and use it for DRAP
register.

NB: r12-r15 aren't made available as the local general purpose registers
since it causes:

FAIL: gcc.target/i386/preserve-none-1.c scan-assembler-not movq
FAIL: gcc.target/i386/preserve-none-30a.c check-function-bodies entry
FAIL: gcc.target/i386/preserve-none-30b.c check-function-bodies entry

which have the same cause as

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124798

gcc/

	PR target/120870
	* config/i386/i386.cc (X86_NO_CALLEE_DRAP_REG): New.
	(ix86_conditional_register_usage): Mark X86_NO_CALLEE_DRAP_REG
	as fixed.
	(find_drap_reg): Return X86_PRESERVE_NONE_DRAP_REG for
	no-callee-saved and preserve_none functions

gcc/testsuite/

	PR target/120870
	* gcc.target/i386/pr120870-1.c: New test.
	* gcc.target/i386/pr120870-2.c: Likewise.

Signed-off-by: H.J. Lu <[email protected]>
---
 gcc/config/i386/i386.cc                    | 71 +++++++++++++++++++---
 gcc/testsuite/gcc.target/i386/pr120870-1.c | 35 +++++++++++
 gcc/testsuite/gcc.target/i386/pr120870-2.c |  6 ++
 3 files changed, 102 insertions(+), 10 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120870-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr120870-2.c

diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
index 9aecd51119f..9541496afb1 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -496,6 +496,14 @@ ix86_profile_before_prologue (void)
   return flag_fentry != 0;
 }
 
+/* In no-callee-saved and preserve_none functions, reserve BX_REG for
+   DRAP register.
+
+   FIXME: Why is BX_REG the only working DRAP register in preserve_none
+   functions?  Is is because only BX_REG is 0 in CALL_USED_REGISTERS
+   which has some permanent impacts on register allocator.  */
+#define X86_NO_CALLEE_DRAP_REG BX_REG
+
 /* Update register usage after having seen the compiler flags.  */
 
 static void
@@ -503,16 +511,54 @@ 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:
+	  /* DRAP register is used to restore stack pointer in epilogue
+	     for stack realignment.  Although DRAP register isn't
+	     preserved, it must be unchanged between prologue and
+	     epilogue so that stack pointer can be restored.  In
+	     no-callee-saved and preserve_none function, mark BX_REG
+	     as fixed and use it for DRAP register.  */
+	  fixed_regs[X86_NO_CALLEE_DRAP_REG] = 1;
+
+	  /* Disabled to avoid
+
+FAIL: gcc.target/i386/preserve-none-1.c scan-assembler-not movq
+FAIL: gcc.target/i386/preserve-none-30a.c check-function-bodies entry
+FAIL: gcc.target/i386/preserve-none-30b.c check-function-bodies entry
+
+	     which have the same cause as
+
+	     https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124798
+
+	   */
+	  if (0 && TARGET_64BIT)
+	    {
+	      /* Make R12_REG, R13_REG, R14_REG, R15_REG available as
+		 the local general purpose registers.  */
+	      call_used_regs[R12_REG] = 1;
+	      call_used_regs[R13_REG] = 1;
+	      call_used_regs[R14_REG] = 1;
+	      call_used_regs[R15_REG] = 1;
+	    }
+	  break;
+      }
 
   /* For 32-bit targets, disable the REX registers.  */
   if (! TARGET_64BIT)
@@ -7940,6 +7986,11 @@ pro_epilogue_adjust_stack (rtx dest, rtx src, rtx offset,
 static unsigned int
 find_drap_reg (void)
 {
+  if (cfun->machine->call_saved_registers == TYPE_PRESERVE_NONE
+      || (cfun->machine->call_saved_registers
+	  == TYPE_NO_CALLEE_SAVED_REGISTERS))
+    return X86_NO_CALLEE_DRAP_REG;
+
   tree decl = cfun->decl;
 
   /* Always use callee-saved register if there are no caller-saved
diff --git a/gcc/testsuite/gcc.target/i386/pr120870-1.c b/gcc/testsuite/gcc.target/i386/pr120870-1.c
new file mode 100644
index 00000000000..31d38391133
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120870-1.c
@@ -0,0 +1,35 @@
+/* { dg-do run } */
+/* { dg-options "-O0 -march=x86-64-v3" } */
+
+void
+ext (long a, long b, long c, long d, long e, long f, long g)
+{
+}
+
+__attribute__ ((preserve_none)) void
+tail (long a, long b)
+{
+}
+
+__attribute__ ((preserve_none)) void
+caller (long a, long b, long c, long d, long e, long f, long g)
+{
+  __attribute__ ((aligned (32))) long x[4]; // for stack alignment
+  ext (a, b, c, d, e, f, g);
+  __attribute__ ((musttail)) return tail (a + b, b + c);
+}
+
+__attribute__ ((noipa))
+static void
+do_test ()
+{
+  caller (1, 2, 3, 4, 5, 6, 7);
+}
+
+int
+main (void)
+{
+  if (__builtin_cpu_supports ("x86-64-v3"))
+    do_test ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr120870-2.c b/gcc/testsuite/gcc.target/i386/pr120870-2.c
new file mode 100644
index 00000000000..7e68718d81a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr120870-2.c
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+/* { dg-options "-O0 -march=x86-64-v3" } */
+
+#define preserve_none no_callee_saved_registers
+
+#include "pr120870-1.c"
-- 
2.54.0

Reply via email to