Files
gps/tools/local/circt/include/llvm/Transforms/Utils/DebugSSAUpdater.h
T
2026-07-12 11:01:51 +03:00

367 lines
12 KiB
C++

//===- 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.
//
// NB: This is an expensive analysis that is generally not suitable for use in
// LLVM passes, but may be useful for standalone tools.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TRANSFORMS_UTILS_DEBUGSSAUPDATER_H
#define LLVM_TRANSFORMS_UTILS_DEBUGSSAUPDATER_H
#include "llvm/ADT/SmallVector.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"
#include "llvm/IR/ValueHandle.h"
#include "llvm/IR/ValueMap.h"
#include "llvm/Support/Compiler.h"
#include <cstdint>
namespace llvm {
////////////////////////////////////////
// SSAUpdater specialization classes
class DbgSSAPhi;
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); }
LLVM_ABI 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});
}
LLVM_ABI 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;
}
LLVM_ABI 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;
}
LLVM_ABI 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>;
using AvailableValsTy = DenseMap<DbgSSABlock *, DbgValueDef>;
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.
AvailableValsTy AV;
/// Pointer to an optionally-passed vector into which, if it is non-null,
/// the PHIs that describe ambiguous variable locations will be inserted.
SmallVectorImpl<DbgSSAPhi *> *InsertedPHIs;
DenseMap<BasicBlock *, DbgSSABlock *> BlockMap;
public:
/// If InsertedPHIs is specified, it will be filled
/// in with all PHI Nodes created by rewriting.
LLVM_ABI explicit DebugSSAUpdater(
SmallVectorImpl<DbgSSAPhi *> *InsertedPHIs = nullptr);
DebugSSAUpdater(const DebugSSAUpdater &) = delete;
DebugSSAUpdater &operator=(const DebugSSAUpdater &) = delete;
~DebugSSAUpdater() {
for (auto &Block : BlockMap)
delete Block.second;
}
void reset() {
for (auto &Block : BlockMap)
delete Block.second;
if (InsertedPHIs)
InsertedPHIs->clear();
BlockMap.clear();
}
LLVM_ABI void initialize();
/// For a given BB, create a wrapper block for it. Stores it in the
/// DebugSSAUpdater block map.
DbgSSABlock *getDbgSSABlock(BasicBlock *BB) {
auto it = BlockMap.find(BB);
if (it == BlockMap.end()) {
BlockMap[BB] = new DbgSSABlock(*BB, *this);
it = BlockMap.find(BB);
}
return it->second;
}
/// Indicate that a rewritten value is available in the specified block
/// with the specified value.
LLVM_ABI void addAvailableValue(DbgSSABlock *BB, DbgValueDef DV);
/// Return true if the DebugSSAUpdater already has a value for the specified
/// block.
LLVM_ABI bool hasValueForBlock(DbgSSABlock *BB) const;
/// Return the value for the specified block if the DebugSSAUpdater has one,
/// otherwise return nullptr.
LLVM_ABI DbgValueDef findValueForBlock(DbgSSABlock *BB) const;
/// Construct SSA form, materializing a value that is live at the end
/// of the specified block.
LLVM_ABI DbgValueDef getValueAtEndOfBlock(DbgSSABlock *BB);
/// Construct SSA form, materializing a value that is live in the
/// middle of the specified block.
///
/// \c getValueInMiddleOfBlock is the same as \c GetValueAtEndOfBlock except
/// in one important case: if there is a definition of the rewritten value
/// after the 'use' in BB. Consider code like this:
///
/// \code
/// X1 = ...
/// SomeBB:
/// use(X)
/// X2 = ...
/// br Cond, SomeBB, OutBB
/// \endcode
///
/// In this case, there are two values (X1 and X2) added to the AvailableVals
/// set by the client of the rewriter, and those values are both live out of
/// their respective blocks. However, the use of X happens in the *middle* of
/// a block. Because of this, we need to insert a new PHI node in SomeBB to
/// merge the appropriate values, and this value isn't live out of the block.
LLVM_ABI DbgValueDef getValueInMiddleOfBlock(DbgSSABlock *BB);
private:
DbgValueDef getValueAtEndOfBlockInternal(DbgSSABlock *BB);
};
struct DbgRangeEntry {
BasicBlock::iterator Start;
BasicBlock::iterator End;
// Should be non-PHI.
DbgValueDef Value;
};
/// Utility class used to store the names of SSA values after their owning
/// modules have been destroyed. Values are added via \c addValue to receive a
/// corresponding ID, which can then be used to retrieve the name of the SSA
/// value via \c getName at any point. Adding the same value multiple times
/// returns the same ID, making \c addValue idempotent.
class SSAValueNameMap {
struct Config : ValueMapConfig<Value *> {
enum { FollowRAUW = false };
};
public:
using ValueID = uint64_t;
LLVM_ABI ValueID addValue(Value *V);
std::string getName(ValueID ID) { return ValueIDToNameMap[ID]; }
private:
DenseMap<ValueID, std::string> ValueIDToNameMap;
ValueMap<Value *, ValueID, Config> ValueToIDMap;
ValueID NextID = 0;
};
/// Utility class used to find and store the live debug ranges for variables in
/// a module. This class uses the DebugSSAUpdater for each variable added with
/// \c addVariable to find either a single-location value, e.g. #dbg_declare, or
/// a set of live value ranges corresponding to the set of #dbg_value records.
class DbgValueRangeTable {
DenseMap<DebugVariableAggregate, SmallVector<DbgRangeEntry>>
OrigVariableValueRangeTable;
DenseMap<DebugVariableAggregate, DbgValueDef> OrigSingleLocVariableValueTable;
public:
LLVM_ABI_FOR_TEST void addVariable(Function *F, DebugVariableAggregate DVA);
bool hasVariableEntry(DebugVariableAggregate DVA) const {
return OrigVariableValueRangeTable.contains(DVA) ||
OrigSingleLocVariableValueTable.contains(DVA);
}
bool hasSingleLocEntry(DebugVariableAggregate DVA) const {
return OrigSingleLocVariableValueTable.contains(DVA);
}
ArrayRef<DbgRangeEntry> getVariableRanges(DebugVariableAggregate DVA) {
return OrigVariableValueRangeTable[DVA];
}
DbgValueDef getSingleLoc(DebugVariableAggregate DVA) {
return OrigSingleLocVariableValueTable[DVA];
}
LLVM_ABI void printValues(DebugVariableAggregate DVA, raw_ostream &OS);
};
} // end namespace llvm
#endif // LLVM_TRANSFORMS_UTILS_DEBUGSSAUPDATER_H