[llvm] Add DebugSSAUpdater class to track debug value liveness (PR #135349)

Jeremy Morse via llvm-commits llvm-commits at lists.llvm.org
Mon Apr 28 18:04:02 PDT 2025


================
@@ -0,0 +1,415 @@
+//===- DebugSSAUpdater.cpp - Debug Variable SSA Update Tool ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the DebugSSAUpdater class.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Utils/DebugSSAUpdater.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/DebugInfo.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/Transforms/Utils/SSAUpdaterImpl.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "debug-ssa-updater"
+
+void DbgValueDef::print(raw_ostream &OS) const {
+  OS << "DbgVal{ ";
+  if (IsUndef) {
+    OS << "undef }";
+    return;
+  }
+  if (Phi) {
+    OS << *Phi << "}";
+    return;
+  }
+  OS << (IsMemory ? "Mem: " : "Def: ") << *Locations << " - " << *Expression
+     << " }";
+}
+
+void DbgSSAPhi::print(raw_ostream &OS) const {
+  OS << "DbgPhi ";
+  for (auto &[BB, DV] : IncomingValues)
+    OS << "[" << BB->BB.getName() << ", " << DV << "] ";
+}
+
+using AvailableValsTy = DenseMap<DbgSSABlock *, DbgValueDef>;
+
+static AvailableValsTy &getAvailableVals(void *AV) {
+  return *static_cast<AvailableValsTy *>(AV);
+}
+
+DebugSSAUpdater::DebugSSAUpdater(SmallVectorImpl<DbgSSAPhi *> *NewPHI)
+    : InsertedPHIs(NewPHI) {}
+
+DebugSSAUpdater::~DebugSSAUpdater() {
+  delete static_cast<AvailableValsTy *>(AV);
+}
+
+void DebugSSAUpdater::initialize() {
+  if (!AV)
+    AV = new AvailableValsTy();
+  else
+    getAvailableVals(AV).clear();
+}
+
+bool DebugSSAUpdater::hasValueForBlock(DbgSSABlock *BB) const {
+  return getAvailableVals(AV).count(BB);
+}
+
+DbgValueDef DebugSSAUpdater::findValueForBlock(DbgSSABlock *BB) const {
+  return getAvailableVals(AV).lookup(BB);
+}
+
+void DebugSSAUpdater::addAvailableValue(DbgSSABlock *BB, DbgValueDef DV) {
+  getAvailableVals(AV)[BB] = DV;
+}
+
+DbgValueDef DebugSSAUpdater::getValueAtEndOfBlock(DbgSSABlock *BB) {
+  DbgValueDef Res = getValueAtEndOfBlockInternal(BB);
+  return Res;
+}
+
+DbgValueDef DebugSSAUpdater::getValueInMiddleOfBlock(DbgSSABlock *BB) {
+  // If there is no definition of the renamed variable in this block, just use
+  // 'getValueAtEndOfBlock' to do our work.
+  if (!hasValueForBlock(BB))
+    return getValueAtEndOfBlock(BB);
+
+  // Otherwise, we have the hard case. Get the live-in values for each
+  // predecessor.
+  SmallVector<std::pair<DbgSSABlock *, DbgValueDef>, 8> PredValues;
+  DbgValueDef SingularValue;
+
+  bool IsFirstPred = true;
+  for (DbgSSABlock *PredBB : BB->predecessors()) {
+    DbgValueDef PredVal = getValueAtEndOfBlock(PredBB);
+    PredValues.push_back(std::make_pair(PredBB, PredVal));
+
+    // Compute SingularValue.
+    if (IsFirstPred) {
+      SingularValue = PredVal;
+      IsFirstPred = false;
+    } else if (!PredVal.agreesWith(SingularValue))
+      SingularValue = DbgValueDef();
+  }
+
+  // If there are no predecessors, just return undef.
+  if (PredValues.empty())
+    return DbgValueDef();
+
+  // Otherwise, if all the merged values are the same, just use it.
+  if (!SingularValue.IsUndef)
+    return SingularValue;
+
+  // Ok, we have no way out, insert a new one now.
+  DbgSSAPhi *InsertedPHI = BB->newPHI();
+
+  // Fill in all the predecessors of the PHI.
+  for (const auto &PredValue : PredValues)
+    InsertedPHI->addIncoming(PredValue.first, PredValue.second);
+
+  // See if the PHI node can be merged to a single value. This can happen in
+  // loop cases when we get a PHI of itself and one other value.
+
+  // If the client wants to know about all new instructions, tell it.
+  if (InsertedPHIs)
+    InsertedPHIs->push_back(InsertedPHI);
+
+  LLVM_DEBUG(dbgs() << "  Inserted PHI: " << *InsertedPHI << "\n");
+  return InsertedPHI;
+}
+
+DbgSSABlock *DbgSSABlockSuccIterator::operator*() {
+  return Updater.getDbgSSABlock(*SuccIt);
+}
+DbgSSABlock *DbgSSABlockPredIterator::operator*() {
+  return Updater.getDbgSSABlock(*PredIt);
+}
+
+namespace llvm {
+
+template <> class SSAUpdaterTraits<DebugSSAUpdater> {
+public:
+  using BlkT = DbgSSABlock;
+  using ValT = DbgValueDef;
+  using PhiT = DbgSSAPhi;
+  using BlkSucc_iterator = DbgSSABlockSuccIterator;
+
+  static BlkSucc_iterator BlkSucc_begin(BlkT *BB) { return BB->succ_begin(); }
+  static BlkSucc_iterator BlkSucc_end(BlkT *BB) { return BB->succ_end(); }
+
+  class PHI_iterator {
+  private:
+    DbgSSAPhi *PHI;
+    unsigned Idx;
+
+  public:
+    explicit PHI_iterator(DbgSSAPhi *P) // begin iterator
+        : PHI(P), Idx(0) {}
+    PHI_iterator(DbgSSAPhi *P, bool) // end iterator
+        : PHI(P), Idx(PHI->getNumIncomingValues()) {}
+
+    PHI_iterator &operator++() {
+      ++Idx;
+      return *this;
+    }
+    bool operator==(const PHI_iterator &X) const { return Idx == X.Idx; }
+    bool operator!=(const PHI_iterator &X) const { return !operator==(X); }
+
+    DbgValueDef getIncomingValue() { return PHI->getIncomingValue(Idx); }
+    DbgSSABlock *getIncomingBlock() { return PHI->getIncomingBlock(Idx); }
+  };
+
+  static PHI_iterator PHI_begin(PhiT *PHI) { return PHI_iterator(PHI); }
+  static PHI_iterator PHI_end(PhiT *PHI) { return PHI_iterator(PHI, true); }
+
+  /// FindPredecessorBlocks - Put the predecessors of Info->BB into the Preds
+  /// vector, set Info->NumPreds, and allocate space in Info->Preds.
+  static void FindPredecessorBlocks(DbgSSABlock *BB,
+                                    SmallVectorImpl<DbgSSABlock *> *Preds) {
+    for (auto PredIt = BB->pred_begin(); PredIt != BB->pred_end(); ++PredIt)
+      Preds->push_back(*PredIt);
+  }
+
+  /// GetPoisonVal - Get an undefined value of the same type as the value
+  /// being handled.
+  static DbgValueDef GetPoisonVal(DbgSSABlock *BB, DebugSSAUpdater *Updater) {
+    return DbgValueDef();
+  }
+
+  /// CreateEmptyPHI - Create a new PHI instruction in the specified block.
+  /// Reserve space for the operands (?) but do not fill them in yet.
+  static DbgSSAPhi *CreateEmptyPHI(DbgSSABlock *BB, unsigned NumPreds,
+                                   DebugSSAUpdater *Updater) {
+    DbgSSAPhi *PHI = BB->newPHI();
+    return PHI;
+  }
+
+  /// AddPHIOperand - Add the specified value as an operand of the PHI for
+  /// the specified predecessor block.
+  static void AddPHIOperand(DbgSSAPhi *PHI, DbgValueDef Val,
+                            DbgSSABlock *Pred) {
+    PHI->addIncoming(Pred, Val);
+  }
+
+  /// ValueIsPHI - Check if a value is a PHI.
+  static DbgSSAPhi *ValueIsPHI(DbgValueDef Val, DebugSSAUpdater *Updater) {
+    return Val.Phi;
+  }
+
+  /// ValueIsNewPHI - Like ValueIsPHI but also check if the PHI has no source
+  /// operands, i.e., it was just added.
+  static DbgSSAPhi *ValueIsNewPHI(DbgValueDef Val, DebugSSAUpdater *Updater) {
+    DbgSSAPhi *PHI = ValueIsPHI(Val, Updater);
+    if (PHI && PHI->getNumIncomingValues() == 0)
+      return PHI;
+    return nullptr;
+  }
+
+  /// GetPHIValue - For the specified PHI instruction, return the value
+  /// that it defines.
+  static DbgValueDef GetPHIValue(DbgSSAPhi *PHI) { return PHI; }
+};
+
+} // end namespace llvm
+
+/// Check to see if AvailableVals has an entry for the specified BB and if so,
+/// return it. If not, construct SSA form by first calculating the required
+/// placement of PHIs and then inserting new PHIs where needed.
+DbgValueDef DebugSSAUpdater::getValueAtEndOfBlockInternal(DbgSSABlock *BB) {
+  AvailableValsTy &AvailableVals = getAvailableVals(AV);
+  if (AvailableVals.contains(BB))
+    return AvailableVals[BB];
+
+  SSAUpdaterImpl<DebugSSAUpdater> Impl(this, &AvailableVals, InsertedPHIs);
+  return Impl.GetValue(BB);
+}
+
+bool isContained(DIScope *Inner, DIScope *Outer) {
+  if (Inner == Outer)
+    return true;
+  if (!Inner->getScope())
+    return false;
+  return isContained(Inner->getScope(), Outer);
+}
+
+void DbgValueRangeTable::addVariable(Function *F, DebugVariableAggregate DVA) {
+  const DILocalVariable *Var = DVA.getVariable();
+  const DILocation *InlinedAt = DVA.getInlinedAt();
+
+  DenseMap<BasicBlock *, SmallVector<DbgVariableRecord *>> BlockDbgRecordValues;
+  DenseSet<BasicBlock *> HasAnyInstructionsInScope;
+  int NumRecordsFound = 0;
+  DbgVariableRecord *LastRecordFound = nullptr;
+  bool DeclareRecordFound = false;
+
+  LLVM_DEBUG(dbgs() << "Finding variable info for " << *Var << " at "
+                    << InlinedAt << "\n");
+
+  for (auto &BB : *F) {
+    auto &DbgRecordValues = BlockDbgRecordValues[&BB];
+    bool FoundInstructionInScope = false;
+    for (auto &I : BB) {
+      LLVM_DEBUG(dbgs() << "Instruction: '" << I << "'\n");
+
+      for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) {
+        if (DVR.getVariable() == Var &&
+            DVR.getDebugLoc().getInlinedAt() == InlinedAt) {
+          assert(!DVR.isDbgAssign() && "No support for #dbg_declare yet.");
+          if (DVR.isDbgDeclare())
+            DeclareRecordFound = true;
+          ++NumRecordsFound;
+          LastRecordFound = &DVR;
+          DbgRecordValues.push_back(&DVR);
+        }
+        if (!FoundInstructionInScope && I.getDebugLoc()) {
+          if (I.getDebugLoc().getInlinedAt() == InlinedAt &&
+              isContained(cast<DILocalScope>(I.getDebugLoc().getScope()),
+                          Var->getScope())) {
+            FoundInstructionInScope = true;
+            HasAnyInstructionsInScope.insert(&BB);
+          }
+        }
----------------
jmorse wrote:

Duplicate of condition below, just this one runs once per DbgRecord? (Doesn't feel right)

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


More information about the llvm-commits mailing list