[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;
----------------
jmorse wrote:
We're deliberately skipping type-checking by this being a void pointer, with some casting happening in the .cpp implementation -- why's this necessary?
https://github.com/llvm/llvm-project/pull/135349
More information about the llvm-commits
mailing list