[llvm] [IR] Simplify DIAssignID by storing its links in the node. NFC (PR #223324)
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 14 01:38:20 PDT 2026
https://github.com/MaskRay updated https://github.com/llvm/llvm-project/pull/223324
>From 5804cbbd91023b7da6fb5387cde6cc602456a990 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sun, 13 Sep 2026 23:00:39 -0700
Subject: [PATCH 1/2] [IR] Simplify DIAssignID by storing its links in the
node. NFC
A DIAssignID links the instructions it is attached to, kept in
LLVMContextImpl::AssignmentIDToInstrs, with the dbg_assign records that
refer to it, found through a permanent use list (#78300 made DIAssignID
the only always-replaceable MDNode for this). Keep both lists in the
node as TinyPtrVector members instead (98% of instruction lists and
88-96% of record lists hold exactly one pointer), and drop the map and
`isAlwaysReplaceable()`.
Aided by Opus 5
---
llvm/include/llvm/IR/DebugInfo.h | 15 ++++--
llvm/include/llvm/IR/DebugInfoMetadata.h | 13 +++--
llvm/include/llvm/IR/Metadata.h | 3 +-
llvm/lib/IR/DebugInfo.cpp | 15 ++----
llvm/lib/IR/LLVMContextImpl.h | 5 --
llvm/lib/IR/Metadata.cpp | 62 +++++++++---------------
llvm/lib/IR/Verifier.cpp | 8 ++-
7 files changed, 50 insertions(+), 71 deletions(-)
diff --git a/llvm/include/llvm/IR/DebugInfo.h b/llvm/include/llvm/IR/DebugInfo.h
index 18b94f8f66e48..5f1c49703040a 100644
--- a/llvm/include/llvm/IR/DebugInfo.h
+++ b/llvm/include/llvm/IR/DebugInfo.h
@@ -185,13 +185,14 @@ namespace at {
// Utilities for enumerating storing instructions from an assignment ID.
//
/// A range of instructions.
-using AssignmentInstRange =
- iterator_range<SmallVectorImpl<Instruction *>::iterator>;
+using AssignmentInstRange = ArrayRef<Instruction *>;
/// Return a range of instructions (typically just one) that have \p ID
/// as an attachment.
/// Iterators invalidated by adding or removing DIAssignID metadata to/from any
/// instruction (including by deleting or cloning instructions).
-LLVM_ABI AssignmentInstRange getAssignmentInsts(DIAssignID *ID);
+inline AssignmentInstRange getAssignmentInsts(DIAssignID *ID) {
+ return ID->getInstructions();
+}
inline AssignmentInstRange getAssignmentInsts(const DbgVariableRecord *DVR) {
assert(DVR->isDbgAssign() &&
@@ -199,12 +200,18 @@ inline AssignmentInstRange getAssignmentInsts(const DbgVariableRecord *DVR) {
return getAssignmentInsts(DVR->getAssignID());
}
+// Return the dbg_assign records linked to ID, most recently linked first. A
+// copy, as callers unlink or relink records while iterating.
+inline SmallVector<DbgVariableRecord *> getAssignmentMarkers(DIAssignID *ID) {
+ return SmallVector<DbgVariableRecord *>(llvm::reverse(ID->getRecords()));
+}
+
/// Return a range of dbg_assign records for which \p Inst performs the
/// assignment they encode.
inline SmallVector<DbgVariableRecord *>
getDVRAssignmentMarkers(const Instruction *Inst) {
if (auto *ID = Inst->getMetadata(LLVMContext::MD_DIAssignID))
- return cast<DIAssignID>(ID)->getAllDbgVariableRecordUsers();
+ return getAssignmentMarkers(cast<DIAssignID>(ID));
return {};
}
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index 3327471f461b6..aca4949bd3f94 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -19,6 +19,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DbgVariableFragmentInfo.h"
@@ -320,6 +321,13 @@ class GenericDINode : public DINode {
class DIAssignID : public MDNode {
friend class LLVMContextImpl;
friend class MDNode;
+ friend class Instruction;
+ friend class DebugValueUser;
+
+ /// The instructions this ID is attached to and the dbg_assign records that
+ /// refer to it, maintained by Instruction and DebugValueUser.
+ TinyPtrVector<Instruction *> Instrs;
+ TinyPtrVector<DbgVariableRecord *> Records;
DIAssignID(LLVMContext &C, StorageType Storage)
: MDNode(C, DIAssignIDKind, Storage, {}) {}
@@ -335,9 +343,8 @@ class DIAssignID : public MDNode {
// This node has no operands to replace.
void replaceOperandWith(unsigned I, Metadata *New) = delete;
- SmallVector<DbgVariableRecord *> getAllDbgVariableRecordUsers() {
- return Context.getReplaceableUses()->getAllDbgVariableRecordUsers();
- }
+ ArrayRef<Instruction *> getInstructions() const { return Instrs; }
+ ArrayRef<DbgVariableRecord *> getRecords() const { return Records; }
static DIAssignID *getDistinct(LLVMContext &Context) {
return getImpl(Context, Distinct);
diff --git a/llvm/include/llvm/IR/Metadata.h b/llvm/include/llvm/IR/Metadata.h
index c677f9cc61769..51af66ec1809e 100644
--- a/llvm/include/llvm/IR/Metadata.h
+++ b/llvm/include/llvm/IR/Metadata.h
@@ -1262,8 +1262,7 @@ class MDNode : public Metadata {
bool isDistinct() const { return Storage == Distinct; }
bool isTemporary() const { return Storage == Temporary; }
- bool isReplaceable() const { return isTemporary() || isAlwaysReplaceable(); }
- bool isAlwaysReplaceable() const { return getMetadataID() == DIAssignIDKind; }
+ bool isReplaceable() const { return isTemporary(); }
unsigned getNumTemporaryUses() const {
assert(isTemporary() && "Only for temporaries");
diff --git a/llvm/lib/IR/DebugInfo.cpp b/llvm/lib/IR/DebugInfo.cpp
index 38959b61148fe..d0f75fcf34002 100644
--- a/llvm/lib/IR/DebugInfo.cpp
+++ b/llvm/lib/IR/DebugInfo.cpp
@@ -2021,17 +2021,6 @@ LLVMMetadataKind LLVMGetMetadataKind(LLVMMetadataRef Metadata) {
}
}
-AssignmentInstRange at::getAssignmentInsts(DIAssignID *ID) {
- assert(ID && "Expected non-null ID");
- LLVMContext &Ctx = ID->getContext();
- auto &Map = Ctx.pImpl->AssignmentIDToInstrs;
-
- auto MapIt = Map.find(ID);
- if (MapIt == Map.end())
- return make_range(nullptr, nullptr);
-
- return make_range(MapIt->second.begin(), MapIt->second.end());
-}
void at::deleteAssignmentMarkers(const Instruction *Inst) {
for (auto *DVR : getDVRAssignmentMarkers(Inst))
@@ -2048,7 +2037,9 @@ void at::RAUW(DIAssignID *Old, DIAssignID *New) {
for (auto *I : InstVec)
I->setMetadata(LLVMContext::MD_DIAssignID, New);
- Old->replaceAllUsesWith(New);
+ for (DbgVariableRecord *DVR :
+ SmallVector<DbgVariableRecord *>(Old->getRecords()))
+ DVR->setAssignId(New);
}
void at::deleteAll(Function *F) {
diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h
index 49a8a803732a5..ad733db864a8f 100644
--- a/llvm/lib/IR/LLVMContextImpl.h
+++ b/llvm/lib/IR/LLVMContextImpl.h
@@ -1764,11 +1764,6 @@ class LLVMContextImpl {
/// builds to ensure that all metadata attachments are properly freed.
unsigned MetadataRecycleSize = 0;
- /// Map DIAssignID -> Instructions with that attachment.
- /// Managed by Instruction via Instruction::updateDIAssignIDMapping.
- /// Query using the at:: functions defined in DebugInfo.h.
- DenseMap<DIAssignID *, SmallVector<Instruction *, 1>> AssignmentIDToInstrs;
-
/// Collection of per-GlobalObject sections used in this context.
DenseMap<const GlobalObject *, StringRef> GlobalObjectSections;
diff --git a/llvm/lib/IR/Metadata.cpp b/llvm/lib/IR/Metadata.cpp
index 87766f4d47437..117757b9514e8 100644
--- a/llvm/lib/IR/Metadata.cpp
+++ b/llvm/lib/IR/Metadata.cpp
@@ -176,27 +176,31 @@ void DebugValueUser::handleChangedValue(void *Old, Metadata *New) {
void DebugValueUser::trackDebugValue(size_t Idx) {
assert(Idx < 3 && "Invalid debug value index.");
Metadata *&MD = DebugValues[Idx];
- if (MD)
- MetadataTracking::track(&MD, *MD, *this);
+ if (!MD)
+ return;
+ MetadataTracking::track(&MD, *MD, *this);
+ if (auto *ID = Idx == 2 ? dyn_cast<DIAssignID>(MD) : nullptr)
+ ID->Records.push_back(getUser());
}
void DebugValueUser::trackDebugValues() {
- for (Metadata *&MD : DebugValues)
- if (MD)
- MetadataTracking::track(&MD, *MD, *this);
+ for (size_t I = 0, E = DebugValues.size(); I != E; ++I)
+ trackDebugValue(I);
}
void DebugValueUser::untrackDebugValue(size_t Idx) {
assert(Idx < 3 && "Invalid debug value index.");
Metadata *&MD = DebugValues[Idx];
- if (MD)
- MetadataTracking::untrack(MD);
+ if (!MD)
+ return;
+ MetadataTracking::untrack(MD);
+ if (auto *ID = Idx == 2 ? dyn_cast<DIAssignID>(MD) : nullptr)
+ ID->Records.erase(llvm::find(ID->Records, getUser()));
}
void DebugValueUser::untrackDebugValues() {
- for (Metadata *&MD : DebugValues)
- if (MD)
- MetadataTracking::untrack(MD);
+ for (size_t I = 0, E = DebugValues.size(); I != E; ++I)
+ untrackDebugValue(I);
}
void DebugValueUser::retrackDebugValues(DebugValueUser &X) {
@@ -204,6 +208,8 @@ void DebugValueUser::retrackDebugValues(DebugValueUser &X) {
for (const auto &[MD, XMD] : zip(DebugValues, X.DebugValues))
if (XMD)
MetadataTracking::retrack(XMD, MD);
+ if (auto *ID = dyn_cast_or_null<DIAssignID>(DebugValues[2]))
+ *llvm::find(ID->Records, X.getUser()) = getUser();
X.DebugValues.fill(nullptr);
}
@@ -459,9 +465,7 @@ void ReplaceableUses::resolveAllUses(bool ResolveUsers) {
// conditional compilation to avoid a compile time regression.
ReplaceableUses *ReplaceableUses::getOrCreate(Metadata &MD) {
if (auto *N = dyn_cast<MDNode>(&MD)) {
- return !N->isResolved() || N->isAlwaysReplaceable()
- ? N->Context.getOrCreateReplaceableUses()
- : nullptr;
+ return N->isResolved() ? nullptr : N->Context.getOrCreateReplaceableUses();
}
if (auto ArgList = dyn_cast<DIArgList>(&MD))
return ArgList;
@@ -470,9 +474,7 @@ ReplaceableUses *ReplaceableUses::getOrCreate(Metadata &MD) {
ReplaceableUses *ReplaceableUses::getIfExists(Metadata &MD) {
if (auto *N = dyn_cast<MDNode>(&MD)) {
- return !N->isResolved() || N->isAlwaysReplaceable()
- ? N->Context.getReplaceableUses()
- : nullptr;
+ return N->isResolved() ? nullptr : N->Context.getReplaceableUses();
}
if (auto ArgList = dyn_cast<DIArgList>(&MD))
return ArgList;
@@ -481,7 +483,7 @@ ReplaceableUses *ReplaceableUses::getIfExists(Metadata &MD) {
bool ReplaceableUses::isReplaceable(const Metadata &MD) {
if (auto *N = dyn_cast<MDNode>(&MD))
- return !N->isResolved() || N->isAlwaysReplaceable();
+ return !N->isResolved();
return isa<ValueAsMetadata>(&MD) || isa<DIArgList>(&MD);
}
@@ -1748,34 +1750,14 @@ void Instruction::dropUnknownNonDebugMetadata(ArrayRef<unsigned> KnownIDs) {
}
void Instruction::updateDIAssignIDMapping(DIAssignID *ID) {
- auto &IDToInstrs = getContext().pImpl->AssignmentIDToInstrs;
- if (const DIAssignID *CurrentID =
+ if (auto *CurrentID =
cast_or_null<DIAssignID>(getMetadata(LLVMContext::MD_DIAssignID))) {
- // Nothing to do if the ID isn't changing.
if (ID == CurrentID)
return;
-
- // Unmap this instruction from its current ID.
- auto InstrsIt = IDToInstrs.find(CurrentID);
- assert(InstrsIt != IDToInstrs.end() &&
- "Expect existing attachment to be mapped");
-
- auto &InstVec = InstrsIt->second;
- auto *InstIt = llvm::find(InstVec, this);
- assert(InstIt != InstVec.end() &&
- "Expect instruction to be mapped to attachment");
- // The vector contains a ptr to this. If this is the only element in the
- // vector, remove the ID:vector entry, otherwise just remove the
- // instruction from the vector.
- if (InstVec.size() == 1)
- IDToInstrs.erase(InstrsIt);
- else
- InstVec.erase(InstIt);
+ CurrentID->Instrs.erase(llvm::find(CurrentID->Instrs, this));
}
-
- // Map this instruction to the new ID.
if (ID)
- IDToInstrs[ID].push_back(this);
+ ID->Instrs.push_back(this);
}
void Instruction::setMetadata(unsigned KindID, MDNode *Node) {
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index c60c1b6ac11b6..dcebdfa795697 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -5562,13 +5562,9 @@ void Verifier::visitDIAssignIDMetadata(Instruction &I, MDNode *MD) {
"dbg.assign not in same function as inst", DAI, &I);
}
}
- for (DbgVariableRecord *DVR :
- cast<DIAssignID>(MD)->getAllDbgVariableRecordUsers()) {
- CheckDI(DVR->isDbgAssign(),
- "!DIAssignID should only be used by Assign DVRs.", MD, DVR);
+ for (DbgVariableRecord *DVR : at::getAssignmentMarkers(cast<DIAssignID>(MD)))
CheckDI(DVR->getFunction() == I.getFunction(),
"DVRAssign not in same function as inst", DVR, &I);
- }
}
void Verifier::visitMMRAMetadata(Instruction &I, MDNode *MD) {
@@ -7391,6 +7387,8 @@ void Verifier::visit(DbgVariableRecord &DVR) {
CheckDI(MD && (isa<ValueAsMetadata>(MD) || isa<DIArgList>(MD) ||
(isa<MDNode>(MD) && !cast<MDNode>(MD)->getNumOperands())),
"invalid #dbg record address/value", &DVR, MD, BB, F);
+ CheckDI(DVR.isDbgAssign() || !isa<DIAssignID>(MD),
+ "!DIAssignID should only be used by Assign DVRs.", MD, &DVR);
if (auto *VAM = dyn_cast<ValueAsMetadata>(MD)) {
visitValueAsMetadata(*VAM, F);
if (DVR.isDbgDeclare()) {
>From 5920897998324c2a8467bff6e8d692832b6c5e61 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Mon, 14 Sep 2026 01:38:09 -0700
Subject: [PATCH 2/2] 2 -> AssignIDIdx
---
llvm/include/llvm/IR/DebugProgramInstruction.h | 2 +-
llvm/include/llvm/IR/Metadata.h | 2 ++
llvm/lib/IR/DebugProgramInstruction.cpp | 4 ++--
llvm/lib/IR/Metadata.cpp | 6 +++---
4 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/llvm/include/llvm/IR/DebugProgramInstruction.h b/llvm/include/llvm/IR/DebugProgramInstruction.h
index 9402889eb45d0..1acb8838bc1fe 100644
--- a/llvm/include/llvm/IR/DebugProgramInstruction.h
+++ b/llvm/include/llvm/IR/DebugProgramInstruction.h
@@ -527,7 +527,7 @@ class DbgVariableRecord : public DbgRecord, protected DebugValueUser {
Metadata *getRawAddress() const {
return isDbgAssign() ? DebugValues[1] : DebugValues[0];
}
- Metadata *getRawAssignID() const { return DebugValues[2]; }
+ Metadata *getRawAssignID() const { return DebugValues[AssignIDIdx]; }
LLVM_ABI DIAssignID *getAssignID() const;
DIExpression *getAddressExpression() const { return AddressExpression.get(); }
MDNode *getRawAddressExpression() const {
diff --git a/llvm/include/llvm/IR/Metadata.h b/llvm/include/llvm/IR/Metadata.h
index 51af66ec1809e..017fd98e704ea 100644
--- a/llvm/include/llvm/IR/Metadata.h
+++ b/llvm/include/llvm/IR/Metadata.h
@@ -225,6 +225,8 @@ class DebugValueUser {
// restructure the DbgVariableRecord class then we can template parameterize
// this array size.
std::array<Metadata *, 3> DebugValues;
+ // The slot holding the DIAssignID of a dbg_assign record.
+ static constexpr size_t AssignIDIdx = 2;
ArrayRef<Metadata *> getDebugValues() const { return DebugValues; }
diff --git a/llvm/lib/IR/DebugProgramInstruction.cpp b/llvm/lib/IR/DebugProgramInstruction.cpp
index fe25cd3959bfb..4df30bedf588b 100644
--- a/llvm/lib/IR/DebugProgramInstruction.cpp
+++ b/llvm/lib/IR/DebugProgramInstruction.cpp
@@ -505,11 +505,11 @@ Value *DbgVariableRecord::getAddress() const {
}
DIAssignID *DbgVariableRecord::getAssignID() const {
- return cast<DIAssignID>(DebugValues[2]);
+ return cast<DIAssignID>(DebugValues[AssignIDIdx]);
}
void DbgVariableRecord::setAssignId(DIAssignID *New) {
- resetDebugValue(2, New);
+ resetDebugValue(AssignIDIdx, New);
}
void DbgVariableRecord::setKillAddress() {
diff --git a/llvm/lib/IR/Metadata.cpp b/llvm/lib/IR/Metadata.cpp
index 117757b9514e8..5021b205f1dcd 100644
--- a/llvm/lib/IR/Metadata.cpp
+++ b/llvm/lib/IR/Metadata.cpp
@@ -179,7 +179,7 @@ void DebugValueUser::trackDebugValue(size_t Idx) {
if (!MD)
return;
MetadataTracking::track(&MD, *MD, *this);
- if (auto *ID = Idx == 2 ? dyn_cast<DIAssignID>(MD) : nullptr)
+ if (auto *ID = Idx == AssignIDIdx ? dyn_cast<DIAssignID>(MD) : nullptr)
ID->Records.push_back(getUser());
}
@@ -194,7 +194,7 @@ void DebugValueUser::untrackDebugValue(size_t Idx) {
if (!MD)
return;
MetadataTracking::untrack(MD);
- if (auto *ID = Idx == 2 ? dyn_cast<DIAssignID>(MD) : nullptr)
+ if (auto *ID = Idx == AssignIDIdx ? dyn_cast<DIAssignID>(MD) : nullptr)
ID->Records.erase(llvm::find(ID->Records, getUser()));
}
@@ -208,7 +208,7 @@ void DebugValueUser::retrackDebugValues(DebugValueUser &X) {
for (const auto &[MD, XMD] : zip(DebugValues, X.DebugValues))
if (XMD)
MetadataTracking::retrack(XMD, MD);
- if (auto *ID = dyn_cast_or_null<DIAssignID>(DebugValues[2]))
+ if (auto *ID = dyn_cast_or_null<DIAssignID>(DebugValues[AssignIDIdx]))
*llvm::find(ID->Records, X.getUser()) = getUser();
X.DebugValues.fill(nullptr);
}
More information about the llvm-commits
mailing list