[llvm] 75ec1a4 - [SPIRV] Emit NonSemantic DebugGlobalVariable (#207230)

via llvm-commits llvm-commits at lists.llvm.org
Fri Jul 17 06:29:37 PDT 2026


Author: Manuel Carrasco
Date: 2026-07-17T14:29:32+01:00
New Revision: 75ec1a4e80a9377f8f1e2ff063b094e016787926

URL: https://github.com/llvm/llvm-project/commit/75ec1a4e80a9377f8f1e2ff063b094e016787926
DIFF: https://github.com/llvm/llvm-project/commit/75ec1a4e80a9377f8f1e2ff063b094e016787926.diff

LOG: [SPIRV] Emit NonSemantic DebugGlobalVariable (#207230)

Add emitDebugGlobalVariable to the NSDI handler, translating each
DIGlobalVariable to a
[DebugGlobalVariable](https://github.khronos.org/SPIRV-Registry/nonsemantic/NonSemantic.Shader.DebugInfo.html#DebugGlobalVariable)
ext inst.

Added: 
    llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-default-address-space.ll
    llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-init-expr.ll
    llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-multi-gve.ll
    llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-no-backing-var.ll
    llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-no-type.ll
    llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-path-null.ll
    llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-skip-static-member.ll
    llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-skip-type-not-in-regs.ll
    llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-static-member.ll
    llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable.ll

Modified: 
    llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
    llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 7303288aaea16..5c03e39e0ce21 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -16,6 +16,7 @@
 #include "llvm/CodeGen/AsmPrinter.h"
 #include "llvm/IR/DebugInfo.h"
 #include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/GlobalVariable.h"
 #include "llvm/IR/Module.h"
 #include "llvm/MC/MCInst.h"
 #include "llvm/MC/MCStreamer.h"
@@ -194,6 +195,7 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
   SubroutineTypes.clear();
   VectorTypes.clear();
   SubprogramDeclarations.clear();
+  GlobalVariableDebugInfoMap.clear();
   DebugFunctionDeclarationRegs.clear();
   ScopeToPathOpStringReg.clear();
   CUToCompilationUnitDbgReg.clear();
@@ -207,6 +209,7 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
   NonSemanticOpStringsSectionEmitted = false;
 #endif
   CachedDebugInfoNoneReg = MCRegister();
+  CachedEmptyStringReg = MCRegister();
   CachedOpTypeVoidReg = MCRegister();
   CachedOpTypeInt32Reg = MCRegister();
 
@@ -251,6 +254,25 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
     if (!SP->isDefinition())
       SubprogramDeclarations.push_back(SP);
   }
+
+  // Walk LLVM globals to map each DIGlobalVariable to its llvm::GlobalVariable.
+  DenseMap<const DIGlobalVariable *, const GlobalVariable *> DIGVToLLVMGV;
+  for (const GlobalVariable &G : M->globals()) {
+    SmallVector<DIGlobalVariableExpression *> GVEs;
+    G.getDebugInfo(GVEs);
+    for (DIGlobalVariableExpression *GVE : GVEs) {
+      if (const DIGlobalVariable *GV = GVE->getVariable()) {
+        DIGVToLLVMGV.try_emplace(GV, &G);
+      }
+    }
+  }
+
+  for (const DIGlobalVariableExpression *GVE : Finder.global_variables()) {
+    const DIGlobalVariable *GV = GVE->getVariable();
+    const DIExpression *Expr = GVE->getExpression();
+    GlobalVariableDebugInfoMap.try_emplace(
+        GV, GlobalVariableDebugInfo{Expr, DIGVToLLVMGV.lookup(GV)});
+  }
 }
 
 void SPIRVNonSemanticDebugHandler::prepareModuleOutput(
@@ -313,6 +335,23 @@ MCRegister SPIRVNonSemanticDebugHandler::getCachedOpStringReg(StringRef S) {
   return It->second;
 }
 
+MCRegister SPIRVNonSemanticDebugHandler::getCachedScopePathOpStringReg(
+    const DIScope *Scope, bool UseEmptyPathIfNullScope) {
+  if (!Scope) {
+    assert(UseEmptyPathIfNullScope &&
+           "null scope path lookup requires UseEmptyPathIfNullScope");
+    assert(CachedEmptyStringReg.isValid() &&
+           "empty path OpString must be cached in emitNonSemanticDebugStrings");
+    return CachedEmptyStringReg;
+  }
+  auto It = ScopeToPathOpStringReg.find(Scope);
+  assert(It != ScopeToPathOpStringReg.end() &&
+         "path OpString must be cached in emitNonSemanticDebugStrings");
+  MCRegister FileStrReg = It->second;
+  assert(FileStrReg.isValid() && "path OpString id must be valid once cached");
+  return FileStrReg;
+}
+
 MCRegister SPIRVNonSemanticDebugHandler::emitOpConstantI32(
     uint32_t Value, MCRegister I32TypeReg, SPIRV::ModuleAnalysisInfo &MAI) {
   auto [It, Inserted] = I32ConstantCache.try_emplace(Value);
@@ -519,13 +558,7 @@ SPIRVNonSemanticDebugHandler::emitDebugFunctionDeclaration(
 
   MCRegister ParentReg = *ParentRegOpt;
 
-  auto PathStrIt = ScopeToPathOpStringReg.find(SP);
-  assert(PathStrIt != ScopeToPathOpStringReg.end() &&
-         "declaration path OpString must be cached in "
-         "emitNonSemanticDebugStrings");
-  MCRegister FileStrReg = PathStrIt->second;
-  assert(FileStrReg.isValid() &&
-         "declaration path OpString id must be valid once cached");
+  MCRegister FileStrReg = getCachedScopePathOpStringReg(SP);
 
   MCRegister NameReg = getCachedOpStringReg(SP->getName());
   MCRegister LinkageReg = getCachedOpStringReg(SP->getLinkageName());
@@ -561,6 +594,96 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::mapDISignatureTypeToReg(
   return lookupOptReg(DebugTypeRegs, Ty);
 }
 
+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.
+
+  // !CompileUnits.empty() was already checked before staring the emission of
+  // NSDI instructions.
+  assert(!CompileUnits.empty() &&
+         "resolveGlobalVariableParent requires non-empty CompileUnits");
+  std::optional<MCRegister> ParentRegOpt =
+      lookupOptReg(CUToCompilationUnitDbgReg, CompileUnits[0].TheCU);
+  assert(ParentRegOpt && "DebugCompilationUnit must be emitted before "
+                         "resolveGlobalVariableParent");
+  // Fallback: first module compile unit (SPIRV-LLVM-Translator default).
+  return *ParentRegOpt;
+}
+
+// Unimplemented no-op; see emitDebugExpression declaration.
+std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugExpression(
+    const DIExpression *, MCRegister, MCRegister, SPIRV::ModuleAnalysisInfo &) {
+  return std::nullopt;
+}
+
+std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugGlobalVariable(
+    const DIGlobalVariable *GV, const GlobalVariableDebugInfo &Info,
+    MCRegister VoidTypeReg, MCRegister I32TypeReg, MCRegister ExtInstSetReg,
+    SPIRV::ModuleAnalysisInfo &MAI) {
+  assert(GV && "GV must not be null in emitDebugGlobalVariable");
+
+  MCRegister ParentReg = resolveGlobalVariableParent(GV);
+
+  // TyReg: DebugInfoNone when GV has no DI type (as done in
+  // SPIRV-LLVM-Translator). Declarations (isDefinition: false) can have null
+  // getType() while definitions must have a non-null one (enforced by the IR
+  // verifier).
+  MCRegister TyReg = CachedDebugInfoNoneReg;
+  if (const DIType *Ty = GV->getType()) {
+    auto TyRegOpt = lookupOptReg(DebugTypeRegs, Ty);
+    if (!TyRegOpt)
+      return std::nullopt;
+    TyReg = *TyRegOpt;
+  }
+
+  std::optional<MCRegister> StaticMemberRegOpt;
+  if (const DIDerivedType *SM = GV->getStaticDataMemberDeclaration()) {
+    StaticMemberRegOpt = lookupOptReg(DebugTypeRegs, SM);
+    if (!StaticMemberRegOpt)
+      return std::nullopt;
+  }
+
+  MCRegister NameReg = getCachedOpStringReg(GV->getName());
+  MCRegister LinkageReg = getCachedOpStringReg(GV->getLinkageName());
+  MCRegister FileStrReg = getCachedScopePathOpStringReg(
+      GV->getFile(), /*UseEmptyPathIfNullScope=*/true);
+  MCRegister SrcReg = getOrEmitDebugSourceForFileStrReg(FileStrReg, VoidTypeReg,
+                                                        ExtInstSetReg, MAI);
+
+  MCRegister LineReg =
+      emitOpConstantI32(static_cast<uint32_t>(GV->getLine()), I32TypeReg, MAI);
+  // DIGlobalVariable or DIGlobalVariableExpression metadata carry no column
+  // field. Column is hardcoded to 0 (because it can't be determined), matching
+  // SPIRV-LLVM-Translator.
+  MCRegister ColReg = emitOpConstantI32(0, I32TypeReg, MAI);
+
+  // Variable: @g OpVariable id when !dbg matches; else a DebugExpression for
+  // the GVE init value when no @g exists; else DebugInfoNone.
+  MCRegister VariableReg = CachedDebugInfoNoneReg;
+  if (const GlobalVariable *LLVMGV = Info.LLVMGV) {
+    MCRegister GVReg = MAI.getGlobalObjReg(LLVMGV);
+    if (GVReg.isValid())
+      VariableReg = GVReg;
+  } else if (Info.Expr) {
+    if (auto ExprReg =
+            emitDebugExpression(Info.Expr, VoidTypeReg, ExtInstSetReg, MAI))
+      VariableReg = *ExprReg;
+  }
+
+  MCRegister FlagsReg = emitOpConstantI32(transDebugFlags(GV), I32TypeReg, MAI);
+
+  SmallVector<MCRegister, 10> Ops = {NameReg,    TyReg,       SrcReg,
+                                     LineReg,    ColReg,      ParentReg,
+                                     LinkageReg, VariableReg, FlagsReg};
+
+  if (StaticMemberRegOpt)
+    Ops.push_back(*StaticMemberRegOpt);
+
+  return emitExtInst(SPIRV::NonSemanticExtInst::DebugGlobalVariable,
+                     VoidTypeReg, ExtInstSetReg, Ops, MAI);
+}
+
 std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugTypeVector(
     const DICompositeType *VT, MCRegister ExtInstSetReg,
     SPIRV::ModuleAnalysisInfo &MAI) {
@@ -619,6 +742,17 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticDebugStrings(
     ScopeToPathOpStringReg[SP] = emitOpStringIfNew(getDebugFullPath(SP), MAI);
   }
 
+  for (const auto &[GV, _] : GlobalVariableDebugInfoMap) {
+    emitOpStringIfNew(GV->getName(), MAI);
+    emitOpStringIfNew(GV->getLinkageName(), MAI);
+    SmallString<128> Path = getDebugFullPath(GV->getFile());
+    MCRegister PathReg = emitOpStringIfNew(Path, MAI);
+    if (const DIFile *F = GV->getFile())
+      ScopeToPathOpStringReg[F] = PathReg;
+  }
+
+  CachedEmptyStringReg = emitOpStringIfNew("", MAI);
+
 #ifndef NDEBUG
   NonSemanticOpStringsSectionEmitted = true;
 #endif
@@ -756,6 +890,11 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
                                                     ExtInstSetReg, MAI))
       DebugFunctionDeclarationRegs[SP] = *DeclReg;
   }
+
+  // Emit DebugGlobalVariable for each collected DIGlobalVariable.
+  for (const auto &[GV, Info] : GlobalVariableDebugInfoMap)
+    emitDebugGlobalVariable(GV, Info, VoidTypeReg, I32TypeReg, ExtInstSetReg,
+                            MAI);
 }
 
 SmallString<128>

diff  --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index ceb5573b55873..1bbe19c1eaa34 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -32,6 +32,7 @@
 
 namespace llvm {
 
+class GlobalVariable;
 class SPIRVSubtarget;
 
 /// AsmPrinter handler that emits NonSemantic.Shader.DebugInfo.100 (NSDI)
@@ -75,6 +76,13 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   // in beginModule() for DebugFunctionDeclaration emission.
   SmallVector<const DISubprogram *> SubprogramDeclarations;
 
+  struct GlobalVariableDebugInfo {
+    const DIExpression *Expr = nullptr;
+    const GlobalVariable *LLVMGV = nullptr;
+  };
+  DenseMap<const DIGlobalVariable *, GlobalVariableDebugInfo>
+      GlobalVariableDebugInfoMap;
+
   // DebugFunctionDeclaration result id per emitted declaration DISubprogram
   // (only entries where emission succeeded).
   DenseMap<const DISubprogram *, MCRegister> DebugFunctionDeclarationRegs;
@@ -103,6 +111,8 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   bool NonSemanticOpStringsSectionEmitted = false;
 #endif
 
+  MCRegister CachedEmptyStringReg;
+
   MCRegister CachedDebugInfoNoneReg;
 
   MCRegister CachedOpTypeVoidReg;
@@ -192,6 +202,14 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   /// Section 10 only: lookup OpString id from cache; asserts if missing or if
   /// section 7 did not complete.
   MCRegister getCachedOpStringReg(StringRef S);
+
+  /// Section 10 only: lookup path \c OpString id for \p Scope from
+  /// \c ScopeToPathOpStringReg; asserts if missing or invalid. When
+  /// \p UseEmptyPathIfNullScope is true and \p Scope is null, returns
+  /// \c CachedEmptyStringReg instead.
+  MCRegister
+  getCachedScopePathOpStringReg(const DIScope *Scope,
+                                bool UseEmptyPathIfNullScope = false);
   MCRegister emitOpConstantI32(uint32_t Value, MCRegister I32TypeReg,
                                SPIRV::ModuleAnalysisInfo &MAI);
   MCRegister emitExtInst(SPIRV::NonSemanticExtInst::NonSemanticExtInst Opcode,
@@ -256,6 +274,39 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
                                MCRegister I32TypeReg, MCRegister ExtInstSetReg,
                                SPIRV::ModuleAnalysisInfo &MAI);
 
+  /// Emit \c DebugGlobalVariable for the source global variable \p GV.
+  ///
+  /// (\c SPIRVDebug::Operand::GlobalVariable): Name, Type, Source, Line,
+  /// Column, Parent, Linkage Name, Variable, Flags, and an optional Static
+  /// Member Declaration. Line, Column, and Flags are emitted as \c OpConstant
+  /// ids as required for non-semantic debug info.
+  ///
+  /// \c DebugInfoNone is used for two operands when LLVM has no value to
+  /// supply:
+  /// \c Type when \p GV is a declaration with no DI type (e.g. \c extern void;
+  /// valid IR, \c isDefinition: false); \c Variable when no \c
+  /// llvm::GlobalVariable in this module carries \p GV in its \c !dbg metadata.
+  ///
+  /// \returns The result id register on success. Returns \c std::nullopt and
+  /// emits nothing if a non-null \p GV type was not emitted in \c
+  /// DebugTypeRegs, or \p GV has a static data member declaration that was not
+  /// emitted in \c DebugTypeRegs.
+  std::optional<MCRegister> emitDebugGlobalVariable(
+      const DIGlobalVariable *GV, const GlobalVariableDebugInfo &Info,
+      MCRegister VoidTypeReg, MCRegister I32TypeReg, MCRegister ExtInstSetReg,
+      SPIRV::ModuleAnalysisInfo &MAI);
+
+  /// Resolve the \c Parent operand for \c DebugGlobalVariable.
+  MCRegister resolveGlobalVariableParent(const DIGlobalVariable *GV) const;
+
+  /// Emit \c DebugExpression for \p Expr. Unimplemented: defined as a no-op
+  /// (\returns \c std::nullopt, emits nothing) so \c emitDebugGlobalVariable
+  /// can complete Variable-operand resolution for the opcodes we support today.
+  std::optional<MCRegister> emitDebugExpression(const DIExpression *Expr,
+                                                MCRegister VoidTypeReg,
+                                                MCRegister ExtInstSetReg,
+                                                SPIRV::ModuleAnalysisInfo &MAI);
+
   /// Emit \c DebugTypeVector for the vector composite type \p VT.
   ///
   /// \returns The result id register on success. Returns \c std::nullopt and

diff  --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-default-address-space.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-default-address-space.ll
new file mode 100644
index 0000000000000..1dcb5cc97b025
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-default-address-space.ll
@@ -0,0 +1,43 @@
+; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %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 %}
+
+; A default-address-space global with !dbg. Such globals do not become a
+; module-scope OpVariable (they are turned into function-local copies), so no
+; result id is registered for them. The DIGlobalVariable is still emitted, but
+; its Variable operand falls back to DebugInfoNone.
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[I32T:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[NAME:%[0-9]+]] = OpString "g"
+; CHECK-DAG: [[STR_INT:%[0-9]+]] = OpString "int"
+; CHECK-DAG: [[C42:%[0-9]+]] = OpConstant [[I32T]] 42
+; CHECK-DAG: [[NONE:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugInfoNone
+; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource
+; CHECK-DAG: [[DTI:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugTypeBasic [[STR_INT]]
+; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugGlobalVariable [[NAME]] [[DTI]] [[DS]] [[C42]] {{%[0-9]+}} {{%[0-9]+}} [[NAME]] [[NONE]]
+
+target triple = "spirv64-unknown-unknown"
+
+ at g = dso_local global i32 0, align 4, !dbg !0
+
+define spir_func void @f() !dbg !9 {
+entry:
+  ret void, !dbg !10
+}
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!12, !13}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "g", linkageName: "g", scope: !2, file: !3, line: 42, type: !8, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "t.c", directory: "/tmp")
+!4 = !{!0}
+!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!9 = distinct !DISubprogram(name: "f", scope: !3, file: !3, line: 1, type: !16, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2)
+!10 = !DILocation(line: 2, column: 1, scope: !9)
+!12 = !{i32 7, !"Dwarf Version", i32 5}
+!13 = !{i32 2, !"Debug Info Version", i32 3}
+!16 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !17)
+!17 = !{null}

diff  --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-init-expr.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-init-expr.ll
new file mode 100644
index 0000000000000..fb83a6350ce03
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-init-expr.ll
@@ -0,0 +1,42 @@
+; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %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 %}
+
+; A DIGlobalVariable with no backing llvm::GlobalVariable but whose
+; DIGlobalVariableExpression carries a non-empty DIExpression (a constant
+; initializer). Since DebugExpression emission is not implemented, the Variable
+; operand falls back to DebugInfoNone. Flags encode IsLocal|IsDefinition (12).
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[I32T:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[NAME:%[0-9]+]] = OpString "constg"
+; CHECK-DAG: [[STR_INT:%[0-9]+]] = OpString "int"
+; CHECK-DAG: [[C42:%[0-9]+]] = OpConstant [[I32T]] 42
+; CHECK-DAG: [[C12:%[0-9]+]] = OpConstant [[I32T]] 12
+; CHECK-DAG: [[NONE:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugInfoNone
+; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource
+; CHECK-DAG: [[DTI:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugTypeBasic [[STR_INT]]
+; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugGlobalVariable [[NAME]] [[DTI]] [[DS]] [[C42]] {{%[0-9]+}} {{%[0-9]+}} [[NAME]] [[NONE]] [[C12]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @f() !dbg !9 {
+entry:
+  ret void, !dbg !10
+}
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!12, !13}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression(DW_OP_constu, 42, DW_OP_stack_value))
+!1 = distinct !DIGlobalVariable(name: "constg", linkageName: "constg", scope: !2, file: !3, line: 42, type: !8, isLocal: true, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "t.c", directory: "/tmp")
+!4 = !{!0}
+!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!9 = distinct !DISubprogram(name: "f", scope: !3, file: !3, line: 1, type: !16, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2)
+!10 = !DILocation(line: 2, column: 1, scope: !9)
+!12 = !{i32 7, !"Dwarf Version", i32 5}
+!13 = !{i32 2, !"Debug Info Version", i32 3}
+!16 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !17)
+!17 = !{null}

diff  --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-multi-gve.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-multi-gve.ll
new file mode 100644
index 0000000000000..1f6ab14e2460b
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-multi-gve.ll
@@ -0,0 +1,67 @@
+; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %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 %}
+
+; Anonymous-union-style debug info: one llvm::GlobalVariable carries multiple
+; DIGlobalVariableExpression attachments (one per named union member). 
+
+;static union {
+;  int x;
+;  float y;
+;};
+;
+;int use(void) {
+;  x = 1;
+;  return x;
+;}
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[I32T:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[PATH:%[0-9]+]] = OpString "{{[/\\]}}AAAAAAAAAA{{[/\\]}}BBBBBBBB{{[/\\]}}CCCCCCCCC{{[/\\]}}debug-global-variable-multi-gve.c"
+; CHECK-DAG: [[NAME_X:%[0-9]+]] = OpString "x"
+; CHECK-DAG: [[NAME_Y:%[0-9]+]] = OpString "y"
+; CHECK-DAG: [[LINK_NAME:%[0-9]+]] = OpString "_Z1x"
+; CHECK-DAG: [[STR_INT:%[0-9]+]] = OpString "int"
+; CHECK-DAG: [[STR_FLOAT:%[0-9]+]] = OpString "float"
+; CHECK-DAG: [[C100:%[0-9]+]] = OpConstant [[I32T]] 100
+; CHECK-DAG: [[C5:%[0-9]+]] = OpConstant [[I32T]] 5
+; CHECK-DAG: [[C0:%[0-9]+]] = OpConstant [[I32T]] 0
+; CHECK-DAG: [[C42:%[0-9]+]] = OpConstant [[I32T]] 42
+; CHECK-DAG: [[C12:%[0-9]+]] = OpConstant [[I32T]] 12
+; CHECK-DAG: [[C32:%[0-9]+]] = OpConstant [[I32T]] 32
+; CHECK-DAG: [[C4ENC:%[0-9]+]] = OpConstant [[I32T]] 4
+; CHECK-DAG: [[C3ENC:%[0-9]+]] = OpConstant [[I32T]] 3
+; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[PATH]]
+; CHECK-DAG: [[CU:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugCompilationUnit [[C100]] [[C5]] [[DS]] [[C0]]
+; CHECK-DAG: [[DTI:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugTypeBasic [[STR_INT]] [[C32]] [[C4ENC]] [[C0]]
+; CHECK-DAG: [[DTF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugTypeBasic [[STR_FLOAT]] [[C32]] [[C3ENC]] [[C0]]
+; CHECK-DAG: [[GV:%[0-9]+]] = OpVariable {{.*}} CrossWorkgroup
+; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugGlobalVariable [[NAME_X]] [[DTI]] [[DS]] [[C42]] [[C0]] [[CU]] [[LINK_NAME]] [[GV]] [[C12]]
+; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugGlobalVariable [[NAME_Y]] [[DTF]] [[DS]] [[C42]] [[C0]] [[CU]] [[LINK_NAME]] [[GV]] [[C12]]
+
+target triple = "spirv64-unknown-unknown"
+
+ at _Z1x = dso_local addrspace(1) global i32 0, align 4, !dbg !0, !dbg !5
+
+define spir_func void @use() {
+entry:
+  store i32 1, ptr addrspace(1) @_Z1x, align 4
+  ret void
+}
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!10, !11, !12, !13}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "x", linkageName: "_Z1x", scope: !2, file: !3, line: 42, type: !9, isLocal: true, isDefinition: true)
+!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression())
+!6 = distinct !DIGlobalVariable(name: "y", linkageName: "_Z1x", scope: !2, file: !3, line: 42, type: !8, isLocal: true, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version XX.X", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "debug-global-variable-multi-gve.c", directory: "/AAAAAAAAAA/BBBBBBBB/CCCCCCCCC", checksumkind: CSK_MD5, checksum: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
+!4 = !{!0, !5}
+!8 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float)
+!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!10 = !{i32 7, !"Dwarf Version", i32 5}
+!11 = !{i32 2, !"Debug Info Version", i32 3}
+!12 = !{i32 1, !"wchar_size", i32 4}
+!13 = !{i32 7, !"frame-pointer", i32 2}

diff  --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-no-backing-var.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-no-backing-var.ll
new file mode 100644
index 0000000000000..d77c0887e8cc4
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-no-backing-var.ll
@@ -0,0 +1,42 @@
+; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %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 %}
+
+; A DIGlobalVariable listed in the compile unit's globals but with no backing
+; llvm::GlobalVariable in the module and an empty DIExpression. The Type operand
+; still resolves (int), but the Variable operand falls back to DebugInfoNone.
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[I32T:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[NAME:%[0-9]+]] = OpString "novar"
+; CHECK-DAG: [[STR_INT:%[0-9]+]] = OpString "int"
+; CHECK-DAG: [[C0:%[0-9]+]] = OpConstant [[I32T]] 0
+; CHECK-DAG: [[C42:%[0-9]+]] = OpConstant [[I32T]] 42
+; CHECK-DAG: [[NONE:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugInfoNone
+; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource
+; CHECK-DAG: [[CU:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugCompilationUnit {{.*}} [[DS]] [[C0]]
+; CHECK-DAG: [[DTI:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugTypeBasic [[STR_INT]]
+; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugGlobalVariable [[NAME]] [[DTI]] [[DS]] [[C42]] [[C0]] [[CU]] [[NAME]] [[NONE]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @f() !dbg !9 {
+entry:
+  ret void, !dbg !10
+}
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!12, !13}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "novar", linkageName: "novar", scope: !2, file: !3, line: 42, type: !8, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "t.c", directory: "/tmp")
+!4 = !{!0}
+!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!9 = distinct !DISubprogram(name: "f", scope: !3, file: !3, line: 1, type: !16, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2)
+!10 = !DILocation(line: 2, column: 1, scope: !9)
+!12 = !{i32 7, !"Dwarf Version", i32 5}
+!13 = !{i32 2, !"Debug Info Version", i32 3}
+!16 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !17)
+!17 = !{null}

diff  --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-no-type.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-no-type.ll
new file mode 100644
index 0000000000000..55591c60063cd
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-no-type.ll
@@ -0,0 +1,42 @@
+; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %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 %}
+
+; A DIGlobalVariable declaration with a null type (isDefinition: false, which the
+; IR verifier permits) and no backing llvm::GlobalVariable. The Type operand
+; falls back to DebugInfoNone, and the Variable operand also falls back to
+; DebugInfoNone because no @g carries this DIGlobalVariable and its
+; DIGlobalVariableExpression has an empty DIExpression.
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[I32T:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[PATH:%[0-9]+]] = OpString "{{[/\\]}}tmp{{[/\\]}}t.c"
+; CHECK-DAG: [[NAME:%[0-9]+]] = OpString "notype"
+; CHECK-DAG: [[C0:%[0-9]+]] = OpConstant [[I32T]] 0
+; CHECK-DAG: [[C42:%[0-9]+]] = OpConstant [[I32T]] 42
+; CHECK-DAG: [[NONE:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugInfoNone
+; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[PATH]]
+; CHECK-DAG: [[CU:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugCompilationUnit {{.*}} [[DS]] [[C0]]
+; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugGlobalVariable [[NAME]] [[NONE]] [[DS]] [[C42]] [[C0]] [[CU]] [[NAME]] [[NONE]] [[C0]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @f() !dbg !9 {
+entry:
+  ret void, !dbg !10
+}
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!12, !13}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "notype", linkageName: "notype", scope: !2, file: !3, line: 42, type: null, isLocal: false, isDefinition: false)
+!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "t.c", directory: "/tmp")
+!4 = !{!0}
+!9 = distinct !DISubprogram(name: "f", scope: !3, file: !3, line: 1, type: !16, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2)
+!10 = !DILocation(line: 2, column: 1, scope: !9)
+!12 = !{i32 7, !"Dwarf Version", i32 5}
+!13 = !{i32 2, !"Debug Info Version", i32 3}
+!16 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !17)
+!17 = !{null}

diff  --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-path-null.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-path-null.ll
new file mode 100644
index 0000000000000..b5d35d830477d
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-path-null.ll
@@ -0,0 +1,49 @@
+; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %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 %}
+
+; DIGlobalVariable with file: null (verifier-legal). Empty full path is lowered via
+; OpString "", DebugSource for that file operand, then DebugGlobalVariable.
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[I32T:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[PATH:%[0-9]+]] = OpString "{{[/\\]}}tmp{{[/\\]}}path-null.c"
+; CHECK-DAG: [[EMPTY:%[0-9]+]] = OpString ""
+; CHECK-DAG: [[NAME:%[0-9]+]] = OpString "no_file_path"
+; CHECK-DAG: [[STR_INT:%[0-9]+]] = OpString "int"
+; CHECK-DAG: [[C100:%[0-9]+]] = OpConstant [[I32T]] 100
+; CHECK-DAG: [[C5:%[0-9]+]] = OpConstant [[I32T]] 5
+; CHECK-DAG: [[C0:%[0-9]+]] = OpConstant [[I32T]] 0
+; CHECK-DAG: [[C42:%[0-9]+]] = OpConstant [[I32T]] 42
+; CHECK-DAG: [[C8:%[0-9]+]] = OpConstant [[I32T]] 8
+; CHECK-DAG: [[DS_CU:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[PATH]]
+; CHECK-DAG: [[CU:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugCompilationUnit [[C100]] [[C5]] [[DS_CU]] [[C0]]
+; CHECK-DAG: [[DTI:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugTypeBasic [[STR_INT]]
+; CHECK-DAG: [[DS_GV:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[EMPTY]]
+; CHECK-DAG: [[GV:%[0-9]+]] = OpVariable {{.*}} CrossWorkgroup
+; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugGlobalVariable [[NAME]] [[DTI]] [[DS_GV]] [[C42]] [[C0]] [[CU]] [[NAME]] [[GV]] [[C8]]
+
+target triple = "spirv64-unknown-unknown"
+
+ at no_file_path = dso_local addrspace(1) global i32 0, align 4, !dbg !0
+
+define spir_func void @f() !dbg !9 {
+entry:
+  ret void, !dbg !10
+}
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!12, !13}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "no_file_path", linkageName: "no_file_path", scope: !2, file: null, line: 42, type: !8, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "path-null.c", directory: "/tmp")
+!4 = !{!0}
+!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!9 = distinct !DISubprogram(name: "f", scope: !3, file: !3, line: 1, type: !16, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2)
+!10 = !DILocation(line: 2, column: 1, scope: !9)
+!12 = !{i32 7, !"Dwarf Version", i32 5}
+!13 = !{i32 2, !"Debug Info Version", i32 3}
+!16 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !17)
+!17 = !{null}

diff  --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-skip-static-member.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-skip-static-member.ll
new file mode 100644
index 0000000000000..f8b4cc38c8951
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-skip-static-member.ll
@@ -0,0 +1,39 @@
+; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %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 %}
+
+; A DIGlobalVariable that is the definition of a static data member. Its
+; declaration DIDerivedType (DW_TAG_member) is not emitted into DebugTypeRegs
+; (member types are not supported yet), so the Static Member Declaration operand
+; cannot be resolved and the whole DebugGlobalVariable is skipped.
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: OpExtInst {{.*}} DebugCompilationUnit
+; CHECK-NOT: DebugGlobalVariable
+
+target triple = "spirv64-unknown-unknown"
+
+ at g = dso_local addrspace(1) global i32 0, align 4, !dbg !0
+
+define spir_func void @f() !dbg !9 {
+entry:
+  ret void, !dbg !10
+}
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!12, !13}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "member", linkageName: "member", scope: !2, file: !3, line: 42, type: !8, isLocal: false, isDefinition: true, declaration: !20)
+!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "t.cpp", directory: "/tmp")
+!4 = !{!0}
+!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!9 = distinct !DISubprogram(name: "f", scope: !3, file: !3, line: 1, type: !16, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2)
+!10 = !DILocation(line: 2, column: 1, scope: !9)
+!12 = !{i32 7, !"Dwarf Version", i32 5}
+!13 = !{i32 2, !"Debug Info Version", i32 3}
+!16 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !17)
+!17 = !{null}
+!20 = !DIDerivedType(tag: DW_TAG_member, name: "member", scope: !21, file: !3, line: 2, baseType: !8, flags: DIFlagStaticMember)
+!21 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A", file: !3, line: 1, size: 8, elements: !22, identifier: "_ZTS1A")
+!22 = !{!20}

diff  --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-skip-type-not-in-regs.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-skip-type-not-in-regs.ll
new file mode 100644
index 0000000000000..e170b9656efd3
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-skip-type-not-in-regs.ll
@@ -0,0 +1,34 @@
+; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %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 %}
+
+; A DIGlobalVariable whose type is a structure composite that is not yet
+; supported and thus never emitted into DebugTypeRegs. The whole
+; DebugGlobalVariable is skipped rather than referencing a missing type id.
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: OpExtInst {{.*}} DebugCompilationUnit
+; CHECK-NOT: DebugGlobalVariable
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @f() !dbg !9 {
+entry:
+  ret void, !dbg !10
+}
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!12, !13}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "structg", linkageName: "structg", scope: !2, file: !3, line: 42, type: !8, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "t.c", directory: "/tmp")
+!4 = !{!0}
+!8 = !DICompositeType(tag: DW_TAG_structure_type, name: "S", file: !3, line: 1, size: 32, elements: !18)
+!9 = distinct !DISubprogram(name: "f", scope: !3, file: !3, line: 1, type: !16, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2)
+!10 = !DILocation(line: 2, column: 1, scope: !9)
+!12 = !{i32 7, !"Dwarf Version", i32 5}
+!13 = !{i32 2, !"Debug Info Version", i32 3}
+!16 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !17)
+!17 = !{null}
+!18 = !{}

diff  --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-static-member.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-static-member.ll
new file mode 100644
index 0000000000000..2021fa00d56cf
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable-static-member.ll
@@ -0,0 +1,40 @@
+; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %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 %}
+
+; DIGlobalVariable with a static data member declaration. The backend does not
+; emit DebugTypeMember yet, so the Static Member Declaration operand cannot be
+; resolved and the whole DebugGlobalVariable is skipped.
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: OpExtInst {{.*}} [[EXT]] DebugCompilationUnit
+; CHECK-NOT: DebugTypeMember
+; CHECK-NOT: DebugGlobalVariable
+
+target triple = "spirv64-unknown-unknown"
+
+ at member = dso_local addrspace(1) global i32 0, align 4, !dbg !0
+
+define spir_func void @f() !dbg !9 {
+entry:
+  ret void, !dbg !10
+}
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!12, !13}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "member", linkageName: "member", scope: !2, file: !3, line: 42, type: !8, isLocal: false, isDefinition: true, declaration: !20)
+!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !21, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "t.cpp", directory: "/tmp")
+!4 = !{!0}
+!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!9 = distinct !DISubprogram(name: "f", scope: !3, file: !3, line: 1, type: !16, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2)
+!10 = !DILocation(line: 2, column: 1, scope: !9)
+!12 = !{i32 7, !"Dwarf Version", i32 5}
+!13 = !{i32 2, !"Debug Info Version", i32 3}
+!16 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !17)
+!17 = !{null}
+!20 = !DIDerivedType(tag: DW_TAG_member, name: "member", scope: !22, file: !3, line: 2, baseType: !8, size: 32, flags: DIFlagStaticMember)
+!21 = !{!20}
+!22 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A", file: !3, line: 1, size: 32, elements: !23, identifier: "_ZTS1A")
+!23 = !{!20}

diff  --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable.ll
new file mode 100644
index 0000000000000..2e0cb197310ad
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-global-variable.ll
@@ -0,0 +1,54 @@
+; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %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 NonSemantic DebugGlobalVariable for a module global with !dbg.
+; The global uses address space 1 so the backend emits a module-scope
+; CrossWorkgroup OpVariable (default-AS globals become function-local copies).
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[I32T:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[PATH:%[0-9]+]] = OpString "{{[/\\]}}AAAAAAAAAA{{[/\\]}}BBBBBBBB{{[/\\]}}CCCCCCCCC{{[/\\]}}debug-global-variable.c"
+; CHECK-DAG: [[NAME:%[0-9]+]] = OpString "g_value"
+; CHECK-DAG: [[STR_INT:%[0-9]+]] = OpString "int"
+; CHECK-DAG: [[C100:%[0-9]+]] = OpConstant [[I32T]] 100
+; CHECK-DAG: [[C5:%[0-9]+]] = OpConstant [[I32T]] 5
+; CHECK-DAG: [[C0:%[0-9]+]] = OpConstant [[I32T]] 0
+; CHECK-DAG: [[C42:%[0-9]+]] = OpConstant [[I32T]] 42
+; CHECK-DAG: [[C8:%[0-9]+]] = OpConstant [[I32T]] 8
+; CHECK-DAG: [[C32:%[0-9]+]] = OpConstant [[I32T]] 32
+; CHECK-DAG: [[C4ENC:%[0-9]+]] = OpConstant [[I32T]] 4
+; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[PATH]]
+; CHECK-DAG: [[CU:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugCompilationUnit [[C100]] [[C5]] [[DS]] [[C0]]
+; CHECK-DAG: [[DTI:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugTypeBasic [[STR_INT]] [[C32]] [[C4ENC]] [[C0]]
+; CHECK-DAG: [[GV:%[0-9]+]] = OpVariable {{.*}} CrossWorkgroup
+; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugGlobalVariable [[NAME]] [[DTI]] [[DS]] [[C42]] [[C0]] [[CU]] [[NAME]] [[GV]] [[C8]]
+
+target triple = "spirv64-unknown-unknown"
+
+ at g_value = dso_local addrspace(1) global i32 0, align 4, !dbg !0
+
+define spir_func void @use_global() !dbg !9 {
+entry:
+  %v = load i32, ptr addrspace(1) @g_value, align 4, !dbg !10
+  ret void, !dbg !11
+}
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!12, !13, !14, !15}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "g_value", linkageName: "g_value", scope: !2, file: !3, line: 42, type: !8, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version XX.X", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "debug-global-variable.c", directory: "/AAAAAAAAAA/BBBBBBBB/CCCCCCCCC", checksumkind: CSK_MD5, checksum: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
+!4 = !{!0}
+!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!9 = distinct !DISubprogram(name: "use_global", linkageName: "use_global", scope: !3, file: !3, line: 1, type: !16, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2)
+!10 = !DILocation(line: 2, column: 3, scope: !9)
+!11 = !DILocation(line: 3, column: 1, scope: !9)
+!12 = !{i32 7, !"Dwarf Version", i32 5}
+!13 = !{i32 2, !"Debug Info Version", i32 3}
+!14 = !{i32 1, !"wchar_size", i32 4}
+!15 = !{i32 7, !"frame-pointer", i32 2}
+!16 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !17)
+!17 = !{!8}


        


More information about the llvm-commits mailing list