[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