[clang] [llvm] [X86] support reserve r8~r15 on X86_64 (PR #180242)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 6 10:19:24 PST 2026
https://github.com/zhouguangyuan0718 updated https://github.com/llvm/llvm-project/pull/180242
>From a140e5c67e911fb83511496c740d14d7a1d9bff6 Mon Sep 17 00:00:00 2001
From: ZhouGuangyuan <zhouguangyuan.xian at gmail.com>
Date: Sat, 7 Feb 2026 00:09:33 +0800
Subject: [PATCH] [X86] support reserve r8~r15 on X86_64
Add new options -ffixed_r{8~15} for clang X86 target, like option "-ffixed_x" for RISCV/AArch64 target.
Also, add target-feature +reserve-r{8~15} for the X86 backend.
The registers which are specified reserved will not be used in RegAlloc/CalleeSave. Then the reserved registers can be maintained by user. It will be useful for the runtime/interpreter implementation.
Other registers are used in specific instructions or mechanism, so they can't be reserved.
Signed-off-by: ZhouGuangyuan <zhouguangyuan.xian at gmail.com>
---
clang/include/clang/Options/Options.td | 8 +-
clang/lib/Basic/Targets/X86.h | 7 ++
clang/lib/Driver/ToolChains/Arch/X86.cpp | 14 ++++
clang/test/Driver/x86_64-fixed-r-register.c | 65 ++++++++++++++
llvm/lib/Target/X86/X86.td | 4 +
llvm/lib/Target/X86/X86FrameLowering.cpp | 8 ++
llvm/lib/Target/X86/X86ISelLowering.cpp | 12 +++
llvm/lib/Target/X86/X86RegisterInfo.cpp | 11 +++
llvm/lib/Target/X86/X86Subtarget.cpp | 3 +-
llvm/lib/Target/X86/X86Subtarget.h | 8 ++
llvm/test/CodeGen/X86/reserveRreg.ll | 93 +++++++++++++++++++++
11 files changed, 230 insertions(+), 3 deletions(-)
create mode 100644 clang/test/Driver/x86_64-fixed-r-register.c
create mode 100644 llvm/test/CodeGen/X86/reserveRreg.ll
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 54e162c262a90..bd90f7b1cf0ec 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -5440,8 +5440,8 @@ def mrestrict_it: Flag<["-"], "mrestrict-it">, Group<m_arm_Features_Group>,
def mno_restrict_it: Flag<["-"], "mno-restrict-it">, Group<m_arm_Features_Group>,
HelpText<"Allow generation of complex IT blocks.">;
def marm : Flag<["-"], "marm">, Alias<mno_thumb>;
-def ffixed_r9 : Flag<["-"], "ffixed-r9">, Group<m_arm_Features_Group>,
- HelpText<"Reserve the r9 register (ARM only)">;
+def ffixed_r9 : Flag<["-"], "ffixed-r9">, Group<m_Group>,
+ HelpText<"Reserve the r9 register (ARM/x86_64 only)">;
def mno_movt : Flag<["-"], "mno-movt">, Group<m_arm_Features_Group>,
HelpText<"Disallow use of movt/movw pairs (ARM only)">;
def mcrc : Flag<["-"], "mcrc">, Group<m_Group>,
@@ -7077,6 +7077,10 @@ def mapxf : Flag<["-"], "mapxf">, Group<m_x86_Features_Group>;
def mno_apxf : Flag<["-"], "mno-apxf">, Group<m_x86_Features_Group>;
def mapx_inline_asm_use_gpr32 : Flag<["-"], "mapx-inline-asm-use-gpr32">, Group<m_Group>,
HelpText<"Enable use of GPR32 in inline assembly for APX">;
+foreach i = {8, 10-15} in
+ def ffixed_r#i : Flag<["-"], "ffixed-r"#i>, Group<m_Group>,
+ HelpText<"Reserve the r"#i#" register (x86_64 only)">;
+
} // let Flags = [TargetSpecific]
// VE feature flags
diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h
index 922e32906cd04..65617e4fcea21 100644
--- a/clang/lib/Basic/Targets/X86.h
+++ b/clang/lib/Basic/Targets/X86.h
@@ -807,6 +807,13 @@ class LLVM_LIBRARY_VISIBILITY X86_64TargetInfo : public X86TargetInfo {
return true;
}
+ StringRef Reg64 = RegName;
+ if (Reg64.back() == 'd' || Reg64.back() == 'w' || Reg64.back() == 'b') {
+ Reg64 = Reg64.substr(0, Reg64.size() - 1);
+ }
+ if (getTargetOpts().FeatureMap.lookup(("reserve-" + Reg64).str()))
+ return true;
+
// Check if the register is a 32-bit register the backend can handle.
return X86TargetInfo::validateGlobalRegisterVariable(RegName, RegSize,
HasSizeMismatch);
diff --git a/clang/lib/Driver/ToolChains/Arch/X86.cpp b/clang/lib/Driver/ToolChains/Arch/X86.cpp
index 61d512f9e093f..c4ccddffca9fa 100644
--- a/clang/lib/Driver/ToolChains/Arch/X86.cpp
+++ b/clang/lib/Driver/ToolChains/Arch/X86.cpp
@@ -332,4 +332,18 @@ void x86::getX86TargetFeatures(const Driver &D, const llvm::Triple &Triple,
if (A->getOption().matches(options::OPT_m3dnow))
D.Diag(diag::warn_drv_clang_unsupported) << A->getAsString(Args);
}
+
+ // Handle features corresponding to "-ffixed-X" options
+#define RESERVE_REG(REG) \
+ if (Args.hasArg(options::OPT_ffixed_##REG)) \
+ Features.push_back("+reserve-" #REG);
+ RESERVE_REG(r8)
+ RESERVE_REG(r9)
+ RESERVE_REG(r10)
+ RESERVE_REG(r11)
+ RESERVE_REG(r12)
+ RESERVE_REG(r13)
+ RESERVE_REG(r14)
+ RESERVE_REG(r15)
+#undef RESERVE_REG
}
diff --git a/clang/test/Driver/x86_64-fixed-r-register.c b/clang/test/Driver/x86_64-fixed-r-register.c
new file mode 100644
index 0000000000000..714db3fdfdc91
--- /dev/null
+++ b/clang/test/Driver/x86_64-fixed-r-register.c
@@ -0,0 +1,65 @@
+// RUN: %clang --target=x86_64-unknown-linux-gnu -ffixed-r8 -### %s 2> %t
+// RUN: FileCheck --check-prefix=CHECK-FIXED-R8 < %t %s
+// CHECK-FIXED-R8: "-target-feature" "+reserve-r8"
+
+// RUN: %clang --target=x86_64-unknown-linux-gnu -ffixed-r9 -### %s 2> %t
+// RUN: FileCheck --check-prefix=CHECK-FIXED-R9 < %t %s
+// CHECK-FIXED-R9: "-target-feature" "+reserve-r9"
+
+// RUN: %clang --target=x86_64-unknown-linux-gnu -ffixed-r10 -### %s 2> %t
+// RUN: FileCheck --check-prefix=CHECK-FIXED-R10 < %t %s
+// CHECK-FIXED-R10: "-target-feature" "+reserve-r10"
+
+// RUN: %clang --target=x86_64-unknown-linux-gnu -ffixed-r11 -### %s 2> %t
+// RUN: FileCheck --check-prefix=CHECK-FIXED-R11 < %t %s
+// CHECK-FIXED-R11: "-target-feature" "+reserve-r11"
+
+// RUN: %clang --target=x86_64-unknown-linux-gnu -ffixed-r12 -### %s 2> %t
+// RUN: FileCheck --check-prefix=CHECK-FIXED-R12 < %t %s
+// CHECK-FIXED-R12: "-target-feature" "+reserve-r12"
+
+// RUN: %clang --target=x86_64-unknown-linux-gnu -ffixed-r13 -### %s 2> %t
+// RUN: FileCheck --check-prefix=CHECK-FIXED-R13 < %t %s
+// CHECK-FIXED-R13: "-target-feature" "+reserve-r13"
+
+// RUN: %clang --target=x86_64-unknown-linux-gnu -ffixed-r14 -### %s 2> %t
+// RUN: FileCheck --check-prefix=CHECK-FIXED-R14 < %t %s
+// CHECK-FIXED-R14: "-target-feature" "+reserve-r14"
+
+// RUN: %clang --target=x86_64-unknown-linux-gnu -ffixed-r15 -### %s 2> %t
+// RUN: FileCheck --check-prefix=CHECK-FIXED-R15 < %t %s
+// CHECK-FIXED-R15: "-target-feature" "+reserve-r15"
+
+// Test multiple of reserve-r# options together.
+// RUN: %clang --target=x86_64-unknown-linux-gnu \
+// RUN: -ffixed-r8 \
+// RUN: -ffixed-r9 \
+// RUN: -ffixed-r15 \
+// RUN: -### %s 2> %t
+// RUN: FileCheck \
+// RUN: --check-prefix=CHECK-FIXED-R8 \
+// RUN: --check-prefix=CHECK-FIXED-R9 \
+// RUN: --check-prefix=CHECK-FIXED-R15 \
+// RUN: < %t %s
+
+// Test all reserve-r# options together.
+// RUN: %clang --target=x86_64-unknown-linux-gnu \
+// RUN: -ffixed-r8 \
+// RUN: -ffixed-r9 \
+// RUN: -ffixed-r10 \
+// RUN: -ffixed-r11 \
+// RUN: -ffixed-r12 \
+// RUN: -ffixed-r13 \
+// RUN: -ffixed-r14 \
+// RUN: -ffixed-r15 \
+// RUN: -### %s 2> %t
+// RUN: FileCheck \
+// RUN: --check-prefix=CHECK-FIXED-R8 \
+// RUN: --check-prefix=CHECK-FIXED-R9 \
+// RUN: --check-prefix=CHECK-FIXED-R10 \
+// RUN: --check-prefix=CHECK-FIXED-R11 \
+// RUN: --check-prefix=CHECK-FIXED-R12 \
+// RUN: --check-prefix=CHECK-FIXED-R13 \
+// RUN: --check-prefix=CHECK-FIXED-R14 \
+// RUN: --check-prefix=CHECK-FIXED-R15 \
+// RUN: < %t %s
diff --git a/llvm/lib/Target/X86/X86.td b/llvm/lib/Target/X86/X86.td
index 1b9a6ee2b4ef4..68491c42bddbd 100644
--- a/llvm/lib/Target/X86/X86.td
+++ b/llvm/lib/Target/X86/X86.td
@@ -32,6 +32,10 @@ def IsX32 : SubtargetFeature<"x32", "IsX32", "true",
// X86 Subtarget ISA features
//===----------------------------------------------------------------------===//
+foreach i = {8-15} in
+ def FeatureReserveR#i : SubtargetFeature<"reserve-r"#i, "ReservedRReg[X86::R"#i#"]", "true",
+ "Reserve R"#i#", making it unavailable as a GPR">;
+
def FeatureX87 : SubtargetFeature<"x87","HasX87", "true",
"Enable X87 float instructions">;
diff --git a/llvm/lib/Target/X86/X86FrameLowering.cpp b/llvm/lib/Target/X86/X86FrameLowering.cpp
index 8bca6344d6521..8791470b0b5e1 100644
--- a/llvm/lib/Target/X86/X86FrameLowering.cpp
+++ b/llvm/lib/Target/X86/X86FrameLowering.cpp
@@ -3211,6 +3211,14 @@ void X86FrameLowering::determineCalleeSaves(MachineFunction &MF,
BasePtr = getX86SubSuperRegister(BasePtr, 64);
SavedRegs.set(BasePtr);
}
+ if (STI.is64Bit()) {
+ for (int Reg = SavedRegs.find_first(); Reg != -1;
+ Reg = SavedRegs.find_next(Reg)) {
+ if (STI.isRegisterReservedByUser(Reg)) {
+ SavedRegs.reset(Reg);
+ }
+ }
+ }
}
static bool HasNestArgument(const MachineFunction *MF) {
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 3ec2bf9b19360..532976d0f4486 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -28703,6 +28703,9 @@ SDValue X86TargetLowering::LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const {
return FrameAddr;
}
+#define GET_REGISTER_MATCHER
+#include "X86GenAsmMatcher.inc"
+
// FIXME? Maybe this could be a TableGen attribute on some registers and
// this table could be generated automatically from RegInfo.
Register X86TargetLowering::getRegisterByName(const char* RegName, LLT VT,
@@ -28731,6 +28734,15 @@ Register X86TargetLowering::getRegisterByName(const char* RegName, LLT VT,
}
#endif
}
+ if (Subtarget.is64Bit()) {
+ if (Reg)
+ return Reg;
+
+ Reg = MatchRegisterName(RegName);
+ if (!Subtarget.isRegisterReservedByUser(Reg))
+ reportFatalUsageError(Twine("Trying to obtain non-reserved register \"" +
+ StringRef(RegName) + "\"."));
+ }
return Reg;
}
diff --git a/llvm/lib/Target/X86/X86RegisterInfo.cpp b/llvm/lib/Target/X86/X86RegisterInfo.cpp
index 72f38133e21ff..b78c4c0393c69 100644
--- a/llvm/lib/Target/X86/X86RegisterInfo.cpp
+++ b/llvm/lib/Target/X86/X86RegisterInfo.cpp
@@ -515,6 +515,17 @@ BitVector X86RegisterInfo::getReservedRegs(const MachineFunction &MF) const {
// Set the Shadow Stack Pointer as reserved.
Reserved.set(X86::SSP);
+ auto &ST = MF.getSubtarget<X86Subtarget>();
+ if (ST.is64Bit()) {
+ for (size_t Reg = 0; Reg < getNumRegs(); Reg++) {
+ // Set r# as reserved register if user required
+ if (ST.isRegisterReservedByUser(Reg)) {
+ for (const MCPhysReg &SubReg : subregs_inclusive(X86::RSP))
+ Reserved.set(SubReg);
+ }
+ }
+ }
+
// Set the instruction pointer register and its aliases as reserved.
for (const MCPhysReg &SubReg : subregs_inclusive(X86::RIP))
Reserved.set(SubReg);
diff --git a/llvm/lib/Target/X86/X86Subtarget.cpp b/llvm/lib/Target/X86/X86Subtarget.cpp
index 4e2e98410f325..f73ca37a45c07 100644
--- a/llvm/lib/Target/X86/X86Subtarget.cpp
+++ b/llvm/lib/Target/X86/X86Subtarget.cpp
@@ -317,7 +317,8 @@ X86Subtarget::X86Subtarget(const Triple &TT, StringRef CPU, StringRef TuneCPU,
unsigned PreferVectorWidthOverride,
unsigned RequiredVectorWidth)
: X86GenSubtargetInfo(TT, CPU, TuneCPU, FS),
- PICStyle(PICStyles::Style::None), TM(TM), TargetTriple(TT),
+ PICStyle(PICStyles::Style::None), TM(TM),
+ ReservedRReg(X86::NUM_TARGET_REGS), TargetTriple(TT),
StackAlignOverride(StackAlignOverride),
PreferVectorWidthOverride(PreferVectorWidthOverride),
RequiredVectorWidth(RequiredVectorWidth),
diff --git a/llvm/lib/Target/X86/X86Subtarget.h b/llvm/lib/Target/X86/X86Subtarget.h
index 3b920bc4ef7c1..dd9a5de3030ed 100644
--- a/llvm/lib/Target/X86/X86Subtarget.h
+++ b/llvm/lib/Target/X86/X86Subtarget.h
@@ -17,6 +17,7 @@
#include "X86ISelLowering.h"
#include "X86InstrInfo.h"
#include "X86SelectionDAGInfo.h"
+#include "llvm/ADT/BitVector.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/TargetParser/Triple.h"
@@ -65,6 +66,9 @@ class X86Subtarget final : public X86GenSubtargetInfo {
#define GET_SUBTARGETINFO_MACRO(ATTRIBUTE, DEFAULT, GETTER) \
bool ATTRIBUTE = DEFAULT;
#include "X86GenSubtargetInfo.inc"
+ /// ReservedRReg R#i is not available as a general purpose register.
+ BitVector ReservedRReg;
+
/// The minimum alignment known to hold of the stack frame on
/// entry to the function and which must be maintained by every function.
Align stackAlignment = Align(4);
@@ -155,6 +159,10 @@ class X86Subtarget final : public X86GenSubtargetInfo {
const LegalizerInfo *getLegalizerInfo() const override;
const RegisterBankInfo *getRegBankInfo() const override;
+ bool isRegisterReservedByUser(Register i) const override {
+ return ReservedRReg[i.id()];
+ }
+
private:
/// Initialize the full set of dependencies so we can use an initializer
/// list for X86Subtarget.
diff --git a/llvm/test/CodeGen/X86/reserveRreg.ll b/llvm/test/CodeGen/X86/reserveRreg.ll
new file mode 100644
index 0000000000000..cfd80022e3cc4
--- /dev/null
+++ b/llvm/test/CodeGen/X86/reserveRreg.ll
@@ -0,0 +1,93 @@
+;; Check if manually reserved registers are always excluded from being saved by
+;; the function prolog/epilog, even for callee-saved ones, as per GCC behavior.
+
+; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s
+
+define preserve_mostcc void @t8() "target-features"="+reserve-r8" {
+; CHECK-LABEL: t8:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $256, %r8d
+; CHECK-NEXT: #APP
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+ call i64 asm sideeffect "", "={r8},{r8}"(i64 256)
+ ret void
+}
+
+define preserve_mostcc void @t9() "target-features"="+reserve-r9" {
+; CHECK-LABEL: t9:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $256, %r9d
+; CHECK-NEXT: #APP
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+ call i64 asm sideeffect "", "={r9},{r9}"(i64 256)
+ ret void
+}
+
+define preserve_mostcc void @t10() "target-features"="+reserve-r10" {
+; CHECK-LABEL: t10:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $256, %r10d
+; CHECK-NEXT: #APP
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+ call i64 asm sideeffect "", "={r10},{r10}"(i64 256)
+ ret void
+}
+
+define preserve_mostcc void @t11() "target-features"="+reserve-r11" {
+; CHECK-LABEL: t11:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $256, %r11d
+; CHECK-NEXT: #APP
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+ call i64 asm sideeffect "", "={r11},{r11}"(i64 256)
+ ret void
+}
+
+define preserve_mostcc void @t12() "target-features"="+reserve-r12" {
+; CHECK-LABEL: t12:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $256, %r12d
+; CHECK-NEXT: #APP
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+ call i64 asm sideeffect "", "={r12},{r12}"(i64 256)
+ ret void
+}
+
+define preserve_mostcc void @t13() "target-features"="+reserve-r13" {
+; CHECK-LABEL: t13:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $256, %r13d
+; CHECK-NEXT: #APP
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+ call i64 asm sideeffect "", "={r13},{r13}"(i64 256)
+ ret void
+}
+
+define preserve_mostcc void @t14() "target-features"="+reserve-r14" {
+; CHECK-LABEL: t14:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $256, %r14d
+; CHECK-NEXT: #APP
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+ call i64 asm sideeffect "", "={r14},{r14}"(i64 256)
+ ret void
+}
+
+define preserve_mostcc void @t15() "target-features"="+reserve-r15" {
+; CHECK-LABEL: t15:
+; CHECK: # %bb.0:
+; CHECK-NEXT: movl $256, %r15d
+; CHECK-NEXT: #APP
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+ call i64 asm sideeffect "", "={r15},{r15}"(i64 256)
+ ret void
+}
+
More information about the cfe-commits
mailing list