Skip to content

Commit 4b42b62

Browse files
committed
Add DebugSSAUpdater class to track debug value liveness
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 b59c888 commit 4b42b62

File tree

8 files changed

+994
-0
lines changed

8 files changed

+994
-0
lines changed

llvm/include/llvm/IR/DebugInfoMetadata.h

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

66+
DebugVariableAggregate::DebugVariableAggregate(const DbgVariableRecord *DVR)
67+
: DebugVariable(DVR->getVariable(), std::nullopt,
68+
DVR->getDebugLoc()->getInlinedAt()) {}
69+
6670
DILocation::DILocation(LLVMContext &C, StorageType Storage, unsigned Line,
6771
unsigned Column, uint64_t AtomGroup, uint8_t AtomRank,
6872
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
DemoteRegToStack.cpp
2425
DXILUpgrade.cpp
2526
EntryExitInstrumenter.cpp

0 commit comments

Comments
 (0)