Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions llvm/include/llvm/IR/DebugInfoMetadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -4665,6 +4665,7 @@ template <> struct DenseMapInfo<DebugVariable> {
/// information).
class DebugVariableAggregate : public DebugVariable {
public:
LLVM_ABI DebugVariableAggregate(const DbgVariableRecord *DVR);
DebugVariableAggregate(const DebugVariable &V)
: DebugVariable(V.getVariable(), std::nullopt, V.getInlinedAt()) {}
};
Expand Down
360 changes: 360 additions & 0 deletions llvm/include/llvm/Transforms/Utils/DebugSSAUpdater.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,360 @@
//===- 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 <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); }

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>;
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.
explicit DebugSSAUpdater(
SmallVectorImpl<DbgSSAPhi *> *InsertedPHIs = nullptr);
DebugSSAUpdater(const DebugSSAUpdater &) = delete;
DebugSSAUpdater &operator=(const DebugSSAUpdater &) = delete;

void reset() {
for (auto &Block : BlockMap)
delete Block.second;

if (InsertedPHIs)
InsertedPHIs->clear();
BlockMap.clear();
}

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.
void addAvailableValue(DbgSSABlock *BB, DbgValueDef DV);

/// Return true if the DebugSSAUpdater already has a value for the specified
/// block.
bool hasValueForBlock(DbgSSABlock *BB) const;

/// Return the value for the specified block if the DebugSSAUpdater has one,
/// otherwise return nullptr.
DbgValueDef findValueForBlock(DbgSSABlock *BB) const;

/// Construct SSA form, materializing a value that is live at the end
/// of the specified block.
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.
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;
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:
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];
}

void printValues(DebugVariableAggregate DVA, raw_ostream &OS);
};

} // end namespace llvm

#endif // LLVM_TRANSFORMS_UTILS_DEBUGSSAUPDATER_H
4 changes: 4 additions & 0 deletions llvm/lib/IR/DebugInfoMetadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ DebugVariable::DebugVariable(const DbgVariableRecord *DVR)
Fragment(DVR->getExpression()->getFragmentInfo()),
InlinedAt(DVR->getDebugLoc().getInlinedAt()) {}

DebugVariableAggregate::DebugVariableAggregate(const DbgVariableRecord *DVR)
: DebugVariable(DVR->getVariable(), std::nullopt,
DVR->getDebugLoc()->getInlinedAt()) {}

DILocation::DILocation(LLVMContext &C, StorageType Storage, unsigned Line,
unsigned Column, uint64_t AtomGroup, uint8_t AtomRank,
ArrayRef<Metadata *> MDs, bool ImplicitCode)
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Transforms/Utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ add_llvm_component_library(LLVMTransformUtils
CtorUtils.cpp
CountVisits.cpp
Debugify.cpp
DebugSSAUpdater.cpp
DeclareRuntimeLibcalls.cpp
DemoteRegToStack.cpp
DXILUpgrade.cpp
Expand Down
Loading