[llvm] [SPIRV] Emit NonSemantic DebugFunctionDeclaration for DISubprograms (declarations). (PR #203615)

Manuel Carrasco via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 12 12:50:09 PDT 2026


https://github.com/mgcarrasco updated https://github.com/llvm/llvm-project/pull/203615

>From 64942259afc4ee92f16285dd7d94eb100578d5e2 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 12 Jun 2026 14:37:23 -0500
Subject: [PATCH] [SPIRV] Emit NonSemantic DebugFunctionDeclaration for
 DISubprograms.

---
 .../SPIRV/SPIRVNonSemanticDebugHandler.cpp    | 173 ++++++++++++++++--
 .../SPIRV/SPIRVNonSemanticDebugHandler.h      |  83 ++++++++-
 ...ug-function-declaration-composite-scope.ll |  44 +++++
 .../debug-function-declaration-path-null.ll   |  46 +++++
 ...ration-skip-type-not-in-debug-type-regs.ll |  45 +++++
 .../debug-info/debug-function-declaration.ll  |  44 +++++
 6 files changed, 413 insertions(+), 22 deletions(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration-composite-scope.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration-path-null.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration-skip-type-not-in-debug-type-regs.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 9cd82d74bc592..d253303995848 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -181,6 +181,11 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
   PointerTypes.clear();
   SubroutineTypes.clear();
   VectorTypes.clear();
+  SubprogramDeclarations.clear();
+  DebugFunctionDeclarationRegs.clear();
+  ScopeToPathOpStringReg.clear();
+  CUToCompilationUnitDbgReg.clear();
+  DebugSourceRegByFileStr.clear();
   DebugTypeRegs.clear();
   OpStringContentCache.clear();
   I32ConstantCache.clear();
@@ -197,6 +202,7 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
   for (const DICompileUnit *CU : M->debug_compile_units()) {
     const DIFile *File = CU->getFile();
     CompileUnitInfo Info;
+    Info.TheCU = CU;
     if (sys::path::is_absolute(File->getFilename()))
       Info.FilePath = File->getFilename();
     else
@@ -228,6 +234,11 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
   llvm::for_each(Finder.types(), [&](DIType *Ty) {
     partitionTypes(Ty, BasicTypes, PointerTypes, SubroutineTypes, VectorTypes);
   });
+
+  for (const DISubprogram *SP : Finder.subprograms()) {
+    if (!SP->isDefinition())
+      SubprogramDeclarations.push_back(SP);
+  }
 }
 
 void SPIRVNonSemanticDebugHandler::prepareModuleOutput(
@@ -265,7 +276,7 @@ SPIRVNonSemanticDebugHandler::emitOpString(StringRef S,
   return Reg;
 }
 
-void SPIRVNonSemanticDebugHandler::emitOpStringIfNew(
+MCRegister SPIRVNonSemanticDebugHandler::emitOpStringIfNew(
     StringRef S, SPIRV::ModuleAnalysisInfo &MAI) {
 #ifndef NDEBUG
   assert(!NonSemanticOpStringsSectionEmitted &&
@@ -273,9 +284,10 @@ void SPIRVNonSemanticDebugHandler::emitOpStringIfNew(
 #endif
   auto [It, Inserted] = OpStringContentCache.try_emplace(S, MCRegister());
   if (!Inserted)
-    return;
+    return It->second;
 
   It->second = emitOpString(S, MAI);
+  return It->second;
 }
 
 MCRegister SPIRVNonSemanticDebugHandler::getCachedOpStringReg(StringRef S) {
@@ -452,6 +464,86 @@ SPIRVNonSemanticDebugHandler::emitDebugTypeFunctionForSubroutineType(
   return getOrEmitDebugTypeFunction(Ops, VoidTypeReg, ExtInstSetReg, MAI);
 }
 
+std::optional<MCRegister>
+SPIRVNonSemanticDebugHandler::resolveDebugFunctionDeclarationParent(
+    const DISubprogram *SP) const {
+  // Current logic matches SPIRV-LLVM-Translator's logic for Parent operand.
+
+  if (const DIScope *Scope = SP->getScope()) {
+    if (!isa<DIFile>(Scope)) {
+      if (const DIType *Ty = dyn_cast<DIType>(Scope)) {
+        auto TIt = DebugTypeRegs.find(Ty);
+        if (TIt != DebugTypeRegs.end())
+          return TIt->second;
+      }
+      // TODO: Complete with other lookups once other scopes are supported
+      // (subclases of DIScope).
+      return std::nullopt;
+    }
+  }
+
+  const DICompileUnit *ParentCU = SP->getUnit();
+  if (!ParentCU && !CompileUnits.empty())
+    ParentCU = CompileUnits[0].TheCU;
+  if (!ParentCU)
+    return std::nullopt;
+  auto CUIt = CUToCompilationUnitDbgReg.find(ParentCU);
+  if (CUIt == CUToCompilationUnitDbgReg.end())
+    return std::nullopt;
+  return CUIt->second;
+}
+
+std::optional<MCRegister>
+SPIRVNonSemanticDebugHandler::emitDebugFunctionDeclaration(
+    const DISubprogram *SP, MCRegister VoidTypeReg, MCRegister I32TypeReg,
+    MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI) {
+  assert(SP && "SP must not be null in emitDebugFunctionDeclaration");
+  assert(!SP->isDefinition() &&
+         "SP must not be a definition in emitDebugFunctionDeclaration");
+
+  // The IR verifier already enforces that this cannot be null.
+  const DISubroutineType *ST = SP->getType();
+
+  auto FnTyIt = DebugTypeRegs.find(ST);
+  if (FnTyIt == DebugTypeRegs.end())
+    return std::nullopt;
+
+  auto ParentRegOpt = resolveDebugFunctionDeclarationParent(SP);
+  if (!ParentRegOpt)
+    return std::nullopt;
+
+  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 NameReg = getCachedOpStringReg(SP->getName());
+  MCRegister LinkageReg = getCachedOpStringReg(SP->getLinkageName());
+  MCRegister SrcReg = getOrEmitDebugSourceForFileStrReg(FileStrReg, VoidTypeReg,
+                                                        ExtInstSetReg, MAI);
+
+  MCRegister LineReg =
+      emitOpConstantI32(static_cast<uint32_t>(SP->getLine()), I32TypeReg, MAI);
+  MCRegister ColReg = emitOpConstantI32(0, I32TypeReg, MAI);
+
+  uint32_t FlagsVal = transDebugFlags(SP);
+  // TODO: When composite scopes are DebugFunctionDeclaration parents (available
+  // in DebugTypeRegs), sync declaration Flags with SPIRV-LLVM-Translator.
+  FlagsVal &= ~NSDIFlagIsDefinition;
+  MCRegister FlagsReg = emitOpConstantI32(FlagsVal, I32TypeReg, MAI);
+
+  return emitExtInst(SPIRV::NonSemanticExtInst::DebugFunctionDeclaration,
+                     VoidTypeReg, ExtInstSetReg,
+                     {NameReg, FnTyIt->second, SrcReg, LineReg, ColReg,
+                      ParentReg, LinkageReg, FlagsReg},
+                     MAI);
+}
+
 std::optional<MCRegister> SPIRVNonSemanticDebugHandler::mapDISignatureTypeToReg(
     const DIType *Ty, MCRegister VoidTypeReg, bool ReturnType) {
   if (!Ty) {
@@ -464,7 +556,6 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::mapDISignatureTypeToReg(
   auto It = DebugTypeRegs.find(Ty);
   if (It != DebugTypeRegs.end())
     return It->second;
-
   return std::nullopt;
 }
 
@@ -508,11 +599,29 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticDebugStrings(
   if (!MAI.getExtInstSetReg(NSSet).isValid())
     return;
 
-  for (const CompileUnitInfo &Info : CompileUnits)
-    emitOpStringIfNew(Info.FilePath, MAI);
+  for (const CompileUnitInfo &Info : CompileUnits) {
+    MCRegister PathReg = emitOpStringIfNew(Info.FilePath, MAI);
+    if (Info.TheCU) {
+      ScopeToPathOpStringReg[Info.TheCU] = PathReg;
+      if (const DIFile *F = Info.TheCU->getFile())
+        ScopeToPathOpStringReg[F] = PathReg;
+    }
+  }
+
+  for (const DIBasicType *BT : BasicTypes) {
+    [[maybe_unused]] MCRegister BasicTypeNameStrReg =
+        emitOpStringIfNew(BT->getName(), MAI);
+  }
 
-  for (const DIBasicType *BT : BasicTypes)
-    emitOpStringIfNew(BT->getName(), MAI);
+  for (const DISubprogram *SP : SubprogramDeclarations) {
+    [[maybe_unused]] MCRegister DeclNameStrReg =
+        emitOpStringIfNew(SP->getName(), MAI);
+    [[maybe_unused]] MCRegister DeclLinkageStrReg =
+        emitOpStringIfNew(SP->getLinkageName(), MAI);
+    SmallString<128> Buf;
+    fillDebugFullPath(SP, Buf);
+    ScopeToPathOpStringReg[SP] = emitOpStringIfNew(Buf, MAI);
+  }
 
 #ifndef NDEBUG
   NonSemanticOpStringsSectionEmitted = true;
@@ -567,15 +676,18 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
 
   // Emit DebugSource and DebugCompilationUnit for each compile unit.
   for (auto [Info, SrcLangReg] : llvm::zip(CompileUnits, SrcLangRegs)) {
-    MCRegister FileStrReg = getCachedOpStringReg(Info.FilePath);
-    MCRegister DebugSourceReg =
-        emitExtInst(SPIRV::NonSemanticExtInst::DebugSource, VoidTypeReg,
-                    ExtInstSetReg, {FileStrReg}, MAI);
-    emitExtInst(
+    MCRegister FileStrReg = ScopeToPathOpStringReg.lookup(Info.TheCU);
+    assert(FileStrReg.isValid() &&
+           "CU path OpString must be emitted in emitNonSemanticDebugStrings");
+    MCRegister DebugSourceReg = getOrEmitDebugSourceForFileStrReg(
+        FileStrReg, VoidTypeReg, ExtInstSetReg, MAI);
+    MCRegister CUDbgReg = emitExtInst(
         SPIRV::NonSemanticExtInst::DebugCompilationUnit, VoidTypeReg,
         ExtInstSetReg,
         {DebugInfoVersionReg, DwarfVersionReg, DebugSourceReg, SrcLangReg},
         MAI);
+    if (Info.TheCU)
+      CUToCompilationUnitDbgReg[Info.TheCU] = CUDbgReg;
   }
 
   // Zero constant used as the Flags operand in DebugTypeBasic and
@@ -641,4 +753,41 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
             emitDebugTypeFunctionForSubroutineType(ST, ExtInstSetReg, MAI))
       DebugTypeRegs[ST] = *FnTyReg;
   }
+
+  // Emit DebugFunctionDeclaration for DISubprogram declarations.
+  for (const DISubprogram *SP : SubprogramDeclarations) {
+    if (auto DeclReg = emitDebugFunctionDeclaration(SP, VoidTypeReg, I32TypeReg,
+                                                    ExtInstSetReg, MAI))
+      DebugFunctionDeclarationRegs[SP] = *DeclReg;
+  }
+}
+
+void SPIRVNonSemanticDebugHandler::fillDebugFullPath(
+    const DIScope *Scope, SmallVectorImpl<char> &Out) const {
+  Out.clear();
+  if (!Scope)
+    return;
+  StringRef Filename = Scope->getFilename();
+  const auto Style = sys::path::Style::native;
+  if (sys::path::is_absolute(Filename, Style))
+    Out.assign(Filename.begin(), Filename.end());
+  else {
+    StringRef Dir = Scope->getDirectory();
+    Out.assign(Dir.begin(), Dir.end());
+    sys::path::append(Out, Style, Filename);
+  }
+}
+
+MCRegister SPIRVNonSemanticDebugHandler::getOrEmitDebugSourceForFileStrReg(
+    MCRegister FileStrReg, MCRegister VoidTypeReg, MCRegister ExtInstSetReg,
+    SPIRV::ModuleAnalysisInfo &MAI) {
+  const unsigned Key = FileStrReg.id();
+  auto It = DebugSourceRegByFileStr.find(Key);
+  if (It != DebugSourceRegByFileStr.end())
+    return It->second;
+
+  MCRegister DS = emitExtInst(SPIRV::NonSemanticExtInst::DebugSource,
+                              VoidTypeReg, ExtInstSetReg, {FileStrReg}, MAI);
+  DebugSourceRegByFileStr[Key] = DS;
+  return DS;
 }
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index 9a9dca36bb984..bc287abbb6930 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -44,19 +44,17 @@ class SPIRVSubtarget;
 ///   emitNonSemanticDebugStrings()    -- OpString for NSDI strings (sec. 7).
 ///   emitNonSemanticGlobalDebugInfo() -- emit DebugSource,
 ///                                       DebugCompilationUnit, DebugTypeBasic,
-///                                       DebugTypePointer, DebugTypeFunction.
+///                                       DebugTypePointer, DebugTypeFunction,
+///                                       DebugFunctionDeclaration.
 ///   beginFunctionImpl()              -- no-op (no per-function DI yet).
 ///   endFunctionImpl()                -- no-op.
 class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   struct CompileUnitInfo {
+    const DICompileUnit *TheCU = nullptr;
     SmallString<128> FilePath;
     unsigned SpirvSourceLanguage = 0; // NonSemantic.Shader.DebugInfo.100 source
                                       // language code (section 4.3)
   };
-  // TODO: When per-function NSDI emission is implemented, augment
-  // CompileUnitInfo with the originating DICompileUnit pointer so that
-  // Parent operands on DebugFunction and similar instructions can resolve
-  // the compile unit's result register.
   SmallVector<CompileUnitInfo> CompileUnits;
   int64_t DwarfVersion = 0;
 
@@ -73,6 +71,27 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   // registers.
   DenseMap<const DIType *, MCRegister> DebugTypeRegs;
 
+  // DISubprogram nodes that are declarations only (!isDefinition()), collected
+  // in beginModule() for DebugFunctionDeclaration emission.
+  SmallVector<const DISubprogram *, 8> SubprogramDeclarations;
+
+  // DebugFunctionDeclaration result id per emitted declaration DISubprogram
+  // (only entries where emission succeeded).
+  DenseMap<const DISubprogram *, MCRegister> DebugFunctionDeclarationRegs;
+
+  // Path \c OpString result id per \c DIScope (CU, \c DIFile, declaration
+  // \c DISubprogram, …). Filled during \c emitNonSemanticDebugStrings() using
+  // \c fillDebugFullPath + \c emitOpStringIfNew; section 10 uses it for
+  // \c DebugSource without recomputing path text.
+  DenseMap<const DIScope *, MCRegister> ScopeToPathOpStringReg;
+
+  // DebugCompilationUnit result id per DICompileUnit (for Parent operands).
+  DenseMap<const DICompileUnit *, MCRegister> CUToCompilationUnitDbgReg;
+
+  // DebugSource result id keyed by path \c OpString id (\c MCRegister::id()),
+  // deduplicating when the same file string is reused.
+  DenseMap<unsigned, MCRegister> DebugSourceRegByFileStr;
+
   // Maps OpString contents to result id. Populated only by emitOpStringIfNew()
   // during section 7; section 10 uses getCachedOpStringReg() (lookup only).
   StringMap<MCRegister> OpStringContentCache;
@@ -116,8 +135,9 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   /// into the debug section (section 7 of the SPIR-V module layout). Must be
   /// called from SPIRVAsmPrinter::outputDebugSourceAndStrings(), after
   /// prepareModuleOutput() has registered the ext inst set. Registers are
-  /// stored in OpStringContentCache; emitNonSemanticGlobalDebugInfo() resolves
-  /// them via getCachedOpStringReg().
+  /// stored in \c OpStringContentCache and \c ScopeToPathOpStringReg;
+  /// \c emitNonSemanticGlobalDebugInfo() resolves them via
+  /// \c getCachedOpStringReg() and path maps.
   void emitNonSemanticDebugStrings(SPIRV::ModuleAnalysisInfo &MAI);
 
   /// Add SPV_KHR_non_semantic_info extension and
@@ -128,7 +148,8 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
                            SPIRV::ModuleAnalysisInfo &MAI);
 
   /// Emit module-scope NSDI instructions (DebugSource, DebugCompilationUnit,
-  /// DebugTypeBasic, DebugTypePointer, DebugTypeFunction). Called by
+  /// DebugTypeBasic, DebugTypePointer, DebugTypeFunction,
+  /// DebugFunctionDeclaration). Called by
   /// SPIRVAsmPrinter::outputModuleSections() at section 10 in place of
   /// outputModuleSection(MB_NonSemanticGlobalDI). Requires
   /// emitNonSemanticDebugStrings() to have run first when NSDI strings apply.
@@ -164,8 +185,9 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   MCRegister emitOpString(StringRef S, SPIRV::ModuleAnalysisInfo &MAI);
 
   /// Section 7 only: emit OpString and cache it if not already present. Must
-  /// not be called after NonSemanticOpStringsSectionEmitted is set.
-  void emitOpStringIfNew(StringRef S, SPIRV::ModuleAnalysisInfo &MAI);
+  /// not be called after NonSemanticOpStringsSectionEmitted is set. Returns
+  /// the path (or string) \c OpString result id.
+  MCRegister emitOpStringIfNew(StringRef S, SPIRV::ModuleAnalysisInfo &MAI);
 
   /// Section 10 only: lookup OpString id from cache; asserts if missing or if
   /// section 7 did not complete.
@@ -219,6 +241,21 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
                                          MCRegister ExtInstSetReg,
                                          SPIRV::ModuleAnalysisInfo &MAI);
 
+  /// Emit \c DebugFunctionDeclaration for a \c DISubprogram that is not a
+  /// definition (\p SP must satisfy \c !isDefinition()).
+  ///
+  /// \returns The result id register on success. Returns \c std::nullopt and
+  /// emits nothing if \p SP is null, is a definition, has no \c
+  /// DISubroutineType type, the signature type was not emitted in \c
+  /// DebugTypeRegs, no path
+  /// \c OpString was recorded for \p SP in section 7, or
+  /// \c resolveDebugFunctionDeclarationParent returns no id for the \c Parent
+  /// operand.
+  std::optional<MCRegister>
+  emitDebugFunctionDeclaration(const DISubprogram *SP, MCRegister VoidTypeReg,
+                               MCRegister I32TypeReg, 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
@@ -247,6 +284,32 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   /// Map a DWARF source language code to a NonSemantic.Shader.DebugInfo.100
   /// source language code.
   static unsigned toNSDISrcLang(unsigned DwarfSrcLang);
+
+  /// Build a full path from debug \p Scope for OpString / DebugSource, matching
+  /// SPIRV-LLVM-Translator \c getFullPath (OCLUtil.h): \c DIScope::getFilename,
+  /// \c getDirectory, and \c sys::path::Style::native. Works for any \c DIScope
+  /// that carries file path fields (e.g. \c DIFile, \c DISubprogram,
+  /// \c DICompileUnit). Clears \p Out when \p Scope is null.
+  void fillDebugFullPath(const DIScope *Scope,
+                         SmallVectorImpl<char> &Out) const;
+
+  /// Return an existing \c DebugSource id for file path \c OpString \p
+  /// FileStrReg or emit \c DebugSource and cache it (keyed by \p FileStrReg
+  /// id).
+  MCRegister getOrEmitDebugSourceForFileStrReg(MCRegister FileStrReg,
+                                               MCRegister VoidTypeReg,
+                                               MCRegister ExtInstSetReg,
+                                               SPIRV::ModuleAnalysisInfo &MAI);
+
+  /// Resolve the \c Parent operand for \c DebugFunctionDeclaration: an emitted
+  /// debug type id when \c SP->getScope() is a \c DIType in \c DebugTypeRegs,
+  /// otherwise \c DebugCompilationUnit for \c SP->getUnit() (or the first
+  /// module CU when \c unit: is absent).
+  /// \returns \c std::nullopt when the scope requires a parent we cannot supply
+  /// (non-file scope that is not a mapped \c DIType) or the CU has no emitted
+  /// id.
+  std::optional<MCRegister>
+  resolveDebugFunctionDeclarationParent(const DISubprogram *SP) const;
 };
 
 } // namespace llvm
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration-composite-scope.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration-composite-scope.ll
new file mode 100644
index 0000000000000..97651fbbea5f5
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration-composite-scope.ll
@@ -0,0 +1,44 @@
+; 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 whose lexical scope is not yet supported, so no DebugFunctionDeclaration is emitted.
+
+; 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 "member_fn"
+; CHECK-DAG: [[PATH:%[0-9]+]] = OpString "/tmp/composite-scope-decl.c"
+; 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: OpExtInst [[VOID]] [[EXT]] DebugTypeFunction [[C0]] [[VOID]]
+; CHECK-NOT: DebugFunctionDeclaration
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @defined() !dbg !9 {
+entry:
+  ret void
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None, retainedTypes: !10)
+!1 = !DIFile(filename: "composite-scope-decl.c", directory: "/tmp", checksumkind: CSK_MD5, checksum: "00000000000000000000000000000000")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 7, !"frame-pointer", i32 2}
+
+!6 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !7)
+!7 = !{}
+!8 = !DICompositeType(tag: DW_TAG_structure_type, name: "S", file: !1, line: 1, elements: !11)
+!11 = !{}
+!10 = !{!8, !12}
+
+!12 = !DISubprogram(name: "member_fn", linkageName: "member_fn", scope: !8, file: !1, line: 2, type: !6, scopeLine: 2, flags: DIFlagPrototyped, spFlags: 0)
+
+!9 = distinct !DISubprogram(name: "defined", linkageName: "defined", scope: !1, file: !1, line: 10, type: !6, scopeLine: 10, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration-path-null.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration-path-null.ll
new file mode 100644
index 0000000000000..8a399d672e4d2
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration-path-null.ll
@@ -0,0 +1,46 @@
+; 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 %}
+
+; Forward DISubprogram with file: null and line: 0 (verifier-legal for declarations).
+; Empty full path is still lowered: OpString "", DebugSource for that file operand,
+; then DebugFunctionDeclaration (same idea as SPIRV-LLVM-Translator getSource).
+
+; 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 "/tmp/path-null.c"
+; CHECK-DAG: [[EMPTY:%[0-9]+]] = OpString ""
+; CHECK-DAG: [[NAME:%[0-9]+]] = OpString "no_file_path"
+; 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: [[C128:%[0-9]+]] = OpConstant [[I32]] 128
+; 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: [[TF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugTypeFunction [[C0]] [[VOID]]
+; CHECK-DAG: [[DS_DECL:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[EMPTY]]
+; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugFunctionDeclaration [[NAME]] [[TF]] [[DS_DECL]] [[C0]] [[C0]] [[CU]] [[NAME]] [[C128]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @defined() !dbg !9 {
+entry:
+  ret void
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None, retainedTypes: !10)
+!1 = !DIFile(filename: "path-null.c", directory: "/tmp", checksumkind: CSK_MD5, checksum: "00000000000000000000000000000000")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 7, !"frame-pointer", i32 2}
+
+!6 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !7)
+!7 = !{}
+!10 = !{!11}
+
+!11 = !DISubprogram(name: "no_file_path", linkageName: "no_file_path", scope: !1, file: null, line: 0, type: !6, scopeLine: 0, flags: DIFlagPrototyped, spFlags: 0)
+!9 = distinct !DISubprogram(name: "defined", linkageName: "defined", scope: !1, file: !1, line: 10, type: !6, scopeLine: 10, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration-skip-type-not-in-debug-type-regs.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration-skip-type-not-in-debug-type-regs.ll
new file mode 100644
index 0000000000000..cb9bf1b7ba568
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration-skip-type-not-in-debug-type-regs.ll
@@ -0,0 +1,45 @@
+; 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 %}
+
+; Covers the case when DISubroutineType is not supported yet.
+
+; 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 "/tmp/skip-type-not-in-regs.c"
+; CHECK-DAG: OpString "uses_opaque_sig"
+; 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: [[CU:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugCompilationUnit [[C100]] [[C5]] [[DS]] [[C0]]
+; CHECK-DAG: [[TF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugTypeFunction [[C0]] [[VOID]]
+; CHECK-NOT: DebugFunctionDeclaration
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @defined() !dbg !9 {
+entry:
+  ret void
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None, retainedTypes: !10)
+!1 = !DIFile(filename: "skip-type-not-in-regs.c", directory: "/tmp", checksumkind: CSK_MD5, checksum: "00000000000000000000000000000000")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 7, !"frame-pointer", i32 2}
+
+!8 = !DICompositeType(tag: DW_TAG_structure_type, name: "opaque_only_in_sig", file: !1, line: 1, elements: !12)
+!12 = !{}
+!6 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !11)
+!11 = !{!8}
+!10 = !{!13}
+
+!13 = !DISubprogram(name: "uses_opaque_sig", linkageName: "uses_opaque_sig", scope: !1, file: !1, line: 2, type: !6, scopeLine: 2, flags: DIFlagPrototyped, spFlags: 0)
+!7 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !14)
+!14 = !{}
+!9 = distinct !DISubprogram(name: "defined", linkageName: "defined", scope: !1, file: !1, line: 10, type: !7, scopeLine: 10, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration.ll
new file mode 100644
index 0000000000000..e1f8a7375ce43
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-declaration.ll
@@ -0,0 +1,44 @@
+; 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 NonSemantic DebugFunctionDeclaration for a DISubprogram that is not
+; a definition.
+
+; 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 "/AAAAAAAAAA/BBBBBBBB/CCCCCCCCC/debug-function-declaration.c"
+; CHECK-DAG: [[NAME:%[0-9]+]] = OpString "fwd_decl"
+; 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: [[C10:%[0-9]+]] = OpConstant [[I32]] 10
+; CHECK-DAG: [[C128:%[0-9]+]] = OpConstant [[I32]] 128
+; 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: [[TF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugTypeFunction [[C0]] [[VOID]]
+; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugFunctionDeclaration [[NAME]] [[TF]] [[DS]] [[C10]] [[C0]] [[CU]] [[NAME]] [[C128]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @defined() !dbg !9 {
+entry:
+  ret void
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version XX.X", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None, retainedTypes: !10)
+!1 = !DIFile(filename: "debug-function-declaration.c", directory: "/AAAAAAAAAA/BBBBBBBB/CCCCCCCCC", checksumkind: CSK_MD5, checksum: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 7, !"frame-pointer", i32 2}
+
+!6 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !7)
+!7 = !{}
+!10 = !{!11}
+
+!11 = !DISubprogram(name: "fwd_decl", linkageName: "fwd_decl", scope: !1, file: !1, line: 10, type: !6, scopeLine: 10, flags: DIFlagPrototyped, spFlags: 0)
+!9 = distinct !DISubprogram(name: "defined", linkageName: "defined", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)



More information about the llvm-commits mailing list