[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,351 @@
+//===- DebugSSAUpdater.h - Debug SSA Update Tool ----------------*- C++ -*-===//
+//
+// 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 declares the DebugSSAUpdater class, which is used to evaluate the
+// live values of debug variables in IR. This uses SSA construction, treating
+// debug value records as definitions, to determine at each point in the program
+// which definition(s) are live at a given point. This is useful for analysis of
+// the state of debug variables, such as measuring the change in values of a
+// variable over time, or calculating coverage stats.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_UTILS_DEBUGSSAUPDATER_H
+#define LLVM_TRANSFORMS_UTILS_DEBUGSSAUPDATER_H
+
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/DebugProgramInstruction.h"
+#include "llvm/IR/Instruction.h"
+
+namespace llvm {
+
+////////////////////////////////////////
+// SSAUpdater specialization classes
+
+class DbgSSAPhi;
+template <typename T> class SmallVectorImpl;
+template <typename T> class SSAUpdaterTraits;
+
+/// A definition of a variable; can represent either a debug value, no
+/// definition (the variable has not yet been defined), or a phi value*.
+/// *Meaning multiple definitions that are live-in to a block from different
+/// predecessors, not a debug value that uses an IR PHINode.
+struct DbgValueDef {
+  DbgSSAPhi *Phi;
+  bool IsUndef;
+  bool IsMemory;
+  Metadata *Locations;
+  DIExpression *Expression;
+
+  DbgValueDef()
+      : Phi(nullptr), IsUndef(true), IsMemory(false), Locations(nullptr),
+        Expression(nullptr) {}
+  DbgValueDef(int)
+      : Phi(nullptr), IsUndef(true), IsMemory(false), Locations(nullptr),
+        Expression(nullptr) {}
+  DbgValueDef(bool IsMemory, Metadata *Locations, DIExpression *Expression)
+      : Phi(nullptr), IsUndef(false), IsMemory(IsMemory), Locations(Locations),
+        Expression(Expression) {}
+  DbgValueDef(DbgVariableRecord *DVR) : Phi(nullptr) {
+    assert(!DVR->isDbgAssign() && "#dbg_assign not yet supported");
+    IsUndef = DVR->isKillLocation();
+    IsMemory = DVR->isAddressOfVariable();
+    Locations = DVR->getRawLocation();
+    Expression = DVR->getExpression();
+  }
+  DbgValueDef(DbgSSAPhi *Phi)
+      : Phi(Phi), IsUndef(false), IsMemory(false), Locations(nullptr),
+        Expression(nullptr) {}
+
+  bool agreesWith(DbgValueDef Other) const {
+    if (IsUndef && Other.IsUndef)
+      return true;
+    return std::tie(Phi, IsUndef, IsMemory, Locations, Expression) ==
+           std::tie(Other.Phi, Other.IsUndef, Other.IsMemory, Other.Locations,
+                    Other.Expression);
+  }
+
+  operator bool() const { return !IsUndef; }
+  bool operator==(DbgValueDef Other) const { return agreesWith(Other); }
+  bool operator!=(DbgValueDef Other) const { return !agreesWith(Other); }
+
+  void print(raw_ostream &OS) const;
+};
+
+class DbgSSABlock;
+class DebugSSAUpdater;
+
+/// Represents the live-in definitions of a variable to a block with multiple
+/// predecessors.
+class DbgSSAPhi {
+public:
+  SmallVector<std::pair<DbgSSABlock *, DbgValueDef>, 4> IncomingValues;
+  DbgSSABlock *ParentBlock;
+  DbgSSAPhi(DbgSSABlock *ParentBlock) : ParentBlock(ParentBlock) {}
+
+  DbgSSABlock *getParent() { return ParentBlock; }
+  unsigned getNumIncomingValues() const { return IncomingValues.size(); }
+  DbgSSABlock *getIncomingBlock(size_t Idx) {
+    return IncomingValues[Idx].first;
+  }
+  DbgValueDef getIncomingValue(size_t Idx) {
+    return IncomingValues[Idx].second;
+  }
+  void addIncoming(DbgSSABlock *BB, DbgValueDef DV) {
+    IncomingValues.push_back({BB, DV});
+  }
+
+  void print(raw_ostream &OS) const;
+};
+
+inline raw_ostream &operator<<(raw_ostream &OS, const DbgValueDef &DV) {
+  DV.print(OS);
+  return OS;
+}
+inline raw_ostream &operator<<(raw_ostream &OS, const DbgSSAPhi &PHI) {
+  PHI.print(OS);
+  return OS;
+}
+
+/// Thin wrapper around a block successor iterator.
+class DbgSSABlockSuccIterator {
+public:
+  succ_iterator SuccIt;
+  DebugSSAUpdater &Updater;
+
+  DbgSSABlockSuccIterator(succ_iterator SuccIt, DebugSSAUpdater &Updater)
+      : SuccIt(SuccIt), Updater(Updater) {}
+
+  bool operator!=(const DbgSSABlockSuccIterator &OtherIt) const {
+    return OtherIt.SuccIt != SuccIt;
+  }
+
+  DbgSSABlockSuccIterator &operator++() {
+    ++SuccIt;
+    return *this;
+  }
+
+  DbgSSABlock *operator*();
+};
+
+/// Thin wrapper around a block successor iterator.
+class DbgSSABlockPredIterator {
+public:
+  pred_iterator PredIt;
+  DebugSSAUpdater &Updater;
+
+  DbgSSABlockPredIterator(pred_iterator PredIt, DebugSSAUpdater &Updater)
+      : PredIt(PredIt), Updater(Updater) {}
+
+  bool operator!=(const DbgSSABlockPredIterator &OtherIt) const {
+    return OtherIt.PredIt != PredIt;
+  }
+
+  DbgSSABlockPredIterator &operator++() {
+    ++PredIt;
+    return *this;
+  }
+
+  DbgSSABlock *operator*();
+};
+
+class DbgSSABlock {
+public:
+  BasicBlock &BB;
+  DebugSSAUpdater &Updater;
+  using PHIListT = SmallVector<DbgSSAPhi, 1>;
+  /// List of PHIs in this block. There should only ever be one, but this needs
+  /// to be a list for the SSAUpdater.
+  PHIListT PHIList;
+
+  DbgSSABlock(BasicBlock &BB, DebugSSAUpdater &Updater)
+      : BB(BB), Updater(Updater) {}
+
+  DbgSSABlockPredIterator pred_begin() {
+    return DbgSSABlockPredIterator(llvm::pred_begin(&BB), Updater);
+  }
+
+  DbgSSABlockPredIterator pred_end() {
+    return DbgSSABlockPredIterator(llvm::pred_end(&BB), Updater);
+  }
+
+  iterator_range<DbgSSABlockPredIterator> predecessors() {
+    return iterator_range(pred_begin(), pred_end());
+  }
+
+  DbgSSABlockSuccIterator succ_begin() {
+    return DbgSSABlockSuccIterator(llvm::succ_begin(&BB), Updater);
+  }
+
+  DbgSSABlockSuccIterator succ_end() {
+    return DbgSSABlockSuccIterator(llvm::succ_end(&BB), Updater);
+  }
+
+  iterator_range<DbgSSABlockSuccIterator> successors() {
+    return iterator_range(succ_begin(), succ_end());
+  }
+
+  /// SSAUpdater has requested a PHI: create that within this block record.
+  DbgSSAPhi *newPHI() {
+    assert(PHIList.empty() &&
+           "Only one PHI should exist per-block per-variable");
+    PHIList.emplace_back(this);
+    return &PHIList.back();
+  }
+
+  /// SSAUpdater wishes to know what PHIs already exist in this block.
+  PHIListT &phis() { return PHIList; }
+};
+
+/// Class used to determine the live ranges of debug variables in IR using
+/// SSA construction (via the SSAUpdaterImpl class), used for analysis purposes.
+class DebugSSAUpdater {
+  friend class SSAUpdaterTraits<DebugSSAUpdater>;
+
+private:
+  /// This keeps track of which value to use on a per-block basis. When we
+  /// insert PHI nodes, we keep track of them here.
+  void *AV = nullptr;
+
+  SmallVectorImpl<DbgSSAPhi *> *InsertedPHIs;
----------------
jmorse wrote:

Wants a docu-comment as a pointer-to-a-vector is moderately abnormal

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


More information about the llvm-commits mailing list