[llvm] Add support for DebugLexicalBlock. (PR #217965)

via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 24 07:34:04 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-spir-v

Author: Manuel Carrasco (mgcarrasco)

<details>
<summary>Changes</summary>

Emit [DebugLexicalBlock](https://github.khronos.org/SPIRV-Registry/nonsemantic/NonSemantic.Shader.DebugInfo.html#DebugLexicalBlock) for LLVM DILexicalBlock and DINamespace. 

This also lets module-scope debug entities (globals, typedefs, functions, composites) reference a lexical parent instead of always choosing the compilation unit.

Summary of changes:

1. Collect DILexicalBlock / DINamespace and keep track of their relative order for later emission.
2. Emit namespace scopes as DebugLexicalBlock before DebugFunction so namespace-scoped functions and globals can use them as Parent.
3. Emit IR-function-body DILexicalBlock scopes as DebugLexicalBlock after DebugFunction so these DebugLexicalBlock can properly reference their parents.
4. Update the already-existing parent scope handling in other opcodes to use the new data available.


---

Patch is 23.35 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/217965.diff


6 Files Affected:

- (modified) llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp (+123-5) 
- (modified) llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h (+31) 
- (modified) llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration-namespace-scope.ll (+9-4) 
- (modified) llvm/test/CodeGen/SPIRV/debug-info/debug-function-namespace-scope.ll (+9-6) 
- (added) llvm/test/CodeGen/SPIRV/debug-info/debug-lexical-block-namespace.ll (+48) 
- (added) llvm/test/CodeGen/SPIRV/debug-info/debug-lexical-block.ll (+61) 


``````````diff
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 74c2fbab99a09..64a239470ce8e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -276,6 +276,20 @@ static void collectUniqueDebugLocations(const Module &M,
   }
 }
 
+// Insert \p S and its enclosing DILexicalBlock/DINamespace chain into \p Out,
+// parent before child, so single-pass emission never needs a forward
+// reference for the Parent operand.
+static void collectLexicalBlockChain(const DIScope *S,
+                                     SetVector<const DIScope *> &Out) {
+  // Walk up child-first, then insert in reverse to get parents in first.
+  SmallVector<const DIScope *, 8> Chain;
+  while (S && !Out.contains(S) && isa<DILexicalBlock, DINamespace>(S)) {
+    Chain.push_back(S);
+    S = S->getScope();
+  }
+  Out.insert(Chain.rbegin(), Chain.rend());
+}
+
 void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
   // The base class sets Asm = nullptr when the module has no compile units,
   // and initializes lexical scope tracking otherwise.
@@ -296,8 +310,10 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
   SubprogramDefinitions.clear();
   UniqueDebugLocations.clear();
   GlobalVariableDebugInfoMap.clear();
+  LexicalBlocks.clear();
   DebugFunctionDeclarationRegs.clear();
   DebugFunctionRegs.clear();
+  DebugLexicalBlockRegs.clear();
   ScopeToPathOpStringReg.clear();
   CUToCompilationUnitDbgReg.clear();
   DebugSourceRegByFileStr.clear();
@@ -381,6 +397,12 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
   }
 
   collectUniqueDebugLocations(*M, UniqueDebugLocations);
+
+  // DILexicalBlock and DINamespace scopes are lowered to DebugLexicalBlock.
+  // Collect them in parent-before-child order so they can be later emitted in a
+  // single pass.
+  for (const DIScope *S : Finder.scopes())
+    collectLexicalBlockChain(S, LexicalBlocks);
 }
 
 void SPIRVNonSemanticDebugHandler::prepareModuleOutput(
@@ -634,6 +656,9 @@ SPIRVNonSemanticDebugHandler::resolveDebugFunctionParent(
     const DISubprogram *SP) const {
   const DIScope *Scope = SP->getScope();
   if (Scope && !isa<DIFile>(Scope)) {
+    // Find the DINamespace that was emitted as a lexical block.
+    if (isa<DINamespace>(Scope))
+      return lookupOptReg(DebugLexicalBlockRegs, Scope);
     // TODO: Complete with other lookups once other scopes are supported
     // (subclasses of DIScope).
     const DIType *Ty = dyn_cast<DIType>(Scope);
@@ -657,14 +682,65 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::resolveTypeScopeParent(
   if (const auto *Ty = dyn_cast_or_null<DIType>(Scope))
     return lookupOptReg(DebugTypeRegs, Ty);
 
-  // For a file, compile-unit, namespace, or absent scope, the parent is the
-  // first module DebugCompilationUnit.
+  // Find the DINamespace that was emitted as a lexical block.
+  if (isa_and_nonnull<DINamespace>(Scope))
+    return lookupOptReg(DebugLexicalBlockRegs, Scope);
+
+  // For a file, compile-unit, or absent scope, the parent is the first module
+  // DebugCompilationUnit.
   if (CompileUnits.empty())
     return std::nullopt;
 
   return lookupOptReg(CUToCompilationUnitDbgReg, CompileUnits[0].TheCU);
 }
 
+std::optional<MCRegister>
+SPIRVNonSemanticDebugHandler::resolveLexicalBlockParent(
+    const DIScope *Scope) const {
+  if (isa_and_nonnull<DILexicalBlock>(Scope) ||
+      isa_and_nonnull<DINamespace>(Scope))
+    return lookupOptReg(DebugLexicalBlockRegs, Scope);
+  if (const auto *SP = dyn_cast_or_null<DISubprogram>(Scope))
+    return lookupOptReg(DebugFunctionRegs, SP);
+  if (CompileUnits.empty())
+    return std::nullopt;
+  return lookupOptReg(CUToCompilationUnitDbgReg, CompileUnits[0].TheCU);
+}
+
+std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugLexicalBlock(
+    const DIScope *S, MCRegister VoidTypeReg, MCRegister I32TypeReg,
+    MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI) {
+  assert((isa<DILexicalBlock>(S) || isa<DINamespace>(S)) &&
+         "S must be a DILexicalBlock or DINamespace in emitDebugLexicalBlock");
+  auto ParentRegOpt = resolveLexicalBlockParent(S->getScope());
+  if (!ParentRegOpt)
+    return std::nullopt;
+
+  MCRegister FileStrReg = getCachedScopePathOpStringReg(
+      S->getFile(), /*UseEmptyPathIfNullScope=*/true);
+  MCRegister SrcReg = getOrEmitDebugSourceForFileStrReg(FileStrReg, VoidTypeReg,
+                                                        ExtInstSetReg, MAI);
+
+  SmallVector<MCRegister, 5> Ops;
+  if (const auto *LB = dyn_cast<DILexicalBlock>(S)) {
+    MCRegister LineReg = emitOpConstantI32(static_cast<uint32_t>(LB->getLine()),
+                                           I32TypeReg, MAI);
+    MCRegister ColReg = emitOpConstantI32(
+        static_cast<uint32_t>(LB->getColumn()), I32TypeReg, MAI);
+    Ops = {SrcReg, LineReg, ColReg, *ParentRegOpt};
+  } else {
+    const auto *NS = cast<DINamespace>(S);
+    // DINamespace carries no line/column info.
+    MCRegister LineReg = emitOpConstantI32(0, I32TypeReg, MAI);
+    MCRegister ColReg = emitOpConstantI32(0, I32TypeReg, MAI);
+    MCRegister NameReg = getCachedOpStringReg(NS->getName());
+    Ops = {SrcReg, LineReg, ColReg, *ParentRegOpt, NameReg};
+  }
+
+  return emitExtInst(SPIRV::NonSemanticExtInst::DebugLexicalBlock, VoidTypeReg,
+                     ExtInstSetReg, Ops, MAI);
+}
+
 std::optional<MCRegister>
 SPIRVNonSemanticDebugHandler::emitDebugFunctionDeclaration(
     const DISubprogram *SP, MCRegister VoidTypeReg, MCRegister I32TypeReg,
@@ -767,9 +843,16 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::mapDISignatureTypeToReg(
 }
 
 MCRegister SPIRVNonSemanticDebugHandler::resolveGlobalVariableParent(
-    const DIGlobalVariable *) const {
-  // TODO: When this backend emits debug instructions for namespace, subprogram,
-  // compilation units, and module scopes return GV->getScope()'s debug id.
+    const DIGlobalVariable *GV) const {
+  // A namespace-scoped global variable's parent is the enclosing
+  // DebugLexicalBlock emitted for that DINamespace.
+  // TODO: When this backend emits debug instructions for subprogram,
+  // compilation units, and module scopes, also return GV->getScope()'s debug
+  // id for those cases.
+  if (isa_and_nonnull<DINamespace>(GV->getScope())) {
+    if (auto ParentRegOpt = lookupOptReg(DebugLexicalBlockRegs, GV->getScope()))
+      return *ParentRegOpt;
+  }
 
   // !CompileUnits.empty() was already checked before staring the emission of
   // NSDI instructions.
@@ -1100,6 +1183,14 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticDebugStrings(
     emitAndCacheScopePathOpStringReg(GV->getFile(), MAI);
   }
 
+  // Cache the path OpString each DebugLexicalBlock uses (source file), plus
+  // the Name OpString for the DINamespace case.
+  for (const DIScope *S : LexicalBlocks) {
+    emitAndCacheScopePathOpStringReg(S->getFile(), MAI);
+    if (const auto *NS = dyn_cast<DINamespace>(S))
+      emitOpStringIfNew(NS->getName(), MAI);
+  }
+
   for (const DILocation *DL : UniqueDebugLocations)
     emitAndCacheScopePathOpStringReg(DL->getScope(), MAI);
 
@@ -1484,6 +1575,21 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
       DebugTypeRegs[ST] = *FnTyReg;
   }
 
+  // Emit DebugLexicalBlock for each collected DINamespace, in parent-before-
+  // child order. Placed before any DINamespace-scoped entity (typedefs,
+  // function declarations, composite types, functions, global variables) so
+  // their Parent operand can reference an already-emitted DebugLexicalBlock.
+  // DINamespace never chains through a DISubprogram (DINamespace::getScope()
+  // returns DIScope, not DILocalScope), so this never depends on
+  // DebugFunctionRegs.
+  for (const DIScope *S : LexicalBlocks) {
+    if (!isa<DINamespace>(S))
+      continue;
+    if (auto LBReg = emitDebugLexicalBlock(S, VoidTypeReg, I32TypeReg,
+                                           ExtInstSetReg, MAI))
+      DebugLexicalBlockRegs[S] = *LBReg;
+  }
+
   // Emit DebugTypedef for each typedef. Placed after the other type loops so a
   // typedef can resolve its underlying type. A typedef whose base type is not
   // emitted is skipped. A typedef whose base is another typedef emitted later
@@ -1528,6 +1634,18 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
       DebugFunctionRegs[SP] = *FnReg;
   }
 
+  // Emit DebugLexicalBlock for each collected DILexicalBlock, in parent-
+  // before-child order. Placed after DebugFunction so a block directly
+  // enclosed by a function (the common case) can resolve its Parent operand;
+  // DINamespace entries were already emitted above.
+  for (const DIScope *S : LexicalBlocks) {
+    if (!isa<DILexicalBlock>(S))
+      continue;
+    if (auto LBReg = emitDebugLexicalBlock(S, VoidTypeReg, I32TypeReg,
+                                           ExtInstSetReg, MAI))
+      DebugLexicalBlockRegs[S] = *LBReg;
+  }
+
   // Emit DebugGlobalVariable for each collected DIGlobalVariable.
   for (const auto &[GV, Info] : GlobalVariableDebugInfoMap)
     emitDebugGlobalVariable(GV, Info, VoidTypeReg, I32TypeReg, ExtInstSetReg,
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index 17ca354778588..0b3c6fa0aa081 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -105,6 +105,10 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   DenseMap<const DIGlobalVariable *, GlobalVariableDebugInfo>
       GlobalVariableDebugInfoMap;
 
+  // Distinct DILexicalBlock and DINamespace scopes, parent-before-child
+  // order, collected in beginModule() for DebugLexicalBlock emission.
+  SetVector<const DIScope *> LexicalBlocks;
+
   // DebugFunctionDeclaration result id per emitted declaration DISubprogram
   // (only entries where emission succeeded).
   DenseMap<const DISubprogram *, MCRegister> DebugFunctionDeclarationRegs;
@@ -113,6 +117,9 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   // where emission succeeded).
   DenseMap<const DISubprogram *, MCRegister> DebugFunctionRegs;
 
+  // DebugLexicalBlock result id per emitted DILexicalBlock/DINamespace.
+  DenseMap<const DIScope *, MCRegister> DebugLexicalBlockRegs;
+
   // 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
@@ -490,6 +497,30 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   /// \returns \c std::nullopt when \p Scope is a \c DIType that has not been
   /// emitted, or when there is no compile unit.
   std::optional<MCRegister> resolveTypeScopeParent(const DIScope *Scope) const;
+
+  /// Resolve the \c Parent operand for \c DebugLexicalBlock, and for any other
+  /// instruction whose LLVM scope may be a \c DILexicalBlock or \c
+  /// DINamespace: an emitted \c DebugLexicalBlock id when \p Scope is a \c
+  /// DILexicalBlock or \c DINamespace already in \c DebugLexicalBlockRegs, an
+  /// emitted \c DebugFunction id when \p Scope is a defining \c DISubprogram,
+  /// otherwise the first module \c DebugCompilationUnit.
+  /// \returns \c std::nullopt when \p Scope requires a parent we cannot
+  /// supply, or the fallback CU has no emitted id.
+  std::optional<MCRegister>
+  resolveLexicalBlockParent(const DIScope *Scope) const;
+
+  /// Emit \c DebugLexicalBlock for \p S, which must be a \c DILexicalBlock or
+  /// a \c DINamespace. A \c DILexicalBlock supplies Line/Column
+  /// from \c getLine()/getColumn(); a \c DINamespace has neither, so both are
+  /// emitted as 0, and its Name is appended as an extra \c OpString operand.
+  ///
+  /// \returns The result id register on success. Returns \c std::nullopt and
+  /// emits nothing if \c resolveLexicalBlockParent returns no id for
+  /// \c S->getScope().
+  std::optional<MCRegister>
+  emitDebugLexicalBlock(const DIScope *S, MCRegister VoidTypeReg,
+                        MCRegister I32TypeReg, MCRegister ExtInstSetReg,
+                        SPIRV::ModuleAnalysisInfo &MAI);
 };
 
 } // namespace llvm
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration-namespace-scope.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration-namespace-scope.ll
index 8d85f70e961cc..712891e3fc5d8 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration-namespace-scope.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration-namespace-scope.ll
@@ -1,21 +1,26 @@
 ; 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 %}
 
-; DISubprogram declaration scoped in a DINamespace. Namespace scopes are not yet
-; supported, so no DebugFunctionDeclaration is emitted.
+; DISubprogram declaration scoped in a DINamespace. The namespace is emitted
+; as a DebugLexicalBlock (with a Name operand) and used as the
+; DebugFunctionDeclaration Parent.
 
 ; CHECK: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
 ; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
 ; CHECK-DAG: [[I32:%[0-9]+]] = OpTypeInt 32 0
 ; CHECK-DAG: OpString "ns_fn"
+; CHECK-DAG: [[NS:%[0-9]+]] = OpString "ns"
 ; CHECK-DAG: [[PATH:%[0-9]+]] = OpString "{{[/\\]}}tmp{{[/\\]}}namespace-scope-decl.c"
+; CHECK-DAG: [[EMPTY_PATH:%[0-9]+]] = OpString ""
 ; CHECK-DAG: [[C100:%[0-9]+]] = OpConstant [[I32]] 100
 ; CHECK-DAG: [[C5:%[0-9]+]] = OpConstant [[I32]] 5
 ; CHECK-DAG: [[C0:%[0-9]+]] = OpConstant [[I32]] 0
 ; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[PATH]]
-; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugCompilationUnit [[C100]] [[C5]] [[DS]] [[C0]]
+; CHECK-DAG: [[CU:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugCompilationUnit [[C100]] [[C5]] [[DS]] [[C0]]
 ; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugTypeFunction [[C0]] [[VOID]]
-; CHECK-NOT: DebugFunctionDeclaration
+; CHECK-DAG: [[NS_SRC:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[EMPTY_PATH]]
+; CHECK-DAG: [[LB:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugLexicalBlock [[NS_SRC]] [[C0]] [[C0]] [[CU]] [[NS]]
+; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugFunctionDeclaration {{%[0-9]+}} {{%[0-9]+}} [[DS]] {{%[0-9]+}} [[C0]] [[LB]]
 
 target triple = "spirv64-unknown-unknown"
 
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-function-namespace-scope.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-namespace-scope.ll
index e97c705abeb33..09d6d593096fe 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-function-namespace-scope.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-namespace-scope.ll
@@ -1,24 +1,27 @@
 ; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o %t.spt
 ; RUN: FileCheck %s --check-prefix=CHECK --input-file %t.spt
-; RUN: FileCheck %s --check-prefix=NO-DBG-FUNC --input-file %t.spt
 ; 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 %}
 
-; DISubprogram definition scoped in a DINamespace. Namespace scopes are not yet
-; supported as DebugFunction Parent, so no DebugFunction is emitted.
+; DISubprogram definition scoped in a DINamespace. The namespace is emitted as
+; a DebugLexicalBlock (with a Name operand) and used as the DebugFunction
+; Parent.
 
 ; CHECK: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
 ; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
 ; CHECK-DAG: [[I32:%[0-9]+]] = OpTypeInt 32 0
 ; CHECK-DAG: OpString "ns_fn"
+; CHECK-DAG: [[NS:%[0-9]+]] = OpString "ns"
 ; CHECK-DAG: [[PATH:%[0-9]+]] = OpString "{{[/\\]}}tmp{{[/\\]}}namespace-scope-fn.c"
+; CHECK-DAG: [[EMPTY_PATH:%[0-9]+]] = OpString ""
 ; CHECK-DAG: [[C100:%[0-9]+]] = OpConstant [[I32]] 100
 ; CHECK-DAG: [[C5:%[0-9]+]] = OpConstant [[I32]] 5
 ; CHECK-DAG: [[C0:%[0-9]+]] = OpConstant [[I32]] 0
 ; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[PATH]]
-; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugCompilationUnit [[C100]] [[C5]] [[DS]] [[C0]]
+; CHECK-DAG: [[CU:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugCompilationUnit [[C100]] [[C5]] [[DS]] [[C0]]
 ; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugTypeFunction [[C0]] [[VOID]]
-
-; NO-DBG-FUNC-NOT: DebugFunction
+; CHECK-DAG: [[NS_SRC:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[EMPTY_PATH]]
+; CHECK-DAG: [[LB:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugLexicalBlock [[NS_SRC]] [[C0]] [[C0]] [[CU]] [[NS]]
+; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugFunction {{%[0-9]+}} {{%[0-9]+}} [[DS]] {{%[0-9]+}} [[C0]] [[LB]]
 
 target triple = "spirv64-unknown-unknown"
 
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-lexical-block-namespace.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-lexical-block-namespace.ll
new file mode 100644
index 0000000000000..8a8d65b71ad38
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-lexical-block-namespace.ll
@@ -0,0 +1,48 @@
+; 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 that a DINamespace becomes a DebugLexicalBlock when it is the Parent of
+; a DebugGlobalVariable. Two cases are covered: a named namespace nested in
+; another named one, and an anonymous namespace nested in a named one.
+; Per the NSDI spec, an anonymous namespace must get an empty OpString as its
+; Name operand. Clang emits such namespaces as `!DINamespace(scope: ...)` with
+; no `name:` field, so DINamespace::getName() returns "".
+
+; 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: [[OUTER_NAME:%[0-9]+]] = OpString "outer"
+; CHECK-DAG: [[INNER_NAME:%[0-9]+]] = OpString "inner"
+; CHECK-DAG: [[GNAME:%[0-9]+]] = OpString "g"
+; CHECK-DAG: [[HNAME:%[0-9]+]] = OpString "h"
+; CHECK-DAG: [[EMPTY:%[0-9]+]] = OpString ""
+; CHECK-DAG: [[CU:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugCompilationUnit {{.*}}
+; CHECK-DAG: [[OUTER_LB:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugLexicalBlock {{%[0-9]+}} {{%[0-9]+}} {{%[0-9]+}} [[CU]] [[OUTER_NAME]]
+; CHECK-DAG: [[INNER_LB:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugLexicalBlock {{%[0-9]+}} {{%[0-9]+}} {{%[0-9]+}} [[OUTER_LB]] [[INNER_NAME]]
+; CHECK-DAG: [[ANON_LB:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugLexicalBlock {{%[0-9]+}} {{%[0-9]+}} {{%[0-9]+}} [[OUTER_LB]] [[EMPTY]]
+; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugGlobalVariable [[GNAME]] {{.*}} [[INNER_LB]] {{.*}}
+; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugGlobalVariable [[HNAME]] {{.*}} [[ANON_LB]] {{.*}}
+
+target triple = "spirv64-unknown-unknown"
+
+ at g = addrspace(1) global i32 0, !dbg !13
+ at h = addrspace(1) global i32 0, !dbg !16
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !12, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-lexical-block-namespace.cpp", directory: "/src")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!8 = !DINamespace(name: "outer", scope: !1)
+!9 = !DINamespace(name: "inner", scope: !8)
+!10 = !DINamespace(scope: !8)
+
+!12 = !{!13, !16}
+!13 = !DIGlobalVariableExpression(var: !14, expr: !DIExpression())
+!14 = distinct !DIGlobalVariable(name: "g", linkageName: "g", scope: !9, file: !1, line: 4, type: !7, isLocal: false, isDefinition: true)
+!16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression())
+!17 = distinct !DIGlobalVariable(name: "h", linkageName: "h", scope:...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/217965


More information about the llvm-commits mailing list