[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