[llvm] [BOLT][RISCV] Implement register analysis hooks (PR #220581)

via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 7 19:56:21 PDT 2026


https://github.com/Thrrreeee updated https://github.com/llvm/llvm-project/pull/220581

>From 67c21aaea056fc389cfbf95752acd084427ea903 Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Wed, 2 Sep 2026 20:39:28 +0800
Subject: [PATCH 1/4] [BOLT][RISCV] Implement register analysis hooks

---
 bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp |  46 +++++++
 bolt/unittests/Core/MCPlusBuilder.cpp        | 130 ++++++++++++++++++-
 2 files changed, 171 insertions(+), 5 deletions(-)

diff --git a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
index 349f38cb11b26..bd792b4346bf2 100644
--- a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
+++ b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
@@ -40,6 +40,25 @@ class RISCVMCPlusBuilder : public MCPlusBuilder {
 public:
   using MCPlusBuilder::MCPlusBuilder;
 
+  MCPhysReg getFlagsReg() const override { return RISCV::NoRegister; }
+
+  bool isCleanRegXOR(const MCInst &Inst) const override {
+    switch (Inst.getOpcode()) {
+    case RISCV::XOR:
+    case RISCV::C_XOR:
+      return Inst.getOperand(1).getReg() == Inst.getOperand(2).getReg();
+    default:
+      return false;
+    }
+  }
+
+  BitVector getRegsUsedAsParams() const override {
+    BitVector Regs(RegInfo->getNumRegs(), false);
+    for (MCPhysReg Reg = RISCV::X10; Reg <= RISCV::X17; ++Reg)
+      Regs |= getAliases(Reg);
+    return Regs;
+  }
+
   std::unique_ptr<MCSymbolizer>
   createTargetSymbolizer(BinaryFunction &Function,
                          bool CreateNewSymbols) const override {
@@ -73,6 +92,33 @@ class RISCVMCPlusBuilder : public MCPlusBuilder {
     Regs |= getAliases(RISCV::X27);
   }
 
+  void getDefaultLiveOut(BitVector &Regs) const override {
+    // The RISC-V psABI uses a0 (x10) and a1 (x11) to return integer and pointer
+    // values.
+    Regs |= getAliases(RISCV::X10);
+    Regs |= getAliases(RISCV::X11);
+  }
+
+  void getGPRegs(BitVector &Regs, bool IncludeAlias = true) const override {
+    for (MCPhysReg Reg = RISCV::X1; Reg <= RISCV::X31; ++Reg) {
+      if (IncludeAlias)
+        Regs |= getAliases(Reg);
+      else
+        Regs.set(Reg);
+    }
+  }
+
+  void removeNonScavengeableRegs(BitVector &Regs) const override {
+    BitVector ExclusionMask(RegInfo->getNumRegs(), false);
+    ExclusionMask |= getAliases(RISCV::X1); // return address
+    ExclusionMask |= getAliases(RISCV::X2); // stack pointer
+    ExclusionMask |= getAliases(RISCV::X3); // global pointer
+    ExclusionMask |= getAliases(RISCV::X4); // thread pointer
+    ExclusionMask |= getAliases(RISCV::X8); // frame pointer
+    ExclusionMask.flip();
+    Regs &= ExclusionMask;
+  }
+
   bool shouldRecordCodeRelocation(uint32_t RelType) const override {
     switch (RelType) {
     case ELF::R_RISCV_JAL:
diff --git a/bolt/unittests/Core/MCPlusBuilder.cpp b/bolt/unittests/Core/MCPlusBuilder.cpp
index a692f45f551eb..17dc7f6a8473f 100644
--- a/bolt/unittests/Core/MCPlusBuilder.cpp
+++ b/bolt/unittests/Core/MCPlusBuilder.cpp
@@ -16,6 +16,11 @@
 #include "X86Subtarget.h"
 #endif // X86_AVAILABLE
 
+#ifdef RISCV_AVAILABLE
+#include "MCTargetDesc/RISCVMCTargetDesc.h"
+#include "RISCVSubtarget.h"
+#endif // RISCV_AVAILABLE
+
 #include "bolt/Core/BinaryBasicBlock.h"
 #include "bolt/Core/BinaryFunction.h"
 #include "bolt/Rewrite/RewriteInstance.h"
@@ -23,6 +28,7 @@
 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
 #include "llvm/MC/MCInstBuilder.h"
 #include "llvm/Support/TargetSelect.h"
+#include "llvm/TargetParser/SubtargetFeature.h"
 #include "gtest/gtest.h"
 
 using namespace llvm;
@@ -56,17 +62,34 @@ struct MCPlusBuilderTester : public testing::TestWithParam<Triple::ArchType> {
     ELF64LE::Ehdr *EHdr = reinterpret_cast<typename ELF64LE::Ehdr *>(ElfBuf);
     EHdr->e_ident[llvm::ELF::EI_CLASS] = llvm::ELF::ELFCLASS64;
     EHdr->e_ident[llvm::ELF::EI_DATA] = llvm::ELF::ELFDATA2LSB;
-    EHdr->e_machine = GetParam() == Triple::aarch64 ? EM_AARCH64 : EM_X86_64;
+    switch (GetParam()) {
+    case Triple::aarch64:
+      EHdr->e_machine = EM_AARCH64;
+      break;
+    case Triple::riscv64:
+      EHdr->e_machine = EM_RISCV;
+      break;
+    case Triple::x86_64:
+      EHdr->e_machine = EM_X86_64;
+      break;
+    default:
+      llvm_unreachable("Unsupported architecture");
+      break;
+    }
     MemoryBufferRef Source(StringRef(ElfBuf, sizeof(ElfBuf)), "ELF");
     ObjFile = cantFail(ObjectFile::createObjectFile(Source));
   }
 
   void initializeBolt() {
-    Relocation::Arch = ObjFile->makeTriple().getArch();
+    const Triple TheTriple = GetParam();
+    Relocation::Arch = TheTriple.getArch();
+    // Minimal test ELFs have no RISC-V attributes. Pass an empty feature set so
+    // createBinaryContext() can add the +relax feature required by BOLT.
+    SubtargetFeatures Features;
     BC = cantFail(BinaryContext::createBinaryContext(
-        ObjFile->makeTriple(), std::make_shared<orc::SymbolStringPool>(),
-        ObjFile->getFileName(), nullptr, true, DWARFContext::create(*ObjFile),
-        {llvm::outs(), llvm::errs()}));
+        TheTriple, std::make_shared<orc::SymbolStringPool>(),
+        ObjFile->getFileName(), TheTriple.isRISCV() ? &Features : nullptr, true,
+        DWARFContext::create(*ObjFile), {llvm::outs(), llvm::errs()}));
     ASSERT_FALSE(!BC);
     BC->initializeTarget(std::unique_ptr<MCPlusBuilder>(
         createMCPlusBuilder(GetParam(), BC->MIA.get(), BC->MII.get(),
@@ -87,6 +110,13 @@ struct MCPlusBuilderTester : public testing::TestWithParam<Triple::ArchType> {
     assertRegMask(RegMask, ExpectedRegs);
   }
 
+  BitVector getAliasMask(std::initializer_list<MCPhysReg> Registers) {
+    BitVector RegMask(BC->MRI->getNumRegs());
+    for (MCPhysReg Reg : Registers)
+      RegMask |= BC->MIB->getAliases(Reg);
+    return RegMask;
+  }
+
   void testRegAliases(Triple::ArchType Arch, uint64_t Register,
                       std::initializer_list<MCPhysReg> ExpectedAliases,
                       bool OnlySmaller = false) {
@@ -892,6 +922,96 @@ TEST_P(MCPlusBuilderTester, AArch64_isCleanRegXOR) {
 
 #endif // AARCH64_AVAILABLE
 
+#ifdef RISCV_AVAILABLE
+
+INSTANTIATE_TEST_SUITE_P(RISCV, MCPlusBuilderTester,
+                         ::testing::Values(Triple::riscv64));
+
+TEST_P(MCPlusBuilderTester, RISCV_NoFlagsRegister) {
+  if (GetParam() != Triple::riscv64)
+    GTEST_SKIP();
+
+  EXPECT_EQ(BC->MIB->getFlagsReg(), RISCV::NoRegister);
+}
+
+TEST_P(MCPlusBuilderTester, RISCV_isCleanRegXOR) {
+  if (GetParam() != Triple::riscv64)
+    GTEST_SKIP();
+
+  MCInst XOR = MCInstBuilder(RISCV::XOR)
+                   .addReg(RISCV::X5)
+                   .addReg(RISCV::X6)
+                   .addReg(RISCV::X6);
+  EXPECT_TRUE(BC->MIB->isCleanRegXOR(XOR));
+
+  MCInst NonCleanXOR = MCInstBuilder(RISCV::XOR)
+                           .addReg(RISCV::X5)
+                           .addReg(RISCV::X6)
+                           .addReg(RISCV::X7);
+  EXPECT_FALSE(BC->MIB->isCleanRegXOR(NonCleanXOR));
+
+  MCInst CompressedXOR = MCInstBuilder(RISCV::C_XOR)
+                             .addReg(RISCV::X8)
+                             .addReg(RISCV::X8)
+                             .addReg(RISCV::X8);
+  EXPECT_TRUE(BC->MIB->isCleanRegXOR(CompressedXOR));
+
+  MCInst ADD = MCInstBuilder(RISCV::ADD)
+                   .addReg(RISCV::X5)
+                   .addReg(RISCV::X6)
+                   .addReg(RISCV::X6);
+  EXPECT_FALSE(BC->MIB->isCleanRegXOR(ADD));
+}
+
+TEST_P(MCPlusBuilderTester, RISCV_ABIRegisterMasks) {
+  if (GetParam() != Triple::riscv64)
+    GTEST_SKIP();
+
+  BitVector ExpectedParams(BC->MRI->getNumRegs());
+  for (MCPhysReg Reg = RISCV::X10; Reg <= RISCV::X17; ++Reg)
+    ExpectedParams |= BC->MIB->getAliases(Reg);
+  EXPECT_EQ(BC->MIB->getRegsUsedAsParams(), ExpectedParams);
+
+  BitVector LiveOut(BC->MRI->getNumRegs());
+  BC->MIB->getDefaultLiveOut(LiveOut);
+  EXPECT_EQ(LiveOut, getAliasMask({RISCV::X10, RISCV::X11}));
+}
+
+TEST_P(MCPlusBuilderTester, RISCV_GPRegisterMasks) {
+  if (GetParam() != Triple::riscv64)
+    GTEST_SKIP();
+
+  BitVector ExpectedWithAliases(BC->MRI->getNumRegs());
+  BitVector ExpectedWithoutAliases(BC->MRI->getNumRegs());
+  for (MCPhysReg Reg = RISCV::X1; Reg <= RISCV::X31; ++Reg) {
+    ExpectedWithAliases |= BC->MIB->getAliases(Reg);
+    ExpectedWithoutAliases.set(Reg);
+  }
+
+  BitVector GPRegs(BC->MRI->getNumRegs());
+  BC->MIB->getGPRegs(GPRegs);
+  EXPECT_EQ(GPRegs, ExpectedWithAliases);
+
+  GPRegs.reset();
+  BC->MIB->getGPRegs(GPRegs, /*IncludeAlias=*/false);
+  EXPECT_EQ(GPRegs, ExpectedWithoutAliases);
+}
+
+TEST_P(MCPlusBuilderTester, RISCV_removeNonScavengeableRegs) {
+  if (GetParam() != Triple::riscv64)
+    GTEST_SKIP();
+
+  BitVector Regs(BC->MRI->getNumRegs(), true);
+  BitVector Expected = Regs;
+  for (MCPhysReg Reg : {RISCV::X1, RISCV::X2, RISCV::X3, RISCV::X4, RISCV::X8})
+    Expected.reset(BC->MIB->getAliases(Reg));
+
+  BC->MIB->removeNonScavengeableRegs(Regs);
+  EXPECT_EQ(Regs, Expected);
+}
+
+#endif // RISCV_AVAILABLE
+
 #ifdef X86_AVAILABLE
 
 INSTANTIATE_TEST_SUITE_P(X86, MCPlusBuilderTester,

>From f3ac72d8dd36b97278034413bc961607b1e02414 Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Thu, 3 Sep 2026 14:47:48 +0800
Subject: [PATCH 2/4] fix

---
 bolt/unittests/Core/MCPlusBuilder.cpp | 17 ++++++-----------
 1 file changed, 6 insertions(+), 11 deletions(-)

diff --git a/bolt/unittests/Core/MCPlusBuilder.cpp b/bolt/unittests/Core/MCPlusBuilder.cpp
index 17dc7f6a8473f..14291e6f0ece7 100644
--- a/bolt/unittests/Core/MCPlusBuilder.cpp
+++ b/bolt/unittests/Core/MCPlusBuilder.cpp
@@ -83,8 +83,8 @@ struct MCPlusBuilderTester : public testing::TestWithParam<Triple::ArchType> {
   void initializeBolt() {
     const Triple TheTriple = GetParam();
     Relocation::Arch = TheTriple.getArch();
-    // Minimal test ELFs have no RISC-V attributes. Pass an empty feature set so
-    // createBinaryContext() can add the +relax feature required by BOLT.
+    // Minimal test ELFs have no RISC-V attributes. RISC-V needs an empty
+    // feature set for +relax, while other targets reject a non-null one.
     SubtargetFeatures Features;
     BC = cantFail(BinaryContext::createBinaryContext(
         TheTriple, std::make_shared<orc::SymbolStringPool>(),
@@ -92,7 +92,7 @@ struct MCPlusBuilderTester : public testing::TestWithParam<Triple::ArchType> {
         DWARFContext::create(*ObjFile), {llvm::outs(), llvm::errs()}));
     ASSERT_FALSE(!BC);
     BC->initializeTarget(std::unique_ptr<MCPlusBuilder>(
-        createMCPlusBuilder(GetParam(), BC->MIA.get(), BC->MII.get(),
+        createMCPlusBuilder(TheTriple.getArch(), BC->MIA.get(), BC->MII.get(),
                             BC->MRI.get(), BC->STI.get())));
   }
 
@@ -110,13 +110,6 @@ struct MCPlusBuilderTester : public testing::TestWithParam<Triple::ArchType> {
     assertRegMask(RegMask, ExpectedRegs);
   }
 
-  BitVector getAliasMask(std::initializer_list<MCPhysReg> Registers) {
-    BitVector RegMask(BC->MRI->getNumRegs());
-    for (MCPhysReg Reg : Registers)
-      RegMask |= BC->MIB->getAliases(Reg);
-    return RegMask;
-  }
-
   void testRegAliases(Triple::ArchType Arch, uint64_t Register,
                       std::initializer_list<MCPhysReg> ExpectedAliases,
                       bool OnlySmaller = false) {
@@ -974,7 +967,9 @@ TEST_P(MCPlusBuilderTester, RISCV_ABIRegisterMasks) {
 
   BitVector LiveOut(BC->MRI->getNumRegs());
   BC->MIB->getDefaultLiveOut(LiveOut);
-  EXPECT_EQ(LiveOut, getAliasMask({RISCV::X10, RISCV::X11}));
+  BitVector ExpectedLiveOut = BC->MIB->getAliases(RISCV::X10);
+  ExpectedLiveOut |= BC->MIB->getAliases(RISCV::X11);
+  EXPECT_EQ(LiveOut, ExpectedLiveOut);
 }
 
 TEST_P(MCPlusBuilderTester, RISCV_GPRegisterMasks) {

>From 53422aa7b86766a637f6b3122dddaa1953a1dc27 Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Thu, 3 Sep 2026 15:34:21 +0800
Subject: [PATCH 3/4] fix XOR function name

---
 bolt/include/bolt/Core/MCPlusBuilder.h        |  2 +-
 bolt/include/bolt/Passes/LivenessAnalysis.h   |  2 +-
 .../Target/AArch64/AArch64MCPlusBuilder.cpp   |  2 +-
 bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp  | 12 ++---
 bolt/lib/Target/X86/X86MCPlusBuilder.cpp      |  2 +-
 bolt/unittests/Core/MCPlusBuilder.cpp         | 46 ++++++++-----------
 6 files changed, 28 insertions(+), 38 deletions(-)

diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h
index be0d58af14fc4..e55db33834ee8 100644
--- a/bolt/include/bolt/Core/MCPlusBuilder.h
+++ b/bolt/include/bolt/Core/MCPlusBuilder.h
@@ -897,7 +897,7 @@ class MCPlusBuilder {
     return false;
   }
 
-  virtual bool isCleanRegXOR(const MCInst &Inst) const {
+  virtual bool isCleanReg(const MCInst &Inst) const {
     llvm_unreachable("not implemented");
     return false;
   }
diff --git a/bolt/include/bolt/Passes/LivenessAnalysis.h b/bolt/include/bolt/Passes/LivenessAnalysis.h
index 6fcb66957f028..5d5bcd29a0ca3 100644
--- a/bolt/include/bolt/Passes/LivenessAnalysis.h
+++ b/bolt/include/bolt/Passes/LivenessAnalysis.h
@@ -137,7 +137,7 @@ class LivenessAnalysis : public DataflowAnalysis<LivenessAnalysis, BitVector,
     Next &= Written;
     // Gen
     if (!this->BC.MIB->isCFI(Point)) {
-      if (BC.MIB->isCleanRegXOR(Point))
+      if (BC.MIB->isCleanReg(Point))
         return Next;
 
       BitVector Used = BitVector(NumRegs, false);
diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
index 8d7fee733b9c3..20731cf0c28d8 100644
--- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
+++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
@@ -2658,7 +2658,7 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
            isAArch64ExclusiveStore(Inst);
   }
 
-  bool isCleanRegXOR(const MCInst &Inst) const override {
+  bool isCleanReg(const MCInst &Inst) const override {
     switch (Inst.getOpcode()) {
     case AArch64::EORXrs:
     case AArch64::EORWrs:
diff --git a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
index bd792b4346bf2..3e013fc79c733 100644
--- a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
+++ b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
@@ -42,14 +42,10 @@ class RISCVMCPlusBuilder : public MCPlusBuilder {
 
   MCPhysReg getFlagsReg() const override { return RISCV::NoRegister; }
 
-  bool isCleanRegXOR(const MCInst &Inst) const override {
-    switch (Inst.getOpcode()) {
-    case RISCV::XOR:
-    case RISCV::C_XOR:
-      return Inst.getOperand(1).getReg() == Inst.getOperand(2).getReg();
-    default:
-      return false;
-    }
+  bool isCleanReg(const MCInst &Inst) const override {
+    return Inst.getOpcode() == RISCV::ADDI && Inst.getOperand(1).isReg() &&
+           Inst.getOperand(1).getReg() == RISCV::X0 &&
+           Inst.getOperand(2).isImm() && Inst.getOperand(2).getImm() == 0;
   }
 
   BitVector getRegsUsedAsParams() const override {
diff --git a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp
index 20eacd88e9735..9bbbd87f53da3 100644
--- a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp
+++ b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp
@@ -379,7 +379,7 @@ class X86MCPlusBuilder : public MCPlusBuilder {
     return MCII.mayStore();
   }
 
-  bool isCleanRegXOR(const MCInst &Inst) const override {
+  bool isCleanReg(const MCInst &Inst) const override {
     switch (Inst.getOpcode()) {
     case X86::XOR16rr:
     case X86::XOR32rr:
diff --git a/bolt/unittests/Core/MCPlusBuilder.cpp b/bolt/unittests/Core/MCPlusBuilder.cpp
index 14291e6f0ece7..aaaa25444839d 100644
--- a/bolt/unittests/Core/MCPlusBuilder.cpp
+++ b/bolt/unittests/Core/MCPlusBuilder.cpp
@@ -854,7 +854,7 @@ TEST_P(MCPlusBuilderTester, AArch64_Psign_Pauth_variants) {
   ASSERT_TRUE(BC->MIB->isPAuthAndRet(Retab));
 }
 
-TEST_P(MCPlusBuilderTester, AArch64_isCleanRegXOR) {
+TEST_P(MCPlusBuilderTester, AArch64_isCleanReg) {
   if (GetParam() != Triple::aarch64)
     GTEST_SKIP();
 
@@ -867,7 +867,7 @@ TEST_P(MCPlusBuilderTester, AArch64_isCleanRegXOR) {
                       .addReg(AArch64::X0)
                       .addReg(AArch64::X0)
                       .addImm(0);
-  ASSERT_TRUE(BC->MIB->isCleanRegXOR(EORXrs));
+  ASSERT_TRUE(BC->MIB->isCleanReg(EORXrs));
 
   // eor w0, w0, w0
   MCInst EORWrs = MCInstBuilder(AArch64::EORWrs)
@@ -875,7 +875,7 @@ TEST_P(MCPlusBuilderTester, AArch64_isCleanRegXOR) {
                       .addReg(AArch64::W0)
                       .addReg(AArch64::W0)
                       .addImm(0);
-  ASSERT_TRUE(BC->MIB->isCleanRegXOR(EORWrs));
+  ASSERT_TRUE(BC->MIB->isCleanReg(EORWrs));
 
   // mov x0, xzr
   MCInst ORRXrs = MCInstBuilder(AArch64::ORRXrs)
@@ -883,7 +883,7 @@ TEST_P(MCPlusBuilderTester, AArch64_isCleanRegXOR) {
                       .addReg(AArch64::XZR)
                       .addReg(AArch64::XZR)
                       .addImm(0);
-  ASSERT_TRUE(BC->MIB->isCleanRegXOR(ORRXrs));
+  ASSERT_TRUE(BC->MIB->isCleanReg(ORRXrs));
 
   // mov w0, wzr
   MCInst ORRWrs = MCInstBuilder(AArch64::ORRWrs)
@@ -891,17 +891,17 @@ TEST_P(MCPlusBuilderTester, AArch64_isCleanRegXOR) {
                       .addReg(AArch64::WZR)
                       .addReg(AArch64::WZR)
                       .addImm(0);
-  ASSERT_TRUE(BC->MIB->isCleanRegXOR(ORRWrs));
+  ASSERT_TRUE(BC->MIB->isCleanReg(ORRWrs));
 
   // mov x0, #0
   MCInst MOVZXi =
       MCInstBuilder(AArch64::MOVZXi).addReg(AArch64::X0).addImm(0).addImm(0);
-  ASSERT_TRUE(BC->MIB->isCleanRegXOR(MOVZXi));
+  ASSERT_TRUE(BC->MIB->isCleanReg(MOVZXi));
 
   // mov w0, #0
   MCInst MOVZWi =
       MCInstBuilder(AArch64::MOVZWi).addReg(AArch64::W0).addImm(0).addImm(0);
-  ASSERT_TRUE(BC->MIB->isCleanRegXOR(MOVZWi));
+  ASSERT_TRUE(BC->MIB->isCleanReg(MOVZWi));
 
   // movz x0, #:abs_g3:symbol
   MCInst MOVZXiWithExpr =
@@ -910,7 +910,7 @@ TEST_P(MCPlusBuilderTester, AArch64_isCleanRegXOR) {
           .addExpr(MCSpecifierExpr::create(BB->getLabel(), AArch64::S_ABS_G3,
                                            *BC->Ctx.get()))
           .addImm(48);
-  ASSERT_FALSE(BC->MIB->isCleanRegXOR(MOVZXiWithExpr));
+  ASSERT_FALSE(BC->MIB->isCleanReg(MOVZXiWithExpr));
 }
 
 #endif // AARCH64_AVAILABLE
@@ -927,33 +927,27 @@ TEST_P(MCPlusBuilderTester, RISCV_NoFlagsRegister) {
   EXPECT_EQ(BC->MIB->getFlagsReg(), RISCV::NoRegister);
 }
 
-TEST_P(MCPlusBuilderTester, RISCV_isCleanRegXOR) {
+TEST_P(MCPlusBuilderTester, RISCV_isCleanReg) {
   if (GetParam() != Triple::riscv64)
     GTEST_SKIP();
 
-  MCInst XOR = MCInstBuilder(RISCV::XOR)
-                   .addReg(RISCV::X5)
-                   .addReg(RISCV::X6)
-                   .addReg(RISCV::X6);
-  EXPECT_TRUE(BC->MIB->isCleanRegXOR(XOR));
+  MCInst ADDI =
+      MCInstBuilder(RISCV::ADDI).addReg(RISCV::X5).addReg(RISCV::X0).addImm(0);
+  EXPECT_TRUE(BC->MIB->isCleanReg(ADDI));
 
-  MCInst NonCleanXOR = MCInstBuilder(RISCV::XOR)
-                           .addReg(RISCV::X5)
-                           .addReg(RISCV::X6)
-                           .addReg(RISCV::X7);
-  EXPECT_FALSE(BC->MIB->isCleanRegXOR(NonCleanXOR));
+  MCInst NonZeroSource =
+      MCInstBuilder(RISCV::ADDI).addReg(RISCV::X5).addReg(RISCV::X6).addImm(0);
+  EXPECT_FALSE(BC->MIB->isCleanReg(NonZeroSource));
 
-  MCInst CompressedXOR = MCInstBuilder(RISCV::C_XOR)
-                             .addReg(RISCV::X8)
-                             .addReg(RISCV::X8)
-                             .addReg(RISCV::X8);
-  EXPECT_TRUE(BC->MIB->isCleanRegXOR(CompressedXOR));
+  MCInst NonZeroImmediate =
+      MCInstBuilder(RISCV::ADDI).addReg(RISCV::X5).addReg(RISCV::X0).addImm(1);
+  EXPECT_FALSE(BC->MIB->isCleanReg(NonZeroImmediate));
 
-  MCInst ADD = MCInstBuilder(RISCV::ADD)
+  MCInst XOR = MCInstBuilder(RISCV::XOR)
                    .addReg(RISCV::X5)
                    .addReg(RISCV::X6)
                    .addReg(RISCV::X6);
-  EXPECT_FALSE(BC->MIB->isCleanRegXOR(ADD));
+  EXPECT_FALSE(BC->MIB->isCleanReg(XOR));
 }
 
 TEST_P(MCPlusBuilderTester, RISCV_ABIRegisterMasks) {

>From 0df4e923114c00170d6b81e056ac622e63c8e39e Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Tue, 8 Sep 2026 10:55:50 +0800
Subject: [PATCH 4/4] [BOLT][RISCV] Recognize C.LI zero as a register-clearing
 instruction

---
 bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp | 13 ++++++++++---
 bolt/unittests/Core/MCPlusBuilder.cpp        | 19 +++++++++++++++++++
 2 files changed, 29 insertions(+), 3 deletions(-)

diff --git a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
index 3e013fc79c733..3fa0572e35fcd 100644
--- a/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
+++ b/bolt/lib/Target/RISCV/RISCVMCPlusBuilder.cpp
@@ -43,9 +43,16 @@ class RISCVMCPlusBuilder : public MCPlusBuilder {
   MCPhysReg getFlagsReg() const override { return RISCV::NoRegister; }
 
   bool isCleanReg(const MCInst &Inst) const override {
-    return Inst.getOpcode() == RISCV::ADDI && Inst.getOperand(1).isReg() &&
-           Inst.getOperand(1).getReg() == RISCV::X0 &&
-           Inst.getOperand(2).isImm() && Inst.getOperand(2).getImm() == 0;
+    switch (Inst.getOpcode()) {
+    case RISCV::ADDI:
+      return Inst.getOperand(1).isReg() &&
+             Inst.getOperand(1).getReg() == RISCV::X0 &&
+             Inst.getOperand(2).isImm() && Inst.getOperand(2).getImm() == 0;
+    case RISCV::C_LI:
+      return Inst.getOperand(1).isImm() && Inst.getOperand(1).getImm() == 0;
+    default:
+      return false;
+    }
   }
 
   BitVector getRegsUsedAsParams() const override {
diff --git a/bolt/unittests/Core/MCPlusBuilder.cpp b/bolt/unittests/Core/MCPlusBuilder.cpp
index aaaa25444839d..4e666d4ac3399 100644
--- a/bolt/unittests/Core/MCPlusBuilder.cpp
+++ b/bolt/unittests/Core/MCPlusBuilder.cpp
@@ -943,6 +943,25 @@ TEST_P(MCPlusBuilderTester, RISCV_isCleanReg) {
       MCInstBuilder(RISCV::ADDI).addReg(RISCV::X5).addReg(RISCV::X0).addImm(1);
   EXPECT_FALSE(BC->MIB->isCleanReg(NonZeroImmediate));
 
+  MCInst CompressedLI = MCInstBuilder(RISCV::C_LI).addReg(RISCV::X5).addImm(0);
+  EXPECT_TRUE(BC->MIB->isCleanReg(CompressedLI));
+
+  for (int64_t Imm : {-32, -1, 1, 31}) {
+    MCInst NonZeroCompressedLI =
+        MCInstBuilder(RISCV::C_LI).addReg(RISCV::X5).addImm(Imm);
+    EXPECT_FALSE(BC->MIB->isCleanReg(NonZeroCompressedLI));
+  }
+
+  MCInst CompressedLIWithExpr =
+      MCInstBuilder(RISCV::C_LI)
+          .addReg(RISCV::X5)
+          .addExpr(MCSymbolRefExpr::create(BB->getLabel(), *BC->Ctx));
+  EXPECT_FALSE(BC->MIB->isCleanReg(CompressedLIWithExpr));
+
+  MCInst CompressedMV =
+      MCInstBuilder(RISCV::C_MV).addReg(RISCV::X5).addReg(RISCV::X6);
+  EXPECT_FALSE(BC->MIB->isCleanReg(CompressedMV));
+
   MCInst XOR = MCInstBuilder(RISCV::XOR)
                    .addReg(RISCV::X5)
                    .addReg(RISCV::X6)



More information about the llvm-commits mailing list