[llvm] [RISCV][DebugInfo] Maintain CFI information during branch-folder (PR #200767)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 1 02:40:17 PDT 2026


https://github.com/sc-clulzze created https://github.com/llvm/llvm-project/pull/200767

This is an extension to https://github.com/llvm/llvm-project/pull/191784, after that change CFI information about saves/restores of CSRs can be placed in multiple blocks, not only prologue/epilogue blocks. Branch-folder does not expect this, and when MBBs are split using common tail, all debug info inside this common tail is moved to a different block and ends up being separated from corresponding CSR reloads, which are left in old block. This change fixes it by moving needed CFIs out of common tail.

>From 69919871cb8ca53a0346fa356c2b0b2455679452 Mon Sep 17 00:00:00 2001
From: sc-clulzze <d.marakulin at syntacore.com>
Date: Thu, 21 May 2026 09:29:33 +0000
Subject: [PATCH] [RISCV][DebugInfo] Maintain CFI information during
 branch-folder

---
 llvm/include/llvm/CodeGen/TargetInstrInfo.h |  3 ++
 llvm/lib/CodeGen/BranchFolding.cpp          | 34 +++++++++++++++++++++
 llvm/lib/CodeGen/BranchFolding.h            |  2 ++
 llvm/lib/CodeGen/CFIFixup.cpp               |  9 +-----
 llvm/lib/CodeGen/MachineVerifier.cpp        | 25 +++++++++++++++
 llvm/lib/CodeGen/TargetInstrInfo.cpp        | 29 ++++++++++++++++++
 6 files changed, 94 insertions(+), 8 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/TargetInstrInfo.h b/llvm/include/llvm/CodeGen/TargetInstrInfo.h
index fef1e4fc85786..a99a328405eb1 100644
--- a/llvm/include/llvm/CodeGen/TargetInstrInfo.h
+++ b/llvm/include/llvm/CodeGen/TargetInstrInfo.h
@@ -167,6 +167,9 @@ class LLVM_ABI TargetInstrInfo : public MCInstrInfo {
   /// (like a call or something with unmodeled side effects).
   virtual bool isGlobalMemoryObject(const MachineInstr *MI) const;
 
+  std::optional<unsigned> isCFIRestoreOfCSR(const MachineInstr *MI) const;
+  std::optional<unsigned> isReloadOfCSR(const MachineInstr *MI) const;
+
   /// Return true if the instruction is trivially rematerializable, meaning it
   /// has no side effects and requires no operands that aren't always available.
   /// This means the only allowed uses are constants and unallocatable physical
diff --git a/llvm/lib/CodeGen/BranchFolding.cpp b/llvm/lib/CodeGen/BranchFolding.cpp
index 4917c5b90821f..371285ae14b6d 100644
--- a/llvm/lib/CodeGen/BranchFolding.cpp
+++ b/llvm/lib/CodeGen/BranchFolding.cpp
@@ -26,6 +26,7 @@
 #include "llvm/CodeGen/Analysis.h"
 #include "llvm/CodeGen/BranchFoldingPass.h"
 #include "llvm/CodeGen/MBFIWrapper.h"
+#include "llvm/MC/MCDwarf.h"
 #include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
 #include "llvm/CodeGen/MachineBranchProbabilityInfo.h"
 #include "llvm/CodeGen/MachineDominators.h"
@@ -387,6 +388,37 @@ static unsigned ComputeCommonTailLength(MachineBasicBlock *MBB1,
   return TailLen;
 }
 
+void BranchFolder::tryFixupCFI(MachineBasicBlock::iterator OldInst) {
+  MachineBasicBlock *MBB = OldInst->getParent();
+  auto CIs = MBB->getParent()->getFrameInstructions();
+  SmallSet<unsigned, 16> Restores;
+  auto MI = MBB->begin();
+  for (; MI != OldInst; ++MI) {
+    if (auto Id = TII->isReloadOfCSR(&*MI))
+      Restores.insert(*Id);
+    if (auto Id = TII->isCFIRestoreOfCSR(&*MI)) {
+      auto RestoredId = find_if(Restores, [&](auto Id2) {
+        return TRI->regsOverlap(*Id, Id2);
+      });
+      assert(Restores.contains(*RestoredId) && "Must've seen reload of register before CFI restore.");
+      Restores.erase(*RestoredId);
+    }
+  }
+  if (Restores.empty())
+    return;
+  auto InsertPos = OldInst--;
+  for (auto E = MBB->end(); MI != E; ++MI) {
+    auto Id = TII->isCFIRestoreOfCSR(&*MI);
+    if (!Id)
+      continue;
+    for (auto Reg2 : Restores) {
+      if (TRI->regsOverlap(*Id, Reg2)) {
+        MI->moveBefore(&*InsertPos);
+      }
+    }
+  }
+}
+
 void BranchFolder::replaceTailWithBranchTo(MachineBasicBlock::iterator OldInst,
                                            MachineBasicBlock &NewDest) {
   if (UpdateLiveIns) {
@@ -417,6 +449,7 @@ void BranchFolder::replaceTailWithBranchTo(MachineBasicBlock::iterator OldInst,
     }
   }
 
+  tryFixupCFI(OldInst);
   TII->ReplaceTailWithBranchTo(OldInst, &NewDest);
   ++NumTailMerge;
 }
@@ -769,6 +802,7 @@ bool BranchFolder::CreateCommonTailOnlyBlock(MachineBasicBlock *&PredBB,
   // SuccBB is an inner loop, the common tail is still part of the inner loop.
   const BasicBlock *BB = (SuccBB && MBB->succ_size() == 1) ?
     SuccBB->getBasicBlock() : MBB->getBasicBlock();
+  tryFixupCFI(BBI);
   MachineBasicBlock *newMBB = SplitMBBAt(*MBB, BBI, BB);
   if (!newMBB) {
     LLVM_DEBUG(dbgs() << "... failed!");
diff --git a/llvm/lib/CodeGen/BranchFolding.h b/llvm/lib/CodeGen/BranchFolding.h
index ff2bbe06c0488..66cc9094981f0 100644
--- a/llvm/lib/CodeGen/BranchFolding.h
+++ b/llvm/lib/CodeGen/BranchFolding.h
@@ -143,6 +143,8 @@ class TargetRegisterInfo;
     void replaceTailWithBranchTo(MachineBasicBlock::iterator OldInst,
                                  MachineBasicBlock &NewDest);
 
+    void tryFixupCFI(MachineBasicBlock::iterator OldInst);
+
     /// Given a machine basic block and an iterator into it, split the MBB so
     /// that the part before the iterator falls into the part starting at the
     /// iterator.  This returns the new MBB.
diff --git a/llvm/lib/CodeGen/CFIFixup.cpp b/llvm/lib/CodeGen/CFIFixup.cpp
index c538acb55049d..4a86e20c3a3f0 100644
--- a/llvm/lib/CodeGen/CFIFixup.cpp
+++ b/llvm/lib/CodeGen/CFIFixup.cpp
@@ -99,13 +99,6 @@ static bool isPrologueCFIInstruction(const MachineInstr &MI) {
          MI.getFlag(MachineInstr::FrameSetup);
 }
 
-static bool containsEpilogue(const MachineBasicBlock &MBB) {
-  return llvm::any_of(llvm::reverse(MBB), [](const auto &MI) {
-    return MI.getOpcode() == TargetOpcode::CFI_INSTRUCTION &&
-           MI.getFlag(MachineInstr::FrameDestroy);
-  });
-}
-
 static MachineBasicBlock *
 findPrologueEnd(MachineFunction &MF, MachineBasicBlock::iterator &PrologueEnd) {
   // Even though we should theoretically traverse the blocks in post-order, we
@@ -159,7 +152,7 @@ computeBlockInfo(const MachineFunction &MF,
     bool HasEpilogue = false;
 
     if (Info.HasFrameOnEntry || HasPrologue)
-      HasEpilogue = containsEpilogue(*MBB);
+      HasEpilogue = MBB->isReturnBlock();
 
     // If the function has a call frame at the entry of the current block or the
     // current block contains the prologue, then the function has a call frame
diff --git a/llvm/lib/CodeGen/MachineVerifier.cpp b/llvm/lib/CodeGen/MachineVerifier.cpp
index af38525af6a6b..ee8d030726246 100644
--- a/llvm/lib/CodeGen/MachineVerifier.cpp
+++ b/llvm/lib/CodeGen/MachineVerifier.cpp
@@ -355,6 +355,7 @@ struct MachineVerifier {
   void verifyStackFrame();
   /// Check that the stack protector is the top-most object in the stack.
   void verifyStackProtector();
+  void verifyCFI();
 
   void verifySlotIndexes() const;
   void verifyProperties(const MachineFunction &MF);
@@ -712,6 +713,7 @@ void MachineVerifier::visitMachineFunctionBefore() {
   MRI->verifyUseLists();
 
   if (!MF->empty()) {
+    verifyCFI();
     verifyStackFrame();
     verifyStackProtector();
   }
@@ -4185,3 +4187,26 @@ void MachineVerifier::verifyStackProtector() {
     }
   }
 }
+
+void MachineVerifier::verifyCFI() {
+  auto CIs = MF->getFrameInstructions();
+  for (auto &MBB : *MF) {
+    SmallVector<unsigned, 16> FrameDestroyCFI;
+    SmallVector<unsigned, 16> Restores;
+    for (auto &MI : MBB) {
+      if (auto Id = TII->isReloadOfCSR(&MI))
+        Restores.push_back(*Id);
+      if (auto Id = TII->isCFIRestoreOfCSR(&MI))
+        FrameDestroyCFI.push_back(*Id);
+    }
+    if (Restores.size() != FrameDestroyCFI.size() || any_of(FrameDestroyCFI, [&](auto Reg) {
+      for (auto Reg2 : Restores)
+        if (TRI->regsOverlap(Reg, Reg2))
+          return false;
+      return true;
+    })) {
+      report("Invalid CFI informarion", MF);
+      OS << "\nCFI info does not match restores of CSRs.\n" << MBB << "\n";
+    }
+  }
+}
\ No newline at end of file
diff --git a/llvm/lib/CodeGen/TargetInstrInfo.cpp b/llvm/lib/CodeGen/TargetInstrInfo.cpp
index f3666b05464b7..9fa80089370dc 100644
--- a/llvm/lib/CodeGen/TargetInstrInfo.cpp
+++ b/llvm/lib/CodeGen/TargetInstrInfo.cpp
@@ -37,6 +37,7 @@
 #include "llvm/Support/InterleavedRange.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Target/TargetMachine.h"
+#include "llvm/MC/MCDwarf.h"
 
 using namespace llvm;
 
@@ -2232,3 +2233,31 @@ bool TargetInstrInfo::isGlobalMemoryObject(const MachineInstr *MI) const {
   return MI->isCall() || MI->hasUnmodeledSideEffects() ||
          (MI->hasOrderedMemoryRef() && !MI->isDereferenceableInvariantLoad());
 }
+
+std::optional<unsigned> TargetInstrInfo::isCFIRestoreOfCSR(const MachineInstr *MI) const {
+  if (!MI->isCFIInstruction() || !MI->getFlag(MachineInstr::FrameDestroy))
+    return std::nullopt;
+  const MachineFunction *MF = MI->getParent()->getParent();
+  auto CIs = MF->getFrameInstructions();
+  auto &MO = MI->getOperand(0);
+  MCCFIInstruction CI = CIs[MO.getCFIIndex()];
+  if (CI.getOperation() != MCCFIInstruction::OpRestore)
+    return std::nullopt;
+  auto Reg = TRI.getLLVMRegNum(CI.getRegister(), false);
+  if (!Reg || !TRI.isCalleeSavedPhysReg(*Reg, *MF))
+    return std::nullopt;
+  return Reg->id();
+}
+
+std::optional<unsigned> TargetInstrInfo::isReloadOfCSR(const MachineInstr *MI) const {
+  if (!MI->mayLoad() || !MI->getFlag(MachineInstr::FrameDestroy))
+    return std::nullopt;
+  const MachineOperand &MO = MI->getOperand(0);
+  if (!MO.isReg())
+    return std::nullopt;
+  Register Reg = MO.getReg();
+  const MachineFunction *MF = MI->getParent()->getParent();
+  if (!Reg.isPhysical() || !TRI.isCalleeSavedPhysReg(Reg, *MF))
+    return std::nullopt;
+  return Reg.id();
+}



More information about the llvm-commits mailing list