[clang] [llvm] [Hexagon] Support reserving R16-R28 registers via -ffixed-rN (PR #197208)
Santanu Das via cfe-commits
cfe-commits at lists.llvm.org
Thu May 21 01:28:59 PDT 2026
https://github.com/quic-santdas updated https://github.com/llvm/llvm-project/pull/197208
>From 0a323e2484bc5e7ad407d5e1275d10f0e1eaddde Mon Sep 17 00:00:00 2001
From: quic-santdas <quic_santdas at quicinc.com>
Date: Tue, 12 May 2026 07:14:00 -0700
Subject: [PATCH 1/4] [Hexagon] Support reserving R16-R28 registers via
-ffixed-rN
Extend register reservation from R19-only to R16-R28.
This allows users to reserve callee-saved registers (R16-R27)
and R28 via command-line flags -ffixed-r16 through -ffixed-r28.
The single bool ReservedR19 is replaced with an array-based
approach (ReservedR[32]) to scale cleanly across all
supported registers.
---
clang/lib/Driver/ToolChains/Hexagon.cpp | 23 ++++++++--
clang/test/Driver/hexagon-toolchain-elf.c | 42 ++++++++++++++++++-
llvm/lib/Target/Hexagon/Hexagon.td | 5 ++-
.../Target/Hexagon/HexagonRegisterInfo.cpp | 10 ++++-
llvm/lib/Target/Hexagon/HexagonSubtarget.h | 4 +-
5 files changed, 73 insertions(+), 11 deletions(-)
diff --git a/clang/lib/Driver/ToolChains/Hexagon.cpp b/clang/lib/Driver/ToolChains/Hexagon.cpp
index 41f03e01b69c1..ce3fd5110953a 100644
--- a/clang/lib/Driver/ToolChains/Hexagon.cpp
+++ b/clang/lib/Driver/ToolChains/Hexagon.cpp
@@ -790,9 +790,26 @@ void HexagonToolChain::addClangTargetOptions(const ArgList &DriverArgs,
UseInitArrayDefault))
CC1Args.push_back("-fno-use-init-array");
- if (DriverArgs.hasArg(options::OPT_ffixed_r19)) {
- CC1Args.push_back("-target-feature");
- CC1Args.push_back("+reserved-r19");
+ static const std::pair<options::ID, const char *> FixedRegs[] = {
+ {options::OPT_ffixed_r16, "+reserved-r16"},
+ {options::OPT_ffixed_r17, "+reserved-r17"},
+ {options::OPT_ffixed_r18, "+reserved-r18"},
+ {options::OPT_ffixed_r19, "+reserved-r19"},
+ {options::OPT_ffixed_r20, "+reserved-r20"},
+ {options::OPT_ffixed_r21, "+reserved-r21"},
+ {options::OPT_ffixed_r22, "+reserved-r22"},
+ {options::OPT_ffixed_r23, "+reserved-r23"},
+ {options::OPT_ffixed_r24, "+reserved-r24"},
+ {options::OPT_ffixed_r25, "+reserved-r25"},
+ {options::OPT_ffixed_r26, "+reserved-r26"},
+ {options::OPT_ffixed_r27, "+reserved-r27"},
+ {options::OPT_ffixed_r28, "+reserved-r28"},
+ };
+ for (const auto &[Opt, Feature] : FixedRegs) {
+ if (DriverArgs.hasArg(Opt)) {
+ CC1Args.push_back("-target-feature");
+ CC1Args.push_back(Feature);
+ }
}
if (isAutoHVXEnabled(DriverArgs)) {
CC1Args.push_back("-mllvm");
diff --git a/clang/test/Driver/hexagon-toolchain-elf.c b/clang/test/Driver/hexagon-toolchain-elf.c
index 6de646a0f836a..83797b731563a 100644
--- a/clang/test/Driver/hexagon-toolchain-elf.c
+++ b/clang/test/Driver/hexagon-toolchain-elf.c
@@ -544,14 +544,52 @@
// CHECK360: {{hexagon-link|ld}}
// -----------------------------------------------------------------------------
-// ffixed-r19
-// -----------------------------------------------------------------------------
+// ffixed-r16 through ffixed-r28
+// -----------------------------------------------------------------------------
+// RUN: %clang -### --target=hexagon-unknown-elf -ffixed-r16 %s 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-R16 %s
+// CHECK-R16: "-target-feature" "+reserved-r16"
+// RUN: %clang -### --target=hexagon-unknown-elf -ffixed-r17 %s 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-R17 %s
+// CHECK-R17: "-target-feature" "+reserved-r17"
+// RUN: %clang -### --target=hexagon-unknown-elf -ffixed-r18 %s 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-R18 %s
+// CHECK-R18: "-target-feature" "+reserved-r18"
// RUN: %clang -### --target=hexagon-unknown-elf -ffixed-r19 %s 2>&1 \
// RUN: | FileCheck --check-prefix=CHECK370 %s
// CHECK370: "-target-feature" "+reserved-r19"
+// RUN: %clang -### --target=hexagon-unknown-elf -ffixed-r20 %s 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-R20 %s
+// CHECK-R20: "-target-feature" "+reserved-r20"
+// RUN: %clang -### --target=hexagon-unknown-elf -ffixed-r21 %s 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-R21 %s
+// CHECK-R21: "-target-feature" "+reserved-r21"
+// RUN: %clang -### --target=hexagon-unknown-elf -ffixed-r22 %s 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-R22 %s
+// CHECK-R22: "-target-feature" "+reserved-r22"
+// RUN: %clang -### --target=hexagon-unknown-elf -ffixed-r23 %s 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-R23 %s
+// CHECK-R23: "-target-feature" "+reserved-r23"
+// RUN: %clang -### --target=hexagon-unknown-elf -ffixed-r24 %s 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-R24 %s
+// CHECK-R24: "-target-feature" "+reserved-r24"
+// RUN: %clang -### --target=hexagon-unknown-elf -ffixed-r25 %s 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-R25 %s
+// CHECK-R25: "-target-feature" "+reserved-r25"
+// RUN: %clang -### --target=hexagon-unknown-elf -ffixed-r26 %s 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-R26 %s
+// CHECK-R26: "-target-feature" "+reserved-r26"
+// RUN: %clang -### --target=hexagon-unknown-elf -ffixed-r27 %s 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-R27 %s
+// CHECK-R27: "-target-feature" "+reserved-r27"
+// RUN: %clang -### --target=hexagon-unknown-elf -ffixed-r28 %s 2>&1 \
+// RUN: | FileCheck --check-prefix=CHECK-R28 %s
+// CHECK-R28: "-target-feature" "+reserved-r28"
// RUN: %clang -### --target=hexagon-unknown-elf %s 2>&1 \
// RUN: | FileCheck --check-prefix=CHECK371 %s
+// CHECK371-NOT: "+reserved-r16"
// CHECK371-NOT: "+reserved-r19"
+// CHECK371-NOT: "+reserved-r28"
// -----------------------------------------------------------------------------
// mcabac
diff --git a/llvm/lib/Target/Hexagon/Hexagon.td b/llvm/lib/Target/Hexagon/Hexagon.td
index 6532cbd085c2d..24052476d2fc4 100644
--- a/llvm/lib/Target/Hexagon/Hexagon.td
+++ b/llvm/lib/Target/Hexagon/Hexagon.td
@@ -116,8 +116,9 @@ def FeatureSmallData: SubtargetFeature<"small-data", "UseSmallData", "true",
"Allow GP-relative addressing of global variables">;
def FeatureDuplex: SubtargetFeature<"duplex", "EnableDuplex", "true",
"Enable generation of duplex instruction">;
-def FeatureReservedR19: SubtargetFeature<"reserved-r19", "ReservedR19",
- "true", "Reserve register R19">;
+foreach i = {16-28} in
+ def FeatureReservedR#i : SubtargetFeature<"reserved-r"#i,
+ "ReservedR["#i#"]", "true", "Reserve register R"#i>;
def FeatureNoreturnStackElim: SubtargetFeature<"noreturn-stack-elim",
"NoreturnStackElim", "true",
"Eliminate stack allocation in a noreturn function when possible">;
diff --git a/llvm/lib/Target/Hexagon/HexagonRegisterInfo.cpp b/llvm/lib/Target/Hexagon/HexagonRegisterInfo.cpp
index a56dfc5f58392..999b6dc239b3d 100644
--- a/llvm/lib/Target/Hexagon/HexagonRegisterInfo.cpp
+++ b/llvm/lib/Target/Hexagon/HexagonRegisterInfo.cpp
@@ -205,8 +205,14 @@ BitVector HexagonRegisterInfo::getReservedRegs(const MachineFunction &MF)
for (auto Reg : Hexagon_MC::GetVectRegRev())
Reserved.set(Reg);
- if (MF.getSubtarget<HexagonSubtarget>().hasReservedR19())
- Reserved.set(Hexagon::R19);
+ static const MCPhysReg RRegs[] = {
+ Hexagon::R16, Hexagon::R17, Hexagon::R18,
+ Hexagon::R19, Hexagon::R20, Hexagon::R21, Hexagon::R22, Hexagon::R23,
+ Hexagon::R24, Hexagon::R25, Hexagon::R26, Hexagon::R27, Hexagon::R28};
+ const auto &HST = MF.getSubtarget<HexagonSubtarget>();
+ for (unsigned i = 0; i < std::size(RRegs); ++i)
+ if (HST.isRRegReserved(i + 16))
+ Reserved.set(RRegs[i]);
Register AP =
MF.getInfo<HexagonMachineFunctionInfo>()->getStackAlignBaseReg();
diff --git a/llvm/lib/Target/Hexagon/HexagonSubtarget.h b/llvm/lib/Target/Hexagon/HexagonSubtarget.h
index dde32298b6889..559f90e64df25 100644
--- a/llvm/lib/Target/Hexagon/HexagonSubtarget.h
+++ b/llvm/lib/Target/Hexagon/HexagonSubtarget.h
@@ -63,7 +63,7 @@ class HexagonSubtarget : public HexagonGenSubtargetInfo {
bool HasPreV65 = false;
bool HasMemNoShuf = false;
bool EnableDuplex = false;
- bool ReservedR19 = false;
+ bool ReservedR[32] = {};
bool NoreturnStackElim = false;
public:
@@ -286,7 +286,7 @@ class HexagonSubtarget : public HexagonGenSubtargetInfo {
bool useHVX64BOps() const { return useHVXOps() && UseHVX64BOps; }
bool hasMemNoShuf() const { return HasMemNoShuf; }
- bool hasReservedR19() const { return ReservedR19; }
+ bool isRRegReserved(unsigned i) const { return ReservedR[i]; }
bool usePredicatedCalls() const;
bool noreturnStackElim() const { return NoreturnStackElim; }
>From b09bee8a408e407fcfb4a1e64bc15d124dfb2cfd Mon Sep 17 00:00:00 2001
From: quic-santdas <quic_santdas at quicinc.com>
Date: Wed, 13 May 2026 07:07:24 -0700
Subject: [PATCH 2/4] Fixed the formatting via clang-format
---
llvm/lib/Target/Hexagon/HexagonRegisterInfo.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Target/Hexagon/HexagonRegisterInfo.cpp b/llvm/lib/Target/Hexagon/HexagonRegisterInfo.cpp
index 999b6dc239b3d..88205358874f3 100644
--- a/llvm/lib/Target/Hexagon/HexagonRegisterInfo.cpp
+++ b/llvm/lib/Target/Hexagon/HexagonRegisterInfo.cpp
@@ -206,9 +206,9 @@ BitVector HexagonRegisterInfo::getReservedRegs(const MachineFunction &MF)
Reserved.set(Reg);
static const MCPhysReg RRegs[] = {
- Hexagon::R16, Hexagon::R17, Hexagon::R18,
- Hexagon::R19, Hexagon::R20, Hexagon::R21, Hexagon::R22, Hexagon::R23,
- Hexagon::R24, Hexagon::R25, Hexagon::R26, Hexagon::R27, Hexagon::R28};
+ Hexagon::R16, Hexagon::R17, Hexagon::R18, Hexagon::R19, Hexagon::R20,
+ Hexagon::R21, Hexagon::R22, Hexagon::R23, Hexagon::R24, Hexagon::R25,
+ Hexagon::R26, Hexagon::R27, Hexagon::R28};
const auto &HST = MF.getSubtarget<HexagonSubtarget>();
for (unsigned i = 0; i < std::size(RRegs); ++i)
if (HST.isRRegReserved(i + 16))
>From d9f18aafe3b9889596f5a3bdc1611f5db2204f3a Mon Sep 17 00:00:00 2001
From: quic-santdas <quic_santdas at quicinc.com>
Date: Tue, 19 May 2026 07:06:39 -0700
Subject: [PATCH 3/4] Addressed comments by quic-k
1. Added codegen test.
2. Modified clang test to add a regex.
3. Added assertion check for reserved register verification.
---
clang/test/Driver/hexagon-toolchain-elf.c | 4 +--
llvm/lib/Target/Hexagon/HexagonSubtarget.h | 5 +++-
llvm/test/CodeGen/Hexagon/reserved-regs.ll | 30 ++++++++++++++++++++++
3 files changed, 35 insertions(+), 4 deletions(-)
create mode 100644 llvm/test/CodeGen/Hexagon/reserved-regs.ll
diff --git a/clang/test/Driver/hexagon-toolchain-elf.c b/clang/test/Driver/hexagon-toolchain-elf.c
index 83797b731563a..b2a3ce750fbc0 100644
--- a/clang/test/Driver/hexagon-toolchain-elf.c
+++ b/clang/test/Driver/hexagon-toolchain-elf.c
@@ -587,9 +587,7 @@
// CHECK-R28: "-target-feature" "+reserved-r28"
// RUN: %clang -### --target=hexagon-unknown-elf %s 2>&1 \
// RUN: | FileCheck --check-prefix=CHECK371 %s
-// CHECK371-NOT: "+reserved-r16"
-// CHECK371-NOT: "+reserved-r19"
-// CHECK371-NOT: "+reserved-r28"
+// CHECK371-NOT: "+reserved-r{{(1[6-9]|2[0-8])}}"
// -----------------------------------------------------------------------------
// mcabac
diff --git a/llvm/lib/Target/Hexagon/HexagonSubtarget.h b/llvm/lib/Target/Hexagon/HexagonSubtarget.h
index 559f90e64df25..950246d95d1f4 100644
--- a/llvm/lib/Target/Hexagon/HexagonSubtarget.h
+++ b/llvm/lib/Target/Hexagon/HexagonSubtarget.h
@@ -286,7 +286,10 @@ class HexagonSubtarget : public HexagonGenSubtargetInfo {
bool useHVX64BOps() const { return useHVXOps() && UseHVX64BOps; }
bool hasMemNoShuf() const { return HasMemNoShuf; }
- bool isRRegReserved(unsigned i) const { return ReservedR[i]; }
+ bool isRRegReserved(unsigned i) const {
+ assert(i >= 16 && i <= 28 && "Register index out of reservable range");
+ return ReservedR[i];
+ }
bool usePredicatedCalls() const;
bool noreturnStackElim() const { return NoreturnStackElim; }
diff --git a/llvm/test/CodeGen/Hexagon/reserved-regs.ll b/llvm/test/CodeGen/Hexagon/reserved-regs.ll
new file mode 100644
index 0000000000000..80e5d77ba2964
--- /dev/null
+++ b/llvm/test/CodeGen/Hexagon/reserved-regs.ll
@@ -0,0 +1,30 @@
+; RUN: llc -mtriple=hexagon -O2 < %s | FileCheck -check-prefix=CHECK-DEFAULT %s
+; RUN: llc -mtriple=hexagon -mattr=+reserved-r16 -O2 < %s | FileCheck -check-prefix=CHECK-R16 %s
+; RUN: llc -mtriple=hexagon -mattr=+reserved-r16,+reserved-r17 -O2 < %s | FileCheck -check-prefix=CHECK-R16R17 %s
+
+; Test that reserved registers are not used by the register allocator.
+; The function has a call, forcing values to be placed in callee-saved
+; registers (R16-R27). Reserving a register must prevent its use.
+
+declare void @bar()
+
+define i32 @pressure(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f) {
+ %v1 = add i32 %a, %b
+ %v2 = mul i32 %v1, %c
+ %v3 = sub i32 %v2, %d
+ %v4 = add i32 %v3, %e
+ %v5 = mul i32 %v4, %f
+ call void @bar()
+ %v6 = add i32 %v5, %v1
+ %v7 = sub i32 %v6, %v2
+ %v8 = add i32 %v7, %v3
+ %v9 = mul i32 %v8, %v4
+ %v10 = sub i32 %v9, %v5
+ ret i32 %v10
+}
+
+; CHECK-DEFAULT: r16
+; CHECK-DEFAULT: r17
+; CHECK-R16-NOT: r16
+; CHECK-R16R17-NOT: r16
+; CHECK-R16R17-NOT: r17
>From 29a60c8b36cb266c8fc0d652bd3380f5a36bb704 Mon Sep 17 00:00:00 2001
From: quic-santdas <quic_santdas at quicinc.com>
Date: Wed, 20 May 2026 07:30:53 -0700
Subject: [PATCH 4/4] Addressed comments by quic-saravan
Leveraged enum defined in tablegen to store reserved
registers as bit values.
Used TargetSubtargetInfo::isRegisterReservedByUser() API
instead of creating a custom function.
---
llvm/lib/Target/Hexagon/Hexagon.td | 3 ++-
llvm/lib/Target/Hexagon/HexagonRegisterInfo.cpp | 7 +++----
llvm/lib/Target/Hexagon/HexagonSubtarget.h | 9 +++++----
3 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/llvm/lib/Target/Hexagon/Hexagon.td b/llvm/lib/Target/Hexagon/Hexagon.td
index 24052476d2fc4..ecb8a0a7a9738 100644
--- a/llvm/lib/Target/Hexagon/Hexagon.td
+++ b/llvm/lib/Target/Hexagon/Hexagon.td
@@ -118,7 +118,8 @@ def FeatureDuplex: SubtargetFeature<"duplex", "EnableDuplex", "true",
"Enable generation of duplex instruction">;
foreach i = {16-28} in
def FeatureReservedR#i : SubtargetFeature<"reserved-r"#i,
- "ReservedR["#i#"]", "true", "Reserve register R"#i>;
+ "UserReservedRegister[Hexagon::R"#i#"]",
+ "true", "Reserve register R"#i>;
def FeatureNoreturnStackElim: SubtargetFeature<"noreturn-stack-elim",
"NoreturnStackElim", "true",
"Eliminate stack allocation in a noreturn function when possible">;
diff --git a/llvm/lib/Target/Hexagon/HexagonRegisterInfo.cpp b/llvm/lib/Target/Hexagon/HexagonRegisterInfo.cpp
index 88205358874f3..9bd614c7b79f8 100644
--- a/llvm/lib/Target/Hexagon/HexagonRegisterInfo.cpp
+++ b/llvm/lib/Target/Hexagon/HexagonRegisterInfo.cpp
@@ -209,10 +209,9 @@ BitVector HexagonRegisterInfo::getReservedRegs(const MachineFunction &MF)
Hexagon::R16, Hexagon::R17, Hexagon::R18, Hexagon::R19, Hexagon::R20,
Hexagon::R21, Hexagon::R22, Hexagon::R23, Hexagon::R24, Hexagon::R25,
Hexagon::R26, Hexagon::R27, Hexagon::R28};
- const auto &HST = MF.getSubtarget<HexagonSubtarget>();
- for (unsigned i = 0; i < std::size(RRegs); ++i)
- if (HST.isRRegReserved(i + 16))
- Reserved.set(RRegs[i]);
+ for (MCPhysReg Reg : RRegs)
+ if (MF.getSubtarget().isRegisterReservedByUser(Reg))
+ Reserved.set(Reg);
Register AP =
MF.getInfo<HexagonMachineFunctionInfo>()->getStackAlignBaseReg();
diff --git a/llvm/lib/Target/Hexagon/HexagonSubtarget.h b/llvm/lib/Target/Hexagon/HexagonSubtarget.h
index 950246d95d1f4..2019bbf64b365 100644
--- a/llvm/lib/Target/Hexagon/HexagonSubtarget.h
+++ b/llvm/lib/Target/Hexagon/HexagonSubtarget.h
@@ -25,6 +25,7 @@
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/MC/MCInstrItineraries.h"
#include "llvm/Support/Alignment.h"
+#include <bitset>
#include <memory>
#include <string>
#include <vector>
@@ -63,7 +64,7 @@ class HexagonSubtarget : public HexagonGenSubtargetInfo {
bool HasPreV65 = false;
bool HasMemNoShuf = false;
bool EnableDuplex = false;
- bool ReservedR[32] = {};
+ std::bitset<Hexagon::NUM_TARGET_REGS> UserReservedRegister;
bool NoreturnStackElim = false;
public:
@@ -286,9 +287,9 @@ class HexagonSubtarget : public HexagonGenSubtargetInfo {
bool useHVX64BOps() const { return useHVXOps() && UseHVX64BOps; }
bool hasMemNoShuf() const { return HasMemNoShuf; }
- bool isRRegReserved(unsigned i) const {
- assert(i >= 16 && i <= 28 && "Register index out of reservable range");
- return ReservedR[i];
+ bool isRegisterReservedByUser(Register i) const override {
+ assert(i.id() < Hexagon::NUM_TARGET_REGS && "Register out of range");
+ return UserReservedRegister[i.id()];
}
bool usePredicatedCalls() const;
More information about the cfe-commits
mailing list