Skip to content

Commit 3946c50

Browse files
authored
Add DebugSSAUpdater class to track debug value liveness (#135349)
This patch adds a class that uses SSA construction, with debug values as definitions, to determine whether and which debug values for a particular variable are live at each point in an IR function. This will be used by the IR reader of llvm-debuginfo-analyzer to compute variable ranges and coverage, although it may be applicable to other debug info IR analyses.
1 parent 3a5cc95 commit 3946c50

File tree

8 files changed

+977
-0
lines changed

8 files changed

+977
-0
lines changed

llvm/include/llvm/IR/DebugInfoMetadata.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4665,6 +4665,7 @@ template <> struct DenseMapInfo<DebugVariable> {
46654665
/// information).
46664666
class DebugVariableAggregate : public DebugVariable {
46674667
public:
4668+
LLVM_ABI DebugVariableAggregate(const DbgVariableRecord *DVR);
46684669
DebugVariableAggregate(const DebugVariable &V)
46694670
: DebugVariable(V.getVariable(), std::nullopt, V.getInlinedAt()) {}
46704671
};
Lines changed: 360 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
1+
//===- DebugSSAUpdater.h - Debug SSA Update Tool ----------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file declares the DebugSSAUpdater class, which is used to evaluate the
10+
// live values of debug variables in IR. This uses SSA construction, treating
11+
// debug value records as definitions, to determine at each point in the program
12+
// which definition(s) are live at a given point. This is useful for analysis of
13+
// the state of debug variables, such as measuring the change in values of a
14+
// variable over time, or calculating coverage stats.
15+
//
16+
// NB: This is an expensive analysis that is generally not suitable for use in
17+
// LLVM passes, but may be useful for standalone tools.
18+
//
19+
//===----------------------------------------------------------------------===//
20+
21+
#ifndef LLVM_TRANSFORMS_UTILS_DEBUGSSAUPDATER_H
22+
#define LLVM_TRANSFORMS_UTILS_DEBUGSSAUPDATER_H
23+
24+
#include "llvm/ADT/SmallVector.h"
25+
#include "llvm/IR/BasicBlock.h"
26+
#include "llvm/IR/CFG.h"
27+
#include "llvm/IR/DebugInfoMetadata.h"
28+
#include "llvm/IR/DebugProgramInstruction.h"
29+
#include "llvm/IR/Instruction.h"
30+
#include "llvm/IR/ValueHandle.h"
31+
#include "llvm/IR/ValueMap.h"
32+
#include <cstdint>
33+
34+
namespace llvm {
35+
36+
////////////////////////////////////////
37+
// SSAUpdater specialization classes
38+
39+
class DbgSSAPhi;
40+
template <typename T> class SSAUpdaterTraits;
41+
42+
/// A definition of a variable; can represent either a debug value, no
43+
/// definition (the variable has not yet been defined), or a phi value*.
44+
/// *Meaning multiple definitions that are live-in to a block from different
45+
/// predecessors, not a debug value that uses an IR PHINode.
46+
struct DbgValueDef {
47+
DbgSSAPhi *Phi;
48+
bool IsUndef;
49+
bool IsMemory;
50+
Metadata *Locations;
51+
DIExpression *Expression;
52+
53+
DbgValueDef()
54+
: Phi(nullptr), IsUndef(true), IsMemory(false), Locations(nullptr),
55+
Expression(nullptr) {}
56+
DbgValueDef(int)
57+
: Phi(nullptr), IsUndef(true), IsMemory(false), Locations(nullptr),
58+
Expression(nullptr) {}
59+
DbgValueDef(bool IsMemory, Metadata *Locations, DIExpression *Expression)
60+
: Phi(nullptr), IsUndef(false), IsMemory(IsMemory), Locations(Locations),
61+
Expression(Expression) {}
62+
DbgValueDef(DbgVariableRecord *DVR) : Phi(nullptr) {
63+
assert(!DVR->isDbgAssign() && "#dbg_assign not yet supported");
64+
IsUndef = DVR->isKillLocation();
65+
IsMemory = DVR->isAddressOfVariable();
66+
Locations = DVR->getRawLocation();
67+
Expression = DVR->getExpression();
68+
}
69+
DbgValueDef(DbgSSAPhi *Phi)
70+
: Phi(Phi), IsUndef(false), IsMemory(false), Locations(nullptr),
71+
Expression(nullptr) {}
72+
73+
bool agreesWith(DbgValueDef Other) const {
74+
if (IsUndef && Other.IsUndef)
75+
return true;
76+
return std::tie(Phi, IsUndef, IsMemory, Locations, Expression) ==
77+
std::tie(Other.Phi, Other.IsUndef, Other.IsMemory, Other.Locations,
78+
Other.Expression);
79+
}
80+
81+
operator bool() const { return !IsUndef; }
82+
bool operator==(DbgValueDef Other) const { return agreesWith(Other); }
83+
bool operator!=(DbgValueDef Other) const { return !agreesWith(Other); }
84+
85+
void print(raw_ostream &OS) const;
86+
};
87+
88+
class DbgSSABlock;
89+
class DebugSSAUpdater;
90+
91+
/// Represents the live-in definitions of a variable to a block with multiple
92+
/// predecessors.
93+
class DbgSSAPhi {
94+
public:
95+
SmallVector<std::pair<DbgSSABlock *, DbgValueDef>, 4> IncomingValues;
96+
DbgSSABlock *ParentBlock;
97+
DbgSSAPhi(DbgSSABlock *ParentBlock) : ParentBlock(ParentBlock) {}
98+
99+
DbgSSABlock *getParent() { return ParentBlock; }
100+
unsigned getNumIncomingValues() const { return IncomingValues.size(); }
101+
DbgSSABlock *getIncomingBlock(size_t Idx) {
102+
return IncomingValues[Idx].first;
103+
}
104+
DbgValueDef getIncomingValue(size_t Idx) {
105+
return IncomingValues[Idx].second;
106+
}
107+
void addIncoming(DbgSSABlock *BB, DbgValueDef DV) {
108+
IncomingValues.push_back({BB, DV});
109+
}
110+
111+
void print(raw_ostream &OS) const;
112+
};
113+
114+
inline raw_ostream &operator<<(raw_ostream &OS, const DbgValueDef &DV) {
115+
DV.print(OS);
116+
return OS;
117+
}
118+
inline raw_ostream &operator<<(raw_ostream &OS, const DbgSSAPhi &PHI) {
119+
PHI.print(OS);
120+
return OS;
121+
}
122+
123+
/// Thin wrapper around a block successor iterator.
124+
class DbgSSABlockSuccIterator {
125+
public:
126+
succ_iterator SuccIt;
127+
DebugSSAUpdater &Updater;
128+
129+
DbgSSABlockSuccIterator(succ_iterator SuccIt, DebugSSAUpdater &Updater)
130+
: SuccIt(SuccIt), Updater(Updater) {}
131+
132+
bool operator!=(const DbgSSABlockSuccIterator &OtherIt) const {
133+
return OtherIt.SuccIt != SuccIt;
134+
}
135+
136+
DbgSSABlockSuccIterator &operator++() {
137+
++SuccIt;
138+
return *this;
139+
}
140+
141+
DbgSSABlock *operator*();
142+
};
143+
144+
/// Thin wrapper around a block successor iterator.
145+
class DbgSSABlockPredIterator {
146+
public:
147+
pred_iterator PredIt;
148+
DebugSSAUpdater &Updater;
149+
150+
DbgSSABlockPredIterator(pred_iterator PredIt, DebugSSAUpdater &Updater)
151+
: PredIt(PredIt), Updater(Updater) {}
152+
153+
bool operator!=(const DbgSSABlockPredIterator &OtherIt) const {
154+
return OtherIt.PredIt != PredIt;
155+
}
156+
157+
DbgSSABlockPredIterator &operator++() {
158+
++PredIt;
159+
return *this;
160+
}
161+
162+
DbgSSABlock *operator*();
163+
};
164+
165+
class DbgSSABlock {
166+
public:
167+
BasicBlock &BB;
168+
DebugSSAUpdater &Updater;
169+
using PHIListT = SmallVector<DbgSSAPhi, 1>;
170+
/// List of PHIs in this block. There should only ever be one, but this needs
171+
/// to be a list for the SSAUpdater.
172+
PHIListT PHIList;
173+
174+
DbgSSABlock(BasicBlock &BB, DebugSSAUpdater &Updater)
175+
: BB(BB), Updater(Updater) {}
176+
177+
DbgSSABlockPredIterator pred_begin() {
178+
return DbgSSABlockPredIterator(llvm::pred_begin(&BB), Updater);
179+
}
180+
181+
DbgSSABlockPredIterator pred_end() {
182+
return DbgSSABlockPredIterator(llvm::pred_end(&BB), Updater);
183+
}
184+
185+
iterator_range<DbgSSABlockPredIterator> predecessors() {
186+
return iterator_range(pred_begin(), pred_end());
187+
}
188+
189+
DbgSSABlockSuccIterator succ_begin() {
190+
return DbgSSABlockSuccIterator(llvm::succ_begin(&BB), Updater);
191+
}
192+
193+
DbgSSABlockSuccIterator succ_end() {
194+
return DbgSSABlockSuccIterator(llvm::succ_end(&BB), Updater);
195+
}
196+
197+
iterator_range<DbgSSABlockSuccIterator> successors() {
198+
return iterator_range(succ_begin(), succ_end());
199+
}
200+
201+
/// SSAUpdater has requested a PHI: create that within this block record.
202+
DbgSSAPhi *newPHI() {
203+
assert(PHIList.empty() &&
204+
"Only one PHI should exist per-block per-variable");
205+
PHIList.emplace_back(this);
206+
return &PHIList.back();
207+
}
208+
209+
/// SSAUpdater wishes to know what PHIs already exist in this block.
210+
PHIListT &phis() { return PHIList; }
211+
};
212+
213+
/// Class used to determine the live ranges of debug variables in IR using
214+
/// SSA construction (via the SSAUpdaterImpl class), used for analysis purposes.
215+
class DebugSSAUpdater {
216+
friend class SSAUpdaterTraits<DebugSSAUpdater>;
217+
using AvailableValsTy = DenseMap<DbgSSABlock *, DbgValueDef>;
218+
219+
private:
220+
/// This keeps track of which value to use on a per-block basis. When we
221+
/// insert PHI nodes, we keep track of them here.
222+
AvailableValsTy AV;
223+
224+
/// Pointer to an optionally-passed vector into which, if it is non-null,
225+
/// the PHIs that describe ambiguous variable locations will be inserted.
226+
SmallVectorImpl<DbgSSAPhi *> *InsertedPHIs;
227+
228+
DenseMap<BasicBlock *, DbgSSABlock *> BlockMap;
229+
230+
public:
231+
/// If InsertedPHIs is specified, it will be filled
232+
/// in with all PHI Nodes created by rewriting.
233+
explicit DebugSSAUpdater(
234+
SmallVectorImpl<DbgSSAPhi *> *InsertedPHIs = nullptr);
235+
DebugSSAUpdater(const DebugSSAUpdater &) = delete;
236+
DebugSSAUpdater &operator=(const DebugSSAUpdater &) = delete;
237+
238+
void reset() {
239+
for (auto &Block : BlockMap)
240+
delete Block.second;
241+
242+
if (InsertedPHIs)
243+
InsertedPHIs->clear();
244+
BlockMap.clear();
245+
}
246+
247+
void initialize();
248+
249+
/// For a given BB, create a wrapper block for it. Stores it in the
250+
/// DebugSSAUpdater block map.
251+
DbgSSABlock *getDbgSSABlock(BasicBlock *BB) {
252+
auto it = BlockMap.find(BB);
253+
if (it == BlockMap.end()) {
254+
BlockMap[BB] = new DbgSSABlock(*BB, *this);
255+
it = BlockMap.find(BB);
256+
}
257+
return it->second;
258+
}
259+
260+
/// Indicate that a rewritten value is available in the specified block
261+
/// with the specified value.
262+
void addAvailableValue(DbgSSABlock *BB, DbgValueDef DV);
263+
264+
/// Return true if the DebugSSAUpdater already has a value for the specified
265+
/// block.
266+
bool hasValueForBlock(DbgSSABlock *BB) const;
267+
268+
/// Return the value for the specified block if the DebugSSAUpdater has one,
269+
/// otherwise return nullptr.
270+
DbgValueDef findValueForBlock(DbgSSABlock *BB) const;
271+
272+
/// Construct SSA form, materializing a value that is live at the end
273+
/// of the specified block.
274+
DbgValueDef getValueAtEndOfBlock(DbgSSABlock *BB);
275+
276+
/// Construct SSA form, materializing a value that is live in the
277+
/// middle of the specified block.
278+
///
279+
/// \c getValueInMiddleOfBlock is the same as \c GetValueAtEndOfBlock except
280+
/// in one important case: if there is a definition of the rewritten value
281+
/// after the 'use' in BB. Consider code like this:
282+
///
283+
/// \code
284+
/// X1 = ...
285+
/// SomeBB:
286+
/// use(X)
287+
/// X2 = ...
288+
/// br Cond, SomeBB, OutBB
289+
/// \endcode
290+
///
291+
/// In this case, there are two values (X1 and X2) added to the AvailableVals
292+
/// set by the client of the rewriter, and those values are both live out of
293+
/// their respective blocks. However, the use of X happens in the *middle* of
294+
/// a block. Because of this, we need to insert a new PHI node in SomeBB to
295+
/// merge the appropriate values, and this value isn't live out of the block.
296+
DbgValueDef getValueInMiddleOfBlock(DbgSSABlock *BB);
297+
298+
private:
299+
DbgValueDef getValueAtEndOfBlockInternal(DbgSSABlock *BB);
300+
};
301+
302+
struct DbgRangeEntry {
303+
BasicBlock::iterator Start;
304+
BasicBlock::iterator End;
305+
// Should be non-PHI.
306+
DbgValueDef Value;
307+
};
308+
309+
/// Utility class used to store the names of SSA values after their owning
310+
/// modules have been destroyed. Values are added via \c addValue to receive a
311+
/// corresponding ID, which can then be used to retrieve the name of the SSA
312+
/// value via \c getName at any point. Adding the same value multiple times
313+
/// returns the same ID, making \c addValue idempotent.
314+
class SSAValueNameMap {
315+
struct Config : ValueMapConfig<Value *> {
316+
enum { FollowRAUW = false };
317+
};
318+
319+
public:
320+
using ValueID = uint64_t;
321+
ValueID addValue(Value *V);
322+
std::string getName(ValueID ID) { return ValueIDToNameMap[ID]; }
323+
324+
private:
325+
DenseMap<ValueID, std::string> ValueIDToNameMap;
326+
ValueMap<Value *, ValueID, Config> ValueToIDMap;
327+
ValueID NextID = 0;
328+
};
329+
330+
/// Utility class used to find and store the live debug ranges for variables in
331+
/// a module. This class uses the DebugSSAUpdater for each variable added with
332+
/// \c addVariable to find either a single-location value, e.g. #dbg_declare, or
333+
/// a set of live value ranges corresponding to the set of #dbg_value records.
334+
class DbgValueRangeTable {
335+
DenseMap<DebugVariableAggregate, SmallVector<DbgRangeEntry>>
336+
OrigVariableValueRangeTable;
337+
DenseMap<DebugVariableAggregate, DbgValueDef> OrigSingleLocVariableValueTable;
338+
339+
public:
340+
void addVariable(Function *F, DebugVariableAggregate DVA);
341+
bool hasVariableEntry(DebugVariableAggregate DVA) const {
342+
return OrigVariableValueRangeTable.contains(DVA) ||
343+
OrigSingleLocVariableValueTable.contains(DVA);
344+
}
345+
bool hasSingleLocEntry(DebugVariableAggregate DVA) const {
346+
return OrigSingleLocVariableValueTable.contains(DVA);
347+
}
348+
ArrayRef<DbgRangeEntry> getVariableRanges(DebugVariableAggregate DVA) {
349+
return OrigVariableValueRangeTable[DVA];
350+
}
351+
DbgValueDef getSingleLoc(DebugVariableAggregate DVA) {
352+
return OrigSingleLocVariableValueTable[DVA];
353+
}
354+
355+
void printValues(DebugVariableAggregate DVA, raw_ostream &OS);
356+
};
357+
358+
} // end namespace llvm
359+
360+
#endif // LLVM_TRANSFORMS_UTILS_DEBUGSSAUPDATER_H

llvm/lib/IR/DebugInfoMetadata.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ DebugVariable::DebugVariable(const DbgVariableRecord *DVR)
5454
Fragment(DVR->getExpression()->getFragmentInfo()),
5555
InlinedAt(DVR->getDebugLoc().getInlinedAt()) {}
5656

57+
DebugVariableAggregate::DebugVariableAggregate(const DbgVariableRecord *DVR)
58+
: DebugVariable(DVR->getVariable(), std::nullopt,
59+
DVR->getDebugLoc()->getInlinedAt()) {}
60+
5761
DILocation::DILocation(LLVMContext &C, StorageType Storage, unsigned Line,
5862
unsigned Column, uint64_t AtomGroup, uint8_t AtomRank,
5963
ArrayRef<Metadata *> MDs, bool ImplicitCode)

llvm/lib/Transforms/Utils/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ add_llvm_component_library(LLVMTransformUtils
2020
CtorUtils.cpp
2121
CountVisits.cpp
2222
Debugify.cpp
23+
DebugSSAUpdater.cpp
2324
DeclareRuntimeLibcalls.cpp
2425
DemoteRegToStack.cpp
2526
DXILUpgrade.cpp

0 commit comments

Comments
 (0)