Skip to content

Commit 26382a4

Browse files
committed
Reapply [Assignment Tracking][5/*] Add core infrastructure for instruction reference
Previously reverted in 41f5a00. Fold in D133576 previously reverted in d29d5ff. --- The Assignment Tracking debug-info feature is outlined in this RFC: https://discourse.llvm.org/t/ rfc-assignment-tracking-a-better-way-of-specifying-variable-locations-in-ir Overview It's possible to find intrinsics linked to an instruction by looking at the MetadataAsValue uses of the attached DIAssignID. That covers instruction -> intrinsic(s) lookup. Add a global DIAssignID -> instruction(s) map which gives us the ability to perform intrinsic -> instruction(s) lookup. Add plumbing to keep the map up to date through optimisations and add utility functions including two that perform those lookups. Finally, add a unittest. Details In llvm/lib/IR/LLVMContextImpl.h add AssignmentIDToInstrs which maps DIAssignID * attachments to Instruction *s. Because the DIAssignID * is the key we can't use a TrackingMDNodeRef for it, and therefore cannot easily update the mapping when a temporary DIAssignID is replaced. Temporary DIAssignID's are only used in IR parsing to deal with metadata forward references. Update llvm/lib/AsmParser/LLParser.cpp to avoid using temporary DIAssignID's for attachments. In llvm/lib/IR/Metadata.cpp add Instruction::updateDIAssignIDMapping which is called to remove or add an entry (or both) to AssignmentIDToInstrs. Call this from Instruction::setMetadata and add a call to setMetadata in Intruction's dtor that explicitly unsets the DIAssignID so that the mappging gets updated. In llvm/lib/IR/DebugInfo.cpp and DebugInfo.h add utility functions: getAssignmentInsts(const DbgAssignIntrinsic *DAI) getAssignmentMarkers(const Instruction *Inst) RAUW(DIAssignID *Old, DIAssignID *New) deleteAll(Function *F) deleteAssignmentMarkers(const Instruction *Inst) These core utils are tested in llvm/unittests/IR/DebugInfoTest.cpp. Reviewed By: jmorse Differential Revision: https://reviews.llvm.org/D132224
1 parent 471f2cf commit 26382a4

File tree

12 files changed

+375
-3
lines changed

12 files changed

+375
-3
lines changed

llvm/include/llvm/AsmParser/LLParser.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@ namespace llvm {
108108

109109
SmallVector<Instruction*, 64> InstsWithTBAATag;
110110

111+
/// DIAssignID metadata does not support temporary RAUW so we cannot use
112+
/// the normal metadata forward reference resolution method. Instead,
113+
/// non-temporary DIAssignID are attached to instructions (recorded here)
114+
/// then replaced later.
115+
DenseMap<MDNode *, SmallVector<Instruction *, 2>> TempDIAssignIDAttachments;
116+
111117
// Type resolution handling data structures. The location is set when we
112118
// have processed a use of the type but not a definition yet.
113119
StringMap<std::pair<Type*, LocTy> > NamedTypes;

llvm/include/llvm/IR/DebugInfo.h

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
#include "llvm/ADT/SmallVector.h"
2222
#include "llvm/ADT/TinyPtrVector.h"
2323
#include "llvm/ADT/iterator_range.h"
24-
#include "llvm/IR/DebugInfoMetadata.h"
24+
#include "llvm/IR/IntrinsicInst.h"
2525

2626
namespace llvm {
2727

@@ -159,6 +159,70 @@ class DebugInfoFinder {
159159
SmallPtrSet<const MDNode *, 32> NodesSeen;
160160
};
161161

162+
/// Assignment Tracking (at).
163+
namespace at {
164+
//
165+
// Utilities for enumerating storing instructions from an assignment ID.
166+
//
167+
/// A range of instructions.
168+
using AssignmentInstRange =
169+
iterator_range<SmallVectorImpl<Instruction *>::iterator>;
170+
/// Return a range of instructions (typically just one) that have \p ID
171+
/// as an attachment.
172+
/// Iterators invalidated by adding or removing DIAssignID metadata to/from any
173+
/// instruction (including by deleting or cloning instructions).
174+
AssignmentInstRange getAssignmentInsts(DIAssignID *ID);
175+
/// Return a range of instructions (typically just one) that perform the
176+
/// assignment that \p DAI encodes.
177+
/// Iterators invalidated by adding or removing DIAssignID metadata to/from any
178+
/// instruction (including by deleting or cloning instructions).
179+
inline AssignmentInstRange getAssignmentInsts(const DbgAssignIntrinsic *DAI) {
180+
return getAssignmentInsts(DAI->getAssignID());
181+
}
182+
183+
//
184+
// Utilities for enumerating llvm.dbg.assign intrinsic from an assignment ID.
185+
//
186+
/// High level: this is an iterator for llvm.dbg.assign intrinsics.
187+
/// Implementation details: this is a wrapper around Value's User iterator that
188+
/// dereferences to a DbgAssignIntrinsic ptr rather than a User ptr.
189+
class DbgAssignIt
190+
: public iterator_adaptor_base<DbgAssignIt, Value::user_iterator,
191+
typename std::iterator_traits<
192+
Value::user_iterator>::iterator_category,
193+
DbgAssignIntrinsic *, std::ptrdiff_t,
194+
DbgAssignIntrinsic **,
195+
DbgAssignIntrinsic *&> {
196+
public:
197+
DbgAssignIt(Value::user_iterator It) : iterator_adaptor_base(It) {}
198+
DbgAssignIntrinsic *operator*() const { return cast<DbgAssignIntrinsic>(*I); }
199+
};
200+
/// A range of llvm.dbg.assign intrinsics.
201+
using AssignmentMarkerRange = iterator_range<DbgAssignIt>;
202+
/// Return a range of dbg.assign intrinsics which use \ID as an operand.
203+
/// Iterators invalidated by deleting an intrinsic contained in this range.
204+
AssignmentMarkerRange getAssignmentMarkers(DIAssignID *ID);
205+
/// Return a range of dbg.assign intrinsics for which \p Inst performs the
206+
/// assignment they encode.
207+
/// Iterators invalidated by deleting an intrinsic contained in this range.
208+
inline AssignmentMarkerRange getAssignmentMarkers(const Instruction *Inst) {
209+
if (auto *ID = Inst->getMetadata(LLVMContext::MD_DIAssignID))
210+
return getAssignmentMarkers(cast<DIAssignID>(ID));
211+
else
212+
return make_range(Value::user_iterator(), Value::user_iterator());
213+
}
214+
215+
/// Delete the llvm.dbg.assign intrinsics linked to \p Inst.
216+
void deleteAssignmentMarkers(const Instruction *Inst);
217+
218+
/// Replace all uses (and attachments) of \p Old with \p New.
219+
void RAUW(DIAssignID *Old, DIAssignID *New);
220+
221+
/// Remove all Assignment Tracking related intrinsics and metadata from \p F.
222+
void deleteAll(Function *F);
223+
224+
} // end namespace at
225+
162226
/// Return true if assignment tracking is enabled.
163227
bool getEnableAssignmentTracking();
164228
} // end namespace llvm

llvm/include/llvm/IR/Instruction.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,10 @@ class Instruction : public User,
515515
void
516516
getAllMetadataImpl(SmallVectorImpl<std::pair<unsigned, MDNode *>> &) const;
517517

518+
/// Update the LLVMContext ID-to-Instruction(s) mapping. If \p ID is nullptr
519+
/// then clear the mapping for this instruction.
520+
void updateDIAssignIDMapping(DIAssignID *ID);
521+
518522
public:
519523
//===--------------------------------------------------------------------===//
520524
// Predicates and helper methods.

llvm/include/llvm/module.modulemap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ module LLVM_intrinsic_gen {
286286
module IR_PatternMatch { header "IR/PatternMatch.h" export * }
287287
module IR_SafepointIRVerifier { header "IR/SafepointIRVerifier.h" export * }
288288
module IR_Statepoint { header "IR/Statepoint.h" export * }
289+
module IR_DebugInfo { header "IR/DebugInfo.h" export * }
289290

290291
export *
291292
}

llvm/lib/AsmParser/LLParser.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -853,7 +853,18 @@ bool LLParser::parseStandaloneMetadata() {
853853
// See if this was forward referenced, if so, handle it.
854854
auto FI = ForwardRefMDNodes.find(MetadataID);
855855
if (FI != ForwardRefMDNodes.end()) {
856-
FI->second.first->replaceAllUsesWith(Init);
856+
auto *ToReplace = FI->second.first.get();
857+
// DIAssignID has its own special forward-reference "replacement" for
858+
// attachments (the temporary attachments are never actually attached).
859+
if (isa<DIAssignID>(Init)) {
860+
for (auto *Inst : TempDIAssignIDAttachments[ToReplace]) {
861+
assert(!Inst->getMetadata(LLVMContext::MD_DIAssignID) &&
862+
"Inst unexpectedly already has DIAssignID attachment");
863+
Inst->setMetadata(LLVMContext::MD_DIAssignID, Init);
864+
}
865+
}
866+
867+
ToReplace->replaceAllUsesWith(Init);
857868
ForwardRefMDNodes.erase(FI);
858869

859870
assert(NumberedMetadata[MetadataID] == Init && "Tracking VH didn't work");
@@ -2082,7 +2093,11 @@ bool LLParser::parseInstructionMetadata(Instruction &Inst) {
20822093
if (parseMetadataAttachment(MDK, N))
20832094
return true;
20842095

2085-
Inst.setMetadata(MDK, N);
2096+
if (MDK == LLVMContext::MD_DIAssignID)
2097+
TempDIAssignIDAttachments[N].push_back(&Inst);
2098+
else
2099+
Inst.setMetadata(MDK, N);
2100+
20862101
if (MDK == LLVMContext::MD_tbaa)
20872102
InstsWithTBAATag.push_back(&Inst);
20882103

llvm/lib/IR/DebugInfo.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//===----------------------------------------------------------------------===//
1313

1414
#include "llvm-c/DebugInfo.h"
15+
#include "LLVMContextImpl.h"
1516
#include "llvm/ADT/DenseMap.h"
1617
#include "llvm/ADT/DenseSet.h"
1718
#include "llvm/ADT/STLExtras.h"
@@ -37,6 +38,7 @@
3738
#include <utility>
3839

3940
using namespace llvm;
41+
using namespace llvm::at;
4042
using namespace llvm::dwarf;
4143

4244
static cl::opt<bool>
@@ -1632,3 +1634,70 @@ LLVMMetadataKind LLVMGetMetadataKind(LLVMMetadataRef Metadata) {
16321634
return (LLVMMetadataKind)LLVMGenericDINodeMetadataKind;
16331635
}
16341636
}
1637+
1638+
AssignmentInstRange at::getAssignmentInsts(DIAssignID *ID) {
1639+
assert(ID && "Expected non-null ID");
1640+
LLVMContext &Ctx = ID->getContext();
1641+
auto &Map = Ctx.pImpl->AssignmentIDToInstrs;
1642+
1643+
auto MapIt = Map.find(ID);
1644+
if (MapIt == Map.end())
1645+
return make_range(nullptr, nullptr);
1646+
1647+
return make_range(MapIt->second.begin(), MapIt->second.end());
1648+
}
1649+
1650+
AssignmentMarkerRange at::getAssignmentMarkers(DIAssignID *ID) {
1651+
assert(ID && "Expected non-null ID");
1652+
LLVMContext &Ctx = ID->getContext();
1653+
1654+
auto *IDAsValue = MetadataAsValue::getIfExists(Ctx, ID);
1655+
1656+
// The ID is only used wrapped in MetadataAsValue(ID), so lets check that
1657+
// one of those already exists first.
1658+
if (!IDAsValue)
1659+
return make_range(Value::user_iterator(), Value::user_iterator());
1660+
1661+
return make_range(IDAsValue->user_begin(), IDAsValue->user_end());
1662+
}
1663+
1664+
void at::deleteAssignmentMarkers(const Instruction *Inst) {
1665+
auto Range = getAssignmentMarkers(Inst);
1666+
if (Range.empty())
1667+
return;
1668+
SmallVector<DbgAssignIntrinsic *> ToDelete(Range.begin(), Range.end());
1669+
for (auto *DAI : ToDelete)
1670+
DAI->eraseFromParent();
1671+
}
1672+
1673+
void at::RAUW(DIAssignID *Old, DIAssignID *New) {
1674+
// Replace MetadataAsValue uses.
1675+
if (auto *OldIDAsValue =
1676+
MetadataAsValue::getIfExists(Old->getContext(), Old)) {
1677+
auto *NewIDAsValue = MetadataAsValue::get(Old->getContext(), New);
1678+
OldIDAsValue->replaceAllUsesWith(NewIDAsValue);
1679+
}
1680+
1681+
// Replace attachments.
1682+
AssignmentInstRange InstRange = getAssignmentInsts(Old);
1683+
// Use intermediate storage for the instruction ptrs because the
1684+
// getAssignmentInsts range iterators will be invalidated by adding and
1685+
// removing DIAssignID attachments.
1686+
SmallVector<Instruction *> InstVec(InstRange.begin(), InstRange.end());
1687+
for (auto *I : InstVec)
1688+
I->setMetadata(LLVMContext::MD_DIAssignID, New);
1689+
}
1690+
1691+
void at::deleteAll(Function *F) {
1692+
SmallVector<DbgAssignIntrinsic *, 12> ToDelete;
1693+
for (BasicBlock &BB : *F) {
1694+
for (Instruction &I : BB) {
1695+
if (auto *DAI = dyn_cast<DbgAssignIntrinsic>(&I))
1696+
ToDelete.push_back(DAI);
1697+
else
1698+
I.setMetadata(LLVMContext::MD_DIAssignID, nullptr);
1699+
}
1700+
}
1701+
for (auto *DAI : ToDelete)
1702+
DAI->eraseFromParent();
1703+
}

llvm/lib/IR/Instruction.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ Instruction::~Instruction() {
5555
// instructions in a BasicBlock are deleted).
5656
if (isUsedByMetadata())
5757
ValueAsMetadata::handleRAUW(this, UndefValue::get(getType()));
58+
59+
// Explicitly remove DIAssignID metadata to clear up ID -> Instruction(s)
60+
// mapping in LLVMContext.
61+
setMetadata(LLVMContext::MD_DIAssignID, nullptr);
5862
}
5963

6064

llvm/lib/IR/LLVMContextImpl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,6 +1499,11 @@ class LLVMContextImpl {
14991499
/// Collection of metadata used in this context.
15001500
DenseMap<const Value *, MDAttachments> ValueMetadata;
15011501

1502+
/// Map DIAssignID -> Instructions with that attachment.
1503+
/// Managed by Instruction via Instruction::updateDIAssignIDMapping.
1504+
/// Query using the at:: functions defined in DebugInfo.h.
1505+
DenseMap<DIAssignID *, SmallVector<Instruction *, 1>> AssignmentIDToInstrs;
1506+
15021507
/// Collection of per-GlobalObject sections used in this context.
15031508
DenseMap<const GlobalObject *, StringRef> GlobalObjectSections;
15041509

llvm/lib/IR/Metadata.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1425,6 +1425,37 @@ void Instruction::dropUnknownNonDebugMetadata(ArrayRef<unsigned> KnownIDs) {
14251425
}
14261426
}
14271427

1428+
void Instruction::updateDIAssignIDMapping(DIAssignID *ID) {
1429+
auto &IDToInstrs = getContext().pImpl->AssignmentIDToInstrs;
1430+
if (const DIAssignID *CurrentID =
1431+
cast_or_null<DIAssignID>(getMetadata(LLVMContext::MD_DIAssignID))) {
1432+
// Nothing to do if the ID isn't changing.
1433+
if (ID == CurrentID)
1434+
return;
1435+
1436+
// Unmap this instruction from its current ID.
1437+
auto InstrsIt = IDToInstrs.find(CurrentID);
1438+
assert(InstrsIt != IDToInstrs.end() &&
1439+
"Expect existing attachment to be mapped");
1440+
1441+
auto &InstVec = InstrsIt->second;
1442+
auto *InstIt = std::find(InstVec.begin(), InstVec.end(), this);
1443+
assert(InstIt != InstVec.end() &&
1444+
"Expect instruction to be mapped to attachment");
1445+
// The vector contains a ptr to this. If this is the only element in the
1446+
// vector, remove the ID:vector entry, otherwise just remove the
1447+
// instruction from the vector.
1448+
if (InstVec.size() == 1)
1449+
IDToInstrs.erase(InstrsIt);
1450+
else
1451+
InstVec.erase(InstIt);
1452+
}
1453+
1454+
// Map this instruction to the new ID.
1455+
if (ID)
1456+
IDToInstrs[ID].push_back(this);
1457+
}
1458+
14281459
void Instruction::setMetadata(unsigned KindID, MDNode *Node) {
14291460
if (!Node && !hasMetadata())
14301461
return;
@@ -1435,6 +1466,16 @@ void Instruction::setMetadata(unsigned KindID, MDNode *Node) {
14351466
return;
14361467
}
14371468

1469+
// Update DIAssignID to Instruction(s) mapping.
1470+
if (KindID == LLVMContext::MD_DIAssignID) {
1471+
// The DIAssignID tracking infrastructure doesn't support RAUWing temporary
1472+
// nodes with DIAssignIDs. The cast_or_null below would also catch this, but
1473+
// having a dedicated assert helps make this obvious.
1474+
assert((!Node || !Node->isTemporary()) &&
1475+
"Temporary DIAssignIDs are invalid");
1476+
updateDIAssignIDMapping(cast_or_null<DIAssignID>(Node));
1477+
}
1478+
14381479
Value::setMetadata(KindID, Node);
14391480
}
14401481

llvm/lib/IR/Verifier.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
#include "llvm/IR/ConstantRange.h"
6969
#include "llvm/IR/Constants.h"
7070
#include "llvm/IR/DataLayout.h"
71+
#include "llvm/IR/DebugInfo.h"
7172
#include "llvm/IR/DebugInfoMetadata.h"
7273
#include "llvm/IR/DebugLoc.h"
7374
#include "llvm/IR/DerivedTypes.h"
@@ -4557,6 +4558,10 @@ void Verifier::visitDIAssignIDMetadata(Instruction &I, MDNode *MD) {
45574558
CheckDI(isa<DbgAssignIntrinsic>(User),
45584559
"!DIAssignID should only be used by llvm.dbg.assign intrinsics",
45594560
MD, User);
4561+
// All of the dbg.assign intrinsics should be in the same function as I.
4562+
if (auto *DAI = dyn_cast<DbgAssignIntrinsic>(User))
4563+
CheckDI(DAI->getFunction() == I.getFunction(),
4564+
"dbg.assign not in same function as inst", DAI, &I);
45604565
}
45614566
}
45624567
}
@@ -6017,6 +6022,10 @@ void Verifier::visitDbgIntrinsic(StringRef Kind, DbgVariableIntrinsic &DII) {
60176022
CheckDI(isa<DIExpression>(DAI->getRawAddressExpression()),
60186023
"invalid llvm.dbg.assign intrinsic address expression", &DII,
60196024
DAI->getRawAddressExpression());
6025+
// All of the linked instructions should be in the same function as DII.
6026+
for (Instruction *I : at::getAssignmentInsts(DAI))
6027+
CheckDI(DAI->getFunction() == I->getFunction(),
6028+
"inst not in same function as dbg.assign", I, DAI);
60206029
}
60216030

60226031
// Ignore broken !dbg attachments; they're checked elsewhere.

0 commit comments

Comments
 (0)