[llvm] Add support for NSDI DebugScope, DebugNoScope and DebugInlinedAt (PR #218959)
Manuel Carrasco via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 7 02:00:24 PDT 2026
https://github.com/mgcarrasco updated https://github.com/llvm/llvm-project/pull/218959
>From 954e91440970d69bf67d3d7f1e15c2359ad53438 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Wed, 26 Aug 2026 10:30:04 -0500
Subject: [PATCH] Add support for NSDI DebugScope, DebugNoScope and
DebugInlinedAt
---
.../SPIRV/SPIRVNonSemanticDebugHandler.cpp | 145 +++++++++++++++---
.../SPIRV/SPIRVNonSemanticDebugHandler.h | 32 ++++
...-function-definition-call-before-alloca.ll | 2 +
.../debug-function-definition-calls.ll | 6 +
...debug-function-definition-external-call.ll | 1 +
.../debug-info/debug-inlined-at-recursive.ll | 83 ++++++++++
.../SPIRV/debug-info/debug-inlined-at.ll | 68 ++++++++
.../SPIRV/debug-info/debug-line-block.ll | 5 +-
.../SPIRV/debug-info/debug-line-calls.ll | 2 +
.../SPIRV/debug-info/debug-line-if-phi.ll | 5 +-
.../SPIRV/debug-info/debug-line-loop-merge.ll | 2 +
.../SPIRV/debug-info/debug-line-loop.ll | 5 +-
.../debug-info/debug-line-selection-merge.ll | 4 +
.../SPIRV/debug-info/debug-line-shared.ll | 1 +
.../CodeGen/SPIRV/debug-info/debug-line.ll | 1 +
.../CodeGen/SPIRV/debug-info/debug-no-line.ll | 3 +
.../SPIRV/debug-info/debug-scope-block.ll | 66 ++++++++
.../SPIRV/debug-info/debug-scope-no-scope.ll | 55 +++++++
.../SPIRV/debug-info/debug-scope-same-line.ll | 63 ++++++++
.../SPIRV/debug-info/debug-scope-shared.ll | 62 ++++++++
.../CodeGen/SPIRV/debug-info/debug-scope.ll | 67 ++++++++
21 files changed, 657 insertions(+), 21 deletions(-)
create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-inlined-at-recursive.ll
create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-inlined-at.ll
create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-scope-block.ll
create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-scope-no-scope.ll
create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-scope-same-line.ll
create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-scope-shared.ll
create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-scope.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index fd289d3a5764c..bb311d61f59c0 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,76 @@ 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.
+ // Both regions are implicitly closed at each basic block boundary. They are
+ // tracked separately because a DebugScope region usually spans several
+ // DebugLine regions, and either one can skip emission on a cache miss.
if (LastLineMI && MI->getParent() != LastLineMI->getParent())
LastLineMI = nullptr;
+ if (LastScopeMI && MI->getParent() != LastScopeMI->getParent())
+ LastScopeMI = nullptr;
+
+ return MI;
+}
+
+void SPIRVNonSemanticDebugHandler::emitDebugScopeForInstruction(
+ const MachineInstr *MI) {
+ assert(DebugFunctionDefinitionEmitted &&
+ "DebugFunctionDefinition must be emitted");
+ assert(CurrentMAI && "CurrentMAI must be set");
+ 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");
+
+ SPIRV::ModuleAnalysisInfo &MAI = *CurrentMAI;
MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
MCRegister ExtInstSetReg = MAI.getExtInstSetReg(NSSet);
@@ -1317,7 +1421,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 +1676,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 ecb23450517c7..ce02b274af75e 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
@@ -166,6 +169,8 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
const MachineInstr *LastLineMI = nullptr;
+ const MachineInstr *LastScopeMI = nullptr;
+
public:
explicit SPIRVNonSemanticDebugHandler(AsmPrinter &AP);
@@ -231,6 +236,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 +500,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.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