[llvm] 531a8ac - Add support for NSDI DebugScope, DebugNoScope and DebugInlinedAt (#218959)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 9 01:16:45 PDT 2026
Author: Manuel Carrasco
Date: 2026-09-09T09:16:38+01:00
New Revision: 531a8ac4c959dbdabcb4fea4a31beecde36bcdd9
URL: https://github.com/llvm/llvm-project/commit/531a8ac4c959dbdabcb4fea4a31beecde36bcdd9
DIFF: https://github.com/llvm/llvm-project/commit/531a8ac4c959dbdabcb4fea4a31beecde36bcdd9.diff
LOG: Add support for NSDI DebugScope, DebugNoScope and DebugInlinedAt (#218959)
Add support for NSDI
[DebugScope](https://github.khronos.org/SPIRV-Registry/nonsemantic/NonSemantic.Shader.DebugInfo.html#DebugScope),
[DebugNoScope](https://github.khronos.org/SPIRV-Registry/nonsemantic/NonSemantic.Shader.DebugInfo.html#DebugNoScope)
and
[DebugInlinedAt](https://github.khronos.org/SPIRV-Registry/nonsemantic/NonSemantic.Shader.DebugInfo.html#DebugInlinedAt).
DebugScope/DebugNoScope are region delimiters following the same region
rule as DebugLine/DebugNoLine. The two regions are tracked separately
because a DebugScope region could span several DebugLine regions, and
either can skip emission on a cache miss at different times.
DebugInlinedAt is not an region instruction, so it is emitted at module
scope. We allow recursive traversal when building them to support
chains.
The PR also adds a test showing a limitation in the DebugLine
deduplication that is at the DILocation pointer level which may lead to
duplicate opcodes.
Added:
llvm/test/CodeGen/SPIRV/debug-info/debug-inlined-at-recursive.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-inlined-at.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-scope-block.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-scope-no-scope-block.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-scope-no-scope.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-scope-same-line.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-scope-shared.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-scope.ll
Modified:
llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-call-before-alloca.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-calls.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-external-call.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-line-block.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-line-calls.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-line-if-phi.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-line-loop-merge.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-line-loop.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-line-selection-merge.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-line-shared.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-line.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-no-line.ll
Removed:
################################################################################
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index fd289d3a5764c..053fee1d59b64 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -312,6 +312,7 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
GlobalVariableDebugInfoMap.clear();
LexicalBlocks.clear();
DebugScopeRegs.clear();
+ DebugInlinedAtRegs.clear();
ScopeToPathOpStringReg.clear();
DebugSourceRegByFileStr.clear();
OpStringContentCache.clear();
@@ -697,6 +698,38 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugLexicalBlock(
ExtInstSetReg, Ops, MAI);
}
+MCRegister SPIRVNonSemanticDebugHandler::getOrEmitDebugInlinedAt(
+ const DILocation *IA, MCRegister VoidTypeReg, MCRegister I32TypeReg,
+ MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI) {
+ assert(IA && "IA must not be null in getOrEmitDebugInlinedAt");
+
+ if (MCRegister Cached = DebugInlinedAtRegs.lookup(IA))
+ return Cached;
+
+ auto ScopeRegOpt = resolveScope(IA->getScope());
+ if (!ScopeRegOpt)
+ return MCRegister();
+
+ MCRegister LineReg =
+ emitOpConstantI32(static_cast<uint32_t>(IA->getLine()), I32TypeReg, MAI);
+
+ SmallVector<MCRegister, 3> Ops{LineReg, *ScopeRegOpt};
+ // Recurse before building this instruction's operands so an outer
+ // inlined-at link is always available.
+ if (const DILocation *Outer = IA->getInlinedAt()) {
+ MCRegister OuterReg = getOrEmitDebugInlinedAt(
+ Outer, VoidTypeReg, I32TypeReg, ExtInstSetReg, MAI);
+ if (!OuterReg.isValid())
+ return MCRegister();
+ Ops.push_back(OuterReg);
+ }
+
+ MCRegister Reg = emitExtInst(SPIRV::NonSemanticExtInst::DebugInlinedAt,
+ VoidTypeReg, ExtInstSetReg, Ops, MAI);
+ DebugInlinedAtRegs[IA] = Reg;
+ return Reg;
+}
+
std::optional<MCRegister>
SPIRVNonSemanticDebugHandler::emitDebugFunctionDeclaration(
const DISubprogram *SP, MCRegister VoidTypeReg, MCRegister I32TypeReg,
@@ -1142,6 +1175,7 @@ void SPIRVNonSemanticDebugHandler::resetPerFunctionDebugState() {
LastFunctionOpVariable = nullptr;
DebugFunctionDefinitionEmitted = false;
LastLineMI = nullptr;
+ LastScopeMI = nullptr;
}
void SPIRVNonSemanticDebugHandler::preparePerFunctionDebug(
@@ -1206,7 +1240,13 @@ void SPIRVNonSemanticDebugHandler::beginInstruction(const MachineInstr *MI) {
if (!DebugFunctionDefinitionEmitted)
return;
- emitDebugLineForInstruction(MI);
+
+ std::optional<const MachineInstr *> Target = resolveDebugLocTarget(MI);
+ if (!Target)
+ return;
+
+ emitDebugScopeForInstruction(*Target);
+ emitDebugLineForInstruction(*Target);
}
static bool isMergeInstruction(unsigned Opcode) {
@@ -1214,8 +1254,8 @@ static bool isMergeInstruction(unsigned Opcode) {
Opcode == SPIRV::OpLoopControlINTEL;
}
-static bool isDebugLineTarget(const MachineInstr *MI,
- SPIRV::ModuleAnalysisInfo &MAI) {
+static bool isDebugLocTarget(const MachineInstr *MI,
+ SPIRV::ModuleAnalysisInfo &MAI) {
if (MAI.getSkipEmission(MI))
return false;
switch (MI->getOpcode()) {
@@ -1243,25 +1283,23 @@ findAdjacentEmittedInstruction(const MachineInstr *MI,
return nullptr;
}
-void SPIRVNonSemanticDebugHandler::emitDebugLineForInstruction(
- const MachineInstr *MI) {
- assert(DebugFunctionDefinitionEmitted &&
- "DebugFunctionDefinition must be emitted");
+std::optional<const MachineInstr *>
+SPIRVNonSemanticDebugHandler::resolveDebugLocTarget(const MachineInstr *MI) {
assert(CurrentMAI && "CurrentMAI must be set");
-
SPIRV::ModuleAnalysisInfo &MAI = *CurrentMAI;
- // Structural opcodes don't require a DebugLine, other opcodes might have
- // already been emitted in the module scope.
- if (!isDebugLineTarget(MI, MAI))
- return;
+ // Structural opcodes don't require a DebugLine/DebugScope, other opcodes
+ // might have already been emitted in the module scope.
+ if (!isDebugLocTarget(MI, MAI))
+ return std::nullopt;
- // DebugLine can be emitted before a merge instruction, but not after it
- // (nothing may sit between the merge and its terminator). We can use either
- // the merge's or the terminator's debug info; we emit the terminator's one.
+ // DebugLine/DebugScope can be emitted before a merge instruction, but not
+ // after it (nothing may sit between the merge and its terminator). We can
+ // use either the merge's or the terminator's debug info; we emit the
+ // terminator's one.
const MachineInstr *Prev = findAdjacentEmittedInstruction(MI, MAI, false);
if (Prev && isMergeInstruction(Prev->getOpcode()))
- return;
+ return std::nullopt;
if (isMergeInstruction(MI->getOpcode())) {
// Use the terminator's debug info; when we reach it later, the check
@@ -1270,10 +1308,80 @@ void SPIRVNonSemanticDebugHandler::emitDebugLineForInstruction(
assert(MI && "Merge instruction must be followed by a terminator");
}
- // The range of DebugLine must be reset at each basic block boundary.
+ return MI;
+}
+
+void SPIRVNonSemanticDebugHandler::emitDebugScopeForInstruction(
+ const MachineInstr *MI) {
+ assert(DebugFunctionDefinitionEmitted &&
+ "DebugFunctionDefinition must be emitted");
+ assert(CurrentMAI && "CurrentMAI must be set");
+
+ // The region is implicitly closed at each basic block boundary, so a
+ // LastScopeMI from another block must be dropped before it is read below:
+ // the new block needs its own DebugScope, and has no region left to close.
+ if (LastScopeMI && MI->getParent() != LastScopeMI->getParent())
+ LastScopeMI = nullptr;
+
+ SPIRV::ModuleAnalysisInfo &MAI = *CurrentMAI;
+ MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
+ MCRegister ExtInstSetReg = MAI.getExtInstSetReg(NSSet);
+
+ const DILocation *CurDL = MI->getDebugLoc().get();
+ if (!CurDL) {
+ // No location for the current instruction.
+ if (LastScopeMI) {
+ // Close the current DebugScope region.
+ emitExtInst(SPIRV::NonSemanticExtInst::DebugNoScope, VoidTypeReg,
+ ExtInstSetReg, {}, MAI);
+ LastScopeMI = nullptr;
+ }
+ return;
+ }
+
+ const DIScope *CurScope = CurDL->getScope();
+ const DILocation *CurInlinedAt = CurDL->getInlinedAt();
+
+ if (LastScopeMI) {
+ const DILocation *LastDL = LastScopeMI->getDebugLoc().get();
+ if (LastDL->getScope() == CurScope &&
+ LastDL->getInlinedAt() == CurInlinedAt)
+ return;
+ }
+
+ auto CurScopeRegOpt = resolveScope(CurScope);
+ if (!CurScopeRegOpt)
+ return;
+
+ SmallVector<MCRegister, 2> Ops{*CurScopeRegOpt};
+ if (CurInlinedAt) {
+ // If the global emission did not include this inlined-at case, we skip it.
+ MCRegister InlinedReg = DebugInlinedAtRegs.lookup(CurInlinedAt);
+ if (!InlinedReg.isValid())
+ return;
+ Ops.push_back(InlinedReg);
+ }
+
+ // A new DebugScope region is needed.
+ emitExtInst(SPIRV::NonSemanticExtInst::DebugScope, VoidTypeReg, ExtInstSetReg,
+ Ops, MAI);
+
+ LastScopeMI = MI;
+}
+
+void SPIRVNonSemanticDebugHandler::emitDebugLineForInstruction(
+ const MachineInstr *MI) {
+ assert(DebugFunctionDefinitionEmitted &&
+ "DebugFunctionDefinition must be emitted");
+ assert(CurrentMAI && "CurrentMAI must be set");
+
+ // The region is implicitly closed at each basic block boundary, so a
+ // LastLineMI from another block must be dropped before it is read below:
+ // the new block needs its own DebugLine, and has no region left to close.
if (LastLineMI && MI->getParent() != LastLineMI->getParent())
LastLineMI = nullptr;
+ SPIRV::ModuleAnalysisInfo &MAI = *CurrentMAI;
MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
MCRegister ExtInstSetReg = MAI.getExtInstSetReg(NSSet);
@@ -1317,7 +1425,7 @@ void SPIRVNonSemanticDebugHandler::emitDebugLineForInstruction(
if (LastLineMI && MI->getDebugLoc() == LastLineMI->getDebugLoc())
return;
- // A new DebugLine region is needed. Emit it and update LastLineMI.
+ // A new DebugLine region is needed.
emitExtInst(SPIRV::NonSemanticExtInst::DebugLine, VoidTypeReg, ExtInstSetReg,
{SrcReg, LineReg, LineReg, ColStartReg, ColEndReg}, MAI);
@@ -1572,6 +1680,11 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
emitDebugGlobalVariable(GV, Info, VoidTypeReg, I32TypeReg, ExtInstSetReg,
MAI);
+ // Emit DebugInlinedAt allowing recursive inlining.
+ for (const DILocation *DL : UniqueDebugLocations)
+ if (const DILocation *IA = DL->getInlinedAt())
+ getOrEmitDebugInlinedAt(IA, VoidTypeReg, I32TypeReg, ExtInstSetReg, MAI);
+
for (const DILocation *DL : UniqueDebugLocations) {
emitOpConstantI32(DL->getLine(), I32TypeReg, MAI);
emitOpConstantI32(DL->getColumn(), I32TypeReg, MAI);
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index d0916c104d890..04f24db0a4a29 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -108,6 +108,9 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
// order, collected in beginModule() for DebugLexicalBlock emission.
SetVector<const DIScope *> LexicalBlocks;
+ // DebugInlinedAt result id per DILocation used as an inlined-at chain link.
+ DenseMap<const DILocation *, MCRegister> DebugInlinedAtRegs;
+
// Path \c OpString result id per \c DIScope (CU, \c DIFile, declaration
// \c DISubprogram, …). Filled during \c emitNonSemanticDebugStrings() using
// \c getDebugFullPath + \c emitOpStringIfNew; section 10 uses it for
@@ -164,7 +167,12 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
bool DebugFunctionDefinitionEmitted = false;
+ // Instruction that opened the DebugLine / DebugScope region currently in
+ // effect, or nullptr when no region is open. The two are tracked separately
+ // because a DebugScope region usually spans several DebugLine regions, and
+ // either one can skip emission on a cache miss.
const MachineInstr *LastLineMI = nullptr;
+ const MachineInstr *LastScopeMI = nullptr;
public:
explicit SPIRVNonSemanticDebugHandler(AsmPrinter &AP);
@@ -231,6 +239,16 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
void resetPerFunctionDebugState();
+ /// Resolve the instruction that a per-instruction DebugLine/DebugScope
+ /// update should attach to: \p MI adjusted forward past a merge
+ /// instruction to its terminator, or \c std::nullopt if \p MI is not a
+ /// valid attachment point (skip-emission, or one of the structural opcodes
+ /// that can never carry DebugLine/DebugScope: OpFunction,
+ /// OpFunctionParameter, OpFunctionEnd, OpLabel, OpPhi).
+ std::optional<const MachineInstr *>
+ resolveDebugLocTarget(const MachineInstr *MI);
+
+ void emitDebugScopeForInstruction(const MachineInstr *MI);
void emitDebugLineForInstruction(const MachineInstr *MI);
void preparePerFunctionDebug(const MachineFunction *MF);
void tryEmitDebugFunctionDefinition(SPIRV::ModuleAnalysisInfo &MAI);
@@ -485,6 +503,23 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
emitDebugLexicalBlock(const DIScope *S, MCRegister VoidTypeReg,
MCRegister I32TypeReg, MCRegister ExtInstSetReg,
SPIRV::ModuleAnalysisInfo &MAI);
+
+ /// Return a cached \c DebugInlinedAt id for \p IA, or emit one (recursing
+ /// into \c IA->getInlinedAt() first for the optional Inlined operand, so
+ /// outer frames are always emitted before the inner frame that references
+ /// them). Must run after \c DebugScopeRegs is populated, since the Scope
+ /// operand is resolved through \c resolveScope. \c DebugInlinedAt is not in
+ /// the spec's in-block instruction list, so this is only ever called from
+ /// module-scope emission (\c emitNonSemanticGlobalDebugInfo), never from
+ /// per-instruction emission.
+ ///
+ /// \returns An invalid (default-constructed) \c MCRegister, and emits
+ /// nothing, if \p IA's Scope does not resolve.
+ MCRegister getOrEmitDebugInlinedAt(const DILocation *IA,
+ MCRegister VoidTypeReg,
+ MCRegister I32TypeReg,
+ MCRegister ExtInstSetReg,
+ SPIRV::ModuleAnalysisInfo &MAI);
};
} // namespace llvm
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-call-before-alloca.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-call-before-alloca.ll
index ed46c25d3ba20..8c3329316d931 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-call-before-alloca.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-call-before-alloca.ll
@@ -33,6 +33,7 @@
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF]] [[CALLER]]
; CHECK-NEXT: OpFunctionCall
; CHECK-NEXT: OpStore
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine
; CHECK-NEXT: OpReturnValue
; CHECK-NEXT: OpFunctionEnd
@@ -44,6 +45,7 @@
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF_NOARGS]] [[CALLER_NOARGS]]
; CHECK-NEXT: OpFunctionCall
; CHECK-NEXT: OpStore
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF_NOARGS]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine
; CHECK-NEXT: OpReturn
; CHECK-NEXT: OpFunctionEnd
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-calls.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-calls.ll
index 8db6cc586a01e..e577379311155 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-calls.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-calls.ll
@@ -26,6 +26,7 @@
; CHECK-NEXT: OpFunctionParameter
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF_LEAF]] [[LEAF]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF_LEAF]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine
; CHECK-NEXT: OpReturnValue
; CHECK-NEXT: OpFunctionEnd
@@ -39,10 +40,13 @@
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF_WITH]] [[WITH]]
; CHECK-NEXT: OpStore
; CHECK-NEXT: OpStore
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF_WITH]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine
; CHECK-NEXT: OpFunctionCall
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugNoScope
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugNoLine
; CHECK-NEXT: OpIAdd
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF_WITH]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine
; CHECK-NEXT: OpReturnValue
; CHECK-NEXT: OpFunctionEnd
@@ -52,6 +56,7 @@
; CHECK-NEXT: OpFunctionParameter
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF_CALLER]] [[CALLER]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF_CALLER]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine
; CHECK-NEXT: OpFunctionCall
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine
@@ -64,6 +69,7 @@
; CHECK: [[ORCH]] = OpFunction %{{.*}} ; -- Begin function orchestrator
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF_ORCH]] [[ORCH]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF_ORCH]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine
; CHECK-NEXT: OpFunctionCall
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-external-call.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-external-call.ll
index bd0c6b56a7dd4..f72fe5c2b01f8 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-external-call.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-external-call.ll
@@ -25,6 +25,7 @@
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF]] [[CALLER]]
; CHECK-NEXT: OpStore
; CHECK-NEXT: OpFunctionCall
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine
; CHECK-NEXT: OpReturnValue
; CHECK-NEXT: OpFunctionEnd
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-inlined-at-recursive.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-inlined-at-recursive.ll
new file mode 100644
index 0000000000000..a490cb80b6faa
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-inlined-at-recursive.ll
@@ -0,0 +1,83 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Exercise a two-level DebugInlinedAt chain.
+;
+; static inline __attribute__((always_inline)) int innermost(int x) {
+; return x * 3; // line 2
+; }
+; static inline __attribute__((always_inline)) int middle(int y) {
+; return innermost(y) ^ 7; // line 6, calls innermost at col 10
+; }
+; int top(int z) {
+; int r = middle(z); // line 10, calls middle at col 11
+; return r - 5; // line 11
+; }
+;
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[I32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[NAME_INNER:%[0-9]+]] = OpString "innermost"
+; CHECK-DAG: [[NAME_MID:%[0-9]+]] = OpString "middle"
+; CHECK-DAG: [[NAME_TOP:%[0-9]+]] = OpString "top"
+; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource
+; CHECK-DAG: [[DF_INNER:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction [[NAME_INNER]]
+; CHECK-DAG: [[DF_MID:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction [[NAME_MID]]
+; CHECK-DAG: [[DF_TOP:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction [[NAME_TOP]]
+; CHECK-DAG: [[V6:%[0-9]+]] = OpConstant [[I32]] 6{{$}}
+; CHECK-DAG: [[V10:%[0-9]+]] = OpConstant [[I32]] 10{{$}}
+
+; CHECK: [[OUTER:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugInlinedAt [[V10]] [[DF_TOP]]
+; CHECK-NEXT: [[INNER:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugInlinedAt [[V6]] [[DF_MID]] [[OUTER]]
+
+; CHECK: [[TOP:%[0-9]+]] = OpFunction {{.*}} ; -- Begin function top
+; CHECK-NEXT: OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF_TOP]] [[TOP]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF_INNER]] [[INNER]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]]
+; CHECK-NEXT: OpIMul
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF_MID]] [[OUTER]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]]
+; CHECK-NEXT: OpBitwiseXor
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF_TOP]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]]
+; CHECK-NEXT: OpIAdd
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]]
+; CHECK-NEXT: OpReturnValue
+; CHECK-NEXT: OpFunctionEnd
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func i32 @top(i32 %z) !dbg !11 {
+entry:
+ %mul.i = mul nsw i32 %z, 3, !dbg !29
+ %xor.i = xor i32 %mul.i, 7, !dbg !30
+ %sub = add nsw i32 %xor.i, -5, !dbg !31
+ ret i32 %sub, !dbg !32
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-inlined-at-recursive.c", directory: "/src")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+
+!11 = distinct !DISubprogram(name: "top", scope: !1, file: !1, line: 9, type: !12, scopeLine: 9, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+!12 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !13)
+!13 = !{!14, !14}
+!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!20 = distinct !DISubprogram(name: "middle", scope: !1, file: !1, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+!25 = distinct !DISubprogram(name: "innermost", scope: !1, file: !1, line: 1, type: !12, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+
+!23 = distinct !DILocation(line: 10, column: 11, scope: !11)
+!28 = distinct !DILocation(line: 6, column: 10, scope: !20, inlinedAt: !23)
+
+!29 = !DILocation(line: 2, column: 12, scope: !25, inlinedAt: !28)
+!30 = !DILocation(line: 6, column: 23, scope: !20, inlinedAt: !23)
+!31 = !DILocation(line: 11, column: 12, scope: !11)
+!32 = !DILocation(line: 11, column: 3, scope: !11)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-inlined-at.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-inlined-at.ll
new file mode 100644
index 0000000000000..8a186fa818dc5
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-inlined-at.ll
@@ -0,0 +1,68 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Exercise a single-level DebugInlinedAt.
+;
+; static inline __attribute__((always_inline)) int callee(int x) {
+; return x * 3; // line 2
+; }
+; int caller(int y) {
+; int r = callee(y); // line 6, calls callee at col 11
+; return r - 5; // line 7
+; }
+;
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[I32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[NAME_CALLEE:%[0-9]+]] = OpString "callee"
+; CHECK-DAG: [[NAME_CALLER:%[0-9]+]] = OpString "caller"
+; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource
+; CHECK-DAG: [[DF_CALLEE:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction [[NAME_CALLEE]]
+; CHECK-DAG: [[DF_CALLER:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction [[NAME_CALLER]]
+; CHECK-DAG: [[V6:%[0-9]+]] = OpConstant [[I32]] 6{{$}}
+
+; CHECK: [[IA:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugInlinedAt [[V6]] [[DF_CALLER]]
+
+; CHECK: [[FN:%[0-9]+]] = OpFunction {{.*}} ; -- Begin function caller
+; CHECK-NEXT: OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF_CALLER]] [[FN]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF_CALLEE]] [[IA]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]]
+; CHECK-NEXT: OpIMul
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF_CALLER]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]]
+; CHECK-NEXT: OpIAdd
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]]
+; CHECK-NEXT: OpReturnValue
+; CHECK-NEXT: OpFunctionEnd
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func i32 @caller(i32 %y) !dbg !11 {
+entry:
+ %mul.i = mul nsw i32 %y, 3, !dbg !24
+ %sub = add nsw i32 %mul.i, -5, !dbg !25
+ ret i32 %sub, !dbg !26
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-inlined-at.c", directory: "/src")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+
+!11 = distinct !DISubprogram(name: "caller", scope: !1, file: !1, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+!12 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !13)
+!13 = !{!14, !14}
+!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!20 = distinct !DISubprogram(name: "callee", scope: !1, file: !1, line: 1, type: !12, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0)
+
+!23 = distinct !DILocation(line: 6, column: 11, scope: !11)
+!24 = !DILocation(line: 2, column: 12, scope: !20, inlinedAt: !23)
+!25 = !DILocation(line: 7, column: 12, scope: !11)
+!26 = !DILocation(line: 7, column: 3, scope: !11)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-line-block.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-line-block.ll
index 48431e188b9d3..b643bc26633eb 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-line-block.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-line-block.ll
@@ -20,13 +20,16 @@
; CHECK-NEXT: OpFunctionParameter
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF]] [[FN]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V3]] [[V3]] [[V10]] [[V11]]
; CHECK-NEXT: [[T0:%[0-9]+]] = OpIAdd [[I32]]
; CHECK-NOT: OpExtInst [[VOID]] [[EXT]] DebugLine
; CHECK-NEXT: OpBranch
-; then block: mul reuses line 3; region must be reopened at the block boundary.
+; then block: mul reuses line 3; both DebugScope and DebugLine regions must be
+; reopened at the block boundary, even though neither scope nor line changed.
; CHECK: OpLabel
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V3]] [[V3]] [[V10]] [[V11]]
; CHECK-NEXT: [[T1:%[0-9]+]] = OpIMul [[I32]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V5]] [[V5]] [[V3]] [[V4]]
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-line-calls.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-line-calls.ll
index 8928b121401bc..f6452e6bffeaf 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-line-calls.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-line-calls.ll
@@ -27,6 +27,7 @@
; CHECK-NEXT: OpFunctionParameter
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF_INC]] [[INC]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF_INC]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V2]] [[V2]] [[V10]] [[V11]]
; CHECK-NEXT: [[T0:%[0-9]+]] = OpIAdd [[I32]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V3]] [[V3]] [[V3]] [[V4]]
@@ -38,6 +39,7 @@
; CHECK-NEXT: OpFunctionParameter
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF_CALLER]] [[CALLER]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF_CALLER]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V6]] [[V6]] [[V12]] [[V13]]
; CHECK-NEXT: OpFunctionCall [[I32]] [[INC]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V7]] [[V7]] [[V12]] [[V13]]
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-line-if-phi.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-line-if-phi.ll
index 14ec10f70aeef..2c9ffb7ab91f4 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-line-if-phi.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-line-if-phi.ll
@@ -1,7 +1,7 @@
; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
-; DebugLine/DebugNoLine must not appear before OpPhi.
+; DebugLine/DebugNoLine/DebugScope/DebugNoScope must not appear before OpPhi.
; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
@@ -22,18 +22,21 @@
; CHECK-NEXT: OpFunctionParameter
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF]] [[FN]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V3]] [[V3]] [[V10]] [[V11]]
; CHECK-NEXT: OpSLessThan
; CHECK-NEXT: OpBranchConditional
; then
; CHECK: OpLabel
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V4]] [[V4]] [[V5]] [[V6]]
; CHECK-NEXT: OpIAdd
; CHECK-NEXT: OpBranch
; CHECK: OpLabel
; CHECK-NEXT: [[PHI:%[0-9]+]] = OpPhi [[I32]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V9]] [[V9]] [[V3]] [[V4]]
; CHECK-NEXT: OpReturnValue [[PHI]]
; CHECK-NEXT: OpFunctionEnd
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-line-loop-merge.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-line-loop-merge.ll
index 171203202bdf4..00db7e47e8191 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-line-loop-merge.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-line-loop-merge.ll
@@ -9,6 +9,7 @@
; CHECK-DAG: [[I32:%[0-9]+]] = OpTypeInt 32 0
; CHECK-DAG: [[PATH:%[0-9]+]] = OpString "{{[/\\]}}src{{[/\\]}}debug-line-loop-merge.c"
; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[PATH]]
+; CHECK-DAG: [[DF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction {{.*}}
; CHECK-DAG: [[V3:%[0-9]+]] = OpConstant [[I32]] 3{{$}}
; CHECK-DAG: [[V4:%[0-9]+]] = OpConstant [[I32]] 4{{$}}
; CHECK-DAG: [[V50:%[0-9]+]] = OpConstant [[I32]] 50{{$}}
@@ -16,6 +17,7 @@
; CHECK-DAG: [[V99:%[0-9]+]] = OpConstant [[I32]] 99{{$}}
; CHECK: OpPhi [[I32]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V4]] [[V4]] [[V3]] [[V4]]
; CHECK-NEXT: OpSLessThan
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V99]] [[V99]] [[V50]] [[V51]]
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-line-loop.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-line-loop.ll
index 6feaea35912ba..fe988b85cd9e9 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-line-loop.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-line-loop.ll
@@ -1,13 +1,15 @@
; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
-; DebugLine/DebugNoLine must not appear before OpPhi in a loop header.
+; DebugLine/DebugNoLine/DebugScope/DebugNoScope must not appear before OpPhi in
+; a loop header.
; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
; CHECK-DAG: [[I32:%[0-9]+]] = OpTypeInt 32 0
; CHECK-DAG: [[PATH:%[0-9]+]] = OpString "{{[/\\]}}src{{[/\\]}}debug-line-loop.c"
; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[PATH]]
+; CHECK-DAG: [[DF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction {{.*}}
; CHECK-DAG: [[V3:%[0-9]+]] = OpConstant [[I32]] 3{{$}}
; CHECK-DAG: [[V4:%[0-9]+]] = OpConstant [[I32]] 4{{$}}
@@ -15,6 +17,7 @@
; CHECK: OpBranch
; CHECK-NEXT: OpLabel
; CHECK-NEXT: [[PHI:%[0-9]+]] = OpPhi [[I32]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V4]] [[V4]] [[V3]] [[V4]]
; CHECK-NEXT: OpSLessThan
; CHECK-NEXT: OpBranchConditional
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-line-selection-merge.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-line-selection-merge.ll
index 9d34026de31db..13d7b048b58b9 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-line-selection-merge.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-line-selection-merge.ll
@@ -26,6 +26,7 @@
; CHECK-NEXT: OpFunctionParameter
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF]] [[FN]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V3]] [[V3]] [[V10]] [[V11]]
; CHECK-NEXT: OpSLessThan
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V99]] [[V99]] [[V50]] [[V51]]
@@ -34,12 +35,14 @@
; else
; CHECK: OpLabel
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V6]] [[V6]] [[V5]] [[V6]]
; CHECK-NEXT: OpISub
; CHECK-NEXT: OpBranch
; then
; CHECK: OpLabel
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V4]] [[V4]] [[V5]] [[V6]]
; CHECK-NEXT: OpIAdd
; CHECK-NEXT: OpBranch
@@ -47,6 +50,7 @@
; merge
; CHECK: OpLabel
; CHECK-NEXT: [[PHI:%[0-9]+]] = OpPhi [[I32]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V9]] [[V9]] [[V3]] [[V4]]
; CHECK-NEXT: OpReturnValue [[PHI]]
; CHECK-NEXT: OpFunctionEnd
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-line-shared.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-line-shared.ll
index 6f106d8ba4d44..0f0f4cd5c4a33 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-line-shared.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-line-shared.ll
@@ -21,6 +21,7 @@
; CHECK-NEXT: [[B:%[0-9]+]] = OpFunctionParameter
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF]] [[FN]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V3]] [[V3]] [[V10]] [[V11]]
; CHECK-NEXT: [[T0:%[0-9]+]] = OpIAdd [[I32]] [[A]] [[B]]
; CHECK-NOT: OpExtInst [[VOID]] [[EXT]] DebugLine
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-line.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-line.ll
index 0178126982ca2..ebcaaea7e78a3 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-line.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-line.ll
@@ -33,6 +33,7 @@
; CHECK-NEXT: [[B:%[0-9]+]] = OpFunctionParameter
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF]] [[FN]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V3]] [[V3]] [[V10]] [[V11]]
; CHECK-NEXT: [[T0:%[0-9]+]] = OpIAdd [[I32]] [[A]] [[B]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V4]] [[V4]] [[V12]] [[V13]]
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-no-line.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-no-line.ll
index ce5acc50391bb..03bf06d507aff 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-no-line.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-no-line.ll
@@ -25,10 +25,13 @@
; CHECK-NEXT: [[B:%[0-9]+]] = OpFunctionParameter
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF]] [[FN]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V3]] [[V3]] [[V10]] [[V11]]
; CHECK-NEXT: [[T0:%[0-9]+]] = OpIAdd [[I32]] [[A]] [[B]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugNoScope
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugNoLine
; CHECK-NEXT: [[T1:%[0-9]+]] = OpIMul [[I32]] [[T0]] [[A]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V5]] [[V5]] [[V3]] [[V4]]
; CHECK-NEXT: OpReturnValue [[T1]]
; CHECK-NEXT: OpFunctionEnd
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-scope-block.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-scope-block.ll
new file mode 100644
index 0000000000000..09ad3976cf40d
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-scope-block.ll
@@ -0,0 +1,66 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Exercise the implicit close of a DebugScope region at a basic block boundary,
+; for a lexical block that spans two blocks.
+;
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[I32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[PATH:%[0-9]+]] = OpString "{{[/\\]}}src{{[/\\]}}debug-scope-block.c"
+; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[PATH]]
+; CHECK-DAG: [[DF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction {{.*}}
+; CHECK-DAG: [[LB:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugLexicalBlock [[DS]] {{.*}} [[DF]]
+; CHECK-DAG: [[V3:%[0-9]+]] = OpConstant [[I32]] 3{{$}}
+; CHECK-DAG: [[V4:%[0-9]+]] = OpConstant [[I32]] 4{{$}}
+; CHECK-DAG: [[V5:%[0-9]+]] = OpConstant [[I32]] 5{{$}}
+; CHECK-DAG: [[V6:%[0-9]+]] = OpConstant [[I32]] 6{{$}}
+; CHECK-DAG: [[V7:%[0-9]+]] = OpConstant [[I32]] 7{{$}}
+
+; CHECK: [[FN:%[0-9]+]] = OpFunction
+; CHECK-NEXT: OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF]] [[FN]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[LB]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V4]] [[V4]] [[V5]] [[V6]]
+; CHECK-NEXT: [[T0:%[0-9]+]] = OpIAdd [[I32]]
+; CHECK-NEXT: OpBranch
+
+; then: both regions reopen even though neither scope nor position changed.
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[LB]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V4]] [[V4]] [[V5]] [[V6]]
+; CHECK-NEXT: [[T1:%[0-9]+]] = OpIMul [[I32]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V7]] [[V7]] [[V3]] [[V4]]
+; CHECK-NEXT: OpReturnValue [[T1]]
+; CHECK-NEXT: OpFunctionEnd
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func i32 @block_scope(i32 %x) !dbg !5 {
+entry:
+ %t0 = add i32 %x, 1, !dbg !9 ; lexical block, line 4, col 5
+ br label %then, !dbg !9 ; same scope and position as the add
+
+then:
+ %t1 = mul i32 %t0, %t0, !dbg !9 ; same scope and position, new block
+ ret i32 %t1, !dbg !12 ; back to function scope, line 7, col 3
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-scope-block.c", directory: "/src")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+
+!4 = !DISubroutineType(types: !6)
+!6 = !{!7, !7}
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!5 = distinct !DISubprogram(name: "block_scope", linkageName: "block_scope", scope: !1, file: !1, line: 1, type: !4, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!10 = distinct !DILexicalBlock(scope: !5, file: !1, line: 3, column: 5)
+!9 = !DILocation(line: 4, column: 5, scope: !10)
+!12 = !DILocation(line: 7, column: 3, scope: !5)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-scope-no-scope-block.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-scope-no-scope-block.ll
new file mode 100644
index 0000000000000..acb4928eabfcd
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-scope-no-scope-block.ll
@@ -0,0 +1,62 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; An instruction without a location that opens a basic block needs no DebugNoScope/DebugNoLine.
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[I32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[PATH:%[0-9]+]] = OpString "{{[/\\]}}src{{[/\\]}}debug-scope-no-scope-block.c"
+; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[PATH]]
+; CHECK-DAG: [[DF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction {{.*}}
+; CHECK-DAG: [[V3:%[0-9]+]] = OpConstant [[I32]] 3{{$}}
+; CHECK-DAG: [[V4:%[0-9]+]] = OpConstant [[I32]] 4{{$}}
+; CHECK-DAG: [[V5:%[0-9]+]] = OpConstant [[I32]] 5{{$}}
+; CHECK-DAG: [[V10:%[0-9]+]] = OpConstant [[I32]] 10{{$}}
+; CHECK-DAG: [[V11:%[0-9]+]] = OpConstant [[I32]] 11{{$}}
+
+; CHECK: [[FN:%[0-9]+]] = OpFunction
+; CHECK-NEXT: OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF]] [[FN]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V3]] [[V3]] [[V10]] [[V11]]
+; CHECK-NEXT: [[T0:%[0-9]+]] = OpIAdd [[I32]]
+; CHECK-NEXT: OpBranch
+
+; then: the multiply carries no location, and the CHECK-NEXT chain pins that
+; neither region is closed again at the top of the block.
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[T1:%[0-9]+]] = OpIMul [[I32]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V5]] [[V5]] [[V3]] [[V4]]
+; CHECK-NEXT: OpReturnValue [[T1]]
+; CHECK-NEXT: OpFunctionEnd
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func i32 @no_location_at_block_entry(i32 %x) !dbg !5 {
+entry:
+ %t0 = add i32 %x, 1, !dbg !8 ; line 3, col 10, opens both regions
+ br label %then, !dbg !8 ; same region as the add
+
+then:
+ %t1 = mul i32 %t0, %t0 ; no debug location, first of the block
+ ret i32 %t1, !dbg !9 ; line 5, col 3, reopens both regions
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-scope-no-scope-block.c", directory: "/src")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+
+!4 = !DISubroutineType(types: !6)
+!6 = !{!7, !7}
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!5 = distinct !DISubprogram(name: "no_location_at_block_entry", linkageName: "no_location_at_block_entry", scope: !1, file: !1, line: 1, type: !4, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!8 = !DILocation(line: 3, column: 10, scope: !5)
+!9 = !DILocation(line: 5, column: 3, scope: !5)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-scope-no-scope.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-scope-no-scope.ll
new file mode 100644
index 0000000000000..a84c1e21aaa9e
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-scope-no-scope.ll
@@ -0,0 +1,55 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[I32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[PATH:%[0-9]+]] = OpString "{{[/\\]}}src{{[/\\]}}debug-scope-no-scope.c"
+; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[PATH]]
+; CHECK-DAG: [[DF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction {{.*}}
+; CHECK-DAG: [[V3:%[0-9]+]] = OpConstant [[I32]] 3{{$}}
+; CHECK-DAG: [[V5:%[0-9]+]] = OpConstant [[I32]] 5{{$}}
+; CHECK-DAG: [[V10:%[0-9]+]] = OpConstant [[I32]] 10{{$}}
+; CHECK-DAG: [[V11:%[0-9]+]] = OpConstant [[I32]] 11{{$}}
+; CHECK-DAG: [[V4:%[0-9]+]] = OpConstant [[I32]] 4{{$}}
+
+; CHECK: [[FN:%[0-9]+]] = OpFunction
+; CHECK-NEXT: [[A:%[0-9]+]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%[0-9]+]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF]] [[FN]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V3]] [[V3]] [[V10]] [[V11]]
+; CHECK-NEXT: [[T0:%[0-9]+]] = OpIAdd [[I32]] [[A]] [[B]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugNoScope
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugNoLine
+; CHECK-NEXT: [[T1:%[0-9]+]] = OpIMul [[I32]] [[T0]] [[A]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V5]] [[V5]] [[V3]] [[V4]]
+; CHECK-NEXT: OpReturnValue [[T1]]
+; CHECK-NEXT: OpFunctionEnd
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func i32 @maybe_scope(i32 %a, i32 %b) !dbg !5 {
+entry:
+ %t0 = add i32 %a, %b, !dbg !8 ; line 3, col 10
+ %t1 = mul i32 %t0, %a ; no debug location
+ ret i32 %t1, !dbg !10 ; line 5, col 3
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-scope-no-scope.c", directory: "/src")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+
+!4 = !DISubroutineType(types: !6)
+!6 = !{!7, !7, !7}
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!5 = distinct !DISubprogram(name: "maybe_scope", linkageName: "maybe_scope", scope: !1, file: !1, line: 1, type: !4, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!8 = !DILocation(line: 3, column: 10, scope: !5)
+!10 = !DILocation(line: 5, column: 3, scope: !5)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-scope-same-line.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-scope-same-line.ll
new file mode 100644
index 0000000000000..606ec21e75754
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-scope-same-line.ll
@@ -0,0 +1,63 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; The scope changes while the source position stays identical.
+; At the moment, the emission of DebugLine depends on the DILocation pointer
+; which leads to an additional DebugLine being emitted.
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[I32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[PATH:%[0-9]+]] = OpString "{{[/\\]}}src{{[/\\]}}debug-scope-same-line.c"
+; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[PATH]]
+; CHECK-DAG: [[DF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction {{.*}}
+; CHECK-DAG: [[LB:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugLexicalBlock [[DS]] {{.*}} [[DF]]
+; CHECK-DAG: [[V3:%[0-9]+]] = OpConstant [[I32]] 3{{$}}
+; CHECK-DAG: [[V4:%[0-9]+]] = OpConstant [[I32]] 4{{$}}
+; CHECK-DAG: [[V5:%[0-9]+]] = OpConstant [[I32]] 5{{$}}
+; CHECK-DAG: [[V6:%[0-9]+]] = OpConstant [[I32]] 6{{$}}
+
+; CHECK: [[FN:%[0-9]+]] = OpFunction
+; CHECK-NEXT: [[N:%[0-9]+]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF]] [[FN]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V3]] [[V3]] [[V5]] [[V6]]
+; CHECK-NEXT: [[A:%[0-9]+]] = OpIAdd [[I32]]
+
+; Scope-only change: new DebugScope, plus a DebugLine identical to the one above.
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[LB]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V3]] [[V3]] [[V5]] [[V6]]
+; CHECK-NEXT: [[B:%[0-9]+]] = OpIAdd [[I32]]
+
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V4]] [[V4]] [[V3]] [[V4]]
+; CHECK-NEXT: OpReturnValue [[B]]
+; CHECK-NEXT: OpFunctionEnd
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func i32 @same_line(i32 %n) !dbg !5 {
+entry:
+ %a = add i32 %n, 1, !dbg !9 ; function scope, line 3, col 5
+ %b = add i32 %a, 1, !dbg !11 ; lexical block, line 3, col 5 -- scope only
+ ret i32 %b, !dbg !12 ; back to function scope, line 4, col 3
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-scope-same-line.c", directory: "/src")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+
+!4 = !DISubroutineType(types: !6)
+!6 = !{!7, !7}
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!5 = distinct !DISubprogram(name: "same_line", linkageName: "same_line", scope: !1, file: !1, line: 1, type: !4, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!9 = !DILocation(line: 3, column: 5, scope: !5)
+!10 = distinct !DILexicalBlock(scope: !5, file: !1, line: 2, column: 6)
+!11 = !DILocation(line: 3, column: 5, scope: !10)
+!12 = !DILocation(line: 4, column: 3, scope: !5)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-scope-shared.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-scope-shared.ll
new file mode 100644
index 0000000000000..402f100b0fa9d
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-scope-shared.ll
@@ -0,0 +1,62 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; One DebugScope region spans several DebugLine regions.
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[I32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[PATH:%[0-9]+]] = OpString "{{[/\\]}}src{{[/\\]}}debug-scope-shared.c"
+; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[PATH]]
+; CHECK-DAG: [[DF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction {{.*}}
+; CHECK-DAG: [[V2:%[0-9]+]] = OpConstant [[I32]] 2{{$}}
+; CHECK-DAG: [[V3:%[0-9]+]] = OpConstant [[I32]] 3{{$}}
+; CHECK-DAG: [[V4:%[0-9]+]] = OpConstant [[I32]] 4{{$}}
+; CHECK-DAG: [[V5:%[0-9]+]] = OpConstant [[I32]] 5{{$}}
+; CHECK-DAG: [[V10:%[0-9]+]] = OpConstant [[I32]] 10{{$}}
+; CHECK-DAG: [[V11:%[0-9]+]] = OpConstant [[I32]] 11{{$}}
+
+; A single DebugScope opens the region; the fully pinned CHECK-NEXT chain below
+; leaves no room for a second one anywhere in the function.
+; CHECK: [[FN:%[0-9]+]] = OpFunction
+; CHECK-NEXT: [[N:%[0-9]+]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF]] [[FN]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V2]] [[V2]] [[V10]] [[V11]]
+; CHECK-NEXT: [[A:%[0-9]+]] = OpIAdd [[I32]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V3]] [[V3]] [[V10]] [[V11]]
+; CHECK-NEXT: [[B:%[0-9]+]] = OpIAdd [[I32]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V4]] [[V4]] [[V10]] [[V11]]
+; CHECK-NEXT: [[C:%[0-9]+]] = OpIAdd [[I32]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V5]] [[V5]] [[V3]] [[V4]]
+; CHECK-NEXT: OpReturnValue [[C]]
+; CHECK-NEXT: OpFunctionEnd
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func i32 @spans(i32 %n) !dbg !5 {
+entry:
+ %a = add i32 %n, 1, !dbg !9 ; line 2, col 10
+ %b = add i32 %a, 1, !dbg !10 ; line 3, col 10 -- line changes, scope does not
+ %c = add i32 %b, 1, !dbg !11 ; line 4, col 10 -- line changes, scope does not
+ ret i32 %c, !dbg !12 ; line 5, col 3 -- line changes, scope does not
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-scope-shared.c", directory: "/src")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+
+!4 = !DISubroutineType(types: !6)
+!6 = !{!7, !7}
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!5 = distinct !DISubprogram(name: "spans", linkageName: "spans", scope: !1, file: !1, line: 1, type: !4, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!9 = !DILocation(line: 2, column: 10, scope: !5)
+!10 = !DILocation(line: 3, column: 10, scope: !5)
+!11 = !DILocation(line: 4, column: 10, scope: !5)
+!12 = !DILocation(line: 5, column: 3, scope: !5)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-scope.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-scope.ll
new file mode 100644
index 0000000000000..ba3ac63f076f1
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-scope.ll
@@ -0,0 +1,67 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[I32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[PATH:%[0-9]+]] = OpString "{{[/\\]}}src{{[/\\]}}debug-scope.c"
+; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[PATH]]
+; CHECK-DAG: [[DF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction {{.*}}
+; CHECK-DAG: [[LB:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugLexicalBlock [[DS]] {{.*}} [[DF]]
+; CHECK-DAG: [[V2:%[0-9]+]] = OpConstant [[I32]] 2{{$}}
+; CHECK-DAG: [[V3:%[0-9]+]] = OpConstant [[I32]] 3{{$}}
+; CHECK-DAG: [[V4:%[0-9]+]] = OpConstant [[I32]] 4{{$}}
+; CHECK-DAG: [[V5:%[0-9]+]] = OpConstant [[I32]] 5{{$}}
+; CHECK-DAG: [[V7:%[0-9]+]] = OpConstant [[I32]] 7{{$}}
+; CHECK-DAG: [[V10:%[0-9]+]] = OpConstant [[I32]] 10{{$}}
+; CHECK-DAG: [[V11:%[0-9]+]] = OpConstant [[I32]] 11{{$}}
+
+; CHECK: [[FN:%[0-9]+]] = OpFunction
+; CHECK-NEXT: [[N:%[0-9]+]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF]] [[FN]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V2]] [[V2]] [[V10]] [[V11]]
+; CHECK-NEXT: [[A:%[0-9]+]] = OpIAdd [[I32]] [[N]]
+; CHECK-NOT: OpExtInst [[VOID]] [[EXT]] DebugScope
+; CHECK-NOT: OpExtInst [[VOID]] [[EXT]] DebugLine
+; CHECK-NEXT: [[B:%[0-9]+]] = OpIAdd [[I32]] [[A]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[LB]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V5]] [[V5]] [[V3]] [[V4]]
+; CHECK-NEXT: [[C:%[0-9]+]] = OpIAdd [[I32]] [[B]]
+; CHECK-NOT: OpExtInst [[VOID]] [[EXT]] DebugScope
+; CHECK-NOT: OpExtInst [[VOID]] [[EXT]] DebugLine
+; CHECK-NEXT: [[D:%[0-9]+]] = OpIAdd [[I32]] [[C]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugScope [[DF]]
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugLine [[DS]] [[V7]] [[V7]] [[V3]] [[V4]]
+; CHECK-NEXT: OpReturnValue [[D]]
+; CHECK-NEXT: OpFunctionEnd
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func i32 @scoped(i32 %n) !dbg !5 {
+entry:
+ %a = add i32 %n, 1, !dbg !9 ; function scope, line 2, col 10
+ %b = add i32 %a, 1, !dbg !9 ; same scope+loc -> dedup
+ %c = add i32 %b, 1, !dbg !11 ; nested lexical block scope, line 5, col 3
+ %d = add i32 %c, 1, !dbg !11 ; same scope+loc -> dedup
+ ret i32 %d, !dbg !13 ; back to function scope, line 7, col 3
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-scope.c", directory: "/src")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+
+!4 = !DISubroutineType(types: !6)
+!6 = !{!7, !7}
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!5 = distinct !DISubprogram(name: "scoped", linkageName: "scoped", scope: !1, file: !1, line: 1, type: !4, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!9 = !DILocation(line: 2, column: 10, scope: !5)
+!10 = distinct !DILexicalBlock(scope: !5, file: !1, line: 4, column: 5)
+!11 = !DILocation(line: 5, column: 3, scope: !10)
+!13 = !DILocation(line: 7, column: 3, scope: !5)
More information about the llvm-commits
mailing list