[llvm] [RISCV] Support Zdinx registers in -fzero-call-used-regs (PR #206323)
Lucas Chollet via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 29 12:02:19 PDT 2026
https://github.com/LucasChollet updated https://github.com/llvm/llvm-project/pull/206323
>From 59a706ac84106906cc703d10c6b0fa09ffa29eeb Mon Sep 17 00:00:00 2001
From: Lucas Chollet <lucas.chollet at serenityos.org>
Date: Sun, 28 Jun 2026 12:51:05 +0200
Subject: [PATCH 1/2] [RISCV] Support Zdinx registers in -fzero-call-used-regs
First, this patch makes the backend consider GPRF16 and GPRF32 registers
as GPR. These registers are aliases of normal GPR but for use with the
Zdinx extension (floating points instructions on GPR).
Second, it teaches the general -fzero-call-used-regs logic to understand
registers that span over multiple physical registers. One example of
this is `GPRPair` in the RISC-V backend. Now, these registers are not
considered as a whole anymore, but simply as their underlying physical
registers, e.g. X10_11 is split into {X10, X11}.
---
.../include/llvm/CodeGen/TargetRegisterInfo.h | 7 +++
llvm/lib/CodeGen/PrologEpilogInserter.cpp | 7 ++-
llvm/lib/Target/RISCV/RISCVFrameLowering.cpp | 49 ++++++++++++++++-
llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp | 10 ++++
llvm/lib/Target/RISCV/RISCVRegisterInfo.h | 2 +
llvm/lib/Target/RISCV/RISCVRegisterInfo.td | 6 +-
.../test/CodeGen/RISCV/zero-call-used-regs.ll | 55 +++++++++++++++++++
7 files changed, 131 insertions(+), 5 deletions(-)
diff --git a/llvm/include/llvm/CodeGen/TargetRegisterInfo.h b/llvm/include/llvm/CodeGen/TargetRegisterInfo.h
index ecf027d97cc15..20008f5fb7f1d 100644
--- a/llvm/include/llvm/CodeGen/TargetRegisterInfo.h
+++ b/llvm/include/llvm/CodeGen/TargetRegisterInfo.h
@@ -1285,6 +1285,13 @@ class LLVM_ABI TargetRegisterInfo : public MCRegisterInfo {
// Whether this register should be ignored when generating CodeView debug
// info, because it's a known there is no mapping available.
virtual bool isIgnoredCVReg(MCRegister LLVMReg) const { return false; }
+
+ // Return all physical registers occupied by Reg.
+ virtual BitVector getUnderlyingRegisters(MCRegister Reg) const {
+ BitVector BackingRegisters(getNumRegs());
+ BackingRegisters.set(Reg.id());
+ return BackingRegisters;
+ }
};
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/CodeGen/PrologEpilogInserter.cpp b/llvm/lib/CodeGen/PrologEpilogInserter.cpp
index d9557a3958978..e2b9548829917 100644
--- a/llvm/lib/CodeGen/PrologEpilogInserter.cpp
+++ b/llvm/lib/CodeGen/PrologEpilogInserter.cpp
@@ -1251,8 +1251,11 @@ void PEIImpl::insertZeroCallUsedRegs(MachineFunction &MF) {
MCRegister Reg = MO.getReg();
if (AllocatableSet[Reg.id()] && !MO.isImplicit() &&
- (MO.isDef() || MO.isUse()))
- UsedRegs.set(Reg.id());
+ (MO.isDef() || MO.isUse())) {
+ BitVector BackingRegisters = TRI.getUnderlyingRegisters(Reg);
+ for (MCRegister PhysicalReg : BackingRegisters.set_bits())
+ UsedRegs.set(PhysicalReg.id());
+ }
}
}
diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index 5c5d081007796..eef2710f8c151 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -1432,6 +1432,52 @@ void RISCVFrameLowering::emitEpilogue(MachineFunction &MF,
emitSiFiveCLICStackSwap(MF, MBB, MBBI, DL);
}
+static MCRegister getPhysicalGPR(MCRegister Reg) {
+ switch (Reg) {
+#define CASE(n) \
+ case RISCV::X##n: \
+ case RISCV::X##n##_H: \
+ case RISCV::X##n##_W: \
+ return RISCV::X##n
+ CASE(0);
+ CASE(1);
+ CASE(2);
+ CASE(3);
+ CASE(4);
+ CASE(5);
+ CASE(6);
+ CASE(7);
+ CASE(8);
+ CASE(9);
+ CASE(10);
+ CASE(11);
+ CASE(12);
+ CASE(13);
+ CASE(14);
+ CASE(15);
+ CASE(16);
+ CASE(17);
+ CASE(18);
+ CASE(19);
+ CASE(20);
+ CASE(21);
+ CASE(22);
+ CASE(23);
+ CASE(24);
+ CASE(25);
+ CASE(26);
+ CASE(27);
+ CASE(28);
+ CASE(29);
+ CASE(30);
+ CASE(31);
+#undef CASE
+ }
+
+ llvm::reportFatalInternalError(
+ "getPhysicalGPR called with unsupported register");
+}
+
static MCRegister getLargestFPRegisterOrZero(const RISCVSubtarget &STI,
const TargetRegisterInfo &TRI,
MCRegister Reg) {
@@ -1480,7 +1526,8 @@ void RISCVFrameLowering::emitZeroCallUsedRegs(BitVector RegsToZero,
for (MCRegister Reg : RegsToZero.set_bits()) {
if (TRI.isGeneralPurposeRegister(MF, Reg)) {
- FinalRegsToZero.set(Reg.id());
+ if (MCRegister MaybeReg = getPhysicalGPR(Reg))
+ FinalRegsToZero.set(MaybeReg.id());
} else if (TRI.isFPRegister(Reg)) {
if (MCRegister MaybeReg = getLargestFPRegisterOrZero(STI, TRI, Reg))
FinalRegsToZero.set(MaybeReg.id());
diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
index 7828f07cdbaa5..cda7c1c156cd0 100644
--- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
@@ -1168,6 +1168,16 @@ void RISCVRegisterInfo::updateRegAllocHint(Register Reg, Register NewReg,
}
}
+BitVector RISCVRegisterInfo::getUnderlyingRegisters(MCRegister Reg) const {
+ if (!RISCV::GPRPairRegClass.contains(Reg))
+ return TargetRegisterInfo::getUnderlyingRegisters(Reg);
+
+ BitVector BackingRegisters(getNumRegs());
+ BackingRegisters.set(getSubReg(Reg, RISCV::sub_gpr_even));
+ BackingRegisters.set(getSubReg(Reg, RISCV::sub_gpr_odd));
+ return BackingRegisters;
+}
+
Register
RISCVRegisterInfo::findVRegWithEncoding(const TargetRegisterClass &RegClass,
uint16_t Encoding) const {
diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.h b/llvm/lib/Target/RISCV/RISCVRegisterInfo.h
index 625c19b3a02fd..7e05ddc1539fb 100644
--- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.h
+++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.h
@@ -184,6 +184,8 @@ struct RISCVRegisterInfo : public RISCVGenRegisterInfo {
RISCV::FPR64RegClass.contains(Reg) ||
RISCV::FPR128RegClass.contains(Reg);
}
+
+ BitVector getUnderlyingRegisters(MCRegister Reg) const override;
};
} // namespace llvm
diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.td b/llvm/lib/Target/RISCV/RISCVRegisterInfo.td
index a3f968b5ab76f..07f002fccfa66 100644
--- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.td
+++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.td
@@ -570,6 +570,7 @@ def GPRF16C : RISCVRegisterClass<[f16], 16, (add (sequence "X%u_H", 10, 15),
(sequence "X%u_H", 8, 9))> {
let DecoderMethod = "DecodeSimpleRegisterClass<RISCV::X8_H, 8>";
}
+def GPRF16X0 : RISCVRegisterClass<[f16], 16, (add X0_H)>;
def GPRF16NoX0 : RISCVRegisterClass<[f16], 16, (sub GPRF16, X0_H)>;
def GPRF32 : RISCVRegisterClass<[f32], 32, (add (sequence "X%u_W", 10, 17),
@@ -584,6 +585,7 @@ def GPRF32C : RISCVRegisterClass<[f32], 32, (add (sequence "X%u_W", 10, 15),
(sequence "X%u_W", 8, 9))> {
let DecoderMethod = "DecodeSimpleRegisterClass<RISCV::X8_W, 8>";
}
+def GPRF32X0 : RISCVRegisterClass<[f32], 32, (add X0_W)>;
def GPRF32NoX0 : RISCVRegisterClass<[f32], 32, (sub GPRF32, X0_W)>;
//===----------------------------------------------------------------------===//
@@ -1024,6 +1026,6 @@ def MR0 : RISCVRegisterClass<[i8, v8i1], 8, (add M0)>;
// Register categories.
//
-def GeneralPurposeRegisters : RegisterCategory<[GPR]>;
+def GeneralPurposeRegisters : RegisterCategory<[GPR, GPRF16, GPRF32]>;
-def FixedRegisters : RegisterCategory<[GPRX0]>;
+def FixedRegisters : RegisterCategory<[GPRX0, GPRF16X0, GPRF32X0]>;
diff --git a/llvm/test/CodeGen/RISCV/zero-call-used-regs.ll b/llvm/test/CodeGen/RISCV/zero-call-used-regs.ll
index 28e6c792b9dfd..46b0cba76194b 100644
--- a/llvm/test/CodeGen/RISCV/zero-call-used-regs.ll
+++ b/llvm/test/CodeGen/RISCV/zero-call-used-regs.ll
@@ -281,4 +281,59 @@ entry:
ret double %mul
}
+define double @used_zdinx(double noundef %a, double noundef %b, float noundef %c) #1 "zero-call-used-regs"="used" {
+; 64-BITS-LABEL: used_zdinx:
+; 64-BITS: # %bb.0: # %entry
+; 64-BITS-NEXT: fmul.d a0, a0, a1
+; 64-BITS-NEXT: fcvt.d.s a1, a2
+; 64-BITS-NEXT: fmul.d a0, a0, a1
+; 64-BITS-NEXT: li a1, 0
+; 64-BITS-NEXT: li a2, 0
+; 64-BITS-NEXT: ret
+;
+; 32-BITS-LABEL: used_zdinx:
+; 32-BITS: # %bb.0: # %entry
+; 32-BITS-NEXT: fmul.d a0, a0, a2
+; 32-BITS-NEXT: fcvt.d.s a2, a4
+; 32-BITS-NEXT: fmul.d a0, a0, a2
+; 32-BITS-NEXT: li a2, 0
+; 32-BITS-NEXT: li a3, 0
+; 32-BITS-NEXT: li a4, 0
+; 32-BITS-NEXT: ret
+
+entry:
+ %conv = fpext float %c to double
+ %mul1 = fmul double %a, %b
+ %mul2 = fmul double %mul1, %conv
+ ret double %mul2
+}
+
+define double @used_arg_zdinx(double noundef %a, double noundef %b, float noundef %c) #1 "zero-call-used-regs"="used-arg" {
+; 64-BITS-LABEL: used_arg_zdinx:
+; 64-BITS: # %bb.0: # %entry
+; 64-BITS-NEXT: fmul.d a0, a0, a1
+; 64-BITS-NEXT: fcvt.d.s a1, a2
+; 64-BITS-NEXT: fmul.d a0, a0, a1
+; 64-BITS-NEXT: li a1, 0
+; 64-BITS-NEXT: li a2, 0
+; 64-BITS-NEXT: ret
+;
+; 32-BITS-LABEL: used_arg_zdinx:
+; 32-BITS: # %bb.0: # %entry
+; 32-BITS-NEXT: fmul.d a0, a0, a2
+; 32-BITS-NEXT: fcvt.d.s a2, a4
+; 32-BITS-NEXT: fmul.d a0, a0, a2
+; 32-BITS-NEXT: li a2, 0
+; 32-BITS-NEXT: li a3, 0
+; 32-BITS-NEXT: li a4, 0
+; 32-BITS-NEXT: ret
+
+entry:
+ %conv = fpext float %c to double
+ %mul1 = fmul double %a, %b
+ %mul2 = fmul double %mul1, %conv
+ ret double %mul2
+}
+
attributes #0 = { "target-cpu"="generic" "target-features"="+m,+f,+d" }
+attributes #1 = { "target-cpu"="generic" "target-features"="+m,+zdinx" }
>From 760a1bfd15462d12099798387ac5791549437cfb Mon Sep 17 00:00:00 2001
From: Lucas Chollet <lucas.chollet at serenityos.org>
Date: Mon, 29 Jun 2026 20:58:18 +0200
Subject: [PATCH 2/2] Use getMatchingSuperReg
---
llvm/lib/Target/RISCV/RISCVFrameLowering.cpp | 59 ++++++--------------
1 file changed, 17 insertions(+), 42 deletions(-)
diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index eef2710f8c151..d089df08d7c1c 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -1432,46 +1432,22 @@ void RISCVFrameLowering::emitEpilogue(MachineFunction &MF,
emitSiFiveCLICStackSwap(MF, MBB, MBBI, DL);
}
-static MCRegister getPhysicalGPR(MCRegister Reg) {
- switch (Reg) {
-#define CASE(n) \
- case RISCV::X##n: \
- case RISCV::X##n##_H: \
- case RISCV::X##n##_W: \
- return RISCV::X##n
- CASE(0);
- CASE(1);
- CASE(2);
- CASE(3);
- CASE(4);
- CASE(5);
- CASE(6);
- CASE(7);
- CASE(8);
- CASE(9);
- CASE(10);
- CASE(11);
- CASE(12);
- CASE(13);
- CASE(14);
- CASE(15);
- CASE(16);
- CASE(17);
- CASE(18);
- CASE(19);
- CASE(20);
- CASE(21);
- CASE(22);
- CASE(23);
- CASE(24);
- CASE(25);
- CASE(26);
- CASE(27);
- CASE(28);
- CASE(29);
- CASE(30);
- CASE(31);
-#undef CASE
+static MCRegister getPhysicalGPR(const TargetRegisterInfo &TRI,
+ MCRegister Reg) {
+
+ if (RISCV::GPRRegClass.contains(Reg))
+ return Reg;
+
+ std::array<TargetRegisterClass const *, 2> RegisterClasses = {
+ &RISCV::GPRF16RegClass, &RISCV::GPRF32RegClass};
+ std::array<unsigned, 2> SubIdx = {RISCV::sub_16, RISCV::sub_32};
+
+ for (auto [RegClass, SubReg] : zip(RegisterClasses, SubIdx)) {
+ if (RegClass->contains(Reg)) {
+ if (MCRegister Super =
+ TRI.getMatchingSuperReg(Reg, SubReg, &RISCV::GPRRegClass))
+ return Super;
+ }
}
llvm::reportFatalInternalError(
@@ -1526,8 +1502,7 @@ void RISCVFrameLowering::emitZeroCallUsedRegs(BitVector RegsToZero,
for (MCRegister Reg : RegsToZero.set_bits()) {
if (TRI.isGeneralPurposeRegister(MF, Reg)) {
- if (MCRegister MaybeReg = getPhysicalGPR(Reg))
- FinalRegsToZero.set(MaybeReg.id());
+ FinalRegsToZero.set(getPhysicalGPR(TRI, Reg).id());
} else if (TRI.isFPRegister(Reg)) {
if (MCRegister MaybeReg = getLargestFPRegisterOrZero(STI, TRI, Reg))
FinalRegsToZero.set(MaybeReg.id());
More information about the llvm-commits
mailing list