[llvm] [CodeGen] Allow multiple location for the same CSR. (PR #168531)

via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 18 05:07:16 PST 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-risc-v

Author: Mikhail Gudim (mgudim)

<details>
<summary>Changes</summary>

Currently there is an implicit assumption in `CFIInstrInserter` that a CSR can be saved only in one location. However, scenarios when this assumption breaks are possible.

---
Full diff: https://github.com/llvm/llvm-project/pull/168531.diff


2 Files Affected:

- (modified) llvm/lib/CodeGen/CFIInstrInserter.cpp (+197-144) 
- (modified) llvm/test/CodeGen/RISCV/cfi-multiple-locations.mir (-2) 


``````````diff
diff --git a/llvm/lib/CodeGen/CFIInstrInserter.cpp b/llvm/lib/CodeGen/CFIInstrInserter.cpp
index 14098bc821617..667928304df50 100644
--- a/llvm/lib/CodeGen/CFIInstrInserter.cpp
+++ b/llvm/lib/CodeGen/CFIInstrInserter.cpp
@@ -66,72 +66,103 @@ class CFIInstrInserter : public MachineFunctionPass {
   }
 
  private:
-  struct MBBCFAInfo {
-    MachineBasicBlock *MBB;
-    /// Value of cfa offset valid at basic block entry.
-    int64_t IncomingCFAOffset = -1;
-    /// Value of cfa offset valid at basic block exit.
-    int64_t OutgoingCFAOffset = -1;
-    /// Value of cfa register valid at basic block entry.
-    unsigned IncomingCFARegister = 0;
-    /// Value of cfa register valid at basic block exit.
-    unsigned OutgoingCFARegister = 0;
-    /// Set of callee saved registers saved at basic block entry.
-    BitVector IncomingCSRSaved;
-    /// Set of callee saved registers saved at basic block exit.
-    BitVector OutgoingCSRSaved;
-    /// If in/out cfa offset and register values for this block have already
-    /// been set or not.
-    bool Processed = false;
-  };
-
 #define INVALID_REG UINT_MAX
 #define INVALID_OFFSET INT_MAX
-  /// contains the location where CSR register is saved.
-  struct CSRSavedLocation {
-    CSRSavedLocation(std::optional<unsigned> R, std::optional<int> O)
-        : Reg(R), Offset(O) {}
-    std::optional<unsigned> Reg;
-    std::optional<int> Offset;
-  };
-
-  /// Contains cfa offset and register values valid at entry and exit of basic
-  /// blocks.
-  std::vector<MBBCFAInfo> MBBVector;
-
-  /// Map the callee save registers to the locations where they are saved.
-  SmallDenseMap<unsigned, CSRSavedLocation, 16> CSRLocMap;
-
-  /// Calculate cfa offset and register values valid at entry and exit for all
-  /// basic blocks in a function.
-  void calculateCFAInfo(MachineFunction &MF);
-  /// Calculate cfa offset and register values valid at basic block exit by
-  /// checking the block for CFI instructions. Block's incoming CFA info remains
-  /// the same.
-  void calculateOutgoingCFAInfo(MBBCFAInfo &MBBInfo);
-  /// Update in/out cfa offset and register values for successors of the basic
-  /// block.
-  void updateSuccCFAInfo(MBBCFAInfo &MBBInfo);
-
-  /// Check if incoming CFA information of a basic block matches outgoing CFA
-  /// information of the previous block. If it doesn't, insert CFI instruction
-  /// at the beginning of the block that corrects the CFA calculation rule for
-  /// that block.
-  bool insertCFIInstrs(MachineFunction &MF);
-  /// Return the cfa offset value that should be set at the beginning of a MBB
-  /// if needed. The negated value is needed when creating CFI instructions that
-  /// set absolute offset.
-  int64_t getCorrectCFAOffset(MachineBasicBlock *MBB) {
-    return MBBVector[MBB->getNumber()].IncomingCFAOffset;
-  }
-
-  void reportCFAError(const MBBCFAInfo &Pred, const MBBCFAInfo &Succ);
-  void reportCSRError(const MBBCFAInfo &Pred, const MBBCFAInfo &Succ);
-  /// Go through each MBB in a function and check that outgoing offset and
-  /// register of its predecessors match incoming offset and register of that
-  /// MBB, as well as that incoming offset and register of its successors match
-  /// outgoing offset and register of the MBB.
-  unsigned verify(MachineFunction &MF);
+   /// contains the location where CSR register is saved.
+   struct CSRSavedLocation {
+     enum Kind { INVALID, REGISTER, CFA_OFFSET };
+     CSRSavedLocation() {
+       K = Kind::INVALID;
+       Reg = 0;
+       Offset = 0;
+     }
+     Kind K;
+     // Dwarf register number
+     unsigned Reg;
+     // CFA offset
+     int64_t Offset;
+     bool isValid() const { return K != Kind::INVALID; }
+     bool operator==(const CSRSavedLocation &RHS) const {
+       switch (K) {
+       case Kind::INVALID:
+         return !RHS.isValid();
+       case Kind::REGISTER:
+         return Reg == RHS.Reg;
+       case Kind::CFA_OFFSET:
+         return Offset == RHS.Offset;
+       }
+       llvm_unreachable("Unknown CSRSavedLocation Kind!");
+     }
+     void dump(raw_ostream &OS) const {
+       switch (K) {
+       case Kind::INVALID:
+         OS << "INVALID";
+         break;
+       case Kind::REGISTER:
+         OS << "In Dwarf register: " << Reg;
+         break;
+       case Kind::CFA_OFFSET:
+         OS << "At CFA offset: " << Offset;
+         break;
+       }
+     }
+   };
+
+   struct MBBCFAInfo {
+     MachineBasicBlock *MBB;
+     /// Value of cfa offset valid at basic block entry.
+     int64_t IncomingCFAOffset = -1;
+     /// Value of cfa offset valid at basic block exit.
+     int64_t OutgoingCFAOffset = -1;
+     /// Value of cfa register valid at basic block entry.
+     unsigned IncomingCFARegister = 0;
+     /// Value of cfa register valid at basic block exit.
+     unsigned OutgoingCFARegister = 0;
+     /// Set of locations where the callee saved registers are at basic block
+     /// entry.
+     SmallVector<CSRSavedLocation> IncomingCSRLocations;
+     /// Set of locations where the callee saved registers are at basic block
+     /// exit.
+     SmallVector<CSRSavedLocation> OutgoingCSRLocations;
+     /// If in/out cfa offset and register values for this block have already
+     /// been set or not.
+     bool Processed = false;
+   };
+
+   /// Contains cfa offset and register values valid at entry and exit of basic
+   /// blocks.
+   std::vector<MBBCFAInfo> MBBVector;
+
+   /// Calculate cfa offset and register values valid at entry and exit for all
+   /// basic blocks in a function.
+   void calculateCFAInfo(MachineFunction &MF);
+   /// Calculate cfa offset and register values valid at basic block exit by
+   /// checking the block for CFI instructions. Block's incoming CFA info
+   /// remains the same.
+   void calculateOutgoingCFAInfo(MBBCFAInfo &MBBInfo);
+   /// Update in/out cfa offset and register values for successors of the basic
+   /// block.
+   void updateSuccCFAInfo(MBBCFAInfo &MBBInfo);
+
+   /// Check if incoming CFA information of a basic block matches outgoing CFA
+   /// information of the previous block. If it doesn't, insert CFI instruction
+   /// at the beginning of the block that corrects the CFA calculation rule for
+   /// that block.
+   bool insertCFIInstrs(MachineFunction &MF);
+   /// Return the cfa offset value that should be set at the beginning of a MBB
+   /// if needed. The negated value is needed when creating CFI instructions
+   /// that set absolute offset.
+   int64_t getCorrectCFAOffset(MachineBasicBlock *MBB) {
+     return MBBVector[MBB->getNumber()].IncomingCFAOffset;
+   }
+
+   void reportCFAError(const MBBCFAInfo &Pred, const MBBCFAInfo &Succ);
+   void reportCSRError(const MBBCFAInfo &Pred, const MBBCFAInfo &Succ);
+   /// Go through each MBB in a function and check that outgoing offset and
+   /// register of its predecessors match incoming offset and register of that
+   /// MBB, as well as that incoming offset and register of its successors match
+   /// outgoing offset and register of the MBB.
+   unsigned verify(MachineFunction &MF);
 };
 }  // namespace
 
@@ -162,10 +193,18 @@ void CFIInstrInserter::calculateCFAInfo(MachineFunction &MF) {
     MBBInfo.OutgoingCFAOffset = InitialOffset;
     MBBInfo.IncomingCFARegister = DwarfInitialRegister;
     MBBInfo.OutgoingCFARegister = DwarfInitialRegister;
-    MBBInfo.IncomingCSRSaved.resize(NumRegs);
-    MBBInfo.OutgoingCSRSaved.resize(NumRegs);
+    MBBInfo.IncomingCSRLocations.resize(NumRegs);
+    MBBInfo.OutgoingCSRLocations.resize(NumRegs);
+  }
+
+  // Record the initial location of all registers.
+  MBBCFAInfo &EntryMBBInfo = MBBVector[MF.front().getNumber()];
+  const MCPhysReg *CSRegs = MF.getRegInfo().getCalleeSavedRegs();
+  for (int i = 0; CSRegs[i]; ++i) {
+    unsigned Reg = TRI.getDwarfRegNum(CSRegs[i], true);
+    CSRSavedLocation &CSRLoc = EntryMBBInfo.IncomingCSRLocations[Reg];
+    CSRLoc.Reg = Reg;
   }
-  CSRLocMap.clear();
 
   // Set in/out cfa info for all blocks in the function. This traversal is based
   // on the assumption that the first block in the function is the entry block
@@ -176,14 +215,18 @@ void CFIInstrInserter::calculateCFAInfo(MachineFunction &MF) {
 
 void CFIInstrInserter::calculateOutgoingCFAInfo(MBBCFAInfo &MBBInfo) {
   // Outgoing cfa offset set by the block.
-  int64_t SetOffset = MBBInfo.IncomingCFAOffset;
+  int64_t &OutgoingCFAOffset = MBBInfo.OutgoingCFAOffset;
+  OutgoingCFAOffset = MBBInfo.IncomingCFAOffset;
   // Outgoing cfa register set by the block.
-  unsigned SetRegister = MBBInfo.IncomingCFARegister;
+  unsigned &OutgoingCFARegister = MBBInfo.OutgoingCFARegister;
+  OutgoingCFARegister = MBBInfo.IncomingCFARegister;
+  // Outgoing locations for each callee-saved register set by the block.
+  SmallVector<CSRSavedLocation> &OutgoingCSRLocations =
+      MBBInfo.OutgoingCSRLocations;
+  OutgoingCSRLocations = MBBInfo.IncomingCSRLocations;
+
   MachineFunction *MF = MBBInfo.MBB->getParent();
   const std::vector<MCCFIInstruction> &Instrs = MF->getFrameInstructions();
-  const TargetRegisterInfo &TRI = *MF->getSubtarget().getRegisterInfo();
-  unsigned NumRegs = TRI.getNumSupportedRegs(*MF);
-  BitVector CSRSaved(NumRegs), CSRRestored(NumRegs);
 
 #ifndef NDEBUG
   int RememberState = 0;
@@ -192,36 +235,51 @@ void CFIInstrInserter::calculateOutgoingCFAInfo(MBBCFAInfo &MBBInfo) {
   // Determine cfa offset and register set by the block.
   for (MachineInstr &MI : *MBBInfo.MBB) {
     if (MI.isCFIInstruction()) {
-      std::optional<unsigned> CSRReg;
-      std::optional<int64_t> CSROffset;
       unsigned CFIIndex = MI.getOperand(0).getCFIIndex();
       const MCCFIInstruction &CFI = Instrs[CFIIndex];
       switch (CFI.getOperation()) {
-      case MCCFIInstruction::OpDefCfaRegister:
-        SetRegister = CFI.getRegister();
+      case MCCFIInstruction::OpDefCfaRegister: {
+        OutgoingCFARegister = CFI.getRegister();
         break;
-      case MCCFIInstruction::OpDefCfaOffset:
-        SetOffset = CFI.getOffset();
+      }
+      case MCCFIInstruction::OpDefCfaOffset: {
+        OutgoingCFAOffset = CFI.getOffset();
         break;
-      case MCCFIInstruction::OpAdjustCfaOffset:
-        SetOffset += CFI.getOffset();
+      }
+      case MCCFIInstruction::OpAdjustCfaOffset: {
+        OutgoingCFAOffset += CFI.getOffset();
         break;
-      case MCCFIInstruction::OpDefCfa:
-        SetRegister = CFI.getRegister();
-        SetOffset = CFI.getOffset();
+      }
+      case MCCFIInstruction::OpDefCfa: {
+        OutgoingCFARegister = CFI.getRegister();
+        OutgoingCFAOffset = CFI.getOffset();
         break;
-      case MCCFIInstruction::OpOffset:
-        CSROffset = CFI.getOffset();
+      }
+      case MCCFIInstruction::OpOffset: {
+        CSRSavedLocation &CSRLocation = OutgoingCSRLocations[CFI.getRegister()];
+        CSRLocation.K = CSRSavedLocation::Kind::CFA_OFFSET;
+        CSRLocation.Offset = CFI.getOffset();
         break;
-      case MCCFIInstruction::OpRegister:
-        CSRReg = CFI.getRegister2();
+      }
+      case MCCFIInstruction::OpRegister: {
+        CSRSavedLocation &CSRLocation = OutgoingCSRLocations[CFI.getRegister()];
+        CSRLocation.K = CSRSavedLocation::Kind::REGISTER;
+        CSRLocation.Reg = CFI.getRegister2();
         break;
-      case MCCFIInstruction::OpRelOffset:
-        CSROffset = CFI.getOffset() - SetOffset;
+      }
+      case MCCFIInstruction::OpRelOffset: {
+        CSRSavedLocation &CSRLocation = OutgoingCSRLocations[CFI.getRegister()];
+        CSRLocation.K = CSRSavedLocation::Kind::CFA_OFFSET;
+        CSRLocation.Offset = CFI.getOffset() - OutgoingCFAOffset;
         break;
-      case MCCFIInstruction::OpRestore:
-        CSRRestored.set(CFI.getRegister());
+      }
+      case MCCFIInstruction::OpRestore: {
+        unsigned Reg = CFI.getRegister();
+        CSRSavedLocation &CSRLocation = OutgoingCSRLocations[Reg];
+        CSRLocation.K = CSRSavedLocation::Kind::REGISTER;
+        CSRLocation.Reg = Reg;
         break;
+      }
       case MCCFIInstruction::OpLLVMDefAspaceCfa:
         // TODO: Add support for handling cfi_def_aspace_cfa.
 #ifndef NDEBUG
@@ -266,16 +324,6 @@ void CFIInstrInserter::calculateOutgoingCFAInfo(MBBCFAInfo &MBBInfo) {
       case MCCFIInstruction::OpValOffset:
         break;
       }
-      if (CSRReg || CSROffset) {
-        auto It = CSRLocMap.find(CFI.getRegister());
-        if (It == CSRLocMap.end()) {
-          CSRLocMap.insert(
-              {CFI.getRegister(), CSRSavedLocation(CSRReg, CSROffset)});
-        } else if (It->second.Reg != CSRReg || It->second.Offset != CSROffset) {
-          llvm_unreachable("Different saved locations for the same CSR");
-        }
-        CSRSaved.set(CFI.getRegister());
-      }
     }
   }
 
@@ -288,15 +336,6 @@ void CFIInstrInserter::calculateOutgoingCFAInfo(MBBCFAInfo &MBBInfo) {
 #endif
 
   MBBInfo.Processed = true;
-
-  // Update outgoing CFA info.
-  MBBInfo.OutgoingCFAOffset = SetOffset;
-  MBBInfo.OutgoingCFARegister = SetRegister;
-
-  // Update outgoing CSR info.
-  BitVector::apply([](auto x, auto y, auto z) { return (x | y) & ~z; },
-                   MBBInfo.OutgoingCSRSaved, MBBInfo.IncomingCSRSaved, CSRSaved,
-                   CSRRestored);
 }
 
 void CFIInstrInserter::updateSuccCFAInfo(MBBCFAInfo &MBBInfo) {
@@ -312,7 +351,7 @@ void CFIInstrInserter::updateSuccCFAInfo(MBBCFAInfo &MBBInfo) {
       if (!SuccInfo.Processed) {
         SuccInfo.IncomingCFAOffset = CurrentInfo.OutgoingCFAOffset;
         SuccInfo.IncomingCFARegister = CurrentInfo.OutgoingCFARegister;
-        SuccInfo.IncomingCSRSaved = CurrentInfo.OutgoingCSRSaved;
+        SuccInfo.IncomingCSRLocations = CurrentInfo.OutgoingCSRLocations;
         Stack.push_back(Succ);
       }
     }
@@ -324,7 +363,6 @@ bool CFIInstrInserter::insertCFIInstrs(MachineFunction &MF) {
   const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
   bool InsertedCFIInstr = false;
 
-  BitVector SetDifference;
   for (MachineBasicBlock &MBB : MF) {
     // Skip the first MBB in a function
     if (MBB.getNumber() == MF.front().getNumber()) continue;
@@ -376,32 +414,34 @@ bool CFIInstrInserter::insertCFIInstrs(MachineFunction &MF) {
       continue;
     }
 
-    BitVector::apply([](auto x, auto y) { return x & ~y; }, SetDifference,
-                     PrevMBBInfo->OutgoingCSRSaved, MBBInfo.IncomingCSRSaved);
-    for (int Reg : SetDifference.set_bits()) {
-      unsigned CFIIndex =
-          MF.addFrameInst(MCCFIInstruction::createRestore(nullptr, Reg));
-      BuildMI(*MBBInfo.MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
-          .addCFIIndex(CFIIndex);
-      InsertedCFIInstr = true;
-    }
-
-    BitVector::apply([](auto x, auto y) { return x & ~y; }, SetDifference,
-                     MBBInfo.IncomingCSRSaved, PrevMBBInfo->OutgoingCSRSaved);
-    for (int Reg : SetDifference.set_bits()) {
-      auto it = CSRLocMap.find(Reg);
-      assert(it != CSRLocMap.end() && "Reg should have an entry in CSRLocMap");
-      unsigned CFIIndex;
-      CSRSavedLocation RO = it->second;
-      if (!RO.Reg && RO.Offset) {
-        CFIIndex = MF.addFrameInst(
-            MCCFIInstruction::createOffset(nullptr, Reg, *RO.Offset));
-      } else if (RO.Reg && !RO.Offset) {
+    for (unsigned i = 0; i < PrevMBBInfo->OutgoingCSRLocations.size(); ++i) {
+      const CSRSavedLocation &PrevOutgoingCSRLoc =
+          PrevMBBInfo->OutgoingCSRLocations[i];
+      const CSRSavedLocation &HasToBeCSRLoc = MBBInfo.IncomingCSRLocations[i];
+      // Ignore non-callee-saved registers, they remain uninitialized (invalid).
+      if (!HasToBeCSRLoc.isValid())
+        continue;
+      if (HasToBeCSRLoc == PrevOutgoingCSRLoc)
+        continue;
+
+      unsigned CFIIndex = (unsigned)(-1);
+      if (HasToBeCSRLoc.K == CSRSavedLocation::Kind::CFA_OFFSET &&
+          HasToBeCSRLoc.Offset != PrevOutgoingCSRLoc.Offset) {
         CFIIndex = MF.addFrameInst(
-            MCCFIInstruction::createRegister(nullptr, Reg, *RO.Reg));
-      } else {
-        llvm_unreachable("RO.Reg and RO.Offset cannot both be valid/invalid");
-      }
+            MCCFIInstruction::createOffset(nullptr, i, HasToBeCSRLoc.Offset));
+      } else if (HasToBeCSRLoc.K == CSRSavedLocation::Kind::REGISTER &&
+                 (HasToBeCSRLoc.Reg != PrevOutgoingCSRLoc.Reg)) {
+        unsigned NewReg = HasToBeCSRLoc.Reg;
+        unsigned DwarfEHReg = i;
+        if (NewReg == DwarfEHReg) {
+          CFIIndex = MF.addFrameInst(
+              MCCFIInstruction::createRestore(nullptr, DwarfEHReg));
+        } else {
+          CFIIndex = MF.addFrameInst(
+              MCCFIInstruction::createRegister(nullptr, i, HasToBeCSRLoc.Reg));
+        }
+      } else
+        llvm_unreachable("Unexpected CSR location.");
       BuildMI(*MBBInfo.MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
           .addCFIIndex(CFIIndex);
       InsertedCFIInstr = true;
@@ -434,13 +474,23 @@ void CFIInstrInserter::reportCSRError(const MBBCFAInfo &Pred,
          << Pred.MBB->getParent()->getName() << " ***\n";
   errs() << "Pred: " << Pred.MBB->getName() << " #" << Pred.MBB->getNumber()
          << " outgoing CSR Saved: ";
-  for (int Reg : Pred.OutgoingCSRSaved.set_bits())
-    errs() << Reg << " ";
+  for (const CSRSavedLocation &OutgoingCSRLocation :
+       Pred.OutgoingCSRLocations) {
+    if (OutgoingCSRLocation.isValid()) {
+      OutgoingCSRLocation.dump(errs());
+      errs() << " ";
+    }
+  }
   errs() << "\n";
   errs() << "Succ: " << Succ.MBB->getName() << " #" << Succ.MBB->getNumber()
          << " incoming CSR Saved: ";
-  for (int Reg : Succ.IncomingCSRSaved.set_bits())
-    errs() << Reg << " ";
+  for (const CSRSavedLocation &IncomingCSRLocation :
+       Succ.IncomingCSRLocations) {
+    if (IncomingCSRLocation.isValid()) {
+      IncomingCSRLocation.dump(errs());
+      errs() << " ";
+    }
+  }
   errs() << "\n";
 }
 
@@ -463,9 +513,12 @@ unsigned CFIInstrInserter::verify(MachineFunction &MF) {
       }
       // Check that IncomingCSRSaved of every successor matches the
       // OutgoingCSRSaved of CurrMBB
-      if (SuccMBBInfo.IncomingCSRSaved != CurrMBBInfo.OutgoingCSRSaved) {
-        reportCSRError(CurrMBBInfo, SuccMBBInfo);
-        ErrorNum++;
+      for (unsigned i = 0; i < CurrMBBInfo.OutgoingCSRLocations.size(); ++i) {
+        if (!(CurrMBBInfo.OutgoingCSRLocations[i] ==
+              SuccMBBInfo.IncomingCSRLocations[i])) {
+          reportCSRError(CurrMBBInfo, SuccMBBInfo);
+          ErrorNum++;
+        }
       }
     }
   }
diff --git a/llvm/test/CodeGen/RISCV/cfi-multiple-locations.mir b/llvm/test/CodeGen/RISCV/cfi-multiple-locations.mir
index 7844589e3f93c..a8547efde90cd 100644
--- a/llvm/test/CodeGen/RISCV/cfi-multiple-locations.mir
+++ b/llvm/test/CodeGen/RISCV/cfi-multiple-locations.mir
@@ -1,10 +1,8 @@
 # RUN: llc %s -mtriple=riscv64 \
 # RUN: -run-pass=cfi-instr-inserter \
 # RUN: -riscv-enable-cfi-instr-inserter=true
-# XFAIL: *
 
 # Technically, it is possible that a callee-saved register is saved in multiple different locations.
-# CFIInstrInserter should handle this, but currently it does not.
 ---
 name: multiple_locations
 tracksRegLiveness: true

``````````

</details>


https://github.com/llvm/llvm-project/pull/168531


More information about the llvm-commits mailing list