[llvm] [DebugInfo] Add DILayerLoc/DILayerLocList and DILocation irlayers operand (PR #215666)
Alexander Yermolovich via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 30 11:57:20 PDT 2026
https://github.com/ayermolo updated https://github.com/llvm/llvm-project/pull/215666
>From e1383dcd780326e5fbe42d2b8b65d7d619766160 Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolovich at nvidia.com>
Date: Tue, 11 Aug 2026 20:51:44 +0000
Subject: [PATCH 01/12] [DebugInfo] Add DILayerLoc/DILayerLocList and
DILocation irlayers operand
Programs lowered through intermediate IRs lose their position in those IRs by
the time they reach a backend: a DILocation records only the original source
coordinate. Add an optional `irlayers` operand carrying one coordinate per
intermediate level, so a debugger can step through the IR text a program was
actually compiled from.
Layers are independent of inlinedAt; a location may carry either, both, or
neither. The presence flag costs one bit of atomGroup, narrowing it from 61 to
60. Textual IR, bitcode and MIR all round-trip the new operand.
---
llvm/docs/KeyInstructionsDebugInfo.md | 2 +-
llvm/docs/LangRef.md | 49 +++
llvm/include/llvm-c/DebugInfo.h | 2 +
llvm/include/llvm/Bitcode/LLVMBitCodes.h | 2 +
llvm/include/llvm/IR/DebugInfoMetadata.h | 174 +++++++-
llvm/include/llvm/IR/DebugLoc.h | 7 +-
llvm/include/llvm/IR/Metadata.def | 2 +
llvm/lib/AsmParser/LLParser.cpp | 44 +-
llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 9 +-
llvm/lib/Bitcode/Reader/MetadataLoader.cpp | 48 ++-
llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 30 ++
llvm/lib/CodeGen/MIRParser/MIParser.cpp | 13 +-
llvm/lib/IR/AsmWriter.cpp | 23 +
llvm/lib/IR/DebugInfoMetadata.cpp | 131 +++++-
llvm/lib/IR/DebugLoc.cpp | 12 +-
llvm/lib/IR/LLVMContextImpl.h | 64 ++-
llvm/lib/IR/Verifier.cpp | 16 +
.../DirectX/DXILWriter/DXILBitcodeWriter.cpp | 8 +
.../intermediate-loc-metadata-location.ll | 58 +++
.../Bitcode/intermediate-loc-roundtrip.ll | 57 +++
.../instructions-debug-location-irlayers.mir | 64 +++
llvm/test/Verifier/intermediate-layerloc.ll | 37 ++
llvm/unittests/IR/DebugInfoTest.cpp | 402 ++++++++++++++++++
23 files changed, 1194 insertions(+), 60 deletions(-)
create mode 100644 llvm/test/Bitcode/intermediate-loc-metadata-location.ll
create mode 100644 llvm/test/Bitcode/intermediate-loc-roundtrip.ll
create mode 100644 llvm/test/CodeGen/MIR/X86/instructions-debug-location-irlayers.mir
create mode 100644 llvm/test/Verifier/intermediate-layerloc.ll
diff --git a/llvm/docs/KeyInstructionsDebugInfo.md b/llvm/docs/KeyInstructionsDebugInfo.md
index 22f83f2b0a381..0ea9b84c180f1 100644
--- a/llvm/docs/KeyInstructionsDebugInfo.md
+++ b/llvm/docs/KeyInstructionsDebugInfo.md
@@ -45,7 +45,7 @@ From the perspective of a source-level debugger user:
Details:
-1. *The metadata* - The two new `DILocation` fields are `atomGroup` and `atomRank` and are both are unsigned integers. `atomGroup` is 61 bits and `atomRank` 3 bits. Instructions in the same function with the same `(atomGroup, inlinedAt)` pair are part of the same source atom. `atomRank` determines `is_stmt` preference within that group, where a lower number is higher precedence. Higher rank instructions act as "backup" `is_stmt` locations, providing good fallback locations if/when the primary candidate gets optimized away. The default values of 0 indicate the instruction isn’t interesting - it's not an `is_stmt` candidate. If `keyInstructions` in `DISubprogram` is false (default) then the new `DILocation` metadata is ignored for the function (including inlined instances) when emitting DWARF.
+1. *The metadata* - The two new `DILocation` fields are `atomGroup` and `atomRank` and are both are unsigned integers. `atomGroup` is 60 bits and `atomRank` 3 bits; the remaining bit of the 64-bit field is `DILocation`'s `irlayers` flag. A group that does not fit in 60 bits is clamped to 0, so an out-of-range value means "not interesting" rather than silently aliasing another group. Instructions in the same function with the same `(atomGroup, inlinedAt)` pair are part of the same source atom. `atomRank` determines `is_stmt` preference within that group, where a lower number is higher precedence. Higher rank instructions act as "backup" `is_stmt` locations, providing good fallback locations if/when the primary candidate gets optimized away. The default values of 0 indicate the instruction isn’t interesting - it's not an `is_stmt` candidate. If `keyInstructions` in `DISubprogram` is false (default) then the new `DILocation` metadata is ignored for the function (including inlined instances) when emitting DWARF.
2. *Clang annotates key instructions* with the new metadata. Variable assignments (stores, memory intrinsics), control flow (branches and their conditions, some unconditional branches), and exception handling instructions are annotated. Calls are ignored as they're unconditionally marked `is_stmt`.
diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index 3a616e8a29fcf..c1642705ab481 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -7170,6 +7170,55 @@ mandatory, and points at an {ref}`DILexicalBlockFile`, an
!0 = !DILocation(line: 2900, column: 42, scope: !1, inlinedAt: !2)
```
+The optional `irlayers:` field points at a {ref}`DILayerLocList`, giving the
+instruction's position in one or more intermediate IRs it was lowered through, in
+addition to its primary source position. It is independent of `inlinedAt:`; a
+location may have either, both, or neither.
+
+```text
+!0 = !DILocation(line: 2900, column: 42, scope: !1, irlayers: !3)
+```
+
+(DILayerLoc)=
+
+##### DILayerLoc
+
+`DILayerLoc` nodes represent a source position in one intermediate IR level that
+a program was lowered through — for example a tile IR or an MLIR module produced
+part-way through compilation. The `kind:` field names the level and the `file:`
+field points at a {ref}`DIFile` for it; both are mandatory. `line:` and
+`column:` are the position within that file.
+
+Unlike a {ref}`DILocation`, a `DILayerLoc` has no scope and no inlined-at
+context: it is a bare coordinate in a file, not a location in a scope tree.
+
+```text
+!0 = !DILayerLoc(line: 100, column: 1, file: !1, kind: "tile ir")
+```
+
+(DILayerLocList)=
+
+##### DILayerLocList
+
+`DILayerLocList` nodes hold a list of {ref}`DILayerLoc` operands, and are
+referenced by a {ref}`DILocation`'s `irlayers:` field.
+
+Operand order is preserved and is part of the node's identity — two lists with
+the same entries in a different order are different nodes — and a consumer sees
+the entries in that order. LLVM itself attaches no meaning to the order: it does
+not define which level comes first and does not check any particular arrangement,
+so any convention (such as listing levels in lowering order) is an agreement
+between a producer and its consumer.
+
+Both node types are uniqued, so instructions sharing a position at some level
+share the corresponding node.
+
+```text
+!0 = !DILayerLocList(!1, !2)
+!1 = !DILayerLoc(line: 100, column: 1, file: !3, kind: "tile ir")
+!2 = !DILayerLoc(line: 7, column: 3, file: !4, kind: "gpu ir")
+```
+
(DILocalVariable)=
##### DILocalVariable
diff --git a/llvm/include/llvm-c/DebugInfo.h b/llvm/include/llvm-c/DebugInfo.h
index 155cbe32ee15e..21b1e021ff310 100644
--- a/llvm/include/llvm-c/DebugInfo.h
+++ b/llvm/include/llvm-c/DebugInfo.h
@@ -211,6 +211,8 @@ enum {
LLVMDIAssignIDMetadataKind,
LLVMDISubrangeTypeMetadataKind,
LLVMDIFixedPointTypeMetadataKind,
+ LLVMDILayerLocMetadataKind,
+ LLVMDILayerLocListMetadataKind,
};
typedef unsigned LLVMMetadataKind;
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index 358f9a65a80af..3f7db3456ea78 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -400,6 +400,8 @@ enum MetadataCodes {
METADATA_ASSIGN_ID = 47, // [distinct, ...]
METADATA_SUBRANGE_TYPE = 48, // [distinct, ...]
METADATA_FIXED_POINT_TYPE = 49, // [distinct, ...]
+ METADATA_LAYERLOC = 50, // [distinct, line, column, file, kind]
+ METADATA_LAYERLOCLIST = 51, // [distinct, n x layerloc]
};
// The constants block (CONSTANTS_BLOCK_ID) describes emission for each
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index 33a1a6e482555..ab6ad11913495 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -2658,6 +2658,114 @@ class DISubprogram : public DILocalScope {
}
};
+/// A single intermediate-IR layer location.
+///
+/// One source coordinate in an intermediate IR level (e.g. TileIR, MLIR) that
+/// sits between the high-level source and the final LLVM IR. Lightweight: it
+/// references its \a DIFile directly (no scope, no discriminator) and stores
+/// line/column in the free Metadata subclass-data slots. Grouped behind a
+/// \a DILayerLocList on \a DILocation's optional `irlayers` operand.
+///
+/// Uses the SubclassData16 and SubclassData32 Metadata slots.
+class DILayerLoc : public MDNode {
+ friend class LLVMContextImpl;
+ friend class MDNode;
+
+ DILayerLoc(LLVMContext &C, StorageType Storage, unsigned Line,
+ unsigned Column, ArrayRef<Metadata *> Ops)
+ : MDNode(C, DILayerLocKind, Storage, Ops) {
+ assert(Ops.size() == 2 && "Expected {kind, file}");
+ assert(Column < (1u << 16) && "Expected 16-bit column");
+ SubclassData32 = Line;
+ SubclassData16 = Column;
+ }
+ ~DILayerLoc() { dropAllReferences(); }
+
+ static DILayerLoc *getImpl(LLVMContext &Context, MDString *Kind,
+ Metadata *File, unsigned Line, unsigned Column,
+ StorageType Storage, bool ShouldCreate = true);
+
+ TempDILayerLoc cloneImpl() const {
+ return getTemporary(getContext(), getRawKind(), getRawFile(), getLine(),
+ getColumn());
+ }
+
+public:
+ DEFINE_MDNODE_GET(DILayerLoc,
+ (MDString * Kind, Metadata *File, unsigned Line,
+ unsigned Column),
+ (Kind, File, Line, Column))
+
+ TempDILayerLoc clone() const { return cloneImpl(); }
+
+ unsigned getLine() const { return SubclassData32; }
+ unsigned getColumn() const { return SubclassData16; }
+
+ MDString *getRawKind() const {
+ return cast_if_present<MDString>(getOperand(0));
+ }
+ StringRef getKind() const {
+ if (MDString *K = getRawKind())
+ return K->getString();
+ return StringRef();
+ }
+ Metadata *getRawFile() const { return getOperand(1); }
+ DIFile *getFile() const { return cast_if_present<DIFile>(getRawFile()); }
+
+ static bool classof(const Metadata *MD) {
+ return MD->getMetadataID() == DILayerLocKind;
+ }
+};
+
+/// A uniqued list of \a DILayerLoc entries.
+///
+/// The container node behind \a DILocation's optional `irlayers` operand:
+/// essentially an \a MDTuple of \a DILayerLoc refs with typed accessors and its
+/// own metadata kind, so consumers can type-check it with
+/// `isa<DILayerLocList>`. Operand order is preserved and is part of the node's
+/// identity, but LLVM assigns it no meaning.
+class DILayerLocList : public MDNode {
+ friend class LLVMContextImpl;
+ friend class MDNode;
+
+ DILayerLocList(LLVMContext &C, StorageType Storage, unsigned Hash,
+ ArrayRef<Metadata *> Ops)
+ : MDNode(C, DILayerLocListKind, Storage, Ops) {
+ setHash(Hash);
+ }
+ ~DILayerLocList() { dropAllReferences(); }
+
+ void setHash(unsigned Hash) { SubclassData32 = Hash; }
+ void recalculateHash();
+
+ static DILayerLocList *getImpl(LLVMContext &Context,
+ ArrayRef<Metadata *> Layers,
+ StorageType Storage, bool ShouldCreate = true);
+
+ TempDILayerLocList cloneImpl() const {
+ return getTemporary(getContext(), SmallVector<Metadata *, 4>(operands()));
+ }
+
+public:
+ /// Get the operand hash (used by the MDNodeOpsKey uniquing key).
+ unsigned getHash() const { return SubclassData32; }
+
+ DEFINE_MDNODE_GET(DILayerLocList, (ArrayRef<Metadata *> Layers), (Layers))
+
+ TempDILayerLocList clone() const { return cloneImpl(); }
+
+ unsigned getNumLayers() const { return getNumOperands(); }
+ DILayerLoc *getLayer(unsigned I) const {
+ return cast_if_present<DILayerLoc>(getOperand(I));
+ }
+ using layer_iterator = MDNode::op_iterator;
+ iterator_range<layer_iterator> layers() const { return operands(); }
+
+ static bool classof(const Metadata *MD) {
+ return MD->getMetadataID() == DILayerLocListKind;
+ }
+};
+
/// Debug location.
///
/// A debug location in source code, used for debug info and otherwise.
@@ -2668,34 +2776,40 @@ class DISubprogram : public DILocalScope {
class DILocation : public MDNode {
friend class LLVMContextImpl;
friend class MDNode;
- uint64_t AtomGroup : 61;
+ uint64_t AtomGroup : 60;
uint64_t AtomRank : 3;
+ // Disambiguates the two optional trailing operands, layout
+ // [scope, (inlinedAt?), (irlayers?)]: irlayers is always last when present.
+ uint64_t HasIRLayers : 1;
DILocation(LLVMContext &C, StorageType Storage, unsigned Line,
unsigned Column, uint64_t AtomGroup, uint8_t AtomRank,
- ArrayRef<Metadata *> MDs, bool ImplicitCode);
+ bool HasIRLayers, ArrayRef<Metadata *> MDs, bool ImplicitCode);
~DILocation() { dropAllReferences(); }
- LLVM_ABI static DILocation *
- getImpl(LLVMContext &Context, unsigned Line, unsigned Column, Metadata *Scope,
- Metadata *InlinedAt, bool ImplicitCode, uint64_t AtomGroup,
- uint8_t AtomRank, StorageType Storage, bool ShouldCreate = true);
+ LLVM_ABI static DILocation *getImpl(LLVMContext &Context, unsigned Line,
+ unsigned Column, Metadata *Scope,
+ Metadata *InlinedAt, bool ImplicitCode,
+ uint64_t AtomGroup, uint8_t AtomRank,
+ Metadata *IRLayers, StorageType Storage,
+ bool ShouldCreate = true);
static DILocation *getImpl(LLVMContext &Context, unsigned Line,
unsigned Column, DILocalScope *Scope,
DILocation *InlinedAt, bool ImplicitCode,
uint64_t AtomGroup, uint8_t AtomRank,
- StorageType Storage, bool ShouldCreate = true) {
+ Metadata *IRLayers, StorageType Storage,
+ bool ShouldCreate = true) {
return getImpl(Context, Line, Column, static_cast<Metadata *>(Scope),
static_cast<Metadata *>(InlinedAt), ImplicitCode, AtomGroup,
- AtomRank, Storage, ShouldCreate);
+ AtomRank, IRLayers, Storage, ShouldCreate);
}
TempDILocation cloneImpl() const {
- // Get the raw scope/inlinedAt since it is possible to invoke this on
- // a DILocation containing temporary metadata.
+ // Get the raw scope/inlinedAt/irlayers since it is possible to invoke this
+ // on a DILocation containing temporary metadata.
return getTemporary(getContext(), getLine(), getColumn(), getRawScope(),
getRawInlinedAt(), isImplicitCode(), getAtomGroup(),
- getAtomRank());
+ getAtomRank(), getRawIRLayers());
}
public:
@@ -2706,7 +2820,8 @@ class DILocation : public MDNode {
if (!getAtomGroup() && !getAtomRank())
return this;
return get(getContext(), getLine(), getColumn(), getScope(), getInlinedAt(),
- isImplicitCode());
+ isImplicitCode(), /*AtomGroup=*/0, /*AtomRank=*/0,
+ getRawIRLayers());
}
// Disallow replacing operands.
@@ -2715,15 +2830,17 @@ class DILocation : public MDNode {
DEFINE_MDNODE_GET(DILocation,
(unsigned Line, unsigned Column, Metadata *Scope,
Metadata *InlinedAt = nullptr, bool ImplicitCode = false,
- uint64_t AtomGroup = 0, uint8_t AtomRank = 0),
+ uint64_t AtomGroup = 0, uint8_t AtomRank = 0,
+ Metadata *IRLayers = nullptr),
(Line, Column, Scope, InlinedAt, ImplicitCode, AtomGroup,
- AtomRank))
+ AtomRank, IRLayers))
DEFINE_MDNODE_GET(DILocation,
(unsigned Line, unsigned Column, DILocalScope *Scope,
DILocation *InlinedAt = nullptr, bool ImplicitCode = false,
- uint64_t AtomGroup = 0, uint8_t AtomRank = 0),
+ uint64_t AtomGroup = 0, uint8_t AtomRank = 0,
+ Metadata *IRLayers = nullptr),
(Line, Column, Scope, InlinedAt, ImplicitCode, AtomGroup,
- AtomRank))
+ AtomRank, IRLayers))
/// Return a (temporary) clone of this.
TempDILocation clone() const { return cloneImpl(); }
@@ -2945,11 +3062,32 @@ class DILocation : public MDNode {
Metadata *getRawScope() const { return getOperand(0); }
Metadata *getRawInlinedAt() const {
- if (getNumOperands() == 2)
+ // Layout: [scope, (inlinedAt?), (irlayers?)]. irlayers, when present, is
+ // always the last operand; discount it before the inlinedAt count trick.
+ unsigned NonLayerOps = getNumOperands() - (HasIRLayers ? 1 : 0);
+ if (NonLayerOps == 2)
return getOperand(1);
return nullptr;
}
+ /// The optional intermediate-IR layer list (\a DILayerLocList), or null.
+ /// Raw form: returns the operand without casting, so it is safe to call
+ /// before forward-ref resolution (the operand may still be a placeholder).
+ Metadata *getRawIRLayers() const {
+ return HasIRLayers ? getOperand(getNumOperands() - 1) : nullptr;
+ }
+ DILayerLocList *getIRLayers() const {
+ return cast_if_present<DILayerLocList>(getRawIRLayers());
+ }
+ unsigned getNumLayers() const {
+ DILayerLocList *L = getIRLayers();
+ return L ? L->getNumLayers() : 0;
+ }
+ DILayerLoc *getLayer(unsigned I) const {
+ DILayerLocList *L = getIRLayers();
+ return L ? L->getLayer(I) : nullptr;
+ }
+
static bool classof(const Metadata *MD) {
return MD->getMetadataID() == DILocationKind;
}
@@ -3103,7 +3241,7 @@ DILocation::cloneWithDiscriminator(unsigned Discriminator) const {
DILexicalBlockFile::get(getContext(), Scope, getFile(), Discriminator);
return DILocation::get(getContext(), getLine(), getColumn(), NewScope,
getInlinedAt(), isImplicitCode(), getAtomGroup(),
- getAtomRank());
+ getAtomRank(), getRawIRLayers());
}
unsigned DILocation::getBaseDiscriminator() const {
diff --git a/llvm/include/llvm/IR/DebugLoc.h b/llvm/include/llvm/IR/DebugLoc.h
index 34e6aadd83cae..c9a5149c80940 100644
--- a/llvm/include/llvm/IR/DebugLoc.h
+++ b/llvm/include/llvm/IR/DebugLoc.h
@@ -246,13 +246,18 @@ class DebugLoc {
return true;
return ((bool)*this == (bool)Other) && getLine() == Other.getLine() &&
getCol() == Other.getCol() && getScope() == Other.getScope() &&
- getInlinedAt() == Other.getInlinedAt();
+ getInlinedAt() == Other.getInlinedAt() &&
+ getRawIRLayers() == Other.getRawIRLayers();
}
LLVM_ABI unsigned getLine() const;
LLVM_ABI unsigned getCol() const;
LLVM_ABI MDNode *getScope() const;
LLVM_ABI DILocation *getInlinedAt() const;
+ /// The raw intermediate-IR layer list (\a DILayerLocList) of the underlying
+ /// location, or null. Out-of-line so this header need not see DILocation's
+ /// definition.
+ LLVM_ABI MDNode *getRawIRLayers() const;
/// Get the fully inlined-at scope for a DebugLoc.
///
diff --git a/llvm/include/llvm/IR/Metadata.def b/llvm/include/llvm/IR/Metadata.def
index 511bf48707f00..80817892d0d99 100644
--- a/llvm/include/llvm/IR/Metadata.def
+++ b/llvm/include/llvm/IR/Metadata.def
@@ -81,6 +81,8 @@ HANDLE_METADATA_LEAF(DIArgList)
HANDLE_MDNODE_BRANCH(MDNode)
HANDLE_MDNODE_LEAF_UNIQUABLE(MDTuple)
HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DILocation)
+HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DILayerLoc)
+HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DILayerLocList)
HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIExpression)
HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIGlobalVariableExpression)
HANDLE_SPECIALIZED_MDNODE_BRANCH(DINode)
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index edff818b3b152..34d583820227f 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -5784,13 +5784,49 @@ bool LLParser::parseDILocation(MDNode *&Result, bool IsDistinct) {
OPTIONAL(inlinedAt, MDField, ); \
OPTIONAL(isImplicitCode, MDBoolField, (false)); \
OPTIONAL(atomGroup, MDUnsignedField, (0, UINT64_MAX)); \
- OPTIONAL(atomRank, MDUnsignedField, (0, UINT8_MAX));
+ OPTIONAL(atomRank, MDUnsignedField, (0, UINT8_MAX)); \
+ OPTIONAL(irlayers, MDField, );
PARSE_MD_FIELDS();
#undef VISIT_MD_FIELDS
- Result = GET_OR_DISTINCT(
- DILocation, (Context, line.Val, column.Val, scope.Val, inlinedAt.Val,
- isImplicitCode.Val, atomGroup.Val, atomRank.Val));
+ Result =
+ GET_OR_DISTINCT(DILocation, (Context, line.Val, column.Val, scope.Val,
+ inlinedAt.Val, isImplicitCode.Val,
+ atomGroup.Val, atomRank.Val, irlayers.Val));
+ return false;
+}
+
+bool LLParser::parseDILayerLoc(MDNode *&Result, bool IsDistinct) {
+#define VISIT_MD_FIELDS(OPTIONAL, REQUIRED) \
+ OPTIONAL(line, LineField, ); \
+ OPTIONAL(column, ColumnField, ); \
+ REQUIRED(file, MDField, (/* AllowNull */ false)); \
+ REQUIRED(kind, MDStringField, );
+ PARSE_MD_FIELDS();
+#undef VISIT_MD_FIELDS
+
+ Result = GET_OR_DISTINCT(DILayerLoc,
+ (Context, kind.Val, file.Val, line.Val, column.Val));
+ return false;
+}
+
+bool LLParser::parseDILayerLocList(MDNode *&Result, bool IsDistinct) {
+ // ::= !DILayerLocList(!a, !b, ...)
+ Lex.Lex(); // eat the '!DILayerLocList' type name
+ if (parseToken(lltok::lparen, "expected '(' here"))
+ return true;
+ SmallVector<Metadata *, 4> Layers;
+ if (!EatIfPresent(lltok::rparen)) {
+ do {
+ Metadata *MD;
+ if (parseMetadata(MD, nullptr))
+ return true;
+ Layers.push_back(MD);
+ } while (EatIfPresent(lltok::comma));
+ if (parseToken(lltok::rparen, "expected ')' here"))
+ return true;
+ }
+ Result = GET_OR_DISTINCT(DILayerLocList, (Context, Layers));
return false;
}
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index ac61ede6395af..b093854488ae0 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -5241,8 +5241,8 @@ Error BitcodeReader::parseFunctionBody(Function *F) {
unsigned Line = Record[0], Col = Record[1];
unsigned ScopeID = Record[2], IAID = Record[3];
bool isImplicitCode = Record.size() >= 5 && Record[4];
- uint64_t AtomGroup = Record.size() == 7 ? Record[5] : 0;
- uint8_t AtomRank = Record.size() == 7 ? Record[6] : 0;
+ uint64_t AtomGroup = Record.size() >= 7 ? Record[5] : 0;
+ uint8_t AtomRank = Record.size() >= 7 ? Record[6] : 0;
MDNode *Scope = nullptr, *IA = nullptr;
if (ScopeID) {
@@ -5257,9 +5257,12 @@ Error BitcodeReader::parseFunctionBody(Function *F) {
if (!IA)
return error("Invalid debug loc record");
}
+ Metadata *IRLayers = nullptr;
+ if (Record.size() >= 8 && Record[7])
+ IRLayers = MDLoader->getMetadataFwdRefOrLoad(Record[7] - 1);
LastLoc = DILocation::get(Scope->getContext(), Line, Col, Scope, IA,
- isImplicitCode, AtomGroup, AtomRank);
+ isImplicitCode, AtomGroup, AtomRank, IRLayers);
I->setDebugLoc(LastLoc);
I = nullptr;
continue;
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index 8b7beb1a8ff9e..4fa47f7db0e21 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -1472,8 +1472,12 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
break;
}
case bitc::METADATA_LOCATION: {
- // 5: inlinedAt, 6: isImplicit, 8: Key Instructions fields.
- if (Record.size() != 5 && Record.size() != 6 && Record.size() != 8)
+ // Defined shapes: 5 (through inlinedAt), 6 (isImplicit), 8 (adds the Key
+ // Instructions fields), 9 (adds irlayers). Anything longer is a record from
+ // a newer writer and its trailing fields are ignored, which is what makes
+ // this encoding append-only; anything shorter, or a 7 (an atomGroup with no
+ // atomRank), is a shape no writer produces.
+ if (Record.size() < 5 || Record.size() == 7)
return error("Invalid record");
IsDistinct = Record[0];
@@ -1482,15 +1486,47 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
Metadata *Scope = getMD(Record[3]);
Metadata *InlinedAt = getMDOrNull(Record[4]);
bool ImplicitCode = Record.size() >= 6 && Record[5];
- uint64_t AtomGroup = Record.size() == 8 ? Record[6] : 0;
- uint8_t AtomRank = Record.size() == 8 ? Record[7] : 0;
+ uint64_t AtomGroup = Record.size() >= 8 ? Record[6] : 0;
+ uint8_t AtomRank = Record.size() >= 8 ? Record[7] : 0;
+ Metadata *IRLayers = Record.size() >= 9 ? getMDOrNull(Record[8]) : nullptr;
MetadataList.assignValue(
- GET_OR_DISTINCT(DILocation, (Context, Line, Column, Scope, InlinedAt,
- ImplicitCode, AtomGroup, AtomRank)),
+ GET_OR_DISTINCT(DILocation,
+ (Context, Line, Column, Scope, InlinedAt, ImplicitCode,
+ AtomGroup, AtomRank, IRLayers)),
NextMetadataNo);
NextMetadataNo++;
break;
}
+ case bitc::METADATA_LAYERLOC: {
+ if (Record.size() != 5)
+ return error("Invalid record");
+
+ IsDistinct = Record[0];
+ unsigned Line = Record[1];
+ unsigned Column = Record[2];
+ Metadata *File = getMD(Record[3]);
+ // Read the kind opaquely and let the verifier report a bad type, as the
+ // other DI readers do: an unchecked cast would assert on malformed bitcode.
+ MDString *Kind = dyn_cast_if_present<MDString>(getMD(Record[4]));
+ MetadataList.assignValue(
+ GET_OR_DISTINCT(DILayerLoc, (Context, Kind, File, Line, Column)),
+ NextMetadataNo);
+ NextMetadataNo++;
+ break;
+ }
+ case bitc::METADATA_LAYERLOCLIST: {
+ if (Record.empty())
+ return error("Invalid record");
+
+ IsDistinct = Record[0];
+ SmallVector<Metadata *, 4> Elts;
+ for (unsigned I = 1, E = Record.size(); I != E; ++I)
+ Elts.push_back(getMDOrNull(Record[I]));
+ MetadataList.assignValue(GET_OR_DISTINCT(DILayerLocList, (Context, Elts)),
+ NextMetadataNo);
+ NextMetadataNo++;
+ break;
+ }
case bitc::METADATA_GENERIC_DEBUG: {
if (Record.size() < 4)
return error("Invalid record");
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 571336c217797..4edba342fed48 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -340,6 +340,10 @@ class ModuleBitcodeWriter : public ModuleBitcodeWriterBase {
unsigned createDILocationAbbrev();
void writeDILocation(const DILocation *N, SmallVectorImpl<uint64_t> &Record,
unsigned &Abbrev);
+ void writeDILayerLoc(const DILayerLoc *N, SmallVectorImpl<uint64_t> &Record,
+ unsigned Abbrev);
+ void writeDILayerLocList(const DILayerLocList *N,
+ SmallVectorImpl<uint64_t> &Record, unsigned Abbrev);
unsigned createGenericDINodeAbbrev();
void writeGenericDINode(const GenericDINode *N,
SmallVectorImpl<uint64_t> &Record, unsigned &Abbrev);
@@ -1898,6 +1902,7 @@ unsigned ModuleBitcodeWriter::createDILocationAbbrev() {
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isImplicitCode
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // atomGroup
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // atomRank
+ Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // irlayers (0 = none)
return Stream.EmitAbbrev(std::move(Abbv));
}
@@ -1915,10 +1920,33 @@ void ModuleBitcodeWriter::writeDILocation(const DILocation *N,
Record.push_back(N->isImplicitCode());
Record.push_back(N->getAtomGroup());
Record.push_back(N->getAtomRank());
+ Record.push_back(VE.getMetadataOrNullID(N->getRawIRLayers()));
Stream.EmitRecord(bitc::METADATA_LOCATION, Record, Abbrev);
Record.clear();
}
+void ModuleBitcodeWriter::writeDILayerLoc(const DILayerLoc *N,
+ SmallVectorImpl<uint64_t> &Record,
+ unsigned Abbrev) {
+ Record.push_back(N->isDistinct());
+ Record.push_back(N->getLine());
+ Record.push_back(N->getColumn());
+ Record.push_back(VE.getMetadataID(N->getRawFile()));
+ Record.push_back(VE.getMetadataID(N->getRawKind()));
+ Stream.EmitRecord(bitc::METADATA_LAYERLOC, Record, Abbrev);
+ Record.clear();
+}
+
+void ModuleBitcodeWriter::writeDILayerLocList(const DILayerLocList *N,
+ SmallVectorImpl<uint64_t> &Record,
+ unsigned Abbrev) {
+ Record.push_back(N->isDistinct());
+ for (auto &I : N->operands())
+ Record.push_back(VE.getMetadataOrNullID(I));
+ Stream.EmitRecord(bitc::METADATA_LAYERLOCLIST, Record, Abbrev);
+ Record.clear();
+}
+
unsigned ModuleBitcodeWriter::createGenericDINodeAbbrev() {
// Assume the column is usually under 128, and always output the inlined-at
// location (it's never more expensive than building an array size 1).
@@ -3873,6 +3901,7 @@ void ModuleBitcodeWriter::writeFunction(
Vals.push_back(DL->isImplicitCode());
Vals.push_back(DL->getAtomGroup());
Vals.push_back(DL->getAtomRank());
+ Vals.push_back(VE.getMetadataOrNullID(DL->getRawIRLayers()));
Stream.EmitRecord(bitc::FUNC_CODE_DEBUG_LOC, Vals,
FUNCTION_DEBUG_LOC_ABBREV);
Vals.clear();
@@ -4281,6 +4310,7 @@ void ModuleBitcodeWriter::writeBlockInfo() {
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1));
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Atom group.
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 3)); // Atom rank.
+ Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // irlayers (0 = none).
if (Stream.EmitBlockInfoAbbrev(bitc::FUNCTION_BLOCK_ID, Abbv) !=
FUNCTION_DEBUG_LOC_ABBREV)
llvm_unreachable("Unexpected abbrev ordering!");
diff --git a/llvm/lib/CodeGen/MIRParser/MIParser.cpp b/llvm/lib/CodeGen/MIRParser/MIParser.cpp
index bb0b87cc042d0..71be0869ef244 100644
--- a/llvm/lib/CodeGen/MIRParser/MIParser.cpp
+++ b/llvm/lib/CodeGen/MIRParser/MIParser.cpp
@@ -2573,6 +2573,7 @@ bool MIParser::parseDILocation(MDNode *&Loc) {
bool ImplicitCode = false;
uint64_t AtomGroup = 0;
uint64_t AtomRank = 0;
+ MDNode *IRLayers = nullptr;
if (expectAndConsume(MIToken::lparen))
return true;
@@ -2670,6 +2671,16 @@ bool MIParser::parseDILocation(MDNode *&Loc) {
lex();
continue;
}
+ if (Token.stringValue() == "irlayers") {
+ lex();
+ if (expectAndConsume(MIToken::colon))
+ return true;
+ if (parseMDNode(IRLayers))
+ return error("expected metadata node");
+ if (!isa<DILayerLocList>(IRLayers))
+ return error("expected DILayerLocList node");
+ continue;
+ }
}
return error(Twine("invalid DILocation argument '") +
Token.stringValue() + "'");
@@ -2685,7 +2696,7 @@ bool MIParser::parseDILocation(MDNode *&Loc) {
return error("DILocation requires a scope");
Loc = DILocation::get(MF.getFunction().getContext(), Line, Column, Scope,
- InlinedAt, ImplicitCode, AtomGroup, AtomRank);
+ InlinedAt, ImplicitCode, AtomGroup, AtomRank, IRLayers);
return false;
}
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index cad4f17b0db91..132a08b7a9dc7 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -2124,6 +2124,29 @@ static void writeDILocation(raw_ostream &Out, const DILocation *DL,
/* Default */ false);
Printer.printInt("atomGroup", DL->getAtomGroup());
Printer.printInt<unsigned>("atomRank", DL->getAtomRank());
+ Printer.printMetadata("irlayers", DL->getRawIRLayers());
+ Out << ")";
+}
+
+static void writeDILayerLoc(raw_ostream &Out, const DILayerLoc *N,
+ AsmWriterContext &WriterCtx) {
+ Out << "!DILayerLoc(";
+ MDFieldPrinter Printer(Out, WriterCtx);
+ Printer.printInt("line", N->getLine(), /* ShouldSkipZero */ false);
+ Printer.printInt("column", N->getColumn());
+ Printer.printMetadata("file", N->getRawFile(), /* ShouldSkipNull */ false);
+ Printer.printString("kind", N->getKind(), /* ShouldSkipEmpty */ false);
+ Out << ")";
+}
+
+static void writeDILayerLocList(raw_ostream &Out, const DILayerLocList *N,
+ AsmWriterContext &WriterCtx) {
+ Out << "!DILayerLocList(";
+ ListSeparator FS;
+ for (const MDOperand &Op : N->operands()) {
+ Out << FS;
+ writeMetadataAsOperand(Out, Op, WriterCtx);
+ }
Out << ")";
}
diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 5e9e0e4e81c6e..779742f5a02da 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -13,6 +13,7 @@
#include "llvm/IR/DebugInfoMetadata.h"
#include "LLVMContextImpl.h"
#include "MetadataImpl.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/BinaryFormat/Dwarf.h"
@@ -26,6 +27,7 @@
#include <numeric>
#include <optional>
+#include <tuple>
using namespace llvm;
@@ -60,15 +62,17 @@ DebugVariableAggregate::DebugVariableAggregate(const DbgVariableRecord *DVR)
DILocation::DILocation(LLVMContext &C, StorageType Storage, unsigned Line,
unsigned Column, uint64_t AtomGroup, uint8_t AtomRank,
- ArrayRef<Metadata *> MDs, bool ImplicitCode)
+ bool HasIRLayers, ArrayRef<Metadata *> MDs,
+ bool ImplicitCode)
: MDNode(C, DILocationKind, Storage, MDs), AtomGroup(AtomGroup),
- AtomRank(AtomRank) {
+ AtomRank(AtomRank), HasIRLayers(HasIRLayers) {
assert(AtomRank <= 7 && "AtomRank number should fit in 3 bits");
+ assert(AtomGroup < (1ULL << 60) && "AtomGroup number should fit in 60 bits");
if (AtomGroup)
C.updateDILocationAtomGroupWaterline(AtomGroup + 1);
- assert((MDs.size() == 1 || MDs.size() == 2) &&
- "Expected a scope and optional inlined-at");
+ assert(MDs.size() >= 1 && MDs.size() <= 3 &&
+ "Expected a scope and optional inlined-at + irlayers");
// Set line and column.
assert(Column < (1u << 16) && "Expected 16-bit column");
@@ -88,15 +92,20 @@ DILocation *DILocation::getImpl(LLVMContext &Context, unsigned Line,
unsigned Column, Metadata *Scope,
Metadata *InlinedAt, bool ImplicitCode,
uint64_t AtomGroup, uint8_t AtomRank,
- StorageType Storage, bool ShouldCreate) {
+ Metadata *IRLayers, StorageType Storage,
+ bool ShouldCreate) {
// Fixup column.
adjustColumn(Column);
+ // Clamp rather than truncate, which would wrap into a different valid group.
+ if (AtomGroup >= (1ULL << 60))
+ AtomGroup = 0;
+
if (Storage == Uniqued) {
- if (auto *N = getUniqued(Context.pImpl->DILocations,
- DILocationInfo::KeyTy(Line, Column, Scope,
- InlinedAt, ImplicitCode,
- AtomGroup, AtomRank)))
+ if (auto *N = getUniqued(
+ Context.pImpl->DILocations,
+ DILocationInfo::KeyTy(Line, Column, Scope, InlinedAt, ImplicitCode,
+ AtomGroup, AtomRank, IRLayers)))
return N;
if (!ShouldCreate)
return nullptr;
@@ -104,16 +113,62 @@ DILocation *DILocation::getImpl(LLVMContext &Context, unsigned Line,
assert(ShouldCreate && "Expected non-uniqued nodes to always be created");
}
- SmallVector<Metadata *, 2> Ops;
+ SmallVector<Metadata *, 3> Ops;
Ops.push_back(Scope);
if (InlinedAt)
Ops.push_back(InlinedAt);
- return storeImpl(new (Ops.size(), Storage)
- DILocation(Context, Storage, Line, Column, AtomGroup,
- AtomRank, Ops, ImplicitCode),
+ if (IRLayers)
+ Ops.push_back(IRLayers);
+ return storeImpl(new (Ops.size(), Storage) DILocation(
+ Context, Storage, Line, Column, AtomGroup, AtomRank,
+ /*HasIRLayers=*/IRLayers != nullptr, Ops, ImplicitCode),
Storage, Context.pImpl->DILocations);
}
+DILayerLoc *DILayerLoc::getImpl(LLVMContext &Context, MDString *Kind,
+ Metadata *File, unsigned Line, unsigned Column,
+ StorageType Storage, bool ShouldCreate) {
+ // Clamp an out-of-range column to 0 (the 16-bit SubclassData limit), as
+ // DILocation::getImpl does; the ctor otherwise asserts and release builds
+ // truncate.
+ adjustColumn(Column);
+ if (Storage == Uniqued) {
+ if (auto *N = getUniqued(Context.pImpl->DILayerLocs,
+ DILayerLocInfo::KeyTy(Kind, File, Line, Column)))
+ return N;
+ if (!ShouldCreate)
+ return nullptr;
+ }
+ Metadata *Ops[] = {Kind, File};
+ return storeImpl(new (std::size(Ops), Storage)
+ DILayerLoc(Context, Storage, Line, Column, Ops),
+ Storage, Context.pImpl->DILayerLocs);
+}
+
+DILayerLocList *DILayerLocList::getImpl(LLVMContext &Context,
+ ArrayRef<Metadata *> Layers,
+ StorageType Storage,
+ bool ShouldCreate) {
+ unsigned Hash = 0;
+ if (Storage == Uniqued) {
+ DILayerLocListInfo::KeyTy Key(Layers);
+ if (auto *N = getUniqued(Context.pImpl->DILayerLocLists, Key))
+ return N;
+ if (!ShouldCreate)
+ return nullptr;
+ Hash = Key.getHash();
+ } else {
+ assert(ShouldCreate && "Expected non-uniqued nodes to always be created");
+ }
+ return storeImpl(new (Layers.size(), Storage)
+ DILayerLocList(Context, Storage, Hash, Layers),
+ Storage, Context.pImpl->DILayerLocLists);
+}
+
+void DILayerLocList::recalculateHash() {
+ setHash(DILayerLocListInfo::KeyTy::calculateHash(this));
+}
+
DILocation *DILocation::getMergedLocations(ArrayRef<DILocation *> Locs) {
if (Locs.empty())
return nullptr;
@@ -220,6 +275,40 @@ struct ScopeLocationsMatcher {
}
};
+// Returns a uniqued DILayerLocList holding the intersection of LocA's and
+// LocB's layer sets, or null if either has no layers or they share none.
+// Entries keep LocA's relative order: LLVM assigns no meaning to layer order,
+// but it is part of a list's identity and visible to consumers, so preserve
+// rather than sort.
+static Metadata *mergeIRLayers(LLVMContext &C, const DILocation *LocA,
+ const DILocation *LocB) {
+ DILayerLocList *LA = LocA->getIRLayers();
+ DILayerLocList *LB = LocB->getIRLayers();
+ if (!LA || !LB)
+ return nullptr;
+ // Entries match on their fields rather than by pointer: DILayerLocs are
+ // uniqued, but `distinct` ones are legal and must not look disjoint. Keying
+ // on the fields also keeps the intersection linear in the two list lengths.
+ using LayerKey = std::tuple<Metadata *, Metadata *, unsigned, unsigned>;
+ auto keyOf = [](const DILayerLoc *L) {
+ return LayerKey(L->getRawKind(), L->getRawFile(), L->getLine(),
+ L->getColumn());
+ };
+ SmallDenseSet<LayerKey, 2> BLayers;
+ for (unsigned I = 0, E = LB->getNumLayers(); I != E; ++I)
+ if (const DILayerLoc *R = LB->getLayer(I))
+ BLayers.insert(keyOf(R));
+ SmallVector<Metadata *, 2> Keep;
+ for (unsigned I = 0, E = LA->getNumLayers(); I != E; ++I) {
+ DILayerLoc *L = LA->getLayer(I);
+ if (L && BLayers.contains(keyOf(L)))
+ Keep.push_back(L);
+ }
+ if (Keep.empty())
+ return nullptr;
+ return DILayerLocList::get(C, Keep);
+}
+
DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) {
if (LocA == LocB)
return LocA;
@@ -304,13 +393,19 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) {
if (L1 == L2)
return DILocation::get(C, L1->getLine(), L1->getColumn(), L1->getScope(),
InlinedAt, L1->isImplicitCode(),
- L1->getAtomGroup(), L1->getAtomRank());
+ L1->getAtomGroup(), L1->getAtomRank(),
+ L1->getRawIRLayers());
// If the locations originate from different subprograms we can't produce
// a common location.
if (L1->getScope()->getSubprogram() != L2->getScope()->getSubprogram())
return nullptr;
+ // Each merged frame keeps the intersection of the two frames'
+ // intermediate-IR layer sets, so a layer on any frame (e.g. the shared
+ // outermost kernel frame) survives the merge.
+ Metadata *MergedLayers = mergeIRLayers(C, L1, L2);
+
// Find nearest common scope inside subprogram.
DIScope *Scope = getNearestMatchingScope<EqualScopesMatcher>(L1, L2).first;
assert(Scope && "No common scope in the same subprogram?");
@@ -333,7 +428,9 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) {
// from CommonLoc. Use it as merged location.
if (Scope->getFile() != L1->getFile() || L1->getFile() != L2->getFile())
return DILocation::get(C, CommonLoc.first, CommonLoc.second,
- CommonLocScope, InlinedAt);
+ CommonLocScope, InlinedAt,
+ /*ImplicitCode=*/false, /*AtomGroup=*/0,
+ /*AtomRank=*/0, MergedLayers);
}
bool SameLine = L1->getLine() == L2->getLine();
@@ -346,7 +443,7 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) {
// further to do if neither location has an atom number.
if (!SameLine || !(L1->getAtomGroup() || L2->getAtomGroup()))
return DILocation::get(C, Line, Col, Scope, InlinedAt, IsImplicitCode,
- /*AtomGroup*/ 0, /*AtomRank*/ 0);
+ /*AtomGroup*/ 0, /*AtomRank*/ 0, MergedLayers);
uint64_t Group = 0;
uint64_t Rank = 0;
@@ -380,7 +477,7 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) {
Rank = 1;
}
return DILocation::get(C, Line, Col, Scope, InlinedAt, IsImplicitCode,
- Group, Rank);
+ Group, Rank, MergedLayers);
};
DILocation *Result = ARIt != ALocs.rend() ? (*ARIt)->getInlinedAt() : nullptr;
diff --git a/llvm/lib/IR/DebugLoc.cpp b/llvm/lib/IR/DebugLoc.cpp
index 16a82c2a05208..87bbb91acb97f 100644
--- a/llvm/lib/IR/DebugLoc.cpp
+++ b/llvm/lib/IR/DebugLoc.cpp
@@ -60,6 +60,11 @@ DILocation *DebugLoc::getInlinedAt() const {
return get()->getInlinedAt();
}
+MDNode *DebugLoc::getRawIRLayers() const {
+ DILocation *L = get();
+ return L ? cast_if_present<MDNode>(L->getRawIRLayers()) : nullptr;
+}
+
MDNode *DebugLoc::getInlinedAtScope() const {
return cast<DILocation>(Loc)->getInlinedAtScope();
}
@@ -149,9 +154,14 @@ DebugLoc DebugLoc::appendInlinedAt(const DebugLoc &DL, DILocation *InlinedAt,
// location (then rebuilding the rest of the chain behind it) and update the
// map of already-constructed inlined-at nodes.
// Key Instructions: InlinedAt fields don't need atom info.
+ // Preserve each frame's irlayers -- the intermediate-IR snapshot can
+ // live on any frame (the one outermost at snapshot time), so the chain
+ // rebuild must not drop it.
for (const DILocation *MD : reverse(InlinedAtLocations))
Cache[MD] = Last = DILocation::getDistinct(
- Ctx, MD->getLine(), MD->getColumn(), MD->getScope(), Last);
+ Ctx, MD->getLine(), MD->getColumn(), MD->getScope(), Last,
+ /*ImplicitCode=*/false, /*AtomGroup=*/0, /*AtomRank=*/0,
+ MD->getRawIRLayers());
return Last;
}
diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h
index 41c8a92c56eda..b7736dd84a714 100644
--- a/llvm/lib/IR/LLVMContextImpl.h
+++ b/llvm/lib/IR/LLVMContextImpl.h
@@ -277,7 +277,8 @@ template <> struct MDNodeKeyImpl<MDTuple> : MDNodeOpsKey {
template <> struct MDNodeKeyImpl<DILocation> {
Metadata *Scope;
Metadata *InlinedAt;
- uint64_t AtomGroup : 61;
+ Metadata *IRLayers;
+ uint64_t AtomGroup : 60;
uint64_t AtomRank : 3;
unsigned Line;
uint16_t Column;
@@ -285,22 +286,23 @@ template <> struct MDNodeKeyImpl<DILocation> {
MDNodeKeyImpl(unsigned Line, uint16_t Column, Metadata *Scope,
Metadata *InlinedAt, bool ImplicitCode, uint64_t AtomGroup,
- uint8_t AtomRank)
- : Scope(Scope), InlinedAt(InlinedAt), AtomGroup(AtomGroup),
- AtomRank(AtomRank), Line(Line), Column(Column),
+ uint8_t AtomRank, Metadata *IRLayers)
+ : Scope(Scope), InlinedAt(InlinedAt), IRLayers(IRLayers),
+ AtomGroup(AtomGroup), AtomRank(AtomRank), Line(Line), Column(Column),
ImplicitCode(ImplicitCode) {}
MDNodeKeyImpl(const DILocation *L)
: Scope(L->getRawScope()), InlinedAt(L->getRawInlinedAt()),
- AtomGroup(L->getAtomGroup()), AtomRank(L->getAtomRank()),
- Line(L->getLine()), Column(L->getColumn()),
+ IRLayers(L->getRawIRLayers()), AtomGroup(L->getAtomGroup()),
+ AtomRank(L->getAtomRank()), Line(L->getLine()), Column(L->getColumn()),
ImplicitCode(L->isImplicitCode()) {}
bool isKeyOf(const DILocation *RHS) const {
return Line == RHS->getLine() && Column == RHS->getColumn() &&
Scope == RHS->getRawScope() && InlinedAt == RHS->getRawInlinedAt() &&
ImplicitCode == RHS->isImplicitCode() &&
- AtomGroup == RHS->getAtomGroup() && AtomRank == RHS->getAtomRank();
+ AtomGroup == RHS->getAtomGroup() && AtomRank == RHS->getAtomRank() &&
+ IRLayers == RHS->getRawIRLayers();
}
unsigned getHashValue() const {
@@ -313,13 +315,59 @@ template <> struct MDNodeKeyImpl<DILocation> {
// messing with the hash distribution* appear to still be massively
// outweighed by the overall compile time savings by performing this check.
// * (hash_combine(x) != hash_combine(x, 0))
- if (AtomGroup || AtomRank)
+ // irlayers is likewise rare, so it is only mixed in when present to keep
+ // the common no-layers hashes unchanged.
+ if (AtomGroup || AtomRank) {
+ if (IRLayers)
+ return hash_combine(LineColumnAndImplicitCode, Scope, InlinedAt,
+ AtomGroup | (uint64_t(AtomRank) << 61), IRLayers);
return hash_combine(LineColumnAndImplicitCode, Scope, InlinedAt,
AtomGroup | (uint64_t(AtomRank) << 61));
+ }
+ if (IRLayers)
+ return hash_combine(LineColumnAndImplicitCode, Scope, InlinedAt,
+ IRLayers);
return hash_combine(LineColumnAndImplicitCode, Scope, InlinedAt);
}
};
+/// DenseMapInfo for DILayerLoc.
+template <> struct MDNodeKeyImpl<DILayerLoc> {
+ Metadata *Kind;
+ Metadata *File;
+ unsigned Line;
+ uint16_t Column;
+
+ MDNodeKeyImpl(Metadata *Kind, Metadata *File, unsigned Line, uint16_t Column)
+ : Kind(Kind), File(File), Line(Line), Column(Column) {}
+ MDNodeKeyImpl(const DILayerLoc *N)
+ : Kind(N->getRawKind()), File(N->getRawFile()), Line(N->getLine()),
+ Column(N->getColumn()) {}
+
+ bool isKeyOf(const DILayerLoc *RHS) const {
+ return Kind == RHS->getRawKind() && File == RHS->getRawFile() &&
+ Line == RHS->getLine() && Column == RHS->getColumn();
+ }
+
+ unsigned getHashValue() const {
+ return hash_combine(Kind, File, Line, Column);
+ }
+};
+
+/// DenseMapInfo for DILayerLocList.
+template <> struct MDNodeKeyImpl<DILayerLocList> : MDNodeOpsKey {
+ MDNodeKeyImpl(ArrayRef<Metadata *> Ops) : MDNodeOpsKey(Ops) {}
+ MDNodeKeyImpl(const DILayerLocList *N) : MDNodeOpsKey(N) {}
+
+ bool isKeyOf(const DILayerLocList *RHS) const { return compareOps(RHS); }
+
+ unsigned getHashValue() const { return getHash(); }
+
+ static unsigned calculateHash(DILayerLocList *N) {
+ return MDNodeOpsKey::calculateHash(N);
+ }
+};
+
/// DenseMapInfo for GenericDINode.
template <> struct MDNodeKeyImpl<GenericDINode> : MDNodeOpsKey {
unsigned Tag;
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index b813069d035f6..32e501bb6064f 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1089,6 +1089,22 @@ void Verifier::visitDILocation(const DILocation &N) {
CheckDI(isa<DILocation>(IA), "inlined-at should be a location", &N, IA);
if (auto *SP = dyn_cast<DISubprogram>(N.getRawScope()))
CheckDI(SP->isDefinition(), "scope points into the type hierarchy", &N);
+ if (auto *L = N.getRawIRLayers())
+ CheckDI(isa<DILayerLocList>(L), "irlayers must be a DILayerLocList", &N, L);
+}
+
+void Verifier::visitDILayerLoc(const DILayerLoc &N) {
+ CheckDI(isa_and_nonnull<MDString>(N.getRawKind()),
+ "layer kind must be a non-null MDString", &N, N.getRawKind());
+ CheckDI(isa_and_nonnull<DIFile>(N.getRawFile()),
+ "layer file must be a non-null DIFile", &N, N.getRawFile());
+}
+
+void Verifier::visitDILayerLocList(const DILayerLocList &N) {
+ CheckDI(N.getNumLayers() > 0, "DILayerLocList must be non-empty", &N);
+ for (const MDOperand &Op : N.layers())
+ CheckDI(isa_and_nonnull<DILayerLoc>(Op.get()),
+ "DILayerLocList entry must be a DILayerLoc", &N, Op.get());
}
void Verifier::visitGenericDINode(const GenericDINode &N) {
diff --git a/llvm/lib/Target/DirectX/DXILWriter/DXILBitcodeWriter.cpp b/llvm/lib/Target/DirectX/DXILWriter/DXILBitcodeWriter.cpp
index 6f14f70014b01..6056414a61018 100644
--- a/llvm/lib/Target/DirectX/DXILWriter/DXILBitcodeWriter.cpp
+++ b/llvm/lib/Target/DirectX/DXILWriter/DXILBitcodeWriter.cpp
@@ -299,6 +299,14 @@ class DXILBitcodeWriter {
// See https://github.com/llvm/llvm-project/issues/58989
llvm_unreachable("DXIL cannot contain DIAssignID Nodes");
}
+ void writeDILayerLoc(const DILayerLoc *N, SmallVectorImpl<uint64_t> &Record,
+ unsigned Abbrev) {
+ llvm_unreachable("DXIL cannot contain DILayerLoc Nodes");
+ }
+ void writeDILayerLocList(const DILayerLocList *N,
+ SmallVectorImpl<uint64_t> &Record, unsigned Abbrev) {
+ llvm_unreachable("DXIL cannot contain DILayerLocList Nodes");
+ }
void writeDIModule(const DIModule *N, SmallVectorImpl<uint64_t> &Record,
unsigned Abbrev);
void writeDITemplateTypeParameter(const DITemplateTypeParameter *N,
diff --git a/llvm/test/Bitcode/intermediate-loc-metadata-location.ll b/llvm/test/Bitcode/intermediate-loc-metadata-location.ll
new file mode 100644
index 0000000000000..89d16e12e5875
--- /dev/null
+++ b/llvm/test/Bitcode/intermediate-loc-metadata-location.ll
@@ -0,0 +1,58 @@
+; RUN: llvm-as < %s | llvm-dis | FileCheck %s
+;;
+;; Checks that irlayers survive a bitcode round-trip on a DILocation written as
+;; a METADATA_LOCATION record -- one reachable only as an inlinedAt target, with
+;; a layer list built through forward references.
+
+define dso_local void @test_kernel(ptr noundef %v) #0 !dbg !8 {
+entry:
+ store ptr %v, ptr %v, align 8, !dbg !20
+ store ptr %v, ptr %v, align 8, !dbg !23
+ ret void, !dbg !21
+}
+
+attributes #0 = { noinline optnone }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+!1 = !DIFile(filename: "test.cu", directory: "/test")
+!2 = !{i32 7, !"Dwarf Version", i32 2}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!8 = distinct !DISubprogram(name: "test_kernel", scope: !1, file: !1, line: 1, type: !9, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0)
+!9 = !DISubroutineType(types: !10)
+!10 = !{null}
+!11 = distinct !DISubprogram(name: "helper", scope: !1, file: !1, line: 20, type: !9, scopeLine: 20, spFlags: DISPFlagDefinition, unit: !0)
+
+!14 = !DIFile(filename: "kernel.tileir", directory: ".", checksumkind: CSK_MD5, checksum: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", source: "tile ir text")
+!15 = !DIFile(filename: "kernel.gpuir", directory: ".", checksumkind: CSK_MD5, checksum: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", source: "gpu ir text")
+
+;; !18 precedes its entries, so they parse as forward references and the list is
+;; re-uniqued once they resolve. !22 names the same entries after they resolve,
+;; so it must land on !18's node -- a stale cached hash would miss the uniquing
+;; lookup and produce a second, identical list.
+!18 = !DILayerLocList(!16, !17)
+!16 = !DILayerLoc(line: 100, column: 1, file: !14, kind: "tile ir")
+!17 = !DILayerLoc(line: 7, column: 3, file: !15, kind: "gpu ir")
+!22 = !DILayerLocList(!16, !17)
+
+!19 = distinct !DILocation(line: 30, column: 1, scope: !8, irlayers: !18)
+!24 = distinct !DILocation(line: 40, column: 1, scope: !8, irlayers: !22)
+!20 = !DILocation(line: 21, column: 5, scope: !11, inlinedAt: !19)
+!21 = !DILocation(line: 22, column: 1, scope: !11, inlinedAt: !19)
+!23 = !DILocation(line: 23, column: 5, scope: !11, inlinedAt: !24)
+
+;; The metadata-block location keeps its layers, and the two entries keep their
+;; order.
+; CHECK-DAG: ![[IA:[0-9]+]] = distinct !DILocation(line: 30, column: 1, scope: !{{[0-9]+}}, irlayers: ![[LIST:[0-9]+]])
+; CHECK-DAG: ![[LIST]] = !DILayerLocList(![[TILE:[0-9]+]], ![[GPU:[0-9]+]])
+; CHECK-DAG: ![[TILE]] = !DILayerLoc(line: 100, column: 1, file: !{{[0-9]+}}, kind: "tile ir")
+; CHECK-DAG: ![[GPU]] = !DILayerLoc(line: 7, column: 3, file: !{{[0-9]+}}, kind: "gpu ir")
+
+;; Both layered locations reference that one list node.
+; CHECK-DAG: = distinct !DILocation(line: 40, column: 1, scope: !{{[0-9]+}}, irlayers: ![[LIST]])
+
+;; The instruction locations reference them as inlinedAt and carry no layers of
+;; their own.
+; CHECK-DAG: !DILocation(line: 21, column: 5, scope: !{{[0-9]+}}, inlinedAt: ![[IA]])
diff --git a/llvm/test/Bitcode/intermediate-loc-roundtrip.ll b/llvm/test/Bitcode/intermediate-loc-roundtrip.ll
new file mode 100644
index 0000000000000..89e060e3efbf1
--- /dev/null
+++ b/llvm/test/Bitcode/intermediate-loc-roundtrip.ll
@@ -0,0 +1,57 @@
+; RUN: llvm-as < %s | llvm-dis | FileCheck %s
+; RUN: verify-uselistorder %s
+
+;; Test that intermediate location metadata (used for multi-level line info)
+;; round-trips correctly through bitcode. The layers live on the
+;; DILocation's typed `irlayers` operand (a DILayerLocList of DILayerLoc
+;; entries), so the whole DILocation/DILayerLocList/DILayerLoc/DIFile chain must
+;; survive .ll -> .bc -> .ll.
+
+define dso_local void @test_kernel(ptr noundef %v) !dbg !8 {
+entry:
+ %v.addr = alloca ptr, align 8
+ store ptr %v, ptr %v.addr, align 8, !dbg !20
+ %0 = load ptr, ptr %v.addr, align 8, !dbg !21
+ ret void, !dbg !22
+}
+
+; CHECK-LABEL: define dso_local void @test_kernel
+; CHECK: store ptr %v, ptr %v.addr, align 8, !dbg ![[DBG1:[0-9]+]]
+; CHECK: load ptr, ptr %v.addr, align 8, !dbg ![[DBG2:[0-9]+]]
+; CHECK: ret void, !dbg ![[DBG3:[0-9]+]]
+
+;; Verify the metadata structure is preserved: each !dbg is a DILocation whose
+;; source line/col comes from the primary location and whose `irlayers` operand
+;; points at a shared (uniqued) DILayerLocList.
+
+; CHECK-DAG: ![[DBG1]] = !DILocation(line: 2, column: 5, scope: ![[SP:[0-9]+]], irlayers: ![[LIST:[0-9]+]])
+; CHECK-DAG: ![[DBG2]] = !DILocation(line: 3, column: 5, scope: ![[SP]], irlayers: ![[LIST]])
+; CHECK-DAG: ![[DBG3]] = !DILocation(line: 4, column: 1, scope: ![[SP]], irlayers: ![[LIST]])
+
+;; The shared layer list holds one DILayerLoc with the kind string and the
+;; intermediate coordinate.
+; CHECK-DAG: ![[LIST]] = !DILayerLocList(![[LAYER:[0-9]+]])
+; CHECK-DAG: ![[LAYER]] = !DILayerLoc(line: 100, column: 10, file: ![[INTFILE:[0-9]+]], kind: "TileIR")
+; CHECK-DAG: ![[INTFILE]] = !DIFile(filename: "intermediate.tileir", directory: ".", checksumkind: CSK_MD5, checksum: "ffffffffffffffffffffffffffffffff")
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+!1 = !DIFile(filename: "test.cu", directory: "/test")
+!2 = !{i32 7, !"Dwarf Version", i32 2}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!8 = distinct !DISubprogram(name: "test_kernel", scope: !1, file: !1, line: 1, type: !9, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0)
+!9 = !DISubroutineType(types: !10)
+!10 = !{null}
+
+;; Intermediate-IR layer: one DILayerLoc, shared (uniqued) across all layered
+;; instructions via a single DILayerLocList.
+!14 = !DIFile(filename: "intermediate.tileir", directory: ".", checksumkind: CSK_MD5, checksum: "ffffffffffffffffffffffffffffffff")
+!30 = !DILayerLocList(!31)
+!31 = !DILayerLoc(line: 100, column: 10, file: !14, kind: "TileIR")
+
+;; Layered instruction locations: primary source loc + shared irlayers.
+!20 = !DILocation(line: 2, column: 5, scope: !8, irlayers: !30)
+!21 = !DILocation(line: 3, column: 5, scope: !8, irlayers: !30)
+!22 = !DILocation(line: 4, column: 1, scope: !8, irlayers: !30)
diff --git a/llvm/test/CodeGen/MIR/X86/instructions-debug-location-irlayers.mir b/llvm/test/CodeGen/MIR/X86/instructions-debug-location-irlayers.mir
new file mode 100644
index 0000000000000..093e9051601b7
--- /dev/null
+++ b/llvm/test/CodeGen/MIR/X86/instructions-debug-location-irlayers.mir
@@ -0,0 +1,64 @@
+# RUN: llc -mtriple=x86_64 -run-pass none -o - %s | FileCheck %s
+# RUN: llc -mtriple=x86_64 -run-pass none -o - %s \
+# RUN: | llc -x mir -mtriple=x86_64 -run-pass none -o - | FileCheck %s
+## Check that the MIR parser accepts `irlayers` on a DILocation and that the
+## printer emits it again. The second RUN line feeds the printed MIR back through
+## llc, so a layer list that prints as something unparsable fails rather than
+## passing quietly. The first location carries irlayers without an inlinedAt, the
+## second carries both.
+
+# CHECK-DAG: ![[LIST:[0-9]+]] = !DILayerLocList(![[LAYER:[0-9]+]])
+# CHECK-DAG: ![[LAYER]] = !DILayerLoc(line: 42, column: 5, file: !{{[0-9]+}}, kind: "tile ir")
+# CHECK-LABEL: name: test
+# CHECK: $eax = COPY %0, debug-location !DILocation(line: 1, scope: !{{[0-9]+}}, irlayers: ![[LIST]])
+# CHECK: RET64 $eax, debug-location !DILocation(line: 2, scope: !{{[0-9]+}}, inlinedAt: !DILocation(line: 1, scope: !{{[0-9]+}}), irlayers: ![[LIST]])
+
+--- |
+
+ define i32 @test(i32 %x) #0 !dbg !4 {
+ entry:
+ %x.addr = alloca i32, align 4
+ store i32 %x, ptr %x.addr, align 4
+ %0 = load i32, ptr %x.addr, align 4, !dbg !15
+ ret i32 %0, !dbg !14
+ }
+
+ attributes #0 = { nounwind "frame-pointer"="none" }
+
+ !llvm.dbg.cu = !{!0}
+ !llvm.module.flags = !{!9, !10}
+
+ !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !2, globals: !2, imports: !2)
+ !1 = !DIFile(filename: "test.ll", directory: "")
+ !2 = !{}
+ !4 = distinct !DISubprogram(name: "test", scope: !5, file: !5, line: 4, type: !6, isDefinition: true, scopeLine: 4, flags: DIFlagPrototyped, isOptimized: false, unit: !0, retainedNodes: !2)
+ !5 = !DIFile(filename: "test.c", directory: "")
+ !6 = !DISubroutineType(types: !7)
+ !7 = !{!8, !8}
+ !8 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed)
+ !9 = !{i32 2, !"Dwarf Version", i32 4}
+ !10 = !{i32 2, !"Debug Info Version", i32 3}
+ !14 = !DILocation(line: 8, scope: !4)
+ !15 = !DILocation(line: 9, scope: !4, irlayers: !22)
+ !20 = !DIFile(filename: "kernel.tileir", directory: ".")
+ !21 = !DILayerLoc(line: 42, column: 5, file: !20, kind: "tile ir")
+ !22 = !DILayerLocList(!21)
+
+...
+---
+name: test
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: gr32 }
+frameInfo:
+ maxAlignment: 4
+stack:
+ - { id: 0, name: x.addr, size: 4, alignment: 4 }
+body: |
+ bb.0.entry:
+ liveins: $edi
+
+ %0 = COPY $edi
+ $eax = COPY %0, debug-location !DILocation(line: 1, scope: !4, irlayers: !22)
+ RET64 $eax, debug-location !DILocation(line: 2, scope: !4, inlinedAt: !DILocation(line: 1, scope: !4), irlayers: !22)
+...
diff --git a/llvm/test/Verifier/intermediate-layerloc.ll b/llvm/test/Verifier/intermediate-layerloc.ll
new file mode 100644
index 0000000000000..c5409ffc168a7
--- /dev/null
+++ b/llvm/test/Verifier/intermediate-layerloc.ll
@@ -0,0 +1,37 @@
+; RUN: llvm-as -disable-output %s -o - 2>&1 | FileCheck --allow-empty %s
+;;
+;; Verification of DILayerLoc is structural only, so a checksum-less intermediate
+;; file verifies clean -- with or without source.
+
+; CHECK-NOT: requires a checksum
+; CHECK-NOT: ignoring invalid debug info
+
+define void @k(ptr %p) !dbg !5 {
+ store ptr null, ptr %p, align 8, !dbg !20
+ store ptr null, ptr %p, align 8, !dbg !21
+ ret void
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, emissionKind: FullDebug)
+!1 = !DIFile(filename: "test.cu", directory: "/test")
+!2 = !{i32 7, !"Dwarf Version", i32 2}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !DISubroutineType(types: !{null})
+!5 = distinct !DISubprogram(name: "k", scope: !1, file: !1, line: 1, type: !4, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0)
+
+;; No checksum, no source.
+!10 = !DIFile(filename: "kernel.tileir", directory: ".")
+!11 = !DILayerLoc(line: 42, column: 5, file: !10, kind: "tile ir")
+!12 = !DILayerLocList(!11)
+
+;; Source but still no checksum -- the case NVPTX would otherwise use a digest
+;; for, and the one that used to be rejected outright.
+!13 = !DIFile(filename: "kernel.gpuir", directory: ".", source: "gpu ir source")
+!14 = !DILayerLoc(line: 7, column: 1, file: !13, kind: "gpu ir")
+!15 = !DILayerLocList(!14)
+
+!20 = !DILocation(line: 2, column: 5, scope: !5, irlayers: !12)
+!21 = !DILocation(line: 3, column: 5, scope: !5, irlayers: !15)
diff --git a/llvm/unittests/IR/DebugInfoTest.cpp b/llvm/unittests/IR/DebugInfoTest.cpp
index 4004ab4b080b2..7e23d38f9ad71 100644
--- a/llvm/unittests/IR/DebugInfoTest.cpp
+++ b/llvm/unittests/IR/DebugInfoTest.cpp
@@ -12,6 +12,7 @@
#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/DebugLoc.h"
#include "llvm/IR/DebugProgramInstruction.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/IntrinsicInst.h"
@@ -1511,4 +1512,405 @@ TEST(DIBuilder, DynamicOffsetAndSize) {
EXPECT_EQ(Field->getRawSizeInBits(), Len);
}
+// Tests for DebugLoc with intermediate location support.
+
+TEST(DebugLocTest, IntermediateLocBasics) {
+ LLVMContext Ctx;
+ auto M = std::make_unique<Module>("MyModule", Ctx);
+ DIBuilder DIB(*M);
+ DIFile *F = DIB.createFile("source.cu", "/");
+ DIFile *IntF = DIB.createFile("intermediate.mlir", "/");
+ DICompileUnit *CU =
+ DIB.createCompileUnit(dwarf::DW_LANG_C, F, "test", false, "", 0);
+ DISubprogram *SP =
+ DIB.createFunction(CU, "foo", "", F, 1, DIB.createSubroutineType({}), 1,
+ DINode::FlagZero, DISubprogram::SPFlagDefinition);
+
+ // Build a layered DILocation: a source coordinate plus one TileIR layer that
+ // hangs off the DILocation's typed `irlayers` operand.
+ MDString *Kind = MDString::get(Ctx, "TileIR");
+ DILayerLoc *Layer = DILayerLoc::get(Ctx, Kind, IntF, 100, 1);
+ DILayerLocList *Layers = DILayerLocList::get(Ctx, {Layer});
+ DILocation *Loc =
+ DILocation::get(Ctx, 10, 5, SP, /*InlinedAt=*/nullptr,
+ /*ImplicitCode=*/false, /*AtomGroup=*/0, /*AtomRank=*/0,
+ /*IRLayers=*/Layers);
+
+ DebugLoc DL(Loc);
+ EXPECT_TRUE((bool)DL);
+ EXPECT_EQ(DL.get(), Loc);
+ EXPECT_EQ(DL.getLine(), 10u);
+ EXPECT_EQ(DL.getCol(), 5u);
+
+ // The layer list is reachable through both DebugLoc and DILocation.
+ EXPECT_EQ(DL.getRawIRLayers(), Layers);
+ ASSERT_EQ(Loc->getIRLayers(), Layers);
+ ASSERT_EQ(Loc->getNumLayers(), 1u);
+ DILayerLoc *L0 = Loc->getLayer(0);
+ ASSERT_NE(L0, nullptr);
+ EXPECT_EQ(L0->getKind(), "TileIR");
+ EXPECT_EQ(L0->getFile(), IntF);
+ EXPECT_EQ(L0->getLine(), 100u);
+ EXPECT_EQ(L0->getColumn(), 1u);
+}
+
+TEST(DebugLocTest, IntermediateLocWithAndWithout) {
+ LLVMContext Ctx;
+ auto M = std::make_unique<Module>("MyModule", Ctx);
+ DIBuilder DIB(*M);
+ DIFile *F = DIB.createFile("source.cu", "/");
+ DIFile *IntF = DIB.createFile("intermediate.mlir", "/");
+ DICompileUnit *CU =
+ DIB.createCompileUnit(dwarf::DW_LANG_C, F, "test", false, "", 0);
+ DISubprogram *SP =
+ DIB.createFunction(CU, "foo", "", F, 1, DIB.createSubroutineType({}), 1,
+ DINode::FlagZero, DISubprogram::SPFlagDefinition);
+
+ // A source-only DILocation has no layers.
+ DILocation *SourceLoc = DILocation::get(Ctx, 10, 5, SP);
+ DebugLoc DLSourceOnly(SourceLoc);
+ EXPECT_EQ(SourceLoc->getRawIRLayers(), nullptr);
+ EXPECT_EQ(SourceLoc->getIRLayers(), nullptr);
+ EXPECT_EQ(SourceLoc->getNumLayers(), 0u);
+ EXPECT_EQ(DLSourceOnly.getRawIRLayers(), nullptr);
+ EXPECT_EQ(DLSourceOnly.get(), SourceLoc);
+
+ // A layered DILocation returns its list.
+ MDString *Kind = MDString::get(Ctx, "TileIR");
+ DILayerLoc *Layer = DILayerLoc::get(Ctx, Kind, IntF, 100, 1);
+ DILayerLocList *Layers = DILayerLocList::get(Ctx, {Layer});
+ DILocation *LayeredLoc =
+ DILocation::get(Ctx, 10, 5, SP, /*InlinedAt=*/nullptr,
+ /*ImplicitCode=*/false,
+ /*AtomGroup=*/0, /*AtomRank=*/0,
+ /*IRLayers=*/Layers);
+ DebugLoc DLWithInt(LayeredLoc);
+ EXPECT_EQ(DLWithInt.getRawIRLayers(), Layers);
+ EXPECT_EQ(LayeredLoc->getIRLayers(), Layers);
+ ASSERT_EQ(LayeredLoc->getNumLayers(), 1u);
+ EXPECT_EQ(LayeredLoc->getLayer(0)->getKind(), "TileIR");
+}
+
+TEST(DebugLocTest, IntermediateLocEquality) {
+ LLVMContext Ctx;
+ auto M = std::make_unique<Module>("MyModule", Ctx);
+ DIBuilder DIB(*M);
+ DIFile *F = DIB.createFile("source.cu", "/");
+ DIFile *IntF = DIB.createFile("intermediate.mlir", "/");
+ DICompileUnit *CU =
+ DIB.createCompileUnit(dwarf::DW_LANG_C, F, "test", false, "", 0);
+ DISubprogram *SP =
+ DIB.createFunction(CU, "foo", "", F, 1, DIB.createSubroutineType({}), 1,
+ DINode::FlagZero, DISubprogram::SPFlagDefinition);
+
+ MDString *Kind = MDString::get(Ctx, "TileIR");
+ DILayerLoc *LayerA = DILayerLoc::get(Ctx, Kind, IntF, 100, 1);
+ // Differs from LayerA in a single field (column only) -- a minimal structural
+ // change must still uniquify to a distinct node, so DL1 and DL3 differ.
+ DILayerLoc *LayerB = DILayerLoc::get(Ctx, Kind, IntF, 100, 2);
+ DILayerLocList *ListA = DILayerLocList::get(Ctx, {LayerA});
+ // A structurally-identical list uniques to the same node.
+ DILayerLocList *ListA2 = DILayerLocList::get(Ctx, {LayerA});
+ DILayerLocList *ListB = DILayerLocList::get(Ctx, {LayerB});
+ EXPECT_EQ(ListA, ListA2);
+ EXPECT_NE(ListA, ListB);
+
+ auto Layered = [&](DILayerLocList *L) {
+ return DILocation::get(Ctx, 10, 5, SP, /*InlinedAt=*/nullptr,
+ /*ImplicitCode=*/false, /*AtomGroup=*/0,
+ /*AtomRank=*/0, /*IRLayers=*/L);
+ };
+ DebugLoc DL1(Layered(ListA));
+ // A *distinct* location with the same fields and the same (uniqued) layer
+ // list, so isSameSourceLocation cannot short-circuit on pointer identity and
+ // must actually run the structural getRawIRLayers() comparison.
+ DebugLoc DL2(DILocation::getDistinct(Ctx, 10, 5, SP, /*InlinedAt=*/nullptr,
+ /*ImplicitCode=*/false, /*AtomGroup=*/0,
+ /*AtomRank=*/0, /*IRLayers=*/ListA));
+ DebugLoc DL3(Layered(ListB));
+ DebugLoc DL4(DILocation::get(Ctx, 10, 5, SP)); // no layers
+
+ ASSERT_NE(DL1.get(), DL2.get());
+ // Distinct nodes with same line/col/scope and identical (uniqued) layers ->
+ // same source location (exercises the layer-aware structural compare).
+ EXPECT_TRUE(DL1.isSameSourceLocation(DL2));
+ // Same line/col/scope but different layers -> NOT the same source location.
+ EXPECT_FALSE(DL1.isSameSourceLocation(DL3));
+ // One has layers, the other doesn't -> NOT the same source location.
+ EXPECT_FALSE(DL1.isSameSourceLocation(DL4));
+}
+
+TEST(DebugLocTest, MergedLocationWithIntermediate) {
+ LLVMContext Ctx;
+ auto M = std::make_unique<Module>("MyModule", Ctx);
+ DIBuilder DIB(*M);
+ DIFile *F = DIB.createFile("source.cu", "/");
+ DIFile *IntF = DIB.createFile("intermediate.mlir", "/");
+ DICompileUnit *CU =
+ DIB.createCompileUnit(dwarf::DW_LANG_C, F, "test", false, "", 0);
+ DISubprogram *SP =
+ DIB.createFunction(CU, "foo", "", F, 1, DIB.createSubroutineType({}), 1,
+ DINode::FlagZero, DISubprogram::SPFlagDefinition);
+
+ MDString *Kind = MDString::get(Ctx, "TileIR");
+ // A layer shared by both locations, plus a distinct layer on each so the two
+ // DILocations are different nodes.
+ DILayerLoc *Shared = DILayerLoc::get(Ctx, Kind, IntF, 100, 1);
+ DILayerLoc *ExtraA = DILayerLoc::get(Ctx, Kind, IntF, 111, 1);
+ DILayerLoc *ExtraB = DILayerLoc::get(Ctx, Kind, IntF, 222, 1);
+ DILayerLocList *List1 = DILayerLocList::get(Ctx, {Shared, ExtraA});
+ DILayerLocList *List2 = DILayerLocList::get(Ctx, {Shared, ExtraB});
+
+ auto Layered = [&](DILayerLocList *L) {
+ return DILocation::get(Ctx, 10, 5, SP, /*InlinedAt=*/nullptr,
+ /*ImplicitCode=*/false, /*AtomGroup=*/0,
+ /*AtomRank=*/0, /*IRLayers=*/L);
+ };
+ DILocation *Loc1 = Layered(List1);
+ DILocation *Loc2 = Layered(List2);
+ ASSERT_NE(Loc1, Loc2);
+
+ // Merging two locations that share a DILayerLoc keeps the shared layer only.
+ DILocation *Merged = DILocation::getMergedLocation(Loc1, Loc2);
+ ASSERT_NE(Merged, nullptr);
+ EXPECT_EQ(Merged->getLine(), 10u);
+ ASSERT_NE(Merged->getIRLayers(), nullptr);
+ ASSERT_EQ(Merged->getNumLayers(), 1u);
+ EXPECT_EQ(Merged->getLayer(0), Shared);
+}
+
+TEST(DebugLocTest, MergedLocationPartialIntermediate) {
+ LLVMContext Ctx;
+ auto M = std::make_unique<Module>("MyModule", Ctx);
+ DIBuilder DIB(*M);
+ DIFile *F = DIB.createFile("source.cu", "/");
+ DIFile *IntF = DIB.createFile("intermediate.mlir", "/");
+ DICompileUnit *CU =
+ DIB.createCompileUnit(dwarf::DW_LANG_C, F, "test", false, "", 0);
+ DISubprogram *SP =
+ DIB.createFunction(CU, "foo", "", F, 1, DIB.createSubroutineType({}), 1,
+ DINode::FlagZero, DISubprogram::SPFlagDefinition);
+
+ MDString *Kind = MDString::get(Ctx, "TileIR");
+ DILayerLoc *LayerA = DILayerLoc::get(Ctx, Kind, IntF, 100, 1);
+ DILayerLocList *LayersA = DILayerLocList::get(Ctx, {LayerA});
+ DILayerLoc *LayerB = DILayerLoc::get(Ctx, Kind, IntF, 200, 2);
+ DILayerLocList *LayersB = DILayerLocList::get(Ctx, {LayerB});
+
+ DILocation *Loc1 =
+ DILocation::get(Ctx, 10, 5, SP, /*InlinedAt=*/nullptr,
+ /*ImplicitCode=*/false, /*AtomGroup=*/0, /*AtomRank=*/0,
+ /*IRLayers=*/LayersA);
+ DILocation *Loc2 = DILocation::get(Ctx, 10, 5, SP); // no layers
+ DILocation *Loc3 =
+ DILocation::get(Ctx, 10, 5, SP, /*InlinedAt=*/nullptr,
+ /*ImplicitCode=*/false, /*AtomGroup=*/0, /*AtomRank=*/0,
+ /*IRLayers=*/LayersB); // disjoint layer set
+
+ // One side has no layers at all -> merged keeps none (the !LA || !LB
+ // early-out in mergeIRLayers).
+ DILocation *M12 = DILocation::getMergedLocation(Loc1, Loc2);
+ ASSERT_NE(M12, nullptr);
+ EXPECT_EQ(M12->getLine(), 10u);
+ EXPECT_EQ(M12->getRawIRLayers(), nullptr);
+
+ // Both sides have non-empty but DISJOINT layer sets -> empty intersection ->
+ // merged keeps no layers (exercises mergeIRLayers' Keep.empty() path, which
+ // must not attach an invalid empty DILayerLocList).
+ DILocation *M13 = DILocation::getMergedLocation(Loc1, Loc3);
+ ASSERT_NE(M13, nullptr);
+ EXPECT_EQ(M13->getRawIRLayers(), nullptr);
+}
+
+// Under the outermost-frame model, two instructions inlined into the same
+// kernel share the kernel (outer) frame that carries the tile-IR snapshot,
+// while their heads (inner frames) differ. Merging them must PRESERVE that
+// shared outer-frame layer (MergeLocPair threads per-frame layers), so the
+// merged instruction still resolves to the kernel's tile-IR line.
+TEST(DebugLocTest, MergedLocationOuterFrameLayerPreserved) {
+ LLVMContext Ctx;
+ auto M = std::make_unique<Module>("MyModule", Ctx);
+ DIBuilder DIB(*M);
+ DIFile *SrcF = DIB.createFile("kernel.py", "/k");
+ DIFile *IntF = DIB.createFile("kernel.tileir", ".");
+ DICompileUnit *CU =
+ DIB.createCompileUnit(dwarf::DW_LANG_C, SrcF, "tile", false, "", 0);
+ DISubprogram *KernelSP = DIB.createFunction(
+ CU, "kernel", "", SrcF, 10, DIB.createSubroutineType({}), 10,
+ DINode::FlagZero, DISubprogram::SPFlagDefinition);
+ DISubprogram *CalleeSP = DIB.createFunction(
+ CU, "helper", "", SrcF, 5, DIB.createSubroutineType({}), 5,
+ DINode::FlagZero, DISubprogram::SPFlagDefinition);
+
+ MDString *Kind = MDString::get(Ctx, "tile ir");
+ DILayerLoc *Layer = DILayerLoc::get(Ctx, Kind, IntF, 100, 1);
+ DILayerLocList *KernelLayers = DILayerLocList::get(Ctx, {Layer});
+
+ // The kernel (outer) frame carries the snapshot layer.
+ DILocation *KernelFrame =
+ DILocation::get(Ctx, 50, 1, KernelSP, /*InlinedAt=*/nullptr,
+ /*ImplicitCode=*/false, /*AtomGroup=*/0, /*AtomRank=*/0,
+ /*IRLayers=*/KernelLayers);
+
+ // Two instructions inlined into that kernel frame: same callee, different
+ // head lines, no layer of their own.
+ DILocation *LocA = DILocation::get(Ctx, 10, 3, CalleeSP, KernelFrame);
+ DILocation *LocB = DILocation::get(Ctx, 11, 5, CalleeSP, KernelFrame);
+
+ DILocation *Merged = DILocation::getMergedLocation(LocA, LocB);
+ ASSERT_NE(Merged, nullptr);
+ // The merged head has no layer of its own...
+ EXPECT_EQ(Merged->getRawIRLayers(), nullptr);
+ // ...but the shared outer (kernel) frame, and its snapshot layer, survives so
+ // the emission walk still resolves to the kernel's tile-IR line.
+ DILocation *MergedOuter = Merged->getInlinedAt();
+ ASSERT_NE(MergedOuter, nullptr);
+ EXPECT_EQ(MergedOuter->getScope(), KernelSP);
+ EXPECT_EQ(MergedOuter->getIRLayers(), KernelLayers);
+}
+
+// Rebuilding a location with a new discriminator must carry `irlayers` over:
+// the discriminator says nothing about the intermediate-IR position. This is
+// the path AddDiscriminators, SampleProfile and loop unrolling rebuild through.
+TEST(DebugLocTest, CloneWithDiscriminatorPreservesLayers) {
+ LLVMContext Ctx;
+ auto M = std::make_unique<Module>("MyModule", Ctx);
+ DIBuilder DIB(*M);
+ DIFile *F = DIB.createFile("source.cu", "/");
+ DIFile *IntF = DIB.createFile("intermediate.tileir", ".");
+ DICompileUnit *CU =
+ DIB.createCompileUnit(dwarf::DW_LANG_C, F, "test", false, "", 0);
+ DISubprogram *SP =
+ DIB.createFunction(CU, "foo", "", F, 1, DIB.createSubroutineType({}), 1,
+ DINode::FlagZero, DISubprogram::SPFlagDefinition);
+
+ DILayerLoc *Layer =
+ DILayerLoc::get(Ctx, MDString::get(Ctx, "tile ir"), IntF, 42, 5);
+ DILayerLocList *List = DILayerLocList::get(Ctx, {Layer});
+ DILocation *Loc =
+ DILocation::get(Ctx, 10, 5, SP, /*InlinedAt=*/nullptr,
+ /*ImplicitCode=*/false, /*AtomGroup=*/0, /*AtomRank=*/0,
+ /*IRLayers=*/List);
+ ASSERT_EQ(Loc->getIRLayers(), List);
+
+ std::optional<const DILocation *> Cloned = Loc->cloneWithBaseDiscriminator(7);
+ ASSERT_TRUE(Cloned.has_value());
+ ASSERT_NE(*Cloned, Loc);
+ EXPECT_EQ((*Cloned)->getBaseDiscriminator(), 7u);
+ EXPECT_EQ((*Cloned)->getIRLayers(), List);
+}
+
+// `distinct !DILayerLoc` is valid IR, so two structurally identical layers can
+// be different pointers. Merging must still recognize them as the same layer.
+TEST(DebugLocTest, MergedLocationDistinctLayersCompareStructurally) {
+ LLVMContext Ctx;
+ auto M = std::make_unique<Module>("MyModule", Ctx);
+ DIBuilder DIB(*M);
+ DIFile *F = DIB.createFile("source.cu", "/");
+ DIFile *IntF = DIB.createFile("intermediate.tileir", ".");
+ DICompileUnit *CU =
+ DIB.createCompileUnit(dwarf::DW_LANG_C, F, "test", false, "", 0);
+ DISubprogram *SP =
+ DIB.createFunction(CU, "foo", "", F, 1, DIB.createSubroutineType({}), 1,
+ DINode::FlagZero, DISubprogram::SPFlagDefinition);
+
+ MDString *Kind = MDString::get(Ctx, "tile ir");
+ // Same fields, but each side holds its own `distinct` node, so the two shared
+ // layers are unequal pointers.
+ DILayerLoc *SharedA = DILayerLoc::getDistinct(Ctx, Kind, IntF, 100, 1);
+ DILayerLoc *SharedB = DILayerLoc::getDistinct(Ctx, Kind, IntF, 100, 1);
+ ASSERT_NE(SharedA, SharedB);
+ DILayerLoc *ExtraA = DILayerLoc::get(Ctx, Kind, IntF, 111, 1);
+ DILayerLoc *ExtraB = DILayerLoc::get(Ctx, Kind, IntF, 222, 1);
+
+ auto Layered = [&](ArrayRef<Metadata *> Layers) {
+ return DILocation::get(Ctx, 10, 5, SP, /*InlinedAt=*/nullptr,
+ /*ImplicitCode=*/false, /*AtomGroup=*/0,
+ /*AtomRank=*/0,
+ /*IRLayers=*/DILayerLocList::get(Ctx, Layers));
+ };
+ DILocation *Loc1 = Layered({SharedA, ExtraA});
+ DILocation *Loc2 = Layered({SharedB, ExtraB});
+
+ DILocation *Merged = DILocation::getMergedLocation(Loc1, Loc2);
+ ASSERT_NE(Merged, nullptr);
+ // The structurally equal distinct layer survives; the divergent ones do not.
+ ASSERT_NE(Merged->getIRLayers(), nullptr);
+ ASSERT_EQ(Merged->getNumLayers(), 1u);
+ EXPECT_EQ(Merged->getLayer(0), SharedA);
+}
+
+// The layer nodes behind `irlayers` are uniqued MDNodes: structurally
+// identical DILayerLoc / DILayerLocList values map to the same pointer.
+TEST(DebugLocTest, IntermediateLocLayerUniquing) {
+ LLVMContext Ctx;
+ auto M = std::make_unique<Module>("MyModule", Ctx);
+ DIBuilder DIB(*M);
+ DIFile *IntF = DIB.createFile("intermediate.mlir", "/");
+ MDString *Kind = MDString::get(Ctx, "TileIR");
+
+ // Two structurally-identical DILayerLoc::get calls return the same node.
+ DILayerLoc *L1 = DILayerLoc::get(Ctx, Kind, IntF, 100, 5);
+ DILayerLoc *L2 = DILayerLoc::get(Ctx, Kind, IntF, 100, 5);
+ EXPECT_EQ(L1, L2);
+
+ // Any differing field yields a distinct node.
+ DILayerLoc *L3 = DILayerLoc::get(Ctx, Kind, IntF, 101, 5);
+ EXPECT_NE(L1, L3);
+
+ // DILayerLocList uniques on its operands as well.
+ DILayerLocList *List1 = DILayerLocList::get(Ctx, {L1});
+ DILayerLocList *List2 = DILayerLocList::get(Ctx, {L2}); // {L2} == {L1}
+ EXPECT_EQ(List1, List2);
+
+ DILayerLocList *List3 = DILayerLocList::get(Ctx, {L1, L3});
+ EXPECT_NE(List1, List3);
+}
+
+TEST(DebugLocTest, PrintIntermediateLocWithInlinedAt) {
+ LLVMContext Ctx;
+ auto M = std::make_unique<Module>("MyModule", Ctx);
+ DIBuilder DIB(*M);
+ DIFile *SrcF = DIB.createFile("caller.py", "/src");
+ DIFile *CalleeF = DIB.createFile("callee.py", "/src");
+ DIFile *IntF = DIB.createFile("callee.tileir", "/ir");
+ DICompileUnit *CU =
+ DIB.createCompileUnit(dwarf::DW_LANG_C, SrcF, "test", false, "", 0);
+ DISubprogram *CallerSP = DIB.createFunction(
+ CU, "caller", "", SrcF, 1, DIB.createSubroutineType({}), 1,
+ DINode::FlagZero, DISubprogram::SPFlagDefinition);
+ DISubprogram *CalleeSP = DIB.createFunction(
+ CU, "callee", "", CalleeF, 1, DIB.createSubroutineType({}), 1,
+ DINode::FlagZero, DISubprogram::SPFlagDefinition);
+
+ // A DILocation carrying BOTH an inlinedAt chain and an irlayers operand.
+ DILocation *CallSiteLoc = DILocation::get(Ctx, 50, 1, CallerSP);
+ MDString *Kind = MDString::get(Ctx, "TileIR");
+ DILayerLoc *Layer = DILayerLoc::get(Ctx, Kind, IntF, 100, 5);
+ DILayerLocList *Layers = DILayerLocList::get(Ctx, {Layer});
+ DILocation *Loc =
+ DILocation::get(Ctx, 10, 3, CalleeSP, CallSiteLoc,
+ /*ImplicitCode=*/false, /*AtomGroup=*/0, /*AtomRank=*/0,
+ /*IRLayers=*/Layers);
+
+ // Print the DILocation node itself so the typed operands are rendered.
+ std::string Result;
+ raw_string_ostream OS(Result);
+ Loc->print(OS, M.get());
+
+ // The printed node names both trailing operands.
+ EXPECT_NE(Result.find("!DILocation("), std::string::npos) << Result;
+ EXPECT_NE(Result.find("inlinedAt:"), std::string::npos) << Result;
+ EXPECT_NE(Result.find("irlayers:"), std::string::npos) << Result;
+
+ // The human-readable DebugLoc form still prints the source coordinate and its
+ // inlinedAt chain.
+ std::string DLResult;
+ raw_string_ostream DLOS(DLResult);
+ DebugLoc(Loc).print(DLOS);
+ EXPECT_NE(DLResult.find("callee.py:10:3"), std::string::npos) << DLResult;
+ EXPECT_NE(DLResult.find("@["), std::string::npos) << DLResult;
+ EXPECT_NE(DLResult.find("caller.py:50:1"), std::string::npos) << DLResult;
+}
+
} // end namespace
>From 02ce5018f011359656a2d142c4bba70ad5b7b880 Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolovich at nvidia.com>
Date: Wed, 12 Aug 2026 18:57:06 +0000
Subject: [PATCH 02/12] [DebugInfo] Keep irlayers when outlining rebuilds a
DILocation chain
DebugLoc::replaceInlinedAtSubprogram rebuilds a location's terminal frame and
each of its inlined-at frames so their scopes point at the outlined function.
Neither rebuild forwarded the new `irlayers` operand, so CodeExtractor -- and
through it hot/cold splitting, partial inlining and loop extraction -- silently
dropped a valid intermediate mapping.
---
llvm/lib/IR/DebugLoc.cpp | 17 +++--
.../transfer-debug-info-irlayers.ll | 74 +++++++++++++++++++
2 files changed, 85 insertions(+), 6 deletions(-)
create mode 100644 llvm/test/Transforms/HotColdSplit/transfer-debug-info-irlayers.ll
diff --git a/llvm/lib/IR/DebugLoc.cpp b/llvm/lib/IR/DebugLoc.cpp
index 87bbb91acb97f..41f60766e09bf 100644
--- a/llvm/lib/IR/DebugLoc.cpp
+++ b/llvm/lib/IR/DebugLoc.cpp
@@ -114,17 +114,22 @@ DebugLoc DebugLoc::replaceInlinedAtSubprogram(
DILocation *LocToUpdate = LocChain.pop_back_val();
DIScope *NewScope = DILocalScope::cloneScopeForSubprogram(
*LocToUpdate->getScope(), NewSP, Ctx, Cache);
- UpdatedLoc = DILocation::get(Ctx, LocToUpdate->getLine(),
- LocToUpdate->getColumn(), NewScope);
+ UpdatedLoc = DILocation::get(
+ Ctx, LocToUpdate->getLine(), LocToUpdate->getColumn(), NewScope,
+ /*InlinedAt=*/nullptr, /*ImplicitCode=*/false, /*AtomGroup=*/0,
+ /*AtomRank=*/0, LocToUpdate->getRawIRLayers());
Cache[LocToUpdate] = UpdatedLoc;
}
// Recreate the location chain, bottom-up, starting at the new scope (or a
- // cached result).
+ // cached result). Each frame keeps its own irlayers, as appendInlinedAt does:
+ // the intermediate-IR snapshot can live on any frame, so outlining must not
+ // drop it.
for (const DILocation *LocToUpdate : reverse(LocChain)) {
- UpdatedLoc =
- DILocation::get(Ctx, LocToUpdate->getLine(), LocToUpdate->getColumn(),
- LocToUpdate->getScope(), UpdatedLoc);
+ UpdatedLoc = DILocation::get(
+ Ctx, LocToUpdate->getLine(), LocToUpdate->getColumn(),
+ LocToUpdate->getScope(), UpdatedLoc, /*ImplicitCode=*/false,
+ /*AtomGroup=*/0, /*AtomRank=*/0, LocToUpdate->getRawIRLayers());
Cache[LocToUpdate] = UpdatedLoc;
}
diff --git a/llvm/test/Transforms/HotColdSplit/transfer-debug-info-irlayers.ll b/llvm/test/Transforms/HotColdSplit/transfer-debug-info-irlayers.ll
new file mode 100644
index 0000000000000..e057c84edbd2b
--- /dev/null
+++ b/llvm/test/Transforms/HotColdSplit/transfer-debug-info-irlayers.ll
@@ -0,0 +1,74 @@
+; RUN: opt -passes=hotcoldsplit -hotcoldsplit-threshold=0 -S < %s | FileCheck %s
+;;
+;; Outlining rebuilds every DILocation in the extracted region so its scope points
+;; at the new cold function: once for the terminal frame, and once per inlined-at
+;; frame. Check that irlayers survive both rebuilds.
+
+target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-apple-macosx10.14.0"
+
+; CHECK-LABEL: define {{.*}}@foo.cold.1
+
+;; A location carrying layers of its own: the terminal frame is rebuilt against
+;; the cold function's scope and keeps its layer list.
+; CHECK: [[ADD:%.*]] = add i32 %{{.*}}, 1, !dbg ![[OWN:[0-9]+]]
+
+;; An inlined location whose inlinedAt frame carries the layers: the chain
+;; rebuild has to keep them on that frame.
+; CHECK: call void @sink(i32 [[ADD]]), !dbg ![[INL:[0-9]+]]
+
+;; Each frame keeps its own coordinate: 100 for the terminal frame, 200 for the
+;; inlined-at frame.
+; CHECK-DAG: ![[OWN]] = !DILocation(line: 1, column: 1, scope: !{{[0-9]+}}, irlayers: ![[OWNLIST:[0-9]+]])
+; CHECK-DAG: ![[OWNLIST]] = !DILayerLocList(![[OWNLAYER:[0-9]+]])
+; CHECK-DAG: ![[OWNLAYER]] = !DILayerLoc(line: 100, column: 1, file: !{{[0-9]+}}, kind: "tile ir")
+
+; CHECK-DAG: ![[INL]] = !DILocation(line: 2, column: 2, scope: !{{[0-9]+}}, inlinedAt: ![[IA:[0-9]+]])
+; CHECK-DAG: ![[IA]] = !DILocation(line: 3, column: 3, scope: !{{[0-9]+}}, irlayers: ![[IALIST:[0-9]+]])
+; CHECK-DAG: ![[IALIST]] = !DILayerLocList(![[IALAYER:[0-9]+]])
+; CHECK-DAG: ![[IALAYER]] = !DILayerLoc(line: 200, column: 1, file: !{{[0-9]+}}, kind: "tile ir")
+
+define void @foo(i32 %arg1, i1 %c) !dbg !6 {
+entry:
+ br i1 %c, label %if.then, label %if.end
+
+if.then:
+ ret void
+
+if.end:
+ %add1 = add i32 %arg1, 1, !dbg !20
+ call void @sink(i32 %add1), !dbg !21
+ ret void
+}
+
+declare void @sink(i32) cold
+
+define void @inline_me() !dbg !12 {
+ ret void
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!5}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "tile", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
+!1 = !DIFile(filename: "<stdin>", directory: "/")
+!2 = !{}
+!5 = !{i32 2, !"Debug Info Version", i32 3}
+!6 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
+!7 = !DISubroutineType(types: !2)
+!12 = distinct !DISubprogram(name: "inline_me", linkageName: "inline_me", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
+
+;; Two distinct coordinates in one intermediate-IR snapshot, so a frame that ends
+;; up with the other frame's layers fails rather than passing quietly.
+!14 = !DIFile(filename: "kernel.tileir", directory: ".", checksumkind: CSK_MD5, checksum: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
+!15 = !DILayerLoc(line: 100, column: 1, file: !14, kind: "tile ir")
+!16 = !DILayerLocList(!15)
+!17 = !DILayerLoc(line: 200, column: 1, file: !14, kind: "tile ir")
+!18 = !DILayerLocList(!17)
+
+;; !20 carries layers directly, exercising the terminal-frame rebuild. !21 is
+;; inlined from @inline_me and its inlinedAt frame !22 carries its own, exercising
+;; the chain rebuild.
+!20 = !DILocation(line: 1, column: 1, scope: !6, irlayers: !16)
+!21 = !DILocation(line: 2, column: 2, scope: !12, inlinedAt: !22)
+!22 = !DILocation(line: 3, column: 3, scope: !6, irlayers: !18)
>From e87cfe569ee655885a94c97ebaedb9e156b422ab Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolovich at nvidia.com>
Date: Wed, 12 Aug 2026 22:05:34 +0000
Subject: [PATCH 03/12] [DebugInfo] Compare only the primary position in
isSameSourceLocation
isSameSourceLocation folded the new irlayers operand into its comparison, which
changed generic equality semantics for every caller. SimplifyCFG uses it to
decide whether to discard a speculated instruction's location, and DwarfDebug
uses it as the line-table emission gate, so a layer-only difference discarded
debug locations and added redundant line-table rows on targets that ignore
layers.
Compare the primary position only. Callers that need layer equality get an
explicit variant (isSameSourceLocationAndIRLayers, added in the upstream stack).
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply at anthropic.com>
---
llvm/include/llvm/IR/DebugLoc.h | 10 ++++++----
llvm/unittests/IR/DebugInfoTest.cpp | 13 +++++++------
2 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/llvm/include/llvm/IR/DebugLoc.h b/llvm/include/llvm/IR/DebugLoc.h
index c9a5149c80940..cb82a7119813b 100644
--- a/llvm/include/llvm/IR/DebugLoc.h
+++ b/llvm/include/llvm/IR/DebugLoc.h
@@ -239,15 +239,17 @@ class DebugLoc {
appendInlinedAt(const DebugLoc &DL, DILocation *InlinedAt, LLVMContext &Ctx,
DenseMap<const MDNode *, MDNode *> &Cache);
- /// Return true if the source locations match, ignoring isImplicitCode and
- /// source atom info.
+ /// Return true if the source locations match, ignoring isImplicitCode,
+ /// source atom info and intermediate-IR layers. Layers are deliberately not
+ /// part of this comparison: two locations at the same source position are the
+ /// same source position regardless of which intermediate IR they came from.
+ /// Callers that must also match layers use isSameSourceLocationAndIRLayers.
bool isSameSourceLocation(const DebugLoc &Other) const {
if (get() == Other.get())
return true;
return ((bool)*this == (bool)Other) && getLine() == Other.getLine() &&
getCol() == Other.getCol() && getScope() == Other.getScope() &&
- getInlinedAt() == Other.getInlinedAt() &&
- getRawIRLayers() == Other.getRawIRLayers();
+ getInlinedAt() == Other.getInlinedAt();
}
LLVM_ABI unsigned getLine() const;
diff --git a/llvm/unittests/IR/DebugInfoTest.cpp b/llvm/unittests/IR/DebugInfoTest.cpp
index 7e23d38f9ad71..b547ced372d9e 100644
--- a/llvm/unittests/IR/DebugInfoTest.cpp
+++ b/llvm/unittests/IR/DebugInfoTest.cpp
@@ -1631,15 +1631,16 @@ TEST(DebugLocTest, IntermediateLocEquality) {
DebugLoc DL4(DILocation::get(Ctx, 10, 5, SP)); // no layers
ASSERT_NE(DL1.get(), DL2.get());
- // Distinct nodes with same line/col/scope and identical (uniqued) layers ->
- // same source location (exercises the layer-aware structural compare).
+ // isSameSourceLocation compares the primary position only, so all three of
+ // these are the same source location: identical layers, differing layers, and
+ // layers versus none. See IntermediateLocEqualityWithLayers for the
+ // layer-aware comparison.
EXPECT_TRUE(DL1.isSameSourceLocation(DL2));
- // Same line/col/scope but different layers -> NOT the same source location.
- EXPECT_FALSE(DL1.isSameSourceLocation(DL3));
- // One has layers, the other doesn't -> NOT the same source location.
- EXPECT_FALSE(DL1.isSameSourceLocation(DL4));
+ EXPECT_TRUE(DL1.isSameSourceLocation(DL3));
+ EXPECT_TRUE(DL1.isSameSourceLocation(DL4));
}
+
TEST(DebugLocTest, MergedLocationWithIntermediate) {
LLVMContext Ctx;
auto M = std::make_unique<Module>("MyModule", Ctx);
>From ce7b99cf23c68383a343334f1005b44b68b6197a Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolovich at nvidia.com>
Date: Wed, 12 Aug 2026 23:41:29 +0000
Subject: [PATCH 04/12] [DebugInfo] Drop references to symbols this branch does
not carry
The isSameSourceLocation doc comment pointed at
isSameSourceLocationAndIRLayers, and a unit-test comment pointed at
IntermediateLocEqualityWithLayers. Neither exists here -- both belong to the
layer-aware comparison that lands separately -- so restore the original doc
comment and drop the dangling test reference.
Also removes a stray blank line that clang-format rejects.
---
llvm/include/llvm/IR/DebugLoc.h | 7 ++-----
llvm/unittests/IR/DebugInfoTest.cpp | 4 +---
2 files changed, 3 insertions(+), 8 deletions(-)
diff --git a/llvm/include/llvm/IR/DebugLoc.h b/llvm/include/llvm/IR/DebugLoc.h
index cb82a7119813b..7d748a4b12375 100644
--- a/llvm/include/llvm/IR/DebugLoc.h
+++ b/llvm/include/llvm/IR/DebugLoc.h
@@ -239,11 +239,8 @@ class DebugLoc {
appendInlinedAt(const DebugLoc &DL, DILocation *InlinedAt, LLVMContext &Ctx,
DenseMap<const MDNode *, MDNode *> &Cache);
- /// Return true if the source locations match, ignoring isImplicitCode,
- /// source atom info and intermediate-IR layers. Layers are deliberately not
- /// part of this comparison: two locations at the same source position are the
- /// same source position regardless of which intermediate IR they came from.
- /// Callers that must also match layers use isSameSourceLocationAndIRLayers.
+ /// Return true if the source locations match, ignoring isImplicitCode and
+ /// source atom info.
bool isSameSourceLocation(const DebugLoc &Other) const {
if (get() == Other.get())
return true;
diff --git a/llvm/unittests/IR/DebugInfoTest.cpp b/llvm/unittests/IR/DebugInfoTest.cpp
index b547ced372d9e..5d3993fe87236 100644
--- a/llvm/unittests/IR/DebugInfoTest.cpp
+++ b/llvm/unittests/IR/DebugInfoTest.cpp
@@ -1633,14 +1633,12 @@ TEST(DebugLocTest, IntermediateLocEquality) {
ASSERT_NE(DL1.get(), DL2.get());
// isSameSourceLocation compares the primary position only, so all three of
// these are the same source location: identical layers, differing layers, and
- // layers versus none. See IntermediateLocEqualityWithLayers for the
- // layer-aware comparison.
+ // layers versus none.
EXPECT_TRUE(DL1.isSameSourceLocation(DL2));
EXPECT_TRUE(DL1.isSameSourceLocation(DL3));
EXPECT_TRUE(DL1.isSameSourceLocation(DL4));
}
-
TEST(DebugLocTest, MergedLocationWithIntermediate) {
LLVMContext Ctx;
auto M = std::make_unique<Module>("MyModule", Ctx);
>From 84daa025d012bae44752b23bcbc4cba5269df811 Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolovich at nvidia.com>
Date: Fri, 14 Aug 2026 00:49:47 +0000
Subject: [PATCH 05/12] [DebugInfo] Document the irlayers contract and layer
merge behavior
State the invariants the verifier already enforces and the code already
implements: a DILayerLocList is non-empty, absence is spelled by omitting
irlayers:, and a list describes the inline frame it is attached to. Correct
"uniqued" to "normally uniqued" -- distinct forms are legal.
Also document how layers merge, which was previously unstated: the result keeps
the entries present in both locations, drops them entirely if either side has
none or the intersection is empty, and preserves LocA's order.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply at anthropic.com>
---
llvm/docs/LangRef.md | 15 ++++++++++-----
llvm/include/llvm/IR/DebugInfoMetadata.h | 2 +-
llvm/include/llvm/IR/DebugLoc.h | 6 ++++++
3 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index c1642705ab481..649a444474524 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -7173,7 +7173,10 @@ mandatory, and points at an {ref}`DILexicalBlockFile`, an
The optional `irlayers:` field points at a {ref}`DILayerLocList`, giving the
instruction's position in one or more intermediate IRs it was lowered through, in
addition to its primary source position. It is independent of `inlinedAt:`; a
-location may have either, both, or neither.
+location may have either, both, or neither. A location with no intermediate
+position omits the field entirely. The field belongs to the location that
+carries it: locations in an `inlinedAt:` chain may each have their own, and LLVM
+defines no relationship between them.
```text
!0 = !DILocation(line: 2900, column: 42, scope: !1, irlayers: !3)
@@ -7200,8 +7203,9 @@ context: it is a bare coordinate in a file, not a location in a scope tree.
##### DILayerLocList
-`DILayerLocList` nodes hold a list of {ref}`DILayerLoc` operands, and are
-referenced by a {ref}`DILocation`'s `irlayers:` field.
+`DILayerLocList` nodes hold a non-empty list of {ref}`DILayerLoc` operands, and
+are referenced by a {ref}`DILocation`'s `irlayers:` field. A location with no
+intermediate position omits `irlayers:` rather than referencing an empty list.
Operand order is preserved and is part of the node's identity — two lists with
the same entries in a different order are different nodes — and a consumer sees
@@ -7210,8 +7214,9 @@ not define which level comes first and does not check any particular arrangement
so any convention (such as listing levels in lowering order) is an agreement
between a producer and its consumer.
-Both node types are uniqued, so instructions sharing a position at some level
-share the corresponding node.
+Both node types are normally uniqued, so instructions sharing a position at some
+level share the corresponding node. `distinct` forms are legal; nothing in LLVM
+requires a layer node to be shared.
```text
!0 = !DILayerLocList(!1, !2)
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index ab6ad11913495..7d8a9d8766510 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -2717,7 +2717,7 @@ class DILayerLoc : public MDNode {
}
};
-/// A uniqued list of \a DILayerLoc entries.
+/// A normally uniqued list of \a DILayerLoc entries (`distinct` is legal).
///
/// The container node behind \a DILocation's optional `irlayers` operand:
/// essentially an \a MDTuple of \a DILayerLoc refs with typed accessors and its
diff --git a/llvm/include/llvm/IR/DebugLoc.h b/llvm/include/llvm/IR/DebugLoc.h
index 7d748a4b12375..0d59fe06b68f5 100644
--- a/llvm/include/llvm/IR/DebugLoc.h
+++ b/llvm/include/llvm/IR/DebugLoc.h
@@ -169,6 +169,12 @@ class DebugLoc {
/// be represented in a line entry. In this case, set line and column as 0
/// and use the scope of any location.
///
+ /// Intermediate-IR layers (`irlayers`) merge independently: the result keeps
+ /// the entries present in both, and none if either side has no layers or the
+ /// intersection is empty. Entries keep \p LocA's order and duplicate count,
+ /// so swapping the operands can yield a different list node with the same
+ /// entries -- LLVM assigns no meaning to layer order.
+ ///
/// \p LocA \p LocB: The locations to be merged.
LLVM_ABI static DebugLoc getMergedLocation(DebugLoc LocA, DebugLoc LocB);
>From f921152f377813796f5507764c70525e46ef7480 Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolovich at nvidia.com>
Date: Wed, 19 Aug 2026 23:08:11 +0000
Subject: [PATCH 06/12] fixed gcc build
---
llvm/include/llvm/IR/DebugInfoMetadata.h | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index 7d8a9d8766510..d2cbe1c4a7d3b 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -3074,7 +3074,9 @@ class DILocation : public MDNode {
/// Raw form: returns the operand without casting, so it is safe to call
/// before forward-ref resolution (the operand may still be a placeholder).
Metadata *getRawIRLayers() const {
- return HasIRLayers ? getOperand(getNumOperands() - 1) : nullptr;
+ if (HasIRLayers)
+ return getOperand(getNumOperands() - 1);
+ return nullptr;
}
DILayerLocList *getIRLayers() const {
return cast_if_present<DILayerLocList>(getRawIRLayers());
>From 0a726a41c03351aa09bece1834d355c4275d7bf8 Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolovich at nvidia.com>
Date: Wed, 19 Aug 2026 23:42:04 +0000
Subject: [PATCH 07/12] added LLVM_ABI
---
llvm/include/llvm/IR/DebugInfoMetadata.h | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index d2cbe1c4a7d3b..bbefceaaf0c13 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -2681,9 +2681,10 @@ class DILayerLoc : public MDNode {
}
~DILayerLoc() { dropAllReferences(); }
- static DILayerLoc *getImpl(LLVMContext &Context, MDString *Kind,
- Metadata *File, unsigned Line, unsigned Column,
- StorageType Storage, bool ShouldCreate = true);
+ LLVM_ABI static DILayerLoc *getImpl(LLVMContext &Context, MDString *Kind,
+ Metadata *File, unsigned Line,
+ unsigned Column, StorageType Storage,
+ bool ShouldCreate = true);
TempDILayerLoc cloneImpl() const {
return getTemporary(getContext(), getRawKind(), getRawFile(), getLine(),
@@ -2738,9 +2739,10 @@ class DILayerLocList : public MDNode {
void setHash(unsigned Hash) { SubclassData32 = Hash; }
void recalculateHash();
- static DILayerLocList *getImpl(LLVMContext &Context,
- ArrayRef<Metadata *> Layers,
- StorageType Storage, bool ShouldCreate = true);
+ LLVM_ABI static DILayerLocList *getImpl(LLVMContext &Context,
+ ArrayRef<Metadata *> Layers,
+ StorageType Storage,
+ bool ShouldCreate = true);
TempDILayerLocList cloneImpl() const {
return getTemporary(getContext(), SmallVector<Metadata *, 4>(operands()));
>From 1c58c86a4c24b57400edaf77c059bb2a34c333cb Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolovich at nvidia.com>
Date: Tue, 1 Sep 2026 02:18:31 +0000
Subject: [PATCH 08/12] switched to two abbrevs
---
llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 66 +++++++++++++++----
llvm/test/Bitcode/intermediate-loc-abbrev.ll | 60 +++++++++++++++++
.../Bitcode/intermediate-loc-roundtrip.ll | 13 ++++
3 files changed, 128 insertions(+), 11 deletions(-)
create mode 100644 llvm/test/Bitcode/intermediate-loc-abbrev.ll
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 4edba342fed48..7c0a9ce741000 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -159,6 +159,7 @@ enum {
FUNCTION_INST_CMP_FLAGS_ABBREV,
FUNCTION_DEBUG_RECORD_VALUE_ABBREV,
FUNCTION_DEBUG_LOC_ABBREV,
+ FUNCTION_DEBUG_LOC_LAYERS_ABBREV,
};
/// Abstract class to manage the bitcode writing, subclassed for each bitcode
@@ -307,6 +308,11 @@ class ModuleBitcodeWriter : public ModuleBitcodeWriterBase {
/// The start bit of the identification block.
uint64_t BitcodeStartBit;
+ /// Abbrev for DILocations that carry an irlayers operand. Locations without
+ /// layers use the shorter abbrev threaded through writeDILocation's \p
+ /// Abbrev parameter, so they pay nothing for a field they do not use.
+ unsigned DILocationLayersAbbrev = 0;
+
public:
/// Constructs a ModuleBitcodeWriter object for the given Module,
/// writing to the provided \p Buffer.
@@ -337,7 +343,7 @@ class ModuleBitcodeWriter : public ModuleBitcodeWriterBase {
SmallVectorImpl<uint64_t> &Record);
void writeMDTuple(const MDTuple *N, SmallVectorImpl<uint64_t> &Record,
unsigned Abbrev);
- unsigned createDILocationAbbrev();
+ unsigned createDILocationAbbrev(bool WithIRLayers);
void writeDILocation(const DILocation *N, SmallVectorImpl<uint64_t> &Record,
unsigned &Abbrev);
void writeDILayerLoc(const DILayerLoc *N, SmallVectorImpl<uint64_t> &Record,
@@ -1889,9 +1895,12 @@ void ModuleBitcodeWriter::writeMDTuple(const MDTuple *N,
Record.clear();
}
-unsigned ModuleBitcodeWriter::createDILocationAbbrev() {
+unsigned ModuleBitcodeWriter::createDILocationAbbrev(bool WithIRLayers) {
// Assume the column is usually under 128, and always output the inlined-at
// location (it's never more expensive than building an array size 1).
+ //
+ // Separate abbrev so a location without layers does not spend a VBR chunk
+ // encoding a zero; writeDILocation picks between the two per record.
auto Abbv = std::make_shared<BitCodeAbbrev>();
Abbv->Add(BitCodeAbbrevOp(bitc::METADATA_LOCATION));
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isDistinct
@@ -1902,7 +1911,8 @@ unsigned ModuleBitcodeWriter::createDILocationAbbrev() {
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isImplicitCode
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // atomGroup
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // atomRank
- Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // irlayers (0 = none)
+ if (WithIRLayers)
+ Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // irlayers
return Stream.EmitAbbrev(std::move(Abbv));
}
@@ -1910,7 +1920,7 @@ void ModuleBitcodeWriter::writeDILocation(const DILocation *N,
SmallVectorImpl<uint64_t> &Record,
unsigned &Abbrev) {
if (!Abbrev)
- Abbrev = createDILocationAbbrev();
+ Abbrev = createDILocationAbbrev(/*WithIRLayers=*/false);
Record.push_back(N->isDistinct());
Record.push_back(N->getLine());
@@ -1920,8 +1930,16 @@ void ModuleBitcodeWriter::writeDILocation(const DILocation *N,
Record.push_back(N->isImplicitCode());
Record.push_back(N->getAtomGroup());
Record.push_back(N->getAtomRank());
- Record.push_back(VE.getMetadataOrNullID(N->getRawIRLayers()));
- Stream.EmitRecord(bitc::METADATA_LOCATION, Record, Abbrev);
+
+ unsigned AbbrevToUse = Abbrev;
+ if (Metadata *IRLayers = N->getRawIRLayers()) {
+ if (!DILocationLayersAbbrev)
+ DILocationLayersAbbrev = createDILocationAbbrev(/*WithIRLayers=*/true);
+ AbbrevToUse = DILocationLayersAbbrev;
+ Record.push_back(VE.getMetadataOrNullID(IRLayers));
+ }
+
+ Stream.EmitRecord(bitc::METADATA_LOCATION, Record, AbbrevToUse);
Record.clear();
}
@@ -2683,7 +2701,9 @@ void ModuleBitcodeWriter::writeModuleMetadata() {
std::vector<unsigned> MDAbbrevs;
MDAbbrevs.resize(MetadataAbbrev::LastPlusOne);
- MDAbbrevs[MetadataAbbrev::DILocationAbbrevID] = createDILocationAbbrev();
+ MDAbbrevs[MetadataAbbrev::DILocationAbbrevID] =
+ createDILocationAbbrev(/*WithIRLayers=*/false);
+ DILocationLayersAbbrev = createDILocationAbbrev(/*WithIRLayers=*/true);
MDAbbrevs[MetadataAbbrev::GenericDINodeAbbrevID] =
createGenericDINodeAbbrev();
@@ -2774,6 +2794,9 @@ void ModuleBitcodeWriter::writeFunctionMetadata(const Function &F) {
return;
Stream.EnterSubblock(bitc::METADATA_BLOCK_ID, 3);
+ // New block, new abbrev id space. Unlike the module block this one is not
+ // randomly accessed, so the irlayers abbrev can be created on first use.
+ DILocationLayersAbbrev = 0;
SmallVector<uint64_t, 64> Record;
writeMetadataStrings(VE.getMDStrings(), Record);
writeMetadataRecords(VE.getNonMDStrings(), Record);
@@ -3901,9 +3924,14 @@ void ModuleBitcodeWriter::writeFunction(
Vals.push_back(DL->isImplicitCode());
Vals.push_back(DL->getAtomGroup());
Vals.push_back(DL->getAtomRank());
- Vals.push_back(VE.getMetadataOrNullID(DL->getRawIRLayers()));
- Stream.EmitRecord(bitc::FUNC_CODE_DEBUG_LOC, Vals,
- FUNCTION_DEBUG_LOC_ABBREV);
+
+ unsigned DLAbbrev = FUNCTION_DEBUG_LOC_ABBREV;
+ if (Metadata *IRLayers = DL->getRawIRLayers()) {
+ DLAbbrev = FUNCTION_DEBUG_LOC_LAYERS_ABBREV;
+ Vals.push_back(VE.getMetadataOrNullID(IRLayers));
+ }
+
+ Stream.EmitRecord(bitc::FUNC_CODE_DEBUG_LOC, Vals, DLAbbrev);
Vals.clear();
LastDL = DL;
}
@@ -4310,11 +4338,27 @@ void ModuleBitcodeWriter::writeBlockInfo() {
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1));
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Atom group.
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 3)); // Atom rank.
- Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // irlayers (0 = none).
if (Stream.EmitBlockInfoAbbrev(bitc::FUNCTION_BLOCK_ID, Abbv) !=
FUNCTION_DEBUG_LOC_ABBREV)
llvm_unreachable("Unexpected abbrev ordering!");
}
+ { // DEBUG_LOC abbrev for FUNCTION_BLOCK, irlayers variant.
+ // Separate abbrev so a location without layers does not spend a VBR chunk
+ // encoding a zero; writeInstruction picks between the two per record.
+ auto Abbv = std::make_shared<BitCodeAbbrev>();
+ Abbv->Add(BitCodeAbbrevOp(bitc::FUNC_CODE_DEBUG_LOC));
+ Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6));
+ Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8));
+ Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6));
+ Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6));
+ Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1));
+ Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Atom group.
+ Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 3)); // Atom rank.
+ Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // irlayers.
+ if (Stream.EmitBlockInfoAbbrev(bitc::FUNCTION_BLOCK_ID, Abbv) !=
+ FUNCTION_DEBUG_LOC_LAYERS_ABBREV)
+ llvm_unreachable("Unexpected abbrev ordering!");
+ }
Stream.ExitBlock();
}
diff --git a/llvm/test/Bitcode/intermediate-loc-abbrev.ll b/llvm/test/Bitcode/intermediate-loc-abbrev.ll
new file mode 100644
index 0000000000000..3044280353f87
--- /dev/null
+++ b/llvm/test/Bitcode/intermediate-loc-abbrev.ll
@@ -0,0 +1,60 @@
+; RUN: llvm-as < %s | llvm-bcanalyzer -dump | FileCheck %s
+
+;; A DILocation with no `irlayers` should not pay for the operand. The writer
+;; has two abbrevs per record kind and picks per record, so an unlayered
+;; location comes out one operand shorter.
+;;
+;; The trailing "/>" is the real check: it proves the operand is absent, not
+;; just zero. A VBR field costs its full width even for zero, so always using
+;; the wide abbrev would still round-trip while growing every record.
+
+;; Layers sit on the location with no inlinedAt -- the root of the inline chain.
+;; Not inlined here, so that is the instruction's own location.
+define void @layered() !dbg !8 {
+ ret void, !dbg !20
+}
+
+;; Inlined, so the layers move to the chain root: !21 and !22 carry none and
+;; their inlinedAt targets do. Those targets are enumerated as nodes, which is
+;; what puts a layered location into the metadata block.
+define void @inlined(i32 %x) !dbg !9 {
+ %a = add i32 %x, 1, !dbg !21
+ ret void, !dbg !22
+}
+
+;; DEBUG_LOC, in the function block: 8 operands layered, 7 unlayered. op3 is the
+;; inlinedAt id, so it is 0 on the layered one and set on the other two.
+; CHECK-DAG: <DEBUG_LOC abbrevid={{[0-9]+}} op0=2 op1=1 op2={{[0-9]+}} op3=0 op4=0 op5=0 op6=0 op7={{[0-9]+}}/>
+; CHECK-DAG: <DEBUG_LOC abbrevid={{[0-9]+}} op0=3 op1=1 op2={{[0-9]+}} op3={{[0-9]+}} op4=0 op5=0 op6=0/>
+; CHECK-DAG: <DEBUG_LOC abbrevid={{[0-9]+}} op0=4 op1=1 op2={{[0-9]+}} op3={{[0-9]+}} op4=0 op5=0 op6=0/>
+
+;; LOCATION, in the metadata block: 9 operands layered, 8 unlayered. Instruction
+;; locations are never enumerated as nodes, so these are the inlinedAt targets
+;; from @inlined -- which is how a layered location reaches this record kind.
+; CHECK-DAG: <LOCATION abbrevid={{[0-9]+}} op0=0 op1=10 op2=1 op3={{[0-9]+}} op4=0 op5=0 op6=0 op7=0 op8={{[0-9]+}}/>
+; CHECK-DAG: <LOCATION abbrevid={{[0-9]+}} op0=0 op1=11 op2=1 op3={{[0-9]+}} op4=0 op5=0 op6=0 op7=0/>
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+!1 = !DIFile(filename: "test.cu", directory: "/test")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!8 = distinct !DISubprogram(name: "layered", scope: !1, file: !1, line: 1, type: !10, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0)
+!9 = distinct !DISubprogram(name: "inlined", scope: !1, file: !1, line: 1, type: !10, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0)
+!10 = !DISubroutineType(types: !11)
+!11 = !{null}
+
+!14 = !DIFile(filename: "intermediate.tileir", directory: ".")
+!30 = !DILayerLocList(!31)
+!31 = !DILayerLoc(line: 100, column: 10, file: !14, kind: "TileIR")
+
+!20 = !DILocation(line: 2, column: 1, scope: !8, irlayers: !30)
+!21 = !DILocation(line: 3, column: 1, scope: !9, inlinedAt: !40)
+!22 = !DILocation(line: 4, column: 1, scope: !9, inlinedAt: !41)
+
+;; Chain roots. !40 carries the layers for the instruction inlined at it; !41
+;; has none, to cover the narrow LOCATION.
+!40 = !DILocation(line: 10, column: 1, scope: !9, irlayers: !30)
+!41 = !DILocation(line: 11, column: 1, scope: !9)
diff --git a/llvm/test/Bitcode/intermediate-loc-roundtrip.ll b/llvm/test/Bitcode/intermediate-loc-roundtrip.ll
index 89e060e3efbf1..09b6c93914c35 100644
--- a/llvm/test/Bitcode/intermediate-loc-roundtrip.ll
+++ b/llvm/test/Bitcode/intermediate-loc-roundtrip.ll
@@ -12,12 +12,17 @@ entry:
%v.addr = alloca ptr, align 8
store ptr %v, ptr %v.addr, align 8, !dbg !20
%0 = load ptr, ptr %v.addr, align 8, !dbg !21
+ ;; A location with no layers at all, so that one module exercises both record
+ ;; shapes: layered locations carry the irlayers operand and unlayered ones are
+ ;; written with a shorter abbrev that omits it entirely.
+ %1 = load ptr, ptr %v.addr, align 8, !dbg !23
ret void, !dbg !22
}
; CHECK-LABEL: define dso_local void @test_kernel
; CHECK: store ptr %v, ptr %v.addr, align 8, !dbg ![[DBG1:[0-9]+]]
; CHECK: load ptr, ptr %v.addr, align 8, !dbg ![[DBG2:[0-9]+]]
+; CHECK: load ptr, ptr %v.addr, align 8, !dbg ![[DBG4:[0-9]+]]
; CHECK: ret void, !dbg ![[DBG3:[0-9]+]]
;; Verify the metadata structure is preserved: each !dbg is a DILocation whose
@@ -28,6 +33,11 @@ entry:
; CHECK-DAG: ![[DBG2]] = !DILocation(line: 3, column: 5, scope: ![[SP]], irlayers: ![[LIST]])
; CHECK-DAG: ![[DBG3]] = !DILocation(line: 4, column: 1, scope: ![[SP]], irlayers: ![[LIST]])
+;; The unlayered location must come back with no irlayers operand. The closing
+;; paren is the assertion -- it is what distinguishes "absent" from "present but
+;; empty".
+; CHECK-DAG: ![[DBG4]] = !DILocation(line: 5, column: 5, scope: ![[SP]])
+
;; The shared layer list holds one DILayerLoc with the kind string and the
;; intermediate coordinate.
; CHECK-DAG: ![[LIST]] = !DILayerLocList(![[LAYER:[0-9]+]])
@@ -55,3 +65,6 @@ entry:
!20 = !DILocation(line: 2, column: 5, scope: !8, irlayers: !30)
!21 = !DILocation(line: 3, column: 5, scope: !8, irlayers: !30)
!22 = !DILocation(line: 4, column: 1, scope: !8, irlayers: !30)
+
+;; Unlayered location, sharing the same scope.
+!23 = !DILocation(line: 5, column: 5, scope: !8)
>From e20e53ed3aab450f9256e0599c76227f94a278f0 Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolovich at nvidia.com>
Date: Tue, 1 Sep 2026 03:35:44 +0000
Subject: [PATCH 09/12] addressed suggestions on comments and small code
changes
---
llvm/docs/KeyInstructionsDebugInfo.md | 2 +-
llvm/docs/LangRef.md | 9 +++------
llvm/include/llvm/IR/DebugInfoMetadata.h | 23 +++++++---------------
llvm/lib/Bitcode/Reader/MetadataLoader.cpp | 10 +++-------
llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 8 ++++----
llvm/lib/IR/DebugInfoMetadata.cpp | 16 +++++++--------
6 files changed, 25 insertions(+), 43 deletions(-)
diff --git a/llvm/docs/KeyInstructionsDebugInfo.md b/llvm/docs/KeyInstructionsDebugInfo.md
index 0ea9b84c180f1..d40c2f3a0e3ea 100644
--- a/llvm/docs/KeyInstructionsDebugInfo.md
+++ b/llvm/docs/KeyInstructionsDebugInfo.md
@@ -45,7 +45,7 @@ From the perspective of a source-level debugger user:
Details:
-1. *The metadata* - The two new `DILocation` fields are `atomGroup` and `atomRank` and are both are unsigned integers. `atomGroup` is 60 bits and `atomRank` 3 bits; the remaining bit of the 64-bit field is `DILocation`'s `irlayers` flag. A group that does not fit in 60 bits is clamped to 0, so an out-of-range value means "not interesting" rather than silently aliasing another group. Instructions in the same function with the same `(atomGroup, inlinedAt)` pair are part of the same source atom. `atomRank` determines `is_stmt` preference within that group, where a lower number is higher precedence. Higher rank instructions act as "backup" `is_stmt` locations, providing good fallback locations if/when the primary candidate gets optimized away. The default values of 0 indicate the instruction isn’t interesting - it's not an `is_stmt` candidate. If `keyInstructions` in `DISubprogram` is false (default) then the new `DILocation` metadata is ignored for the function (including inlined instances) when emitting DWARF.
+1. *The metadata* - The two new `DILocation` fields are `atomGroup` and `atomRank` and are both are unsigned integers. `atomGroup` is 60 bits and `atomRank` 3 bits; the remaining bit of the 64-bit field is `DILocation`'s `irlayers` flag. Instructions in the same function with the same `(atomGroup, inlinedAt)` pair are part of the same source atom. `atomRank` determines `is_stmt` preference within that group, where a lower number is higher precedence. Higher rank instructions act as "backup" `is_stmt` locations, providing good fallback locations if/when the primary candidate gets optimized away. The default values of 0 indicate the instruction isn’t interesting - it's not an `is_stmt` candidate. If `keyInstructions` in `DISubprogram` is false (default) then the new `DILocation` metadata is ignored for the function (including inlined instances) when emitting DWARF.
2. *Clang annotates key instructions* with the new metadata. Variable assignments (stores, memory intrinsics), control flow (branches and their conditions, some unconditional branches), and exception handling instructions are annotated. Calls are ignored as they're unconditionally marked `is_stmt`.
diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index 649a444474524..d69f35a02a851 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -7207,12 +7207,9 @@ context: it is a bare coordinate in a file, not a location in a scope tree.
are referenced by a {ref}`DILocation`'s `irlayers:` field. A location with no
intermediate position omits `irlayers:` rather than referencing an empty list.
-Operand order is preserved and is part of the node's identity — two lists with
-the same entries in a different order are different nodes — and a consumer sees
-the entries in that order. LLVM itself attaches no meaning to the order: it does
-not define which level comes first and does not check any particular arrangement,
-so any convention (such as listing levels in lowering order) is an agreement
-between a producer and its consumer.
+The operands are a sequence: order is part of the node's identity, so two lists
+with the same entries in a different order are different nodes. LLVM attaches no
+meaning to the order and does not require any particular arrangement.
Both node types are normally uniqued, so instructions sharing a position at some
level share the corresponding node. `distinct` forms are legal; nothing in LLVM
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index bbefceaaf0c13..ac95306f1e179 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -2661,10 +2661,9 @@ class DISubprogram : public DILocalScope {
/// A single intermediate-IR layer location.
///
/// One source coordinate in an intermediate IR level (e.g. TileIR, MLIR) that
-/// sits between the high-level source and the final LLVM IR. Lightweight: it
-/// references its \a DIFile directly (no scope, no discriminator) and stores
-/// line/column in the free Metadata subclass-data slots. Grouped behind a
-/// \a DILayerLocList on \a DILocation's optional `irlayers` operand.
+/// sits between the high-level source and the final LLVM IR. It has no scope
+/// and references its \a DIFile directly. Grouped behind a \a DILayerLocList on
+/// \a DILocation's optional `irlayers` operand.
///
/// Uses the SubclassData16 and SubclassData32 Metadata slots.
class DILayerLoc : public MDNode {
@@ -2718,13 +2717,7 @@ class DILayerLoc : public MDNode {
}
};
-/// A normally uniqued list of \a DILayerLoc entries (`distinct` is legal).
-///
-/// The container node behind \a DILocation's optional `irlayers` operand:
-/// essentially an \a MDTuple of \a DILayerLoc refs with typed accessors and its
-/// own metadata kind, so consumers can type-check it with
-/// `isa<DILayerLocList>`. Operand order is preserved and is part of the node's
-/// identity, but LLVM assigns it no meaning.
+/// A sequence of \a DILayerLoc entries.
class DILayerLocList : public MDNode {
friend class LLVMContextImpl;
friend class MDNode;
@@ -2745,7 +2738,7 @@ class DILayerLocList : public MDNode {
bool ShouldCreate = true);
TempDILayerLocList cloneImpl() const {
- return getTemporary(getContext(), SmallVector<Metadata *, 4>(operands()));
+ return getTemporary(getContext(), SmallVector<Metadata *>(operands()));
}
public:
@@ -3064,10 +3057,8 @@ class DILocation : public MDNode {
Metadata *getRawScope() const { return getOperand(0); }
Metadata *getRawInlinedAt() const {
- // Layout: [scope, (inlinedAt?), (irlayers?)]. irlayers, when present, is
- // always the last operand; discount it before the inlinedAt count trick.
- unsigned NonLayerOps = getNumOperands() - (HasIRLayers ? 1 : 0);
- if (NonLayerOps == 2)
+ // irlayers, when present, is always the last operand.
+ if (getNumOperands() - HasIRLayers == 2)
return getOperand(1);
return nullptr;
}
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index 4fa47f7db0e21..b1dd965abda37 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -1472,12 +1472,8 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
break;
}
case bitc::METADATA_LOCATION: {
- // Defined shapes: 5 (through inlinedAt), 6 (isImplicit), 8 (adds the Key
- // Instructions fields), 9 (adds irlayers). Anything longer is a record from
- // a newer writer and its trailing fields are ignored, which is what makes
- // this encoding append-only; anything shorter, or a 7 (an atomGroup with no
- // atomRank), is a shape no writer produces.
- if (Record.size() < 5 || Record.size() == 7)
+ // 9: irlayers.
+ if (Record.size() < 5 || Record.size() == 7 || Record.size() > 9)
return error("Invalid record");
IsDistinct = Record[0];
@@ -1521,7 +1517,7 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
IsDistinct = Record[0];
SmallVector<Metadata *, 4> Elts;
for (unsigned I = 1, E = Record.size(); I != E; ++I)
- Elts.push_back(getMDOrNull(Record[I]));
+ Elts.push_back(getMD(Record[I]));
MetadataList.assignValue(GET_OR_DISTINCT(DILayerLocList, (Context, Elts)),
NextMetadataNo);
NextMetadataNo++;
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 7c0a9ce741000..3decb8be8433d 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -1932,7 +1932,7 @@ void ModuleBitcodeWriter::writeDILocation(const DILocation *N,
Record.push_back(N->getAtomRank());
unsigned AbbrevToUse = Abbrev;
- if (Metadata *IRLayers = N->getRawIRLayers()) {
+ if (DILayerLocList *IRLayers = N->getIRLayers()) {
if (!DILocationLayersAbbrev)
DILocationLayersAbbrev = createDILocationAbbrev(/*WithIRLayers=*/true);
AbbrevToUse = DILocationLayersAbbrev;
@@ -1959,8 +1959,8 @@ void ModuleBitcodeWriter::writeDILayerLocList(const DILayerLocList *N,
SmallVectorImpl<uint64_t> &Record,
unsigned Abbrev) {
Record.push_back(N->isDistinct());
- for (auto &I : N->operands())
- Record.push_back(VE.getMetadataOrNullID(I));
+ for (const MDOperand &Op : N->layers())
+ Record.push_back(VE.getMetadataID(Op.get()));
Stream.EmitRecord(bitc::METADATA_LAYERLOCLIST, Record, Abbrev);
Record.clear();
}
@@ -3926,7 +3926,7 @@ void ModuleBitcodeWriter::writeFunction(
Vals.push_back(DL->getAtomRank());
unsigned DLAbbrev = FUNCTION_DEBUG_LOC_ABBREV;
- if (Metadata *IRLayers = DL->getRawIRLayers()) {
+ if (DILayerLocList *IRLayers = DL->getIRLayers()) {
DLAbbrev = FUNCTION_DEBUG_LOC_LAYERS_ABBREV;
Vals.push_back(VE.getMetadataOrNullID(IRLayers));
}
diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 779742f5a02da..9f5117a3bb61c 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -295,13 +295,12 @@ static Metadata *mergeIRLayers(LLVMContext &C, const DILocation *LocA,
L->getColumn());
};
SmallDenseSet<LayerKey, 2> BLayers;
- for (unsigned I = 0, E = LB->getNumLayers(); I != E; ++I)
- if (const DILayerLoc *R = LB->getLayer(I))
- BLayers.insert(keyOf(R));
+ for (const MDOperand &Op : LB->layers())
+ BLayers.insert(keyOf(cast<DILayerLoc>(Op.get())));
SmallVector<Metadata *, 2> Keep;
- for (unsigned I = 0, E = LA->getNumLayers(); I != E; ++I) {
- DILayerLoc *L = LA->getLayer(I);
- if (L && BLayers.contains(keyOf(L)))
+ for (const MDOperand &Op : LA->layers()) {
+ auto *L = cast<DILayerLoc>(Op.get());
+ if (BLayers.contains(keyOf(L)))
Keep.push_back(L);
}
if (Keep.empty())
@@ -401,9 +400,8 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) {
if (L1->getScope()->getSubprogram() != L2->getScope()->getSubprogram())
return nullptr;
- // Each merged frame keeps the intersection of the two frames'
- // intermediate-IR layer sets, so a layer on any frame (e.g. the shared
- // outermost kernel frame) survives the merge.
+ // Each merged location keeps the intersection of the two inputs'
+ // intermediate-IR layer sets, so a layer common to both survives.
Metadata *MergedLayers = mergeIRLayers(C, L1, L2);
// Find nearest common scope inside subprogram.
>From 53ed184ae6756ecc61d8f5a36eed2bc36cb7f806 Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolovich at nvidia.com>
Date: Tue, 29 Sep 2026 21:55:57 +0000
Subject: [PATCH 10/12] addressed comments
---
llvm/lib/Bitcode/Reader/MetadataLoader.cpp | 2 +-
.../intermediate-loc-metadata-location.ll | 18 +-----
llvm/test/Verifier/intermediate-layerloc.ll | 62 +++++++++++--------
3 files changed, 40 insertions(+), 42 deletions(-)
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index b1dd965abda37..94737da7fd8fc 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -1472,7 +1472,7 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
break;
}
case bitc::METADATA_LOCATION: {
- // 9: irlayers.
+ // 5: inlinedAt, 6: isImplicit, 8: Key Instructions fields, 9: irlayers.
if (Record.size() < 5 || Record.size() == 7 || Record.size() > 9)
return error("Invalid record");
diff --git a/llvm/test/Bitcode/intermediate-loc-metadata-location.ll b/llvm/test/Bitcode/intermediate-loc-metadata-location.ll
index 89d16e12e5875..01014b391c8ad 100644
--- a/llvm/test/Bitcode/intermediate-loc-metadata-location.ll
+++ b/llvm/test/Bitcode/intermediate-loc-metadata-location.ll
@@ -1,13 +1,11 @@
; RUN: llvm-as < %s | llvm-dis | FileCheck %s
;;
;; Checks that irlayers survive a bitcode round-trip on a DILocation written as
-;; a METADATA_LOCATION record -- one reachable only as an inlinedAt target, with
-;; a layer list built through forward references.
+;; a METADATA_LOCATION record -- one reachable only as an inlinedAt target.
define dso_local void @test_kernel(ptr noundef %v) #0 !dbg !8 {
entry:
store ptr %v, ptr %v, align 8, !dbg !20
- store ptr %v, ptr %v, align 8, !dbg !23
ret void, !dbg !21
}
@@ -28,20 +26,13 @@ attributes #0 = { noinline optnone }
!14 = !DIFile(filename: "kernel.tileir", directory: ".", checksumkind: CSK_MD5, checksum: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", source: "tile ir text")
!15 = !DIFile(filename: "kernel.gpuir", directory: ".", checksumkind: CSK_MD5, checksum: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", source: "gpu ir text")
-;; !18 precedes its entries, so they parse as forward references and the list is
-;; re-uniqued once they resolve. !22 names the same entries after they resolve,
-;; so it must land on !18's node -- a stale cached hash would miss the uniquing
-;; lookup and produce a second, identical list.
-!18 = !DILayerLocList(!16, !17)
!16 = !DILayerLoc(line: 100, column: 1, file: !14, kind: "tile ir")
!17 = !DILayerLoc(line: 7, column: 3, file: !15, kind: "gpu ir")
-!22 = !DILayerLocList(!16, !17)
+!18 = !DILayerLocList(!16, !17)
!19 = distinct !DILocation(line: 30, column: 1, scope: !8, irlayers: !18)
-!24 = distinct !DILocation(line: 40, column: 1, scope: !8, irlayers: !22)
!20 = !DILocation(line: 21, column: 5, scope: !11, inlinedAt: !19)
!21 = !DILocation(line: 22, column: 1, scope: !11, inlinedAt: !19)
-!23 = !DILocation(line: 23, column: 5, scope: !11, inlinedAt: !24)
;; The metadata-block location keeps its layers, and the two entries keep their
;; order.
@@ -50,9 +41,6 @@ attributes #0 = { noinline optnone }
; CHECK-DAG: ![[TILE]] = !DILayerLoc(line: 100, column: 1, file: !{{[0-9]+}}, kind: "tile ir")
; CHECK-DAG: ![[GPU]] = !DILayerLoc(line: 7, column: 3, file: !{{[0-9]+}}, kind: "gpu ir")
-;; Both layered locations reference that one list node.
-; CHECK-DAG: = distinct !DILocation(line: 40, column: 1, scope: !{{[0-9]+}}, irlayers: ![[LIST]])
-
-;; The instruction locations reference them as inlinedAt and carry no layers of
+;; The instruction locations reference it as inlinedAt and carry no layers of
;; their own.
; CHECK-DAG: !DILocation(line: 21, column: 5, scope: !{{[0-9]+}}, inlinedAt: ![[IA]])
diff --git a/llvm/test/Verifier/intermediate-layerloc.ll b/llvm/test/Verifier/intermediate-layerloc.ll
index c5409ffc168a7..afb8147a2b96c 100644
--- a/llvm/test/Verifier/intermediate-layerloc.ll
+++ b/llvm/test/Verifier/intermediate-layerloc.ll
@@ -1,37 +1,47 @@
-; RUN: llvm-as -disable-output %s -o - 2>&1 | FileCheck --allow-empty %s
-;;
-;; Verification of DILayerLoc is structural only, so a checksum-less intermediate
-;; file verifies clean -- with or without source.
+; RUN: llvm-as -disable-output %s -o - 2>&1 | FileCheck %s
-; CHECK-NOT: requires a checksum
-; CHECK-NOT: ignoring invalid debug info
-
-define void @k(ptr %p) !dbg !5 {
- store ptr null, ptr %p, align 8, !dbg !20
- store ptr null, ptr %p, align 8, !dbg !21
+define void @f(ptr %p) !dbg !5 {
+; CHECK: irlayers must be a DILayerLocList
+ store i32 0, ptr %p, !dbg !20
+; CHECK: layer kind must be a non-null MDString
+ store i32 1, ptr %p, !dbg !21
+; CHECK: layer file must be a non-null DIFile
+ store i32 2, ptr %p, !dbg !22
+; CHECK: DILayerLocList must be non-empty
+ store i32 3, ptr %p, !dbg !23
+; CHECK: DILayerLocList entry must be a DILayerLoc
+ store i32 4, ptr %p, !dbg !24
ret void
}
+; CHECK: warning: ignoring invalid debug info
+
!llvm.dbg.cu = !{!0}
-!llvm.module.flags = !{!2, !3}
+!llvm.module.flags = !{!2}
!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, emissionKind: FullDebug)
-!1 = !DIFile(filename: "test.cu", directory: "/test")
-!2 = !{i32 7, !"Dwarf Version", i32 2}
-!3 = !{i32 2, !"Debug Info Version", i32 3}
+!1 = !DIFile(filename: "t.c", directory: "/")
+!2 = !{i32 2, !"Debug Info Version", i32 3}
!4 = !DISubroutineType(types: !{null})
-!5 = distinct !DISubprogram(name: "k", scope: !1, file: !1, line: 1, type: !4, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0)
+!5 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !4, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0)
+
+!10 = !DIFile(filename: "t.tileir", directory: "/")
+!11 = !DILayerLoc(line: 1, file: !10, kind: "tile ir")
+
+;; irlayers points at a layer rather than a list.
+!20 = !DILocation(line: 2, scope: !5, irlayers: !11)
+
+;; An empty kind string parses to a null kind.
+!21 = !DILocation(line: 3, scope: !5, irlayers: !30)
+!30 = !DILayerLocList(!31)
+!31 = !DILayerLoc(line: 1, file: !10, kind: "")
-;; No checksum, no source.
-!10 = !DIFile(filename: "kernel.tileir", directory: ".")
-!11 = !DILayerLoc(line: 42, column: 5, file: !10, kind: "tile ir")
-!12 = !DILayerLocList(!11)
+!22 = !DILocation(line: 4, scope: !5, irlayers: !32)
+!32 = !DILayerLocList(!33)
+!33 = !DILayerLoc(line: 1, file: !4, kind: "tile ir")
-;; Source but still no checksum -- the case NVPTX would otherwise use a digest
-;; for, and the one that used to be rejected outright.
-!13 = !DIFile(filename: "kernel.gpuir", directory: ".", source: "gpu ir source")
-!14 = !DILayerLoc(line: 7, column: 1, file: !13, kind: "gpu ir")
-!15 = !DILayerLocList(!14)
+!23 = !DILocation(line: 5, scope: !5, irlayers: !34)
+!34 = !DILayerLocList()
-!20 = !DILocation(line: 2, column: 5, scope: !5, irlayers: !12)
-!21 = !DILocation(line: 3, column: 5, scope: !5, irlayers: !15)
+!24 = !DILocation(line: 6, scope: !5, irlayers: !35)
+!35 = !DILayerLocList(!10)
>From 47afc359b066407abb1bb621dc8ada5a0d117069 Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolovich at nvidia.com>
Date: Wed, 30 Sep 2026 00:25:20 +0000
Subject: [PATCH 11/12] one more pass to make comments/names domain agnostic
---
llvm/docs/LangRef.md | 8 +-
llvm/include/llvm/IR/DebugInfoMetadata.h | 2 +-
llvm/lib/IR/DebugLoc.cpp | 10 +-
llvm/test/Bitcode/intermediate-loc-abbrev.ll | 6 +-
.../intermediate-loc-metadata-location.ll | 20 ++--
.../Bitcode/intermediate-loc-roundtrip.ll | 16 +--
.../instructions-debug-location-irlayers.mir | 6 +-
.../transfer-debug-info-irlayers.ll | 40 +++----
llvm/test/Verifier/intermediate-layerloc.ll | 6 +-
llvm/unittests/IR/DebugInfoTest.cpp | 107 ++++++++----------
10 files changed, 106 insertions(+), 115 deletions(-)
diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index d69f35a02a851..b245270ecb913 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -7187,7 +7187,7 @@ defines no relationship between them.
##### DILayerLoc
`DILayerLoc` nodes represent a source position in one intermediate IR level that
-a program was lowered through — for example a tile IR or an MLIR module produced
+a program was lowered through — for example an MLIR module produced
part-way through compilation. The `kind:` field names the level and the `file:`
field points at a {ref}`DIFile` for it; both are mandatory. `line:` and
`column:` are the position within that file.
@@ -7196,7 +7196,7 @@ Unlike a {ref}`DILocation`, a `DILayerLoc` has no scope and no inlined-at
context: it is a bare coordinate in a file, not a location in a scope tree.
```text
-!0 = !DILayerLoc(line: 100, column: 1, file: !1, kind: "tile ir")
+!0 = !DILayerLoc(line: 100, column: 1, file: !1, kind: "HighLevelIR")
```
(DILayerLocList)=
@@ -7217,8 +7217,8 @@ requires a layer node to be shared.
```text
!0 = !DILayerLocList(!1, !2)
-!1 = !DILayerLoc(line: 100, column: 1, file: !3, kind: "tile ir")
-!2 = !DILayerLoc(line: 7, column: 3, file: !4, kind: "gpu ir")
+!1 = !DILayerLoc(line: 100, column: 1, file: !3, kind: "HighLevelIR")
+!2 = !DILayerLoc(line: 7, column: 3, file: !4, kind: "LowLevelIR")
```
(DILocalVariable)=
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index ac95306f1e179..a54498bafaa2d 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -2660,7 +2660,7 @@ class DISubprogram : public DILocalScope {
/// A single intermediate-IR layer location.
///
-/// One source coordinate in an intermediate IR level (e.g. TileIR, MLIR) that
+/// One source coordinate in an intermediate IR level (e.g. MLIR) that
/// sits between the high-level source and the final LLVM IR. It has no scope
/// and references its \a DIFile directly. Grouped behind a \a DILayerLocList on
/// \a DILocation's optional `irlayers` operand.
diff --git a/llvm/lib/IR/DebugLoc.cpp b/llvm/lib/IR/DebugLoc.cpp
index 41f60766e09bf..85a8b0b28840c 100644
--- a/llvm/lib/IR/DebugLoc.cpp
+++ b/llvm/lib/IR/DebugLoc.cpp
@@ -122,9 +122,8 @@ DebugLoc DebugLoc::replaceInlinedAtSubprogram(
}
// Recreate the location chain, bottom-up, starting at the new scope (or a
- // cached result). Each frame keeps its own irlayers, as appendInlinedAt does:
- // the intermediate-IR snapshot can live on any frame, so outlining must not
- // drop it.
+ // cached result). Each location in the chain keeps its own irlayers, as in
+ // appendInlinedAt: any of them may carry layers.
for (const DILocation *LocToUpdate : reverse(LocChain)) {
UpdatedLoc = DILocation::get(
Ctx, LocToUpdate->getLine(), LocToUpdate->getColumn(),
@@ -159,9 +158,8 @@ DebugLoc DebugLoc::appendInlinedAt(const DebugLoc &DL, DILocation *InlinedAt,
// location (then rebuilding the rest of the chain behind it) and update the
// map of already-constructed inlined-at nodes.
// Key Instructions: InlinedAt fields don't need atom info.
- // Preserve each frame's irlayers -- the intermediate-IR snapshot can
- // live on any frame (the one outermost at snapshot time), so the chain
- // rebuild must not drop it.
+ // Each location in the chain keeps its own irlayers; any of them may carry
+ // layers, so rebuilding the chain must not drop them.
for (const DILocation *MD : reverse(InlinedAtLocations))
Cache[MD] = Last = DILocation::getDistinct(
Ctx, MD->getLine(), MD->getColumn(), MD->getScope(), Last,
diff --git a/llvm/test/Bitcode/intermediate-loc-abbrev.ll b/llvm/test/Bitcode/intermediate-loc-abbrev.ll
index 3044280353f87..9248139da21a1 100644
--- a/llvm/test/Bitcode/intermediate-loc-abbrev.ll
+++ b/llvm/test/Bitcode/intermediate-loc-abbrev.ll
@@ -38,7 +38,7 @@ define void @inlined(i32 %x) !dbg !9 {
!llvm.module.flags = !{!2, !3}
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
-!1 = !DIFile(filename: "test.cu", directory: "/test")
+!1 = !DIFile(filename: "test.cpp", directory: "/test")
!2 = !{i32 7, !"Dwarf Version", i32 5}
!3 = !{i32 2, !"Debug Info Version", i32 3}
!8 = distinct !DISubprogram(name: "layered", scope: !1, file: !1, line: 1, type: !10, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0)
@@ -46,9 +46,9 @@ define void @inlined(i32 %x) !dbg !9 {
!10 = !DISubroutineType(types: !11)
!11 = !{null}
-!14 = !DIFile(filename: "intermediate.tileir", directory: ".")
+!14 = !DIFile(filename: "intermediate.ir", directory: ".")
!30 = !DILayerLocList(!31)
-!31 = !DILayerLoc(line: 100, column: 10, file: !14, kind: "TileIR")
+!31 = !DILayerLoc(line: 100, column: 10, file: !14, kind: "IntermediateIR")
!20 = !DILocation(line: 2, column: 1, scope: !8, irlayers: !30)
!21 = !DILocation(line: 3, column: 1, scope: !9, inlinedAt: !40)
diff --git a/llvm/test/Bitcode/intermediate-loc-metadata-location.ll b/llvm/test/Bitcode/intermediate-loc-metadata-location.ll
index 01014b391c8ad..0ce746f5f72de 100644
--- a/llvm/test/Bitcode/intermediate-loc-metadata-location.ll
+++ b/llvm/test/Bitcode/intermediate-loc-metadata-location.ll
@@ -3,7 +3,7 @@
;; Checks that irlayers survive a bitcode round-trip on a DILocation written as
;; a METADATA_LOCATION record -- one reachable only as an inlinedAt target.
-define dso_local void @test_kernel(ptr noundef %v) #0 !dbg !8 {
+define dso_local void @test(ptr noundef %v) #0 !dbg !8 {
entry:
store ptr %v, ptr %v, align 8, !dbg !20
ret void, !dbg !21
@@ -15,19 +15,19 @@ attributes #0 = { noinline optnone }
!llvm.module.flags = !{!2, !3}
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
-!1 = !DIFile(filename: "test.cu", directory: "/test")
+!1 = !DIFile(filename: "test.cpp", directory: "/test")
!2 = !{i32 7, !"Dwarf Version", i32 2}
!3 = !{i32 2, !"Debug Info Version", i32 3}
-!8 = distinct !DISubprogram(name: "test_kernel", scope: !1, file: !1, line: 1, type: !9, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0)
+!8 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 1, type: !9, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0)
!9 = !DISubroutineType(types: !10)
!10 = !{null}
!11 = distinct !DISubprogram(name: "helper", scope: !1, file: !1, line: 20, type: !9, scopeLine: 20, spFlags: DISPFlagDefinition, unit: !0)
-!14 = !DIFile(filename: "kernel.tileir", directory: ".", checksumkind: CSK_MD5, checksum: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", source: "tile ir text")
-!15 = !DIFile(filename: "kernel.gpuir", directory: ".", checksumkind: CSK_MD5, checksum: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", source: "gpu ir text")
+!14 = !DIFile(filename: "high-level.ir", directory: ".", checksumkind: CSK_MD5, checksum: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", source: "high-level ir text")
+!15 = !DIFile(filename: "low-level.ir", directory: ".", checksumkind: CSK_MD5, checksum: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", source: "low-level ir text")
-!16 = !DILayerLoc(line: 100, column: 1, file: !14, kind: "tile ir")
-!17 = !DILayerLoc(line: 7, column: 3, file: !15, kind: "gpu ir")
+!16 = !DILayerLoc(line: 100, column: 1, file: !14, kind: "HighLevelIR")
+!17 = !DILayerLoc(line: 7, column: 3, file: !15, kind: "LowLevelIR")
!18 = !DILayerLocList(!16, !17)
!19 = distinct !DILocation(line: 30, column: 1, scope: !8, irlayers: !18)
@@ -37,9 +37,9 @@ attributes #0 = { noinline optnone }
;; The metadata-block location keeps its layers, and the two entries keep their
;; order.
; CHECK-DAG: ![[IA:[0-9]+]] = distinct !DILocation(line: 30, column: 1, scope: !{{[0-9]+}}, irlayers: ![[LIST:[0-9]+]])
-; CHECK-DAG: ![[LIST]] = !DILayerLocList(![[TILE:[0-9]+]], ![[GPU:[0-9]+]])
-; CHECK-DAG: ![[TILE]] = !DILayerLoc(line: 100, column: 1, file: !{{[0-9]+}}, kind: "tile ir")
-; CHECK-DAG: ![[GPU]] = !DILayerLoc(line: 7, column: 3, file: !{{[0-9]+}}, kind: "gpu ir")
+; CHECK-DAG: ![[LIST]] = !DILayerLocList(![[HIGH:[0-9]+]], ![[LOW:[0-9]+]])
+; CHECK-DAG: ![[HIGH]] = !DILayerLoc(line: 100, column: 1, file: !{{[0-9]+}}, kind: "HighLevelIR")
+; CHECK-DAG: ![[LOW]] = !DILayerLoc(line: 7, column: 3, file: !{{[0-9]+}}, kind: "LowLevelIR")
;; The instruction locations reference it as inlinedAt and carry no layers of
;; their own.
diff --git a/llvm/test/Bitcode/intermediate-loc-roundtrip.ll b/llvm/test/Bitcode/intermediate-loc-roundtrip.ll
index 09b6c93914c35..ddfd45fbea6f9 100644
--- a/llvm/test/Bitcode/intermediate-loc-roundtrip.ll
+++ b/llvm/test/Bitcode/intermediate-loc-roundtrip.ll
@@ -7,7 +7,7 @@
;; entries), so the whole DILocation/DILayerLocList/DILayerLoc/DIFile chain must
;; survive .ll -> .bc -> .ll.
-define dso_local void @test_kernel(ptr noundef %v) !dbg !8 {
+define dso_local void @test(ptr noundef %v) !dbg !8 {
entry:
%v.addr = alloca ptr, align 8
store ptr %v, ptr %v.addr, align 8, !dbg !20
@@ -19,7 +19,7 @@ entry:
ret void, !dbg !22
}
-; CHECK-LABEL: define dso_local void @test_kernel
+; CHECK-LABEL: define dso_local void @test(
; CHECK: store ptr %v, ptr %v.addr, align 8, !dbg ![[DBG1:[0-9]+]]
; CHECK: load ptr, ptr %v.addr, align 8, !dbg ![[DBG2:[0-9]+]]
; CHECK: load ptr, ptr %v.addr, align 8, !dbg ![[DBG4:[0-9]+]]
@@ -41,25 +41,25 @@ entry:
;; The shared layer list holds one DILayerLoc with the kind string and the
;; intermediate coordinate.
; CHECK-DAG: ![[LIST]] = !DILayerLocList(![[LAYER:[0-9]+]])
-; CHECK-DAG: ![[LAYER]] = !DILayerLoc(line: 100, column: 10, file: ![[INTFILE:[0-9]+]], kind: "TileIR")
-; CHECK-DAG: ![[INTFILE]] = !DIFile(filename: "intermediate.tileir", directory: ".", checksumkind: CSK_MD5, checksum: "ffffffffffffffffffffffffffffffff")
+; CHECK-DAG: ![[LAYER]] = !DILayerLoc(line: 100, column: 10, file: ![[INTFILE:[0-9]+]], kind: "IntermediateIR")
+; CHECK-DAG: ![[INTFILE]] = !DIFile(filename: "intermediate.ir", directory: ".", checksumkind: CSK_MD5, checksum: "ffffffffffffffffffffffffffffffff")
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!2, !3}
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
-!1 = !DIFile(filename: "test.cu", directory: "/test")
+!1 = !DIFile(filename: "test.cpp", directory: "/test")
!2 = !{i32 7, !"Dwarf Version", i32 2}
!3 = !{i32 2, !"Debug Info Version", i32 3}
-!8 = distinct !DISubprogram(name: "test_kernel", scope: !1, file: !1, line: 1, type: !9, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0)
+!8 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 1, type: !9, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0)
!9 = !DISubroutineType(types: !10)
!10 = !{null}
;; Intermediate-IR layer: one DILayerLoc, shared (uniqued) across all layered
;; instructions via a single DILayerLocList.
-!14 = !DIFile(filename: "intermediate.tileir", directory: ".", checksumkind: CSK_MD5, checksum: "ffffffffffffffffffffffffffffffff")
+!14 = !DIFile(filename: "intermediate.ir", directory: ".", checksumkind: CSK_MD5, checksum: "ffffffffffffffffffffffffffffffff")
!30 = !DILayerLocList(!31)
-!31 = !DILayerLoc(line: 100, column: 10, file: !14, kind: "TileIR")
+!31 = !DILayerLoc(line: 100, column: 10, file: !14, kind: "IntermediateIR")
;; Layered instruction locations: primary source loc + shared irlayers.
!20 = !DILocation(line: 2, column: 5, scope: !8, irlayers: !30)
diff --git a/llvm/test/CodeGen/MIR/X86/instructions-debug-location-irlayers.mir b/llvm/test/CodeGen/MIR/X86/instructions-debug-location-irlayers.mir
index 093e9051601b7..8640081cd6e1b 100644
--- a/llvm/test/CodeGen/MIR/X86/instructions-debug-location-irlayers.mir
+++ b/llvm/test/CodeGen/MIR/X86/instructions-debug-location-irlayers.mir
@@ -8,7 +8,7 @@
## second carries both.
# CHECK-DAG: ![[LIST:[0-9]+]] = !DILayerLocList(![[LAYER:[0-9]+]])
-# CHECK-DAG: ![[LAYER]] = !DILayerLoc(line: 42, column: 5, file: !{{[0-9]+}}, kind: "tile ir")
+# CHECK-DAG: ![[LAYER]] = !DILayerLoc(line: 42, column: 5, file: !{{[0-9]+}}, kind: "IntermediateIR")
# CHECK-LABEL: name: test
# CHECK: $eax = COPY %0, debug-location !DILocation(line: 1, scope: !{{[0-9]+}}, irlayers: ![[LIST]])
# CHECK: RET64 $eax, debug-location !DILocation(line: 2, scope: !{{[0-9]+}}, inlinedAt: !DILocation(line: 1, scope: !{{[0-9]+}}), irlayers: ![[LIST]])
@@ -40,8 +40,8 @@
!10 = !{i32 2, !"Debug Info Version", i32 3}
!14 = !DILocation(line: 8, scope: !4)
!15 = !DILocation(line: 9, scope: !4, irlayers: !22)
- !20 = !DIFile(filename: "kernel.tileir", directory: ".")
- !21 = !DILayerLoc(line: 42, column: 5, file: !20, kind: "tile ir")
+ !20 = !DIFile(filename: "intermediate.ir", directory: ".")
+ !21 = !DILayerLoc(line: 42, column: 5, file: !20, kind: "IntermediateIR")
!22 = !DILayerLocList(!21)
...
diff --git a/llvm/test/Transforms/HotColdSplit/transfer-debug-info-irlayers.ll b/llvm/test/Transforms/HotColdSplit/transfer-debug-info-irlayers.ll
index e057c84edbd2b..772cbc4ba61f5 100644
--- a/llvm/test/Transforms/HotColdSplit/transfer-debug-info-irlayers.ll
+++ b/llvm/test/Transforms/HotColdSplit/transfer-debug-info-irlayers.ll
@@ -1,32 +1,32 @@
; RUN: opt -passes=hotcoldsplit -hotcoldsplit-threshold=0 -S < %s | FileCheck %s
;;
-;; Outlining rebuilds every DILocation in the extracted region so its scope points
-;; at the new cold function: once for the terminal frame, and once per inlined-at
-;; frame. Check that irlayers survive both rebuilds.
+;; Outlining rebuilds every DILocation in the extracted region so its scope
+;; points at the new cold function: the instruction's own location, and each
+;; location in its inlinedAt chain. Check that irlayers survive both rebuilds.
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.14.0"
; CHECK-LABEL: define {{.*}}@foo.cold.1
-;; A location carrying layers of its own: the terminal frame is rebuilt against
-;; the cold function's scope and keeps its layer list.
+;; A location carrying layers of its own is rebuilt against the cold function's
+;; scope and keeps its layer list.
; CHECK: [[ADD:%.*]] = add i32 %{{.*}}, 1, !dbg ![[OWN:[0-9]+]]
-;; An inlined location whose inlinedAt frame carries the layers: the chain
-;; rebuild has to keep them on that frame.
+;; An inlined location whose inlinedAt location carries the layers: the chain
+;; rebuild has to keep them there.
; CHECK: call void @sink(i32 [[ADD]]), !dbg ![[INL:[0-9]+]]
-;; Each frame keeps its own coordinate: 100 for the terminal frame, 200 for the
-;; inlined-at frame.
+;; Each location keeps its own coordinate: 100 for the instruction's location,
+;; 200 for its inlinedAt location.
; CHECK-DAG: ![[OWN]] = !DILocation(line: 1, column: 1, scope: !{{[0-9]+}}, irlayers: ![[OWNLIST:[0-9]+]])
; CHECK-DAG: ![[OWNLIST]] = !DILayerLocList(![[OWNLAYER:[0-9]+]])
-; CHECK-DAG: ![[OWNLAYER]] = !DILayerLoc(line: 100, column: 1, file: !{{[0-9]+}}, kind: "tile ir")
+; CHECK-DAG: ![[OWNLAYER]] = !DILayerLoc(line: 100, column: 1, file: !{{[0-9]+}}, kind: "IntermediateIR")
; CHECK-DAG: ![[INL]] = !DILocation(line: 2, column: 2, scope: !{{[0-9]+}}, inlinedAt: ![[IA:[0-9]+]])
; CHECK-DAG: ![[IA]] = !DILocation(line: 3, column: 3, scope: !{{[0-9]+}}, irlayers: ![[IALIST:[0-9]+]])
; CHECK-DAG: ![[IALIST]] = !DILayerLocList(![[IALAYER:[0-9]+]])
-; CHECK-DAG: ![[IALAYER]] = !DILayerLoc(line: 200, column: 1, file: !{{[0-9]+}}, kind: "tile ir")
+; CHECK-DAG: ![[IALAYER]] = !DILayerLoc(line: 200, column: 1, file: !{{[0-9]+}}, kind: "IntermediateIR")
define void @foo(i32 %arg1, i1 %c) !dbg !6 {
entry:
@@ -50,7 +50,7 @@ define void @inline_me() !dbg !12 {
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!5}
-!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "tile", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
+!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "<stdin>", directory: "/")
!2 = !{}
!5 = !{i32 2, !"Debug Info Version", i32 3}
@@ -58,17 +58,17 @@ define void @inline_me() !dbg !12 {
!7 = !DISubroutineType(types: !2)
!12 = distinct !DISubprogram(name: "inline_me", linkageName: "inline_me", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
-;; Two distinct coordinates in one intermediate-IR snapshot, so a frame that ends
-;; up with the other frame's layers fails rather than passing quietly.
-!14 = !DIFile(filename: "kernel.tileir", directory: ".", checksumkind: CSK_MD5, checksum: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
-!15 = !DILayerLoc(line: 100, column: 1, file: !14, kind: "tile ir")
+;; Two distinct coordinates in the same intermediate file, so a location that
+;; ends up with the other one's layers fails rather than passing quietly.
+!14 = !DIFile(filename: "intermediate.ir", directory: ".", checksumkind: CSK_MD5, checksum: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
+!15 = !DILayerLoc(line: 100, column: 1, file: !14, kind: "IntermediateIR")
!16 = !DILayerLocList(!15)
-!17 = !DILayerLoc(line: 200, column: 1, file: !14, kind: "tile ir")
+!17 = !DILayerLoc(line: 200, column: 1, file: !14, kind: "IntermediateIR")
!18 = !DILayerLocList(!17)
-;; !20 carries layers directly, exercising the terminal-frame rebuild. !21 is
-;; inlined from @inline_me and its inlinedAt frame !22 carries its own, exercising
-;; the chain rebuild.
+;; !20 carries layers directly, exercising the rebuild of the instruction's own
+;; location. !21 is inlined from @inline_me and its inlinedAt location !22
+;; carries its own, exercising the chain rebuild.
!20 = !DILocation(line: 1, column: 1, scope: !6, irlayers: !16)
!21 = !DILocation(line: 2, column: 2, scope: !12, inlinedAt: !22)
!22 = !DILocation(line: 3, column: 3, scope: !6, irlayers: !18)
diff --git a/llvm/test/Verifier/intermediate-layerloc.ll b/llvm/test/Verifier/intermediate-layerloc.ll
index afb8147a2b96c..655028eb12c37 100644
--- a/llvm/test/Verifier/intermediate-layerloc.ll
+++ b/llvm/test/Verifier/intermediate-layerloc.ll
@@ -25,8 +25,8 @@ define void @f(ptr %p) !dbg !5 {
!4 = !DISubroutineType(types: !{null})
!5 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !4, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0)
-!10 = !DIFile(filename: "t.tileir", directory: "/")
-!11 = !DILayerLoc(line: 1, file: !10, kind: "tile ir")
+!10 = !DIFile(filename: "t.ir", directory: "/")
+!11 = !DILayerLoc(line: 1, file: !10, kind: "IntermediateIR")
;; irlayers points at a layer rather than a list.
!20 = !DILocation(line: 2, scope: !5, irlayers: !11)
@@ -38,7 +38,7 @@ define void @f(ptr %p) !dbg !5 {
!22 = !DILocation(line: 4, scope: !5, irlayers: !32)
!32 = !DILayerLocList(!33)
-!33 = !DILayerLoc(line: 1, file: !4, kind: "tile ir")
+!33 = !DILayerLoc(line: 1, file: !4, kind: "IntermediateIR")
!23 = !DILocation(line: 5, scope: !5, irlayers: !34)
!34 = !DILayerLocList()
diff --git a/llvm/unittests/IR/DebugInfoTest.cpp b/llvm/unittests/IR/DebugInfoTest.cpp
index 5d3993fe87236..3699e74e789f0 100644
--- a/llvm/unittests/IR/DebugInfoTest.cpp
+++ b/llvm/unittests/IR/DebugInfoTest.cpp
@@ -1518,17 +1518,17 @@ TEST(DebugLocTest, IntermediateLocBasics) {
LLVMContext Ctx;
auto M = std::make_unique<Module>("MyModule", Ctx);
DIBuilder DIB(*M);
- DIFile *F = DIB.createFile("source.cu", "/");
- DIFile *IntF = DIB.createFile("intermediate.mlir", "/");
+ DIFile *F = DIB.createFile("source.c", "/");
+ DIFile *IntF = DIB.createFile("intermediate.ir", "/");
DICompileUnit *CU =
DIB.createCompileUnit(dwarf::DW_LANG_C, F, "test", false, "", 0);
DISubprogram *SP =
DIB.createFunction(CU, "foo", "", F, 1, DIB.createSubroutineType({}), 1,
DINode::FlagZero, DISubprogram::SPFlagDefinition);
- // Build a layered DILocation: a source coordinate plus one TileIR layer that
- // hangs off the DILocation's typed `irlayers` operand.
- MDString *Kind = MDString::get(Ctx, "TileIR");
+ // Build a layered DILocation: a source coordinate plus one layer that hangs
+ // off the DILocation's typed `irlayers` operand.
+ MDString *Kind = MDString::get(Ctx, "IntermediateIR");
DILayerLoc *Layer = DILayerLoc::get(Ctx, Kind, IntF, 100, 1);
DILayerLocList *Layers = DILayerLocList::get(Ctx, {Layer});
DILocation *Loc =
@@ -1548,7 +1548,7 @@ TEST(DebugLocTest, IntermediateLocBasics) {
ASSERT_EQ(Loc->getNumLayers(), 1u);
DILayerLoc *L0 = Loc->getLayer(0);
ASSERT_NE(L0, nullptr);
- EXPECT_EQ(L0->getKind(), "TileIR");
+ EXPECT_EQ(L0->getKind(), "IntermediateIR");
EXPECT_EQ(L0->getFile(), IntF);
EXPECT_EQ(L0->getLine(), 100u);
EXPECT_EQ(L0->getColumn(), 1u);
@@ -1558,8 +1558,8 @@ TEST(DebugLocTest, IntermediateLocWithAndWithout) {
LLVMContext Ctx;
auto M = std::make_unique<Module>("MyModule", Ctx);
DIBuilder DIB(*M);
- DIFile *F = DIB.createFile("source.cu", "/");
- DIFile *IntF = DIB.createFile("intermediate.mlir", "/");
+ DIFile *F = DIB.createFile("source.c", "/");
+ DIFile *IntF = DIB.createFile("intermediate.ir", "/");
DICompileUnit *CU =
DIB.createCompileUnit(dwarf::DW_LANG_C, F, "test", false, "", 0);
DISubprogram *SP =
@@ -1576,7 +1576,7 @@ TEST(DebugLocTest, IntermediateLocWithAndWithout) {
EXPECT_EQ(DLSourceOnly.get(), SourceLoc);
// A layered DILocation returns its list.
- MDString *Kind = MDString::get(Ctx, "TileIR");
+ MDString *Kind = MDString::get(Ctx, "IntermediateIR");
DILayerLoc *Layer = DILayerLoc::get(Ctx, Kind, IntF, 100, 1);
DILayerLocList *Layers = DILayerLocList::get(Ctx, {Layer});
DILocation *LayeredLoc =
@@ -1588,22 +1588,22 @@ TEST(DebugLocTest, IntermediateLocWithAndWithout) {
EXPECT_EQ(DLWithInt.getRawIRLayers(), Layers);
EXPECT_EQ(LayeredLoc->getIRLayers(), Layers);
ASSERT_EQ(LayeredLoc->getNumLayers(), 1u);
- EXPECT_EQ(LayeredLoc->getLayer(0)->getKind(), "TileIR");
+ EXPECT_EQ(LayeredLoc->getLayer(0)->getKind(), "IntermediateIR");
}
TEST(DebugLocTest, IntermediateLocEquality) {
LLVMContext Ctx;
auto M = std::make_unique<Module>("MyModule", Ctx);
DIBuilder DIB(*M);
- DIFile *F = DIB.createFile("source.cu", "/");
- DIFile *IntF = DIB.createFile("intermediate.mlir", "/");
+ DIFile *F = DIB.createFile("source.c", "/");
+ DIFile *IntF = DIB.createFile("intermediate.ir", "/");
DICompileUnit *CU =
DIB.createCompileUnit(dwarf::DW_LANG_C, F, "test", false, "", 0);
DISubprogram *SP =
DIB.createFunction(CU, "foo", "", F, 1, DIB.createSubroutineType({}), 1,
DINode::FlagZero, DISubprogram::SPFlagDefinition);
- MDString *Kind = MDString::get(Ctx, "TileIR");
+ MDString *Kind = MDString::get(Ctx, "IntermediateIR");
DILayerLoc *LayerA = DILayerLoc::get(Ctx, Kind, IntF, 100, 1);
// Differs from LayerA in a single field (column only) -- a minimal structural
// change must still uniquify to a distinct node, so DL1 and DL3 differ.
@@ -1643,15 +1643,15 @@ TEST(DebugLocTest, MergedLocationWithIntermediate) {
LLVMContext Ctx;
auto M = std::make_unique<Module>("MyModule", Ctx);
DIBuilder DIB(*M);
- DIFile *F = DIB.createFile("source.cu", "/");
- DIFile *IntF = DIB.createFile("intermediate.mlir", "/");
+ DIFile *F = DIB.createFile("source.c", "/");
+ DIFile *IntF = DIB.createFile("intermediate.ir", "/");
DICompileUnit *CU =
DIB.createCompileUnit(dwarf::DW_LANG_C, F, "test", false, "", 0);
DISubprogram *SP =
DIB.createFunction(CU, "foo", "", F, 1, DIB.createSubroutineType({}), 1,
DINode::FlagZero, DISubprogram::SPFlagDefinition);
- MDString *Kind = MDString::get(Ctx, "TileIR");
+ MDString *Kind = MDString::get(Ctx, "IntermediateIR");
// A layer shared by both locations, plus a distinct layer on each so the two
// DILocations are different nodes.
DILayerLoc *Shared = DILayerLoc::get(Ctx, Kind, IntF, 100, 1);
@@ -1682,15 +1682,15 @@ TEST(DebugLocTest, MergedLocationPartialIntermediate) {
LLVMContext Ctx;
auto M = std::make_unique<Module>("MyModule", Ctx);
DIBuilder DIB(*M);
- DIFile *F = DIB.createFile("source.cu", "/");
- DIFile *IntF = DIB.createFile("intermediate.mlir", "/");
+ DIFile *F = DIB.createFile("source.c", "/");
+ DIFile *IntF = DIB.createFile("intermediate.ir", "/");
DICompileUnit *CU =
DIB.createCompileUnit(dwarf::DW_LANG_C, F, "test", false, "", 0);
DISubprogram *SP =
DIB.createFunction(CU, "foo", "", F, 1, DIB.createSubroutineType({}), 1,
DINode::FlagZero, DISubprogram::SPFlagDefinition);
- MDString *Kind = MDString::get(Ctx, "TileIR");
+ MDString *Kind = MDString::get(Ctx, "IntermediateIR");
DILayerLoc *LayerA = DILayerLoc::get(Ctx, Kind, IntF, 100, 1);
DILayerLocList *LayersA = DILayerLocList::get(Ctx, {LayerA});
DILayerLoc *LayerB = DILayerLoc::get(Ctx, Kind, IntF, 200, 2);
@@ -1721,51 +1721,44 @@ TEST(DebugLocTest, MergedLocationPartialIntermediate) {
EXPECT_EQ(M13->getRawIRLayers(), nullptr);
}
-// Under the outermost-frame model, two instructions inlined into the same
-// kernel share the kernel (outer) frame that carries the tile-IR snapshot,
-// while their heads (inner frames) differ. Merging them must PRESERVE that
-// shared outer-frame layer (MergeLocPair threads per-frame layers), so the
-// merged instruction still resolves to the kernel's tile-IR line.
-TEST(DebugLocTest, MergedLocationOuterFrameLayerPreserved) {
+// Two locations inlined at the same call site share its location as their
+// inlinedAt, and only that location carries layers. The merged location keeps
+// it, layers included.
+TEST(DebugLocTest, MergedLocationKeepsInlinedAtLayers) {
LLVMContext Ctx;
auto M = std::make_unique<Module>("MyModule", Ctx);
DIBuilder DIB(*M);
- DIFile *SrcF = DIB.createFile("kernel.py", "/k");
- DIFile *IntF = DIB.createFile("kernel.tileir", ".");
+ DIFile *SrcF = DIB.createFile("source.c", "/");
+ DIFile *IntF = DIB.createFile("intermediate.ir", ".");
DICompileUnit *CU =
- DIB.createCompileUnit(dwarf::DW_LANG_C, SrcF, "tile", false, "", 0);
- DISubprogram *KernelSP = DIB.createFunction(
- CU, "kernel", "", SrcF, 10, DIB.createSubroutineType({}), 10,
+ DIB.createCompileUnit(dwarf::DW_LANG_C, SrcF, "test", false, "", 0);
+ DISubprogram *CallerSP = DIB.createFunction(
+ CU, "caller", "", SrcF, 10, DIB.createSubroutineType({}), 10,
DINode::FlagZero, DISubprogram::SPFlagDefinition);
DISubprogram *CalleeSP = DIB.createFunction(
CU, "helper", "", SrcF, 5, DIB.createSubroutineType({}), 5,
DINode::FlagZero, DISubprogram::SPFlagDefinition);
- MDString *Kind = MDString::get(Ctx, "tile ir");
+ MDString *Kind = MDString::get(Ctx, "IntermediateIR");
DILayerLoc *Layer = DILayerLoc::get(Ctx, Kind, IntF, 100, 1);
- DILayerLocList *KernelLayers = DILayerLocList::get(Ctx, {Layer});
+ DILayerLocList *CallSiteLayers = DILayerLocList::get(Ctx, {Layer});
- // The kernel (outer) frame carries the snapshot layer.
- DILocation *KernelFrame =
- DILocation::get(Ctx, 50, 1, KernelSP, /*InlinedAt=*/nullptr,
+ DILocation *CallSite =
+ DILocation::get(Ctx, 50, 1, CallerSP, /*InlinedAt=*/nullptr,
/*ImplicitCode=*/false, /*AtomGroup=*/0, /*AtomRank=*/0,
- /*IRLayers=*/KernelLayers);
+ /*IRLayers=*/CallSiteLayers);
- // Two instructions inlined into that kernel frame: same callee, different
- // head lines, no layer of their own.
- DILocation *LocA = DILocation::get(Ctx, 10, 3, CalleeSP, KernelFrame);
- DILocation *LocB = DILocation::get(Ctx, 11, 5, CalleeSP, KernelFrame);
+ // Same callee, different lines, no layers of their own.
+ DILocation *LocA = DILocation::get(Ctx, 10, 3, CalleeSP, CallSite);
+ DILocation *LocB = DILocation::get(Ctx, 11, 5, CalleeSP, CallSite);
DILocation *Merged = DILocation::getMergedLocation(LocA, LocB);
ASSERT_NE(Merged, nullptr);
- // The merged head has no layer of its own...
EXPECT_EQ(Merged->getRawIRLayers(), nullptr);
- // ...but the shared outer (kernel) frame, and its snapshot layer, survives so
- // the emission walk still resolves to the kernel's tile-IR line.
- DILocation *MergedOuter = Merged->getInlinedAt();
- ASSERT_NE(MergedOuter, nullptr);
- EXPECT_EQ(MergedOuter->getScope(), KernelSP);
- EXPECT_EQ(MergedOuter->getIRLayers(), KernelLayers);
+ DILocation *MergedInlinedAt = Merged->getInlinedAt();
+ ASSERT_NE(MergedInlinedAt, nullptr);
+ EXPECT_EQ(MergedInlinedAt->getScope(), CallerSP);
+ EXPECT_EQ(MergedInlinedAt->getIRLayers(), CallSiteLayers);
}
// Rebuilding a location with a new discriminator must carry `irlayers` over:
@@ -1775,8 +1768,8 @@ TEST(DebugLocTest, CloneWithDiscriminatorPreservesLayers) {
LLVMContext Ctx;
auto M = std::make_unique<Module>("MyModule", Ctx);
DIBuilder DIB(*M);
- DIFile *F = DIB.createFile("source.cu", "/");
- DIFile *IntF = DIB.createFile("intermediate.tileir", ".");
+ DIFile *F = DIB.createFile("source.c", "/");
+ DIFile *IntF = DIB.createFile("intermediate.ir", ".");
DICompileUnit *CU =
DIB.createCompileUnit(dwarf::DW_LANG_C, F, "test", false, "", 0);
DISubprogram *SP =
@@ -1784,7 +1777,7 @@ TEST(DebugLocTest, CloneWithDiscriminatorPreservesLayers) {
DINode::FlagZero, DISubprogram::SPFlagDefinition);
DILayerLoc *Layer =
- DILayerLoc::get(Ctx, MDString::get(Ctx, "tile ir"), IntF, 42, 5);
+ DILayerLoc::get(Ctx, MDString::get(Ctx, "IntermediateIR"), IntF, 42, 5);
DILayerLocList *List = DILayerLocList::get(Ctx, {Layer});
DILocation *Loc =
DILocation::get(Ctx, 10, 5, SP, /*InlinedAt=*/nullptr,
@@ -1805,15 +1798,15 @@ TEST(DebugLocTest, MergedLocationDistinctLayersCompareStructurally) {
LLVMContext Ctx;
auto M = std::make_unique<Module>("MyModule", Ctx);
DIBuilder DIB(*M);
- DIFile *F = DIB.createFile("source.cu", "/");
- DIFile *IntF = DIB.createFile("intermediate.tileir", ".");
+ DIFile *F = DIB.createFile("source.c", "/");
+ DIFile *IntF = DIB.createFile("intermediate.ir", ".");
DICompileUnit *CU =
DIB.createCompileUnit(dwarf::DW_LANG_C, F, "test", false, "", 0);
DISubprogram *SP =
DIB.createFunction(CU, "foo", "", F, 1, DIB.createSubroutineType({}), 1,
DINode::FlagZero, DISubprogram::SPFlagDefinition);
- MDString *Kind = MDString::get(Ctx, "tile ir");
+ MDString *Kind = MDString::get(Ctx, "IntermediateIR");
// Same fields, but each side holds its own `distinct` node, so the two shared
// layers are unequal pointers.
DILayerLoc *SharedA = DILayerLoc::getDistinct(Ctx, Kind, IntF, 100, 1);
@@ -1845,8 +1838,8 @@ TEST(DebugLocTest, IntermediateLocLayerUniquing) {
LLVMContext Ctx;
auto M = std::make_unique<Module>("MyModule", Ctx);
DIBuilder DIB(*M);
- DIFile *IntF = DIB.createFile("intermediate.mlir", "/");
- MDString *Kind = MDString::get(Ctx, "TileIR");
+ DIFile *IntF = DIB.createFile("intermediate.ir", "/");
+ MDString *Kind = MDString::get(Ctx, "IntermediateIR");
// Two structurally-identical DILayerLoc::get calls return the same node.
DILayerLoc *L1 = DILayerLoc::get(Ctx, Kind, IntF, 100, 5);
@@ -1872,7 +1865,7 @@ TEST(DebugLocTest, PrintIntermediateLocWithInlinedAt) {
DIBuilder DIB(*M);
DIFile *SrcF = DIB.createFile("caller.py", "/src");
DIFile *CalleeF = DIB.createFile("callee.py", "/src");
- DIFile *IntF = DIB.createFile("callee.tileir", "/ir");
+ DIFile *IntF = DIB.createFile("callee.ir", "/ir");
DICompileUnit *CU =
DIB.createCompileUnit(dwarf::DW_LANG_C, SrcF, "test", false, "", 0);
DISubprogram *CallerSP = DIB.createFunction(
@@ -1884,7 +1877,7 @@ TEST(DebugLocTest, PrintIntermediateLocWithInlinedAt) {
// A DILocation carrying BOTH an inlinedAt chain and an irlayers operand.
DILocation *CallSiteLoc = DILocation::get(Ctx, 50, 1, CallerSP);
- MDString *Kind = MDString::get(Ctx, "TileIR");
+ MDString *Kind = MDString::get(Ctx, "IntermediateIR");
DILayerLoc *Layer = DILayerLoc::get(Ctx, Kind, IntF, 100, 5);
DILayerLocList *Layers = DILayerLocList::get(Ctx, {Layer});
DILocation *Loc =
>From 77236eac1d4e27a90f258f39f030f3f82e5af3ca Mon Sep 17 00:00:00 2001
From: Alexander Yermolovich <ayermolovich at nvidia.com>
Date: Wed, 30 Sep 2026 18:53:35 +0000
Subject: [PATCH 12/12] another pass over tests, mainly comments
---
llvm/test/Bitcode/intermediate-loc-abbrev.ll | 13 +++--
.../Bitcode/intermediate-loc-roundtrip.ll | 5 +-
.../transfer-debug-info-irlayers.ll | 52 ++++++++++++-------
llvm/unittests/IR/DebugInfoTest.cpp | 48 ++++++++---------
4 files changed, 63 insertions(+), 55 deletions(-)
diff --git a/llvm/test/Bitcode/intermediate-loc-abbrev.ll b/llvm/test/Bitcode/intermediate-loc-abbrev.ll
index 9248139da21a1..9dab411d137e6 100644
--- a/llvm/test/Bitcode/intermediate-loc-abbrev.ll
+++ b/llvm/test/Bitcode/intermediate-loc-abbrev.ll
@@ -8,15 +8,14 @@
;; just zero. A VBR field costs its full width even for zero, so always using
;; the wide abbrev would still round-trip while growing every record.
-;; Layers sit on the location with no inlinedAt -- the root of the inline chain.
-;; Not inlined here, so that is the instruction's own location.
+;; Not inlined: the instruction's own location carries the layers.
define void @layered() !dbg !8 {
ret void, !dbg !20
}
-;; Inlined, so the layers move to the chain root: !21 and !22 carry none and
-;; their inlinedAt targets do. Those targets are enumerated as nodes, which is
-;; what puts a layered location into the metadata block.
+;; Inlined: the instruction locations carry no layers. Their inlinedAt targets
+;; are written to the metadata block as LOCATION records, one with layers (!40)
+;; and one without (!41).
define void @inlined(i32 %x) !dbg !9 {
%a = add i32 %x, 1, !dbg !21
ret void, !dbg !22
@@ -54,7 +53,7 @@ define void @inlined(i32 %x) !dbg !9 {
!21 = !DILocation(line: 3, column: 1, scope: !9, inlinedAt: !40)
!22 = !DILocation(line: 4, column: 1, scope: !9, inlinedAt: !41)
-;; Chain roots. !40 carries the layers for the instruction inlined at it; !41
-;; has none, to cover the narrow LOCATION.
+;; The inlinedAt targets. !40 carries layers; !41 has none, to cover the narrow
+;; LOCATION.
!40 = !DILocation(line: 10, column: 1, scope: !9, irlayers: !30)
!41 = !DILocation(line: 11, column: 1, scope: !9)
diff --git a/llvm/test/Bitcode/intermediate-loc-roundtrip.ll b/llvm/test/Bitcode/intermediate-loc-roundtrip.ll
index ddfd45fbea6f9..5a30acec6a752 100644
--- a/llvm/test/Bitcode/intermediate-loc-roundtrip.ll
+++ b/llvm/test/Bitcode/intermediate-loc-roundtrip.ll
@@ -33,9 +33,8 @@ entry:
; CHECK-DAG: ![[DBG2]] = !DILocation(line: 3, column: 5, scope: ![[SP]], irlayers: ![[LIST]])
; CHECK-DAG: ![[DBG3]] = !DILocation(line: 4, column: 1, scope: ![[SP]], irlayers: ![[LIST]])
-;; The unlayered location must come back with no irlayers operand. The closing
-;; paren is the assertion -- it is what distinguishes "absent" from "present but
-;; empty".
+;; The unlayered location must come back with no irlayers operand; the closing
+;; paren is the assertion.
; CHECK-DAG: ![[DBG4]] = !DILocation(line: 5, column: 5, scope: ![[SP]])
;; The shared layer list holds one DILayerLoc with the kind string and the
diff --git a/llvm/test/Transforms/HotColdSplit/transfer-debug-info-irlayers.ll b/llvm/test/Transforms/HotColdSplit/transfer-debug-info-irlayers.ll
index 772cbc4ba61f5..527847121d7f7 100644
--- a/llvm/test/Transforms/HotColdSplit/transfer-debug-info-irlayers.ll
+++ b/llvm/test/Transforms/HotColdSplit/transfer-debug-info-irlayers.ll
@@ -1,30 +1,45 @@
; RUN: opt -passes=hotcoldsplit -hotcoldsplit-threshold=0 -S < %s | FileCheck %s
;;
-;; Outlining rebuilds every DILocation in the extracted region so its scope
-;; points at the new cold function: the instruction's own location, and each
-;; location in its inlinedAt chain. Check that irlayers survive both rebuilds.
+;; Outlining rebuilds every DILocation in the extracted region. The outermost
+;; location of each inlinedAt chain is rebuilt with the cold function's scope,
+;; and the locations inlined at it are rebuilt to point at the result. Check the
+;; rebuilt scopes, that every location keeps its own irlayers through both
+;; rebuilds, and that a location without layers does not pick up those of the
+;; location it is inlined at.
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.14.0"
-; CHECK-LABEL: define {{.*}}@foo.cold.1
+; CHECK-LABEL: define {{.*}}@foo.cold.1(
+; CHECK-SAME: !dbg ![[COLDSP:[0-9]+]]
-;; A location carrying layers of its own is rebuilt against the cold function's
-;; scope and keeps its layer list.
+;; !20 is not inlined, so it is the outermost location of its own chain.
; CHECK: [[ADD:%.*]] = add i32 %{{.*}}, 1, !dbg ![[OWN:[0-9]+]]
-;; An inlined location whose inlinedAt location carries the layers: the chain
-;; rebuild has to keep them there.
+;; !21 is inlined at !22: !22 gets the cold function's scope, and !21 is
+;; rebuilt to point at the new !22. !21 has no layers and must not get !22's.
; CHECK: call void @sink(i32 [[ADD]]), !dbg ![[INL:[0-9]+]]
-;; Each location keeps its own coordinate: 100 for the instruction's location,
-;; 200 for its inlinedAt location.
-; CHECK-DAG: ![[OWN]] = !DILocation(line: 1, column: 1, scope: !{{[0-9]+}}, irlayers: ![[OWNLIST:[0-9]+]])
+;; !25 is also inlined at !22, but carries layers of its own, which the rebuild
+;; must keep.
+; CHECK: call void @sink(i32 %{{.*}}), !dbg ![[INLOWN:[0-9]+]]
+
+;; !20 and !22 now have the cold function as their scope, while !21 and !25 keep
+;; @inline_me's. Each location keeps its own coordinate: 100 for !20, 200 for
+;; !22 and 300 for !25. !21 comes back with no irlayers; the closing paren is
+;; the assertion.
+; CHECK-DAG: ![[INLINESP:[0-9]+]] = distinct !DISubprogram(name: "inline_me"
+; CHECK-DAG: ![[OWN]] = !DILocation(line: 1, column: 1, scope: ![[COLDSP]], irlayers: ![[OWNLIST:[0-9]+]])
; CHECK-DAG: ![[OWNLIST]] = !DILayerLocList(![[OWNLAYER:[0-9]+]])
; CHECK-DAG: ![[OWNLAYER]] = !DILayerLoc(line: 100, column: 1, file: !{{[0-9]+}}, kind: "IntermediateIR")
-; CHECK-DAG: ![[INL]] = !DILocation(line: 2, column: 2, scope: !{{[0-9]+}}, inlinedAt: ![[IA:[0-9]+]])
-; CHECK-DAG: ![[IA]] = !DILocation(line: 3, column: 3, scope: !{{[0-9]+}}, irlayers: ![[IALIST:[0-9]+]])
+; CHECK-DAG: ![[INL]] = !DILocation(line: 2, column: 2, scope: ![[INLINESP]], inlinedAt: ![[IA:[0-9]+]])
+
+; CHECK-DAG: ![[INLOWN]] = !DILocation(line: 4, column: 4, scope: ![[INLINESP]], inlinedAt: ![[IA]], irlayers: ![[OWNINLLIST:[0-9]+]])
+; CHECK-DAG: ![[OWNINLLIST]] = !DILayerLocList(![[OWNINLLAYER:[0-9]+]])
+; CHECK-DAG: ![[OWNINLLAYER]] = !DILayerLoc(line: 300, column: 1, file: !{{[0-9]+}}, kind: "IntermediateIR")
+
+; CHECK-DAG: ![[IA]] = !DILocation(line: 3, column: 3, scope: ![[COLDSP]], irlayers: ![[IALIST:[0-9]+]])
; CHECK-DAG: ![[IALIST]] = !DILayerLocList(![[IALAYER:[0-9]+]])
; CHECK-DAG: ![[IALAYER]] = !DILayerLoc(line: 200, column: 1, file: !{{[0-9]+}}, kind: "IntermediateIR")
@@ -38,6 +53,7 @@ if.then:
if.end:
%add1 = add i32 %arg1, 1, !dbg !20
call void @sink(i32 %add1), !dbg !21
+ call void @sink(i32 %arg1), !dbg !25
ret void
}
@@ -58,17 +74,17 @@ define void @inline_me() !dbg !12 {
!7 = !DISubroutineType(types: !2)
!12 = distinct !DISubprogram(name: "inline_me", linkageName: "inline_me", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
-;; Two distinct coordinates in the same intermediate file, so a location that
-;; ends up with the other one's layers fails rather than passing quietly.
+;; Three distinct coordinates in the same intermediate file, so a location that
+;; ends up with another one's layers fails rather than passing quietly.
!14 = !DIFile(filename: "intermediate.ir", directory: ".", checksumkind: CSK_MD5, checksum: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
!15 = !DILayerLoc(line: 100, column: 1, file: !14, kind: "IntermediateIR")
!16 = !DILayerLocList(!15)
!17 = !DILayerLoc(line: 200, column: 1, file: !14, kind: "IntermediateIR")
!18 = !DILayerLocList(!17)
+!23 = !DILayerLoc(line: 300, column: 1, file: !14, kind: "IntermediateIR")
+!24 = !DILayerLocList(!23)
-;; !20 carries layers directly, exercising the rebuild of the instruction's own
-;; location. !21 is inlined from @inline_me and its inlinedAt location !22
-;; carries its own, exercising the chain rebuild.
!20 = !DILocation(line: 1, column: 1, scope: !6, irlayers: !16)
!21 = !DILocation(line: 2, column: 2, scope: !12, inlinedAt: !22)
!22 = !DILocation(line: 3, column: 3, scope: !6, irlayers: !18)
+!25 = !DILocation(line: 4, column: 4, scope: !12, inlinedAt: !22, irlayers: !24)
diff --git a/llvm/unittests/IR/DebugInfoTest.cpp b/llvm/unittests/IR/DebugInfoTest.cpp
index 3699e74e789f0..627dbc91a2afd 100644
--- a/llvm/unittests/IR/DebugInfoTest.cpp
+++ b/llvm/unittests/IR/DebugInfoTest.cpp
@@ -1606,7 +1606,7 @@ TEST(DebugLocTest, IntermediateLocEquality) {
MDString *Kind = MDString::get(Ctx, "IntermediateIR");
DILayerLoc *LayerA = DILayerLoc::get(Ctx, Kind, IntF, 100, 1);
// Differs from LayerA in a single field (column only) -- a minimal structural
- // change must still uniquify to a distinct node, so DL1 and DL3 differ.
+ // change must still uniquify to a distinct node, so DL1 and DL2 differ.
DILayerLoc *LayerB = DILayerLoc::get(Ctx, Kind, IntF, 100, 2);
DILayerLocList *ListA = DILayerLocList::get(Ctx, {LayerA});
// A structurally-identical list uniques to the same node.
@@ -1621,22 +1621,16 @@ TEST(DebugLocTest, IntermediateLocEquality) {
/*AtomRank=*/0, /*IRLayers=*/L);
};
DebugLoc DL1(Layered(ListA));
- // A *distinct* location with the same fields and the same (uniqued) layer
- // list, so isSameSourceLocation cannot short-circuit on pointer identity and
- // must actually run the structural getRawIRLayers() comparison.
- DebugLoc DL2(DILocation::getDistinct(Ctx, 10, 5, SP, /*InlinedAt=*/nullptr,
- /*ImplicitCode=*/false, /*AtomGroup=*/0,
- /*AtomRank=*/0, /*IRLayers=*/ListA));
- DebugLoc DL3(Layered(ListB));
- DebugLoc DL4(DILocation::get(Ctx, 10, 5, SP)); // no layers
+ DebugLoc DL2(Layered(ListB));
+ DebugLoc DL3(Layered(nullptr)); // no layers
+ // Distinct nodes, so isSameSourceLocation cannot short-circuit on identity.
ASSERT_NE(DL1.get(), DL2.get());
- // isSameSourceLocation compares the primary position only, so all three of
- // these are the same source location: identical layers, differing layers, and
- // layers versus none.
+ ASSERT_NE(DL1.get(), DL3.get());
+ // isSameSourceLocation compares the primary position only, so differing
+ // layers and layers versus none are both the same source location.
EXPECT_TRUE(DL1.isSameSourceLocation(DL2));
EXPECT_TRUE(DL1.isSameSourceLocation(DL3));
- EXPECT_TRUE(DL1.isSameSourceLocation(DL4));
}
TEST(DebugLocTest, MergedLocationWithIntermediate) {
@@ -1721,10 +1715,10 @@ TEST(DebugLocTest, MergedLocationPartialIntermediate) {
EXPECT_EQ(M13->getRawIRLayers(), nullptr);
}
-// Two locations inlined at the same call site share its location as their
-// inlinedAt, and only that location carries layers. The merged location keeps
-// it, layers included.
-TEST(DebugLocTest, MergedLocationKeepsInlinedAtLayers) {
+// Two locations inlined from different callees at the same call site have
+// only the call site in common, so merging them rebuilds the call site's
+// location. The rebuilt location keeps the call site's layers.
+TEST(DebugLocTest, MergedLocationKeepsCallSiteLayers) {
LLVMContext Ctx;
auto M = std::make_unique<Module>("MyModule", Ctx);
DIBuilder DIB(*M);
@@ -1735,8 +1729,11 @@ TEST(DebugLocTest, MergedLocationKeepsInlinedAtLayers) {
DISubprogram *CallerSP = DIB.createFunction(
CU, "caller", "", SrcF, 10, DIB.createSubroutineType({}), 10,
DINode::FlagZero, DISubprogram::SPFlagDefinition);
- DISubprogram *CalleeSP = DIB.createFunction(
- CU, "helper", "", SrcF, 5, DIB.createSubroutineType({}), 5,
+ DISubprogram *CalleeASP = DIB.createFunction(
+ CU, "calleeA", "", SrcF, 5, DIB.createSubroutineType({}), 5,
+ DINode::FlagZero, DISubprogram::SPFlagDefinition);
+ DISubprogram *CalleeBSP = DIB.createFunction(
+ CU, "calleeB", "", SrcF, 20, DIB.createSubroutineType({}), 20,
DINode::FlagZero, DISubprogram::SPFlagDefinition);
MDString *Kind = MDString::get(Ctx, "IntermediateIR");
@@ -1748,17 +1745,14 @@ TEST(DebugLocTest, MergedLocationKeepsInlinedAtLayers) {
/*ImplicitCode=*/false, /*AtomGroup=*/0, /*AtomRank=*/0,
/*IRLayers=*/CallSiteLayers);
- // Same callee, different lines, no layers of their own.
- DILocation *LocA = DILocation::get(Ctx, 10, 3, CalleeSP, CallSite);
- DILocation *LocB = DILocation::get(Ctx, 11, 5, CalleeSP, CallSite);
+ DILocation *LocA = DILocation::get(Ctx, 6, 3, CalleeASP, CallSite);
+ DILocation *LocB = DILocation::get(Ctx, 21, 3, CalleeBSP, CallSite);
DILocation *Merged = DILocation::getMergedLocation(LocA, LocB);
ASSERT_NE(Merged, nullptr);
- EXPECT_EQ(Merged->getRawIRLayers(), nullptr);
- DILocation *MergedInlinedAt = Merged->getInlinedAt();
- ASSERT_NE(MergedInlinedAt, nullptr);
- EXPECT_EQ(MergedInlinedAt->getScope(), CallerSP);
- EXPECT_EQ(MergedInlinedAt->getIRLayers(), CallSiteLayers);
+ EXPECT_EQ(Merged->getScope(), CallerSP);
+ EXPECT_EQ(Merged->getInlinedAt(), nullptr);
+ EXPECT_EQ(Merged->getIRLayers(), CallSiteLayers);
}
// Rebuilding a location with a new discriminator must carry `irlayers` over:
More information about the llvm-commits
mailing list