[llvm] [SPIRV] Extend NSDI debug handling for DebugTypeFunction. (PR #197003)
Manuel Carrasco via llvm-commits
llvm-commits at lists.llvm.org
Fri Jun 5 06:45:37 PDT 2026
https://github.com/mgcarrasco updated https://github.com/llvm/llvm-project/pull/197003
>From aaabe242cf31b08c7e624975b21eed1def525f27 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Mon, 11 May 2026 12:14:16 -0500
Subject: [PATCH 01/21] [SPIRV] Extend NSDI debug handling for
DebugTypeFunction.
Changes:
- Collect required types with DebugInfoFinder instead of walking DbgVariableRecords only. This allows processing types that might not be available in these records, and avoids code duplication for the traversals.
- Emit DebugTypeFunction for DISubroutineType when every signature slot maps to an
emitted debug type.
- Reset per-module state in beginModule().
- Replace parallel FileStringRegs/BasicTypeNameRegs with a StringMap OpString
cache, deduplicating identical strings.
- Add LLVM DINode flag lowering for NSDI.
- Cache DebugInfoNone, OpTypeVoid and OpTypeInt32 registers to avoid duplicate instructions.
---
.../SPIRV/SPIRVNonSemanticDebugHandler.cpp | 323 ++++++++++++++----
.../SPIRV/SPIRVNonSemanticDebugHandler.h | 93 +++--
.../debug-type-function-int-string-dedup.ll | 44 +++
...debug-type-function-multi-scalar-params.ll | 39 +++
.../debug-info/debug-type-function-omit.ll | 44 +++
...g-type-function-pointer-debug-none-base.ll | 38 +++
.../debug-type-function-pointer-param.ll | 35 ++
.../debug-type-function-scalar-returns.ll | 32 ++
...bug-type-function-subroutine-type-flags.ll | 36 ++
.../debug-type-function-void-prototypes.ll | 42 +++
10 files changed, 635 insertions(+), 91 deletions(-)
create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-int-string-dedup.ll
create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-multi-scalar-params.ll
create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-omit.ll
create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-debug-none-base.ll
create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-param.ll
create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-scalar-returns.ll
create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-subroutine-type-flags.ll
create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index a6c647eddcd04..a25368863e1a7 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -14,15 +14,123 @@
#include "llvm/ADT/SmallVectorExtras.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/CodeGen/AsmPrinter.h"
+#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/DebugInfoMetadata.h"
-#include "llvm/IR/DebugProgramInstruction.h"
#include "llvm/IR/Module.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCStreamer.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Path.h"
+#include <cassert>
using namespace llvm;
+namespace {
+
+/// Partition \p Ty into \p BasicTypes, \p PointerTypes, and \p SubroutineTypes
+/// for NSDI emission. Used when iterating DebugInfoFinder.types(); each DI
+/// node is seen once, so no recursion into pointer bases. Other composites and
+/// non-pointer derived kinds are ignored.
+static void
+partitionTypes(const DIType *Ty, SetVector<const DIBasicType *> &BasicTypes,
+ SetVector<const DIDerivedType *> &PointerTypes,
+ SetVector<const DISubroutineType *> &SubroutineTypes) {
+ if (const auto *BT = dyn_cast<DIBasicType>(Ty)) {
+ BasicTypes.insert(BT);
+ return;
+ }
+ if (const auto *ST = dyn_cast<DISubroutineType>(Ty)) {
+ SubroutineTypes.insert(ST);
+ return;
+ }
+ const auto *DT = dyn_cast<DIDerivedType>(Ty);
+ if (DT && DT->getTag() == dwarf::DW_TAG_pointer_type)
+ PointerTypes.insert(DT);
+}
+
+enum : uint32_t {
+ NSDIFlagIsProtected = 1u << 0,
+ NSDIFlagIsPrivate = 1u << 1,
+ NSDIFlagIsPublic = NSDIFlagIsPrivate | NSDIFlagIsProtected,
+ NSDIFlagIsLocal = 1u << 2,
+ NSDIFlagIsDefinition = 1u << 3,
+ NSDIFlagIsFwdDecl = 1u << 4,
+ NSDIFlagIsArtificial = 1u << 5,
+ NSDIFlagIsExplicit = 1u << 6,
+ NSDIFlagIsPrototyped = 1u << 7,
+ NSDIFlagIsObjectPointer = 1u << 8,
+ NSDIFlagIsStaticMember = 1u << 9,
+ NSDIFlagIsLValueReference = 1u << 11,
+ NSDIFlagIsRValueReference = 1u << 12,
+ NSDIFlagIsOptimized = 1u << 13,
+ NSDIFlagIsEnumClass = 1u << 14,
+ NSDIFlagTypePassByValue = 1u << 15,
+ NSDIFlagTypePassByReference = 1u << 16,
+};
+
+static uint32_t mapDIFlagsToNonSemantic(DINode::DIFlags DFlags) {
+ uint32_t Flags = 0;
+ if ((DFlags & DINode::FlagAccessibility) == DINode::FlagPublic)
+ Flags |= NSDIFlagIsPublic;
+ if ((DFlags & DINode::FlagAccessibility) == DINode::FlagProtected)
+ Flags |= NSDIFlagIsProtected;
+ if ((DFlags & DINode::FlagAccessibility) == DINode::FlagPrivate)
+ Flags |= NSDIFlagIsPrivate;
+ if (DFlags & DINode::FlagFwdDecl)
+ Flags |= NSDIFlagIsFwdDecl;
+ if (DFlags & DINode::FlagArtificial)
+ Flags |= NSDIFlagIsArtificial;
+ if (DFlags & DINode::FlagExplicit)
+ Flags |= NSDIFlagIsExplicit;
+ if (DFlags & DINode::FlagPrototyped)
+ Flags |= NSDIFlagIsPrototyped;
+ if (DFlags & DINode::FlagObjectPointer)
+ Flags |= NSDIFlagIsObjectPointer;
+ if (DFlags & DINode::FlagStaticMember)
+ Flags |= NSDIFlagIsStaticMember;
+ if (DFlags & DINode::FlagLValueReference)
+ Flags |= NSDIFlagIsLValueReference;
+ if (DFlags & DINode::FlagRValueReference)
+ Flags |= NSDIFlagIsRValueReference;
+ if (DFlags & DINode::FlagTypePassByValue)
+ Flags |= NSDIFlagTypePassByValue;
+ if (DFlags & DINode::FlagTypePassByReference)
+ Flags |= NSDIFlagTypePassByReference;
+ if (DFlags & DINode::FlagEnumClass)
+ Flags |= NSDIFlagIsEnumClass;
+ return Flags;
+}
+
+static uint32_t transDebugFlags(const DINode *DN) {
+ uint32_t Flags = 0;
+ if (const auto *GV = dyn_cast<DIGlobalVariable>(DN)) {
+ if (GV->isLocalToUnit())
+ Flags |= NSDIFlagIsLocal;
+ if (GV->isDefinition())
+ Flags |= NSDIFlagIsDefinition;
+ }
+ if (const auto *SP = dyn_cast<DISubprogram>(DN)) {
+ if (SP->isLocalToUnit())
+ Flags |= NSDIFlagIsLocal;
+ if (SP->isOptimized())
+ Flags |= NSDIFlagIsOptimized;
+ if (SP->isDefinition())
+ Flags |= NSDIFlagIsDefinition;
+ Flags |= mapDIFlagsToNonSemantic(SP->getFlags());
+ }
+ if (DN->getTag() == dwarf::DW_TAG_reference_type)
+ Flags |= NSDIFlagIsLValueReference;
+ if (DN->getTag() == dwarf::DW_TAG_rvalue_reference_type)
+ Flags |= NSDIFlagIsRValueReference;
+ if (const auto *Ty = dyn_cast<DIType>(DN))
+ Flags |= mapDIFlagsToNonSemantic(Ty->getFlags());
+ if (const auto *LV = dyn_cast<DILocalVariable>(DN))
+ Flags |= mapDIFlagsToNonSemantic(LV->getFlags());
+ return Flags;
+}
+
+} // namespace
+
SPIRVNonSemanticDebugHandler::SPIRVNonSemanticDebugHandler(AsmPrinter &AP)
: DebugHandlerBase(&AP) {}
@@ -58,6 +166,17 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
if (!Asm)
return;
+ CompileUnits.clear();
+ BasicTypes.clear();
+ PointerTypes.clear();
+ SubroutineTypes.clear();
+ DebugTypeRegs.clear();
+ OpStringContentCache.clear();
+ NonSemanticOpStringsSectionEmitted = false;
+ CachedDebugInfoNoneReg = MCRegister();
+ CachedOpTypeVoidReg = MCRegister();
+ CachedOpTypeInt32Reg = MCRegister();
+
// Collect compile-unit info: file paths and source languages.
for (const DICompileUnit *CU : M->debug_compile_units()) {
const DIFile *File = CU->getFile();
@@ -87,27 +206,12 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
}
}
- // Collect basic and pointer types referenced by debug variable records.
- for (const auto &F : *M) {
- for (const auto &BB : F) {
- for (const auto &I : BB) {
- for (const DbgVariableRecord &DVR :
- filterDbgVars(I.getDbgRecordRange())) {
- const DIType *Ty = DVR.getVariable()->getType();
- if (const auto *BT = dyn_cast<DIBasicType>(Ty)) {
- BasicTypes.insert(BT);
- } else if (const auto *DT = dyn_cast<DIDerivedType>(Ty)) {
- if (DT->getTag() == dwarf::DW_TAG_pointer_type) {
- PointerTypes.insert(DT);
- if (const auto *BT =
- dyn_cast_or_null<DIBasicType>(DT->getBaseType()))
- BasicTypes.insert(BT);
- }
- }
- }
- }
- }
- }
+ // Find all debug info types that may be referenced by NSDI instructions.
+ DebugInfoFinder Finder;
+ Finder.processModule(*M);
+ llvm::for_each(Finder.types(), [&](DIType *Ty) {
+ partitionTypes(Ty, BasicTypes, PointerTypes, SubroutineTypes);
+ });
}
void SPIRVNonSemanticDebugHandler::prepareModuleOutput(
@@ -145,6 +249,29 @@ SPIRVNonSemanticDebugHandler::emitOpString(StringRef S,
return Reg;
}
+MCRegister SPIRVNonSemanticDebugHandler::emitOpStringIfNew(
+ StringRef S, SPIRV::ModuleAnalysisInfo &MAI) {
+ assert(!NonSemanticOpStringsSectionEmitted &&
+ "emitOpStringIfNew is only valid while emitting SPIR-V section 7");
+ auto It = OpStringContentCache.find(S);
+ if (It != OpStringContentCache.end())
+ return It->second;
+ MCRegister Reg = emitOpString(S, MAI);
+ OpStringContentCache.insert(std::make_pair(S, Reg));
+ return Reg;
+}
+
+MCRegister SPIRVNonSemanticDebugHandler::getCachedOpStringReg(StringRef S) {
+ assert(NonSemanticOpStringsSectionEmitted &&
+ "getCachedOpStringReg requires emitNonSemanticDebugStrings() first");
+ auto It = OpStringContentCache.find(S);
+ if (It == OpStringContentCache.end())
+ report_fatal_error(
+ "NSDI OpString missing from cache; emitNonSemanticDebugStrings must "
+ "cache every string used in section 10");
+ return It->second;
+}
+
MCRegister SPIRVNonSemanticDebugHandler::emitOpConstantI32(
uint32_t Value, MCRegister I32TypeReg, SPIRV::ModuleAnalysisInfo &MAI) {
auto [It, Inserted] = I32ConstantCache.try_emplace(Value);
@@ -179,6 +306,20 @@ MCRegister SPIRVNonSemanticDebugHandler::emitExtInst(
return Reg;
}
+MCRegister SPIRVNonSemanticDebugHandler::getOrEmitOpTypeVoidReg(
+ SPIRV::ModuleAnalysisInfo &MAI) {
+ if (!CachedOpTypeVoidReg.isValid())
+ CachedOpTypeVoidReg = findOrEmitOpTypeVoid(MAI);
+ return CachedOpTypeVoidReg;
+}
+
+MCRegister SPIRVNonSemanticDebugHandler::getOrEmitOpTypeInt32Reg(
+ SPIRV::ModuleAnalysisInfo &MAI) {
+ if (!CachedOpTypeInt32Reg.isValid())
+ CachedOpTypeInt32Reg = findOrEmitOpTypeInt32(MAI);
+ return CachedOpTypeInt32Reg;
+}
+
MCRegister SPIRVNonSemanticDebugHandler::findOrEmitOpTypeVoid(
SPIRV::ModuleAnalysisInfo &MAI) {
for (const MachineInstr *MI : MAI.getMSInstrs(SPIRV::MB_TypeConstVars)) {
@@ -210,15 +351,18 @@ MCRegister SPIRVNonSemanticDebugHandler::findOrEmitOpTypeInt32(
return Reg;
}
-void SPIRVNonSemanticDebugHandler::emitDebugTypePointer(
- const DIDerivedType *PT, MCRegister VoidTypeReg, MCRegister I32TypeReg,
- MCRegister ExtInstSetReg, MCRegister I32ZeroReg,
- const DenseMap<const DIBasicType *, MCRegister> &BasicTypeRegs,
+std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugTypePointer(
+ const DIDerivedType *PT, MCRegister ExtInstSetReg,
SPIRV::ModuleAnalysisInfo &MAI) {
// A DWARF address space is required to determine the SPIR-V storage class.
// Skip pointer types that do not carry one.
if (!PT->getDWARFAddressSpace().has_value())
- return;
+ return std::nullopt;
+
+ MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
+ MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI);
+ MCRegister DebugTypePointerFlagsReg =
+ emitOpConstantI32(transDebugFlags(PT), I32TypeReg, MAI);
// For SPIR-V targets, Clang sets DwarfAddressSpace to the LLVM IR address
// space, which addressSpaceToStorageClass expects.
@@ -227,22 +371,67 @@ void SPIRVNonSemanticDebugHandler::emitDebugTypePointer(
addressSpaceToStorageClass(PT->getDWARFAddressSpace().value(), ST),
I32TypeReg, MAI);
- if (const auto *BaseType = dyn_cast_or_null<DIBasicType>(PT->getBaseType())) {
- auto BTIt = BasicTypeRegs.find(BaseType);
- if (BTIt != BasicTypeRegs.end())
- emitExtInst(SPIRV::NonSemanticExtInst::DebugTypePointer, VoidTypeReg,
- ExtInstSetReg, {BTIt->second, StorageClassReg, I32ZeroReg},
- MAI);
+ if (const DIType *BaseTy = PT->getBaseType()) {
+ auto BaseIt = DebugTypeRegs.find(BaseTy);
+ if (BaseIt != DebugTypeRegs.end())
+ return emitExtInst(
+ SPIRV::NonSemanticExtInst::DebugTypePointer, VoidTypeReg,
+ ExtInstSetReg,
+ {BaseIt->second, StorageClassReg, DebugTypePointerFlagsReg}, MAI);
+ } else {
+ // No getBaseType() (typical for void*): use DebugInfoNone as Base Type,
+ // same as SPIRV-LLVM-Translator (see issue #109287 and the DISABLED
+ // spirv-val run in debug-type-pointer.ll). spirv-val may still reject this
+ // encoding; see https://github.com/KhronosGroup/SPIRV-Registry/pull/287.
+ return emitExtInst(
+ SPIRV::NonSemanticExtInst::DebugTypePointer, VoidTypeReg, ExtInstSetReg,
+ {CachedDebugInfoNoneReg, StorageClassReg, DebugTypePointerFlagsReg},
+ MAI);
+ }
+
+ return std::nullopt;
+}
+
+std::optional<MCRegister>
+SPIRVNonSemanticDebugHandler::emitDebugTypeFunctionForSubroutineType(
+ const DISubroutineType *ST, MCRegister ExtInstSetReg,
+ SPIRV::ModuleAnalysisInfo &MAI) {
+ MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
+ MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI);
+ MCRegister DebugTypeFunctionFlagsReg =
+ emitOpConstantI32(transDebugFlags(ST), I32TypeReg, MAI);
+ DITypeArray TA = ST->getTypeArray();
+ SmallVector<MCRegister, 8> Ops;
+ Ops.push_back(DebugTypeFunctionFlagsReg);
+ // Empty DI type tuple: no explicit return or parameter slots (hand-written IR
+ // may use !{}). Emit void-only prototype. Same as SPIRV-LLVM-Translator when
+ // DISubroutineType::getTypeArray() has zero elements.
+ if (TA.empty()) {
+ Ops.push_back(VoidTypeReg);
} else {
- // Void pointer: use DebugInfoNone for the base type. Note that
- // spirv-val currently rejects DebugInfoNone as the base type of
- // DebugTypePointer; see issue #109287 and the DISABLED spirv-val run
- // in debug-type-pointer.ll.
- MCRegister NoneReg = emitExtInst(SPIRV::NonSemanticExtInst::DebugInfoNone,
- VoidTypeReg, ExtInstSetReg, {}, MAI);
- emitExtInst(SPIRV::NonSemanticExtInst::DebugTypePointer, VoidTypeReg,
- ExtInstSetReg, {NoneReg, StorageClassReg, I32ZeroReg}, MAI);
+ for (DIType *Elem : TA) {
+ auto OptReg = mapDISignatureTypeToReg(Elem, VoidTypeReg);
+ // No emitted DebugType* id for this slot (e.g., pointer that
+ // was skipped due missing address space, etc.).
+ if (!OptReg)
+ return std::nullopt;
+ Ops.push_back(*OptReg);
+ }
}
+ return emitExtInst(SPIRV::NonSemanticExtInst::DebugTypeFunction, VoidTypeReg,
+ ExtInstSetReg, Ops, MAI);
+}
+
+std::optional<MCRegister>
+SPIRVNonSemanticDebugHandler::mapDISignatureTypeToReg(const DIType *Ty,
+ MCRegister VoidTypeReg) {
+ if (!Ty)
+ return VoidTypeReg;
+ auto It = DebugTypeRegs.find(Ty);
+ if (It != DebugTypeRegs.end())
+ return It->second;
+
+ return std::nullopt;
}
void SPIRVNonSemanticDebugHandler::emitNonSemanticDebugStrings(
@@ -258,10 +447,12 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticDebugStrings(
return;
for (const CompileUnitInfo &Info : CompileUnits)
- FileStringRegs.push_back(emitOpString(Info.FilePath, MAI));
+ (void)emitOpStringIfNew(Info.FilePath, MAI);
for (const DIBasicType *BT : BasicTypes)
- BasicTypeNameRegs.push_back(emitOpString(BT->getName(), MAI));
+ (void)emitOpStringIfNew(BT->getName(), MAI);
+
+ NonSemanticOpStringsSectionEmitted = true;
}
void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
@@ -277,8 +468,15 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
if (!ExtInstSetReg.isValid())
return; // Extension not available.
- MCRegister VoidTypeReg = findOrEmitOpTypeVoid(MAI);
- MCRegister I32TypeReg = findOrEmitOpTypeInt32(MAI);
+ assert(NonSemanticOpStringsSectionEmitted &&
+ "emitNonSemanticDebugStrings() must run before "
+ "emitNonSemanticGlobalDebugInfo()");
+
+ MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
+ MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI);
+
+ CachedDebugInfoNoneReg = emitExtInst(SPIRV::NonSemanticExtInst::DebugInfoNone,
+ VoidTypeReg, ExtInstSetReg, {}, MAI);
// Emit integer constants shared across all NSDI instructions. The constant
// cache ensures each value is emitted at most once even when referenced from
@@ -302,11 +500,8 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
});
// Emit DebugSource and DebugCompilationUnit for each compile unit.
- // FileStringRegs was populated by emitNonSemanticDebugStrings() in section 7.
- assert(FileStringRegs.size() == CompileUnits.size() &&
- "FileStringRegs must be populated by emitNonSemanticDebugStrings()");
- for (auto [Info, FileStrReg, SrcLangReg] :
- llvm::zip(CompileUnits, FileStringRegs, SrcLangRegs)) {
+ for (auto [Info, SrcLangReg] : llvm::zip(CompileUnits, SrcLangRegs)) {
+ MCRegister FileStrReg = getCachedOpStringReg(Info.FilePath);
MCRegister DebugSourceReg =
emitExtInst(SPIRV::NonSemanticExtInst::DebugSource, VoidTypeReg,
ExtInstSetReg, {FileStrReg}, MAI);
@@ -321,18 +516,10 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
// DebugTypePointer. Cached with other i32 constants.
MCRegister I32ZeroReg = emitOpConstantI32(0, I32TypeReg, MAI);
- // Maps each DIBasicType to its DebugTypeBasic result register for use as
- // operands in DebugTypePointer instructions.
- DenseMap<const DIBasicType *, MCRegister> BasicTypeRegs;
+ DebugTypeRegs.clear();
- // BasicTypeNameRegs was populated by emitNonSemanticDebugStrings() in
- // section 7.
- assert(
- BasicTypeNameRegs.size() == BasicTypes.size() &&
- "BasicTypeNameRegs must be populated by emitNonSemanticDebugStrings()");
- unsigned BTIdx = 0;
for (const DIBasicType *BT : BasicTypes) {
- MCRegister NameReg = BasicTypeNameRegs[BTIdx++];
+ MCRegister NameReg = getCachedOpStringReg(BT->getName());
MCRegister SizeReg = emitOpConstantI32(
static_cast<uint32_t>(BT->getSizeInBits()), I32TypeReg, MAI);
@@ -367,11 +554,19 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
MCRegister BTReg = emitExtInst(
SPIRV::NonSemanticExtInst::DebugTypeBasic, VoidTypeReg, ExtInstSetReg,
{NameReg, SizeReg, EncodingReg, I32ZeroReg}, MAI);
- BasicTypeRegs[BT] = BTReg;
+ DebugTypeRegs[BT] = BTReg;
}
// Emit DebugTypePointer for each referenced pointer type.
- for (const DIDerivedType *PT : PointerTypes)
- emitDebugTypePointer(PT, VoidTypeReg, I32TypeReg, ExtInstSetReg, I32ZeroReg,
- BasicTypeRegs, MAI);
+ for (const DIDerivedType *PT : PointerTypes) {
+ if (auto PtrReg = emitDebugTypePointer(PT, ExtInstSetReg, MAI))
+ DebugTypeRegs[PT] = *PtrReg;
+ }
+
+ // Emit DebugTypeFunction for each distinct DISubroutineType.
+ for (const DISubroutineType *ST : SubroutineTypes) {
+ if (auto FnTyReg =
+ emitDebugTypeFunctionForSubroutineType(ST, ExtInstSetReg, MAI))
+ DebugTypeRegs[ST] = *FnTyReg;
+ }
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index 7d8bac0c5ab5a..2e98f54760e29 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -24,10 +24,12 @@
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
#include "llvm/CodeGen/DebugHandlerBase.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCRegister.h"
+#include <optional>
namespace llvm {
@@ -40,9 +42,10 @@ class SPIRVSubtarget;
/// Call sequence:
/// beginModule() -- collect compile-unit metadata.
/// prepareModuleOutput() -- add extension + ext inst set to MAI.
-/// emitNonSemanticGlobalDebugInfo() -- emit DebugSource,
+/// emitNonSemanticDebugStrings() -- OpString for NSDI paths/names (sec.
+/// 7). emitNonSemanticGlobalDebugInfo() -- emit DebugSource,
/// DebugCompilationUnit, DebugTypeBasic,
-/// DebugTypePointer.
+/// DebugTypePointer, DebugTypeFunction.
/// beginFunctionImpl() -- no-op (no per-function DI yet).
/// endFunctionImpl() -- no-op.
class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
@@ -58,22 +61,35 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
SmallVector<CompileUnitInfo> CompileUnits;
int64_t DwarfVersion = 0;
- // Types referenced by debug variable records, collected in beginModule().
+ // DI types partitioned from DebugInfoFinder.types() in beginModule()
+ // (basics, pointers, subroutine types NSDI v1 may emit).
SetVector<const DIBasicType *> BasicTypes;
SetVector<const DIDerivedType *> PointerTypes;
+ SetVector<const DISubroutineType *> SubroutineTypes;
+
+ // Filled in emitNonSemanticGlobalDebugInfo(): DI types to their result
+ // registers.
+ DenseMap<const DIType *, MCRegister> DebugTypeRegs;
+
+ // Maps OpString contents to result id. Populated only by emitOpStringIfNew()
+ // during section 7; section 10 uses getCachedOpStringReg() (lookup only).
+ StringMap<MCRegister> OpStringContentCache;
+
+ // True after emitNonSemanticDebugStrings() emitted the NSDI OpStrings for
+ // this module. SPIRVAsmPrinter calls that before
+ // emitNonSemanticGlobalDebugInfo().
+ bool NonSemanticOpStringsSectionEmitted = false;
+
+ MCRegister CachedDebugInfoNoneReg;
+
+ MCRegister CachedOpTypeVoidReg;
+
+ MCRegister CachedOpTypeInt32Reg;
// Cache of already-emitted i32 constants, keyed by value. Prevents
// duplicate OpConstant instructions for the same integer value.
DenseMap<uint32_t, MCRegister> I32ConstantCache;
- // OpString registers for NSDI instructions, populated by
- // emitNonSemanticDebugStrings() (section 7) and consumed by
- // emitNonSemanticGlobalDebugInfo() (section 10). OpString must appear in
- // section 7 per the SPIR-V module layout; it cannot be emitted alongside the
- // OpExtInst instructions in section 10.
- SmallVector<MCRegister> FileStringRegs; // one per CompileUnits entry
- SmallVector<MCRegister> BasicTypeNameRegs; // one per BasicTypes entry
-
// True once emitNonSemanticGlobalDebugInfo() has run. Both
// SPIRVAsmPrinter::emitFunctionHeader() and emitEndOfAsmFile() may call
// outputModuleSections(), each guarded by ModuleSectionsEmitted, so only
@@ -91,9 +107,9 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
/// Emit OpString instructions for all NSDI file paths and basic type names
/// 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. The resulting
- /// registers are cached in FileStringRegs and BasicTypeNameRegs for use by
- /// emitNonSemanticGlobalDebugInfo().
+ /// prepareModuleOutput() has registered the ext inst set. Registers are
+ /// stored in OpStringContentCache; emitNonSemanticGlobalDebugInfo() resolves
+ /// them via getCachedOpStringReg().
void emitNonSemanticDebugStrings(SPIRV::ModuleAnalysisInfo &MAI);
/// Add SPV_KHR_non_semantic_info extension and
@@ -104,9 +120,10 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
SPIRV::ModuleAnalysisInfo &MAI);
/// Emit module-scope NSDI instructions (DebugSource, DebugCompilationUnit,
- /// DebugTypeBasic, DebugTypePointer). Called by SPIRVAsmPrinter::
- /// outputModuleSections() at section 10 in place of
- /// outputModuleSection(MB_NonSemanticGlobalDI).
+ /// DebugTypeBasic, DebugTypePointer, DebugTypeFunction). Called by
+ /// SPIRVAsmPrinter::outputModuleSections() at section 10 in place of
+ /// outputModuleSection(MB_NonSemanticGlobalDI). Requires
+ /// emitNonSemanticDebugStrings() to have run first when NSDI strings apply.
void emitNonSemanticGlobalDebugInfo(SPIRV::ModuleAnalysisInfo &MAI);
protected:
@@ -137,6 +154,14 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
private:
void emitMCInst(MCInst &Inst);
MCRegister emitOpString(StringRef S, SPIRV::ModuleAnalysisInfo &MAI);
+
+ /// Section 7 only: return cached id or emit OpString and cache it. Must not
+ /// be called after NonSemanticOpStringsSectionEmitted is set.
+ 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.
+ MCRegister getCachedOpStringReg(StringRef S);
MCRegister emitOpConstantI32(uint32_t Value, MCRegister I32TypeReg,
SPIRV::ModuleAnalysisInfo &MAI);
MCRegister emitExtInst(SPIRV::NonSemanticExtInst::NonSemanticExtInst Opcode,
@@ -144,6 +169,12 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
ArrayRef<MCRegister> Operands,
SPIRV::ModuleAnalysisInfo &MAI);
+ /// Return OpTypeVoid id for this module (lazy lookup / emit, then cache).
+ MCRegister getOrEmitOpTypeVoidReg(SPIRV::ModuleAnalysisInfo &MAI);
+
+ /// Return OpTypeInt 32 0 id for this module (lazy lookup / emit, then cache).
+ MCRegister getOrEmitOpTypeInt32Reg(SPIRV::ModuleAnalysisInfo &MAI);
+
/// Find OpTypeVoid in the already-emitted TypeConstVars section, or emit one
/// if the module does not contain it (e.g. no void-returning functions).
MCRegister findOrEmitOpTypeVoid(SPIRV::ModuleAnalysisInfo &MAI);
@@ -152,16 +183,24 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
/// one if the module does not contain it.
MCRegister findOrEmitOpTypeInt32(SPIRV::ModuleAnalysisInfo &MAI);
- /// Emit a DebugTypePointer instruction for PT. Skips pointer types that do
- /// not carry a DWARF address space. For pointers whose base type is a
- /// DIBasicType, looks up the base type's DebugTypeBasic register in
- /// BasicTypeRegs. All other pointers (void pointers and pointers whose base
- /// type is not a DIBasicType) use DebugInfoNone as the base type operand.
- void emitDebugTypePointer(
- const DIDerivedType *PT, MCRegister VoidTypeReg, MCRegister I32TypeReg,
- MCRegister ExtInstSetReg, MCRegister I32ZeroReg,
- const DenseMap<const DIBasicType *, MCRegister> &BasicTypeRegs,
- SPIRV::ModuleAnalysisInfo &MAI);
+ /// Emit a DebugTypePointer instruction for PT. Returns std::nullopt when the
+ /// pointer is skipped (no DWARF address space). If the base DI type has an
+ /// entry in DebugTypeRegs, use it as the Base Type operand; otherwise use
+ /// DebugInfoNone.
+ std::optional<MCRegister>
+ emitDebugTypePointer(const DIDerivedType *PT, MCRegister ExtInstSetReg,
+ SPIRV::ModuleAnalysisInfo &MAI);
+
+ /// Emit one DebugTypeFunction for ST when every DI operand maps to a debug
+ /// type id; otherwise emit nothing and return std::nullopt.
+ std::optional<MCRegister>
+ emitDebugTypeFunctionForSubroutineType(const DISubroutineType *ST,
+ MCRegister ExtInstSetReg,
+ SPIRV::ModuleAnalysisInfo &MAI);
+
+ /// Map DISubroutineType slot DI types using DebugTypeRegs.
+ std::optional<MCRegister> mapDISignatureTypeToReg(const DIType *Ty,
+ MCRegister VoidTypeReg);
/// Map a DWARF source language code to a NonSemantic.Shader.DebugInfo.100
/// source language code.
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-int-string-dedup.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-int-string-dedup.ll
new file mode 100644
index 0000000000000..1f67a7001fd5d
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-int-string-dedup.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 --check-prefix=CHECK-SPIRV
+; 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 %}
+
+; Two distinct !DIBasicType nodes named "int" share one OpString "int".
+
+; CHECK-SPIRV: [[ext_inst_non_semantic:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-SPIRV-COUNT-1: OpString "int"
+; CHECK-SPIRV-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-SPIRV-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-SPIRV-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeFunction [[flag_zero]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func i32 @dedupe_a() !dbg !10 {
+entry:
+ ret i32 0
+}
+
+define spir_func i32 @dedupe_b() !dbg !11 {
+entry:
+ ret i32 0
+}
+
+!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)
+!1 = !DIFile(filename: "debug-type-function-int-string-dedup.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 = !{!8}
+!8 = distinct !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!9 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !12)
+!12 = !{!13}
+!13 = distinct !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!10 = distinct !DISubprogram(name: "dedupe_a", linkageName: "dedupe_a", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!11 = distinct !DISubprogram(name: "dedupe_b", linkageName: "dedupe_b", scope: !1, file: !1, line: 2, type: !9, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-multi-scalar-params.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-multi-scalar-params.ll
new file mode 100644
index 0000000000000..ab5fc00c0f978
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-multi-scalar-params.ll
@@ -0,0 +1,39 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK
+; 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 %}
+;
+; Multi-slot signature mapping order: DebugTypeFunction must keep operand order
+; for void(int, float, int).
+
+; CHECK: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-DAG: [[str_int:%[0-9]+]] = OpString "int"
+; CHECK-DAG: [[str_float:%[0-9]+]] = OpString "float"
+; CHECK-DAG: [[dbg_int:%[0-9]+]] = OpExtInst [[type_void]] [[ext]] DebugTypeBasic [[str_int]]
+; CHECK-DAG: [[dbg_float:%[0-9]+]] = OpExtInst [[type_void]] [[ext]] DebugTypeBasic [[str_float]]
+; CHECK: OpExtInst [[type_void]] [[ext]] DebugTypeFunction [[flag_zero]] [[type_void]] [[dbg_int]] [[dbg_float]] [[dbg_int]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @multi_params(i32 %a, float %b, i32 %c) !dbg !10 {
+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)
+!1 = !DIFile(filename: "debug-type-function-multi-scalar-params.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 = !{null, !8, !9, !8}
+!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!9 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float)
+
+!10 = distinct !DISubprogram(name: "multi_params", linkageName: "multi_params", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-omit.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-omit.ll
new file mode 100644
index 0000000000000..62d2e8afd51e5
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-omit.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 --implicit-check-not=DebugTypeFunction --check-prefix=CHECK
+; 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 %}
+
+; Unsupported composite parameter and pointer without dwarfAddressSpace — DISubroutineType not lowered to DebugTypeFunction.
+
+; CHECK: OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK: DebugCompilationUnit
+; CHECK-NOT: DebugTypePointer
+; CHECK-NOT: DebugTypeFunction
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @opaque_struct_param(i32 %x) !dbg !10 {
+entry:
+ ret void
+}
+
+define spir_func void @ptr_no_as(ptr %p) !dbg !11 {
+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)
+!1 = !DIFile(filename: "debug-type-function-omit.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 = !{null, !8}
+!8 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "opaque_sig", file: !1, line: 50, size: 32, elements: !9, identifier: "opaque_sig")
+!9 = !{}
+
+!12 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !13)
+!13 = !{null, !14}
+!14 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !15, size: 64)
+!15 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!10 = distinct !DISubprogram(name: "opaque_struct_param", linkageName: "opaque_struct_param", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!11 = distinct !DISubprogram(name: "ptr_no_as", linkageName: "ptr_no_as", scope: !1, file: !1, line: 2, type: !12, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-debug-none-base.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-debug-none-base.ll
new file mode 100644
index 0000000000000..9e7d148e40c15
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-debug-none-base.ll
@@ -0,0 +1,38 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK
+; TODO(#109287): spirv-val coverage remains disabled for DebugTypePointer with
+; DebugInfoNone as the base type.
+;
+; Pointer parameter with null baseType should lower to DebugTypePointer using
+; DebugInfoNone as Base Type, and still be consumed by DebugTypeFunction.
+
+; CHECK: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-DAG: [[debug_none:%[0-9]+]] = OpExtInst [[type_void]] [[ext]] DebugInfoNone
+; CHECK-DAG: [[storage_class:%[0-9]+]] = OpConstant [[type_int32]] 8{{$}}
+; CHECK: [[dbg_ptr:%[0-9]+]] = OpExtInst [[type_void]] [[ext]] DebugTypePointer [[debug_none]] [[storage_class]] [[flag_zero]]
+; CHECK: OpExtInst [[type_void]] [[ext]] DebugTypeFunction [[flag_zero]] [[type_void]] [[dbg_ptr]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @ptr_null_base(ptr addrspace(4) %p) !dbg !10 {
+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)
+!1 = !DIFile(filename: "debug-type-function-pointer-debug-none-base.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 = !{null, !8}
+!8 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64, dwarfAddressSpace: 4)
+
+!10 = distinct !DISubprogram(name: "ptr_null_base", linkageName: "ptr_null_base", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-param.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-param.ll
new file mode 100644
index 0000000000000..854b73ebed017
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-param.ll
@@ -0,0 +1,35 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; 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 %}
+
+; void (*)(addrspace(4) i32*) — DebugTypePointer + DebugTypeFunction includes the pointer parameter operand.
+
+; CHECK-SPIRV: [[ext_inst_non_semantic:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-SPIRV-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-SPIRV-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-SPIRV-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypePointer
+; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeFunction [[flag_zero]] [[type_void]] {{%[0-9]+}}
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @ptr_param(ptr addrspace(4) %p) !dbg !10 {
+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)
+!1 = !DIFile(filename: "debug-type-function-pointer-param.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 = !{null, !8}
+!8 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !9, size: 64, dwarfAddressSpace: 4)
+!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!10 = distinct !DISubprogram(name: "ptr_param", linkageName: "ptr_param", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-scalar-returns.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-scalar-returns.ll
new file mode 100644
index 0000000000000..6770af122b35b
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-scalar-returns.ll
@@ -0,0 +1,32 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-SPIRV: [[ext_inst_non_semantic:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-SPIRV-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-SPIRV-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-SPIRV-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-SPIRV-DAG: OpString "float"
+; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeFunction [[flag_zero]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func float @float_sig() !dbg !9 {
+entry:
+ ret float 0.000000e+00
+}
+
+!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)
+!1 = !DIFile(filename: "debug-type-function-scalar-returns.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 = !{!8}
+!8 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float)
+
+!9 = distinct !DISubprogram(name: "float_sig", linkageName: "float_sig", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-subroutine-type-flags.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-subroutine-type-flags.ll
new file mode 100644
index 0000000000000..9ef42724a42ea
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-subroutine-type-flags.ll
@@ -0,0 +1,36 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK
+; 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 %}
+
+; Test the encoding of the flags in the DISubroutineType
+
+; CHECK: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-DAG: [[flag_prototyped:%[0-9]+]] = OpConstant [[type_int32]] 128{{$}}
+; CHECK-DAG: [[dbg_ptr:%[0-9]+]] = OpExtInst [[type_void]] [[ext]] DebugTypePointer {{.*}} {{.*}} [[flag_zero]]
+; CHECK: OpExtInst [[type_void]] [[ext]] DebugTypeFunction [[flag_prototyped]] [[type_void]] [[dbg_ptr]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @ptr_param(ptr addrspace(4) %p) !dbg !10 {
+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)
+!1 = !DIFile(filename: "debug-type-function-subroutine-type-flags.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, flags: DIFlagPrototyped, types: !7)
+!7 = !{null, !8}
+!8 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !9, size: 64, dwarfAddressSpace: 4)
+!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!10 = distinct !DISubprogram(name: "ptr_param", linkageName: "ptr_param", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll
new file mode 100644
index 0000000000000..c46dc74e03ef9
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll
@@ -0,0 +1,42 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; 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 %}
+
+; Void function types in LLVM metadata may be written as an empty type list (!{}) or with an explicit
+; null return slot (!{null}); both should produce the same SPIR-V DebugTypeFunction (flags + void only).
+
+; CHECK-SPIRV: [[ext_inst_non_semantic:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-SPIRV-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-SPIRV-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-SPIRV-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeFunction [[flag_zero]] [[type_void]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @void_fn() !dbg !10 {
+entry:
+ ret void
+}
+
+define spir_func void @void_explicit_null() !dbg !11 {
+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)
+!1 = !DIFile(filename: "debug-type-function-void-prototypes.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 = !{}
+
+!8 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !9)
+!9 = !{null}
+
+!10 = distinct !DISubprogram(name: "void_fn", linkageName: "void_fn", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!11 = distinct !DISubprogram(name: "void_explicit_null", linkageName: "void_explicit_null", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
>From cdb0bbc1e7e87a72ff38de83b5d4a93f96ccdef8 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 12:25:20 -0500
Subject: [PATCH 02/21] [reviews] Fix docs.
---
.../Target/SPIRV/SPIRVNonSemanticDebugHandler.h | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index 2e98f54760e29..ed9c1af1263a6 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -183,10 +183,16 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
/// one if the module does not contain it.
MCRegister findOrEmitOpTypeInt32(SPIRV::ModuleAnalysisInfo &MAI);
- /// Emit a DebugTypePointer instruction for PT. Returns std::nullopt when the
- /// pointer is skipped (no DWARF address space). If the base DI type has an
- /// entry in DebugTypeRegs, use it as the Base Type operand; otherwise use
- /// DebugInfoNone.
+ /// Emit \c DebugTypePointer for pointer metadata \p PT.
+ ///
+ /// \returns The result id register on success. Returns \c std::nullopt and
+ /// emits nothing if \p PT has no DWARF address space (needed to pick the
+ /// SPIR-V storage class), or if \p PT has a non-null base DI type that is not
+ /// yet in \c DebugTypeRegs (the pointee was not emitted as a debug type).
+ ///
+ /// Base Type operand: the register from \c DebugTypeRegs for \p PT's base
+ /// type when it is set and mapped; \c DebugInfoNone when there is no base
+ /// type (e.g. \c void * in IR), consistent with SPIRV-LLVM-Translator.
std::optional<MCRegister>
emitDebugTypePointer(const DIDerivedType *PT, MCRegister ExtInstSetReg,
SPIRV::ModuleAnalysisInfo &MAI);
>From ba67fa218abbf8a994fcb5335a630796a7df678c Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 12:28:15 -0500
Subject: [PATCH 03/21] [reviews] Initialize missing member variables.
---
llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index a25368863e1a7..4a0669b0162c9 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -172,6 +172,8 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
SubroutineTypes.clear();
DebugTypeRegs.clear();
OpStringContentCache.clear();
+ I32ConstantCache.clear();
+ GlobalDIEmitted = false;
NonSemanticOpStringsSectionEmitted = false;
CachedDebugInfoNoneReg = MCRegister();
CachedOpTypeVoidReg = MCRegister();
>From 243cae036372df7f6fe1aed91e7d8e24e117951a Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 12:29:21 -0500
Subject: [PATCH 04/21] [reviews] Use reportFatalInternalError to report
internal invariant violation.
---
llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 4a0669b0162c9..efdc2ca0bd2a8 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -268,7 +268,7 @@ MCRegister SPIRVNonSemanticDebugHandler::getCachedOpStringReg(StringRef S) {
"getCachedOpStringReg requires emitNonSemanticDebugStrings() first");
auto It = OpStringContentCache.find(S);
if (It == OpStringContentCache.end())
- report_fatal_error(
+ reportFatalInternalError(
"NSDI OpString missing from cache; emitNonSemanticDebugStrings must "
"cache every string used in section 10");
return It->second;
>From 32637f370bf607e14b94a767f43150ecebe17840 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 12:34:46 -0500
Subject: [PATCH 05/21] [reviews] Refactor
---
llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index efdc2ca0bd2a8..a9ccd8e8b8a8e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -255,11 +255,12 @@ MCRegister SPIRVNonSemanticDebugHandler::emitOpStringIfNew(
StringRef S, SPIRV::ModuleAnalysisInfo &MAI) {
assert(!NonSemanticOpStringsSectionEmitted &&
"emitOpStringIfNew is only valid while emitting SPIR-V section 7");
- auto It = OpStringContentCache.find(S);
- if (It != OpStringContentCache.end())
+ auto [It, Inserted] = OpStringContentCache.try_emplace(S, MCRegister());
+ if (!Inserted)
return It->second;
+
MCRegister Reg = emitOpString(S, MAI);
- OpStringContentCache.insert(std::make_pair(S, Reg));
+ It->second = Reg;
return Reg;
}
>From 941b4e566dbae149a5fded7643adcba3f35fb9d7 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 12:41:56 -0500
Subject: [PATCH 06/21] [review] Switch to assert.
---
llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index a9ccd8e8b8a8e..0e7fa8e8d8eee 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -268,10 +268,9 @@ MCRegister SPIRVNonSemanticDebugHandler::getCachedOpStringReg(StringRef S) {
assert(NonSemanticOpStringsSectionEmitted &&
"getCachedOpStringReg requires emitNonSemanticDebugStrings() first");
auto It = OpStringContentCache.find(S);
- if (It == OpStringContentCache.end())
- reportFatalInternalError(
- "NSDI OpString missing from cache; emitNonSemanticDebugStrings must "
- "cache every string used in section 10");
+ assert(It == OpStringContentCache.end() &&
+ "NSDI OpString missing from cache; emitNonSemanticDebugStrings must "
+ "cache every string used in section 10");
return It->second;
}
>From 667432612dcf92272c3aee3334d26146cacaf29d Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 12:46:36 -0500
Subject: [PATCH 07/21] [reviews] Fix docs after clang-format.
---
llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index ed9c1af1263a6..50ce503af3201 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -42,10 +42,10 @@ class SPIRVSubtarget;
/// Call sequence:
/// beginModule() -- collect compile-unit metadata.
/// prepareModuleOutput() -- add extension + ext inst set to MAI.
-/// emitNonSemanticDebugStrings() -- OpString for NSDI paths/names (sec.
-/// 7). emitNonSemanticGlobalDebugInfo() -- emit DebugSource,
-/// DebugCompilationUnit, DebugTypeBasic,
-/// DebugTypePointer, DebugTypeFunction.
+/// emitNonSemanticDebugStrings() -- OpString for NSDI strings (sec. 7).
+/// emitNonSemanticGlobalDebugInfo() -- emit DebugSource, DebugTypeBasic,
+/// DebugTypePointer, DebugTypeFunction,
+/// DebugCompilationUnit.
/// beginFunctionImpl() -- no-op (no per-function DI yet).
/// endFunctionImpl() -- no-op.
class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
>From 606e1725c7060c1a0f4d2d44222925f5cb70b444 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 12:58:24 -0500
Subject: [PATCH 08/21] [reviews] Use vectors.
---
.../Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 12 ++++++------
llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h | 7 +++----
2 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 0e7fa8e8d8eee..bbc8133e53694 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -32,20 +32,20 @@ namespace {
/// node is seen once, so no recursion into pointer bases. Other composites and
/// non-pointer derived kinds are ignored.
static void
-partitionTypes(const DIType *Ty, SetVector<const DIBasicType *> &BasicTypes,
- SetVector<const DIDerivedType *> &PointerTypes,
- SetVector<const DISubroutineType *> &SubroutineTypes) {
+partitionTypes(const DIType *Ty, SmallVector<const DIBasicType *> &BasicTypes,
+ SmallVector<const DIDerivedType *> &PointerTypes,
+ SmallVector<const DISubroutineType *> &SubroutineTypes) {
if (const auto *BT = dyn_cast<DIBasicType>(Ty)) {
- BasicTypes.insert(BT);
+ BasicTypes.push_back(BT);
return;
}
if (const auto *ST = dyn_cast<DISubroutineType>(Ty)) {
- SubroutineTypes.insert(ST);
+ SubroutineTypes.push_back(ST);
return;
}
const auto *DT = dyn_cast<DIDerivedType>(Ty);
if (DT && DT->getTag() == dwarf::DW_TAG_pointer_type)
- PointerTypes.insert(DT);
+ PointerTypes.push_back(DT);
}
enum : uint32_t {
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index 50ce503af3201..57704d64e8158 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -21,7 +21,6 @@
#include "MCTargetDesc/SPIRVBaseInfo.h"
#include "SPIRVModuleAnalysis.h"
#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
@@ -63,9 +62,9 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
// DI types partitioned from DebugInfoFinder.types() in beginModule()
// (basics, pointers, subroutine types NSDI v1 may emit).
- SetVector<const DIBasicType *> BasicTypes;
- SetVector<const DIDerivedType *> PointerTypes;
- SetVector<const DISubroutineType *> SubroutineTypes;
+ SmallVector<const DIBasicType *> BasicTypes;
+ SmallVector<const DIDerivedType *> PointerTypes;
+ SmallVector<const DISubroutineType *> SubroutineTypes;
// Filled in emitNonSemanticGlobalDebugInfo(): DI types to their result
// registers.
>From 3bdcaa7469f00543a9d9b47ece8a3d852fb4a02f Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 13:03:16 -0500
Subject: [PATCH 09/21] Fix typo.
---
llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index bbc8133e53694..7ddfe039e5349 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -268,7 +268,7 @@ MCRegister SPIRVNonSemanticDebugHandler::getCachedOpStringReg(StringRef S) {
assert(NonSemanticOpStringsSectionEmitted &&
"getCachedOpStringReg requires emitNonSemanticDebugStrings() first");
auto It = OpStringContentCache.find(S);
- assert(It == OpStringContentCache.end() &&
+ assert(It != OpStringContentCache.end() &&
"NSDI OpString missing from cache; emitNonSemanticDebugStrings must "
"cache every string used in section 10");
return It->second;
>From 57f966b38899e860f8d139fb0d28b429aba3ad72 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 14:00:19 -0500
Subject: [PATCH 10/21] Fix handling for DebugTypeFunction.
---
.../SPIRV/SPIRVNonSemanticDebugHandler.cpp | 19 ++++++++++++-------
.../SPIRV/SPIRVNonSemanticDebugHandler.h | 15 +++++++++++++--
2 files changed, 25 insertions(+), 9 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 7ddfe039e5349..b488326d0c6ba 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -411,8 +411,9 @@ SPIRVNonSemanticDebugHandler::emitDebugTypeFunctionForSubroutineType(
if (TA.empty()) {
Ops.push_back(VoidTypeReg);
} else {
- for (DIType *Elem : TA) {
- auto OptReg = mapDISignatureTypeToReg(Elem, VoidTypeReg);
+ for (unsigned I = 0, E = TA.size(); I != E; ++I) {
+ bool IsReturnType = I == 0;
+ auto OptReg = mapDISignatureTypeToReg(TA[I], VoidTypeReg, IsReturnType);
// No emitted DebugType* id for this slot (e.g., pointer that
// was skipped due missing address space, etc.).
if (!OptReg)
@@ -424,11 +425,15 @@ SPIRVNonSemanticDebugHandler::emitDebugTypeFunctionForSubroutineType(
ExtInstSetReg, Ops, MAI);
}
-std::optional<MCRegister>
-SPIRVNonSemanticDebugHandler::mapDISignatureTypeToReg(const DIType *Ty,
- MCRegister VoidTypeReg) {
- if (!Ty)
- return VoidTypeReg;
+std::optional<MCRegister> SPIRVNonSemanticDebugHandler::mapDISignatureTypeToReg(
+ const DIType *Ty, MCRegister VoidTypeReg, bool ReturnType) {
+ if (!Ty) {
+ if (ReturnType)
+ return VoidTypeReg;
+ assert(CachedDebugInfoNoneReg.isValid() &&
+ "DebugInfoNone must be emitted before DISubroutineType operands");
+ return CachedDebugInfoNoneReg;
+ }
auto It = DebugTypeRegs.find(Ty);
if (It != DebugTypeRegs.end())
return It->second;
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index 57704d64e8158..91e8bcde43679 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -203,9 +203,20 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
MCRegister ExtInstSetReg,
SPIRV::ModuleAnalysisInfo &MAI);
- /// Map DISubroutineType slot DI types using DebugTypeRegs.
+ /// Map a \c DISubroutineType::getTypeArray() element to an operand register
+ /// for
+ /// \c DebugTypeFunction. Non-null \p Ty resolves via \c DebugTypeRegs; if the
+ /// type was never emitted, returns \c std::nullopt.
+ ///
+ /// LLVM encodes a void return as a null first element (and may use null in
+ /// later slots). NonSemantic \c DebugTypeFunction
+ /// requires a concrete return-type operand, so when \p ReturnType is true and
+ /// \p Ty is null, this returns \p VoidTypeReg (\c OpTypeVoid). When
+ /// \p ReturnType is false and \p Ty is null, this returns
+ /// \c CachedDebugInfoNoneReg (\c DebugInfoNone).
std::optional<MCRegister> mapDISignatureTypeToReg(const DIType *Ty,
- MCRegister VoidTypeReg);
+ MCRegister VoidTypeReg,
+ bool ReturnType);
/// Map a DWARF source language code to a NonSemantic.Shader.DebugInfo.100
/// source language code.
>From 072f27425c77991cf3c2c01ed17fc888f9de953c Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 14:33:30 -0500
Subject: [PATCH 11/21] [reviews] Simplify code and add missing test.
---
.../SPIRV/SPIRVNonSemanticDebugHandler.cpp | 20 +++++-----
.../debug-type-pointer-composite-pointee.ll | 38 +++++++++++++++++++
2 files changed, 47 insertions(+), 11 deletions(-)
create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-type-pointer-composite-pointee.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index b488326d0c6ba..61601cbb1af40 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -380,18 +380,16 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugTypePointer(
SPIRV::NonSemanticExtInst::DebugTypePointer, VoidTypeReg,
ExtInstSetReg,
{BaseIt->second, StorageClassReg, DebugTypePointerFlagsReg}, MAI);
- } else {
- // No getBaseType() (typical for void*): use DebugInfoNone as Base Type,
- // same as SPIRV-LLVM-Translator (see issue #109287 and the DISABLED
- // spirv-val run in debug-type-pointer.ll). spirv-val may still reject this
- // encoding; see https://github.com/KhronosGroup/SPIRV-Registry/pull/287.
- return emitExtInst(
- SPIRV::NonSemanticExtInst::DebugTypePointer, VoidTypeReg, ExtInstSetReg,
- {CachedDebugInfoNoneReg, StorageClassReg, DebugTypePointerFlagsReg},
- MAI);
+ // Unsupported type, no DebugType* id available.
+ return std::nullopt;
}
-
- return std::nullopt;
+ // No getBaseType() (typical for void*): use DebugInfoNone as Base Type,
+ // same as SPIRV-LLVM-Translator (see issue #109287 and the DISABLED
+ // spirv-val run in debug-type-pointer.ll). spirv-val may still reject this
+ // encoding; see https://github.com/KhronosGroup/SPIRV-Registry/pull/287.
+ return emitExtInst(
+ SPIRV::NonSemanticExtInst::DebugTypePointer, VoidTypeReg, ExtInstSetReg,
+ {CachedDebugInfoNoneReg, StorageClassReg, DebugTypePointerFlagsReg}, MAI);
}
std::optional<MCRegister>
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-pointer-composite-pointee.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-pointer-composite-pointee.ll
new file mode 100644
index 0000000000000..ce0a45452f6e4
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-pointer-composite-pointee.ll
@@ -0,0 +1,38 @@
+; 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 %}
+
+; DW_TAG_array_type composite not yet supported
+
+; CHECK: OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK: OpExtInst {{.*}} DebugCompilationUnit
+; CHECK-NOT: DebugTypePointer
+
+define spir_func void @ptr_to_array() !dbg !10 {
+entry:
+ %p = alloca ptr addrspace(4), align 8
+ #dbg_declare(ptr %p, !11, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !14)
+ 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)
+!1 = !DIFile(filename: "debug-type-pointer-composite-pointee.c", directory: "/src", 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 = !{null}
+!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!9 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 256, elements: !12)
+!12 = !{!13}
+!13 = !DISubrange(count: 8)
+
+!10 = distinct !DISubprogram(name: "ptr_to_array", linkageName: "ptr_to_array", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !15)
+!15 = !{}
+!11 = !DILocalVariable(name: "pa", scope: !10, file: !1, line: 2, type: !16)
+!16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !9, size: 64, dwarfAddressSpace: 4)
+!14 = !DILocation(line: 2, column: 5, scope: !10)
>From f7d08e920ed2752ed53c23b2586d74cf2b4198bb Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 14:45:20 -0500
Subject: [PATCH 12/21] [reviews] Simplify tests.
---
.../debug-type-function-int-string-dedup.ll | 14 +++++++-------
.../debug-type-function-multi-scalar-params.ll | 2 +-
.../SPIRV/debug-info/debug-type-function-omit.ll | 2 +-
.../debug-type-function-pointer-debug-none-base.ll | 2 +-
.../debug-type-function-pointer-param.ll | 14 +++++++-------
.../debug-type-function-scalar-returns.ll | 14 +++++++-------
.../debug-type-function-subroutine-type-flags.ll | 2 +-
.../debug-type-function-void-prototypes.ll | 12 ++++++------
8 files changed, 31 insertions(+), 31 deletions(-)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-int-string-dedup.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-int-string-dedup.ll
index 1f67a7001fd5d..c28e2b062dfcc 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-int-string-dedup.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-int-string-dedup.ll
@@ -1,14 +1,14 @@
-; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; 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 %}
; Two distinct !DIBasicType nodes named "int" share one OpString "int".
-; CHECK-SPIRV: [[ext_inst_non_semantic:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
-; CHECK-SPIRV-COUNT-1: OpString "int"
-; CHECK-SPIRV-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
-; CHECK-SPIRV-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
-; CHECK-SPIRV-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
-; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeFunction [[flag_zero]]
+; CHECK: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-COUNT-1: OpString "int"
+; CHECK-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-DAG: OpExtInst [[type_void]] [[ext]] DebugTypeFunction [[flag_zero]]
target triple = "spirv64-unknown-unknown"
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-multi-scalar-params.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-multi-scalar-params.ll
index ab5fc00c0f978..a49eaede1f374 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-multi-scalar-params.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-multi-scalar-params.ll
@@ -1,4 +1,4 @@
-; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK
+; 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 %}
;
; Multi-slot signature mapping order: DebugTypeFunction must keep operand order
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-omit.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-omit.ll
index 62d2e8afd51e5..c49af8964e0f0 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-omit.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-omit.ll
@@ -1,4 +1,4 @@
-; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --implicit-check-not=DebugTypeFunction --check-prefix=CHECK
+; 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 %}
; Unsupported composite parameter and pointer without dwarfAddressSpace — DISubroutineType not lowered to DebugTypeFunction.
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-debug-none-base.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-debug-none-base.ll
index 9e7d148e40c15..3eb3774a317af 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-debug-none-base.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-debug-none-base.ll
@@ -1,4 +1,4 @@
-; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
; TODO(#109287): spirv-val coverage remains disabled for DebugTypePointer with
; DebugInfoNone as the base type.
;
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-param.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-param.ll
index 854b73ebed017..808d90efec896 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-param.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-param.ll
@@ -1,14 +1,14 @@
-; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; 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 %}
; void (*)(addrspace(4) i32*) — DebugTypePointer + DebugTypeFunction includes the pointer parameter operand.
-; CHECK-SPIRV: [[ext_inst_non_semantic:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
-; CHECK-SPIRV-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
-; CHECK-SPIRV-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
-; CHECK-SPIRV-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
-; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypePointer
-; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeFunction [[flag_zero]] [[type_void]] {{%[0-9]+}}
+; CHECK: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-DAG: OpExtInst [[type_void]] [[ext]] DebugTypePointer
+; CHECK-DAG: OpExtInst [[type_void]] [[ext]] DebugTypeFunction [[flag_zero]] [[type_void]] {{%[0-9]+}}
target triple = "spirv64-unknown-unknown"
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-scalar-returns.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-scalar-returns.ll
index 6770af122b35b..37e002a21f2ab 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-scalar-returns.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-scalar-returns.ll
@@ -1,12 +1,12 @@
-; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
-; CHECK-SPIRV: [[ext_inst_non_semantic:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
-; CHECK-SPIRV-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
-; CHECK-SPIRV-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
-; CHECK-SPIRV-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
-; CHECK-SPIRV-DAG: OpString "float"
-; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeFunction [[flag_zero]]
+; CHECK: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-DAG: OpString "float"
+; CHECK-DAG: OpExtInst [[type_void]] [[ext]] DebugTypeFunction [[flag_zero]]
target triple = "spirv64-unknown-unknown"
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-subroutine-type-flags.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-subroutine-type-flags.ll
index 9ef42724a42ea..b6d6b429bbd72 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-subroutine-type-flags.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-subroutine-type-flags.ll
@@ -1,4 +1,4 @@
-; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK
+; 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 %}
; Test the encoding of the flags in the DISubroutineType
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll
index c46dc74e03ef9..13e31d3af057f 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll
@@ -1,14 +1,14 @@
-; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; 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 %}
; Void function types in LLVM metadata may be written as an empty type list (!{}) or with an explicit
; null return slot (!{null}); both should produce the same SPIR-V DebugTypeFunction (flags + void only).
-; CHECK-SPIRV: [[ext_inst_non_semantic:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
-; CHECK-SPIRV-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
-; CHECK-SPIRV-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
-; CHECK-SPIRV-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
-; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeFunction [[flag_zero]] [[type_void]]
+; CHECK: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-DAG: OpExtInst [[type_void]] [[ext]] DebugTypeFunction [[flag_zero]] [[type_void]]
target triple = "spirv64-unknown-unknown"
>From 46f1d7ac94d0e560cee3bdf6f338fa55b9c832c9 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 29 May 2026 04:53:02 -0500
Subject: [PATCH 13/21] Improve comment.
---
llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 61601cbb1af40..9cee76b28b352 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -30,7 +30,7 @@ namespace {
/// Partition \p Ty into \p BasicTypes, \p PointerTypes, and \p SubroutineTypes
/// for NSDI emission. Used when iterating DebugInfoFinder.types(); each DI
/// node is seen once, so no recursion into pointer bases. Other composites and
-/// non-pointer derived kinds are ignored.
+/// non-pointer derived kinds are ignored because they are not yet supported.
static void
partitionTypes(const DIType *Ty, SmallVector<const DIBasicType *> &BasicTypes,
SmallVector<const DIDerivedType *> &PointerTypes,
>From 0971592362f8ed9a581115d45d44224d7508d4f7 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 29 May 2026 05:03:59 -0500
Subject: [PATCH 14/21] [reviews] Improve enum value names and add missing
ones.
---
.../SPIRV/SPIRVNonSemanticDebugHandler.cpp | 38 ++++++++++---------
1 file changed, 20 insertions(+), 18 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 9cee76b28b352..943012350714c 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -54,18 +54,20 @@ enum : uint32_t {
NSDIFlagIsPublic = NSDIFlagIsPrivate | NSDIFlagIsProtected,
NSDIFlagIsLocal = 1u << 2,
NSDIFlagIsDefinition = 1u << 3,
- NSDIFlagIsFwdDecl = 1u << 4,
- NSDIFlagIsArtificial = 1u << 5,
- NSDIFlagIsExplicit = 1u << 6,
- NSDIFlagIsPrototyped = 1u << 7,
- NSDIFlagIsObjectPointer = 1u << 8,
- NSDIFlagIsStaticMember = 1u << 9,
- NSDIFlagIsLValueReference = 1u << 11,
- NSDIFlagIsRValueReference = 1u << 12,
+ NSDIFlagFwdDecl = 1u << 4,
+ NSDIFlagArtificial = 1u << 5,
+ NSDIFlagExplicit = 1u << 6,
+ NSDIFlagPrototyped = 1u << 7,
+ NSDIFlagObjectPointer = 1u << 8,
+ NSDIFlagStaticMember = 1u << 9,
+ NSDIFlagIndirectVariable = 1u << 10,
+ NSDIFlagLValueReference = 1u << 11,
+ NSDIFlagRValueReference = 1u << 12,
NSDIFlagIsOptimized = 1u << 13,
NSDIFlagIsEnumClass = 1u << 14,
NSDIFlagTypePassByValue = 1u << 15,
NSDIFlagTypePassByReference = 1u << 16,
+ NSDIFlagUnknownPhysicalLayout = 1u << 17,
};
static uint32_t mapDIFlagsToNonSemantic(DINode::DIFlags DFlags) {
@@ -77,21 +79,21 @@ static uint32_t mapDIFlagsToNonSemantic(DINode::DIFlags DFlags) {
if ((DFlags & DINode::FlagAccessibility) == DINode::FlagPrivate)
Flags |= NSDIFlagIsPrivate;
if (DFlags & DINode::FlagFwdDecl)
- Flags |= NSDIFlagIsFwdDecl;
+ Flags |= NSDIFlagFwdDecl;
if (DFlags & DINode::FlagArtificial)
- Flags |= NSDIFlagIsArtificial;
+ Flags |= NSDIFlagArtificial;
if (DFlags & DINode::FlagExplicit)
- Flags |= NSDIFlagIsExplicit;
+ Flags |= NSDIFlagExplicit;
if (DFlags & DINode::FlagPrototyped)
- Flags |= NSDIFlagIsPrototyped;
+ Flags |= NSDIFlagPrototyped;
if (DFlags & DINode::FlagObjectPointer)
- Flags |= NSDIFlagIsObjectPointer;
+ Flags |= NSDIFlagObjectPointer;
if (DFlags & DINode::FlagStaticMember)
- Flags |= NSDIFlagIsStaticMember;
+ Flags |= NSDIFlagStaticMember;
if (DFlags & DINode::FlagLValueReference)
- Flags |= NSDIFlagIsLValueReference;
+ Flags |= NSDIFlagLValueReference;
if (DFlags & DINode::FlagRValueReference)
- Flags |= NSDIFlagIsRValueReference;
+ Flags |= NSDIFlagRValueReference;
if (DFlags & DINode::FlagTypePassByValue)
Flags |= NSDIFlagTypePassByValue;
if (DFlags & DINode::FlagTypePassByReference)
@@ -119,9 +121,9 @@ static uint32_t transDebugFlags(const DINode *DN) {
Flags |= mapDIFlagsToNonSemantic(SP->getFlags());
}
if (DN->getTag() == dwarf::DW_TAG_reference_type)
- Flags |= NSDIFlagIsLValueReference;
+ Flags |= NSDIFlagLValueReference;
if (DN->getTag() == dwarf::DW_TAG_rvalue_reference_type)
- Flags |= NSDIFlagIsRValueReference;
+ Flags |= NSDIFlagRValueReference;
if (const auto *Ty = dyn_cast<DIType>(DN))
Flags |= mapDIFlagsToNonSemantic(Ty->getFlags());
if (const auto *LV = dyn_cast<DILocalVariable>(DN))
>From 98f19137f425ef479f736dbf3e92efe6849dcc76 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 29 May 2026 05:38:39 -0500
Subject: [PATCH 15/21] [reviews] Deduplicate DebugTypeFunction by operand ids
Add getOrEmitDebugTypeFunction() to reuse identical DebugTypeFunction
instructions when distinct DISubroutineType nodes lower to the same
operands (e.g. !{} vs !{null} void prototypes). Update the lit test to
assert a single emission.
---
.../SPIRV/SPIRVNonSemanticDebugHandler.cpp | 19 +++++++++++++++++--
.../SPIRV/SPIRVNonSemanticDebugHandler.h | 11 +++++++++++
.../debug-type-function-void-prototypes.ll | 2 +-
3 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 943012350714c..bdc5b3787e10f 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -175,6 +175,7 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
DebugTypeRegs.clear();
OpStringContentCache.clear();
I32ConstantCache.clear();
+ DebugTypeFunctionCache.clear();
GlobalDIEmitted = false;
NonSemanticOpStringsSectionEmitted = false;
CachedDebugInfoNoneReg = MCRegister();
@@ -310,6 +311,21 @@ MCRegister SPIRVNonSemanticDebugHandler::emitExtInst(
return Reg;
}
+MCRegister SPIRVNonSemanticDebugHandler::getOrEmitDebugTypeFunction(
+ ArrayRef<MCRegister> Ops, MCRegister VoidTypeReg, MCRegister ExtInstSetReg,
+ SPIRV::ModuleAnalysisInfo &MAI) {
+ auto [It, Inserted] = DebugTypeFunctionCache.try_emplace(
+ SmallVector<MCRegister, 8>(Ops));
+ if (!Inserted)
+ return It->second;
+
+ MCRegister Reg = emitExtInst(
+ SPIRV::NonSemanticExtInst::DebugTypeFunction, VoidTypeReg, ExtInstSetReg,
+ Ops, MAI);
+ It->second = Reg;
+ return Reg;
+}
+
MCRegister SPIRVNonSemanticDebugHandler::getOrEmitOpTypeVoidReg(
SPIRV::ModuleAnalysisInfo &MAI) {
if (!CachedOpTypeVoidReg.isValid())
@@ -421,8 +437,7 @@ SPIRVNonSemanticDebugHandler::emitDebugTypeFunctionForSubroutineType(
Ops.push_back(*OptReg);
}
}
- return emitExtInst(SPIRV::NonSemanticExtInst::DebugTypeFunction, VoidTypeReg,
- ExtInstSetReg, Ops, MAI);
+ return getOrEmitDebugTypeFunction(Ops, VoidTypeReg, ExtInstSetReg, MAI);
}
std::optional<MCRegister> SPIRVNonSemanticDebugHandler::mapDISignatureTypeToReg(
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index 91e8bcde43679..8ab8031bc8ff7 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -89,6 +89,10 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
// duplicate OpConstant instructions for the same integer value.
DenseMap<uint32_t, MCRegister> I32ConstantCache;
+ // Cache of already-emitted DebugTypeFunction instructions, keyed by operand
+ // ids (flags, return type, parameters).
+ DenseMap<SmallVector<MCRegister, 8>, MCRegister> DebugTypeFunctionCache;
+
// True once emitNonSemanticGlobalDebugInfo() has run. Both
// SPIRVAsmPrinter::emitFunctionHeader() and emitEndOfAsmFile() may call
// outputModuleSections(), each guarded by ModuleSectionsEmitted, so only
@@ -168,6 +172,13 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
ArrayRef<MCRegister> Operands,
SPIRV::ModuleAnalysisInfo &MAI);
+ /// Return a cached DebugTypeFunction id when \p Ops matches a prior emission,
+ /// otherwise emit and cache a new instruction.
+ MCRegister getOrEmitDebugTypeFunction(ArrayRef<MCRegister> Ops,
+ MCRegister VoidTypeReg,
+ MCRegister ExtInstSetReg,
+ SPIRV::ModuleAnalysisInfo &MAI);
+
/// Return OpTypeVoid id for this module (lazy lookup / emit, then cache).
MCRegister getOrEmitOpTypeVoidReg(SPIRV::ModuleAnalysisInfo &MAI);
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll
index 13e31d3af057f..d9f41f4cf0bab 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll
@@ -8,7 +8,7 @@
; CHECK-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
; CHECK-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
; CHECK-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
-; CHECK-DAG: OpExtInst [[type_void]] [[ext]] DebugTypeFunction [[flag_zero]] [[type_void]]
+; CHECK-COUNT-1: OpExtInst [[type_void]] [[ext]] DebugTypeFunction [[flag_zero]] [[type_void]]
target triple = "spirv64-unknown-unknown"
>From 83cd9783ca3a9c60fbd3fa91635de911e3a3f70e Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 29 May 2026 07:19:05 -0500
Subject: [PATCH 16/21] [reviews] Improve assertions.
---
llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 10 ++++++++++
llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h | 2 ++
2 files changed, 12 insertions(+)
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index bdc5b3787e10f..38abd374d30b1 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -177,7 +177,9 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
I32ConstantCache.clear();
DebugTypeFunctionCache.clear();
GlobalDIEmitted = false;
+#ifndef NDEBUG
NonSemanticOpStringsSectionEmitted = false;
+#endif
CachedDebugInfoNoneReg = MCRegister();
CachedOpTypeVoidReg = MCRegister();
CachedOpTypeInt32Reg = MCRegister();
@@ -256,8 +258,10 @@ SPIRVNonSemanticDebugHandler::emitOpString(StringRef S,
MCRegister SPIRVNonSemanticDebugHandler::emitOpStringIfNew(
StringRef S, SPIRV::ModuleAnalysisInfo &MAI) {
+#ifndef NDEBUG
assert(!NonSemanticOpStringsSectionEmitted &&
"emitOpStringIfNew is only valid while emitting SPIR-V section 7");
+#endif
auto [It, Inserted] = OpStringContentCache.try_emplace(S, MCRegister());
if (!Inserted)
return It->second;
@@ -268,8 +272,10 @@ MCRegister SPIRVNonSemanticDebugHandler::emitOpStringIfNew(
}
MCRegister SPIRVNonSemanticDebugHandler::getCachedOpStringReg(StringRef S) {
+#ifndef NDEBUG
assert(NonSemanticOpStringsSectionEmitted &&
"getCachedOpStringReg requires emitNonSemanticDebugStrings() first");
+#endif
auto It = OpStringContentCache.find(S);
assert(It != OpStringContentCache.end() &&
"NSDI OpString missing from cache; emitNonSemanticDebugStrings must "
@@ -474,7 +480,9 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticDebugStrings(
for (const DIBasicType *BT : BasicTypes)
(void)emitOpStringIfNew(BT->getName(), MAI);
+#ifndef NDEBUG
NonSemanticOpStringsSectionEmitted = true;
+#endif
}
void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
@@ -490,9 +498,11 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
if (!ExtInstSetReg.isValid())
return; // Extension not available.
+#ifndef NDEBUG
assert(NonSemanticOpStringsSectionEmitted &&
"emitNonSemanticDebugStrings() must run before "
"emitNonSemanticGlobalDebugInfo()");
+#endif
MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI);
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index 8ab8031bc8ff7..fbf40d2a0e1ac 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -74,10 +74,12 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
// during section 7; section 10 uses getCachedOpStringReg() (lookup only).
StringMap<MCRegister> OpStringContentCache;
+#ifndef NDEBUG
// True after emitNonSemanticDebugStrings() emitted the NSDI OpStrings for
// this module. SPIRVAsmPrinter calls that before
// emitNonSemanticGlobalDebugInfo().
bool NonSemanticOpStringsSectionEmitted = false;
+#endif
MCRegister CachedDebugInfoNoneReg;
>From 33d3654e370f7f26644953ac58b91ab5a9eba58e Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 29 May 2026 07:28:41 -0500
Subject: [PATCH 17/21] Fix format.
---
llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 38abd374d30b1..60f97cbf07074 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -320,14 +320,13 @@ MCRegister SPIRVNonSemanticDebugHandler::emitExtInst(
MCRegister SPIRVNonSemanticDebugHandler::getOrEmitDebugTypeFunction(
ArrayRef<MCRegister> Ops, MCRegister VoidTypeReg, MCRegister ExtInstSetReg,
SPIRV::ModuleAnalysisInfo &MAI) {
- auto [It, Inserted] = DebugTypeFunctionCache.try_emplace(
- SmallVector<MCRegister, 8>(Ops));
+ auto [It, Inserted] =
+ DebugTypeFunctionCache.try_emplace(SmallVector<MCRegister, 8>(Ops));
if (!Inserted)
return It->second;
- MCRegister Reg = emitExtInst(
- SPIRV::NonSemanticExtInst::DebugTypeFunction, VoidTypeReg, ExtInstSetReg,
- Ops, MAI);
+ MCRegister Reg = emitExtInst(SPIRV::NonSemanticExtInst::DebugTypeFunction,
+ VoidTypeReg, ExtInstSetReg, Ops, MAI);
It->second = Reg;
return Reg;
}
>From 8b7f9918d881a6dda1526a90f3495dc6f867ce95 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 5 Jun 2026 05:38:07 -0500
Subject: [PATCH 18/21] Add comment.
---
llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 60f97cbf07074..e30abd26727e9 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -31,6 +31,7 @@ namespace {
/// for NSDI emission. Used when iterating DebugInfoFinder.types(); each DI
/// node is seen once, so no recursion into pointer bases. Other composites and
/// non-pointer derived kinds are ignored because they are not yet supported.
+/// Only types that are supported (later used) are partitioned.
static void
partitionTypes(const DIType *Ty, SmallVector<const DIBasicType *> &BasicTypes,
SmallVector<const DIDerivedType *> &PointerTypes,
>From a8f9f255bc19608b4042503b0228b6ed044bd32e Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 5 Jun 2026 05:39:59 -0500
Subject: [PATCH 19/21] Add comment.
---
llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index fbf40d2a0e1ac..3f586a8eb976b 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -74,7 +74,7 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
// during section 7; section 10 uses getCachedOpStringReg() (lookup only).
StringMap<MCRegister> OpStringContentCache;
-#ifndef NDEBUG
+#ifndef NDEBUG // Only declare the variable for debugging purposes.
// True after emitNonSemanticDebugStrings() emitted the NSDI OpStrings for
// this module. SPIRVAsmPrinter calls that before
// emitNonSemanticGlobalDebugInfo().
>From 52bfa6de567453c97145a397889bc3dae55720c8 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 5 Jun 2026 05:43:39 -0500
Subject: [PATCH 20/21] Use [[maybe_unused]].
---
llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index e30abd26727e9..5f45c5597e489 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -474,11 +474,13 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticDebugStrings(
if (!MAI.getExtInstSetReg(NSSet).isValid())
return;
- for (const CompileUnitInfo &Info : CompileUnits)
- (void)emitOpStringIfNew(Info.FilePath, MAI);
+ for (const CompileUnitInfo &Info : CompileUnits) {
+ [[maybe_unused]] MCRegister Reg = emitOpStringIfNew(Info.FilePath, MAI);
+ }
- for (const DIBasicType *BT : BasicTypes)
- (void)emitOpStringIfNew(BT->getName(), MAI);
+ for (const DIBasicType *BT : BasicTypes) {
+ [[maybe_unused]] MCRegister Reg = emitOpStringIfNew(BT->getName(), MAI);
+ }
#ifndef NDEBUG
NonSemanticOpStringsSectionEmitted = true;
>From a47854fe2f11a0285946e7cac5933f78a9e087e9 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 5 Jun 2026 05:51:40 -0500
Subject: [PATCH 21/21] Minor update.
---
llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 5f45c5597e489..06704702a1378 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -434,7 +434,7 @@ SPIRVNonSemanticDebugHandler::emitDebugTypeFunctionForSubroutineType(
Ops.push_back(VoidTypeReg);
} else {
for (unsigned I = 0, E = TA.size(); I != E; ++I) {
- bool IsReturnType = I == 0;
+ bool IsReturnType = (I == 0);
auto OptReg = mapDISignatureTypeToReg(TA[I], VoidTypeReg, IsReturnType);
// No emitted DebugType* id for this slot (e.g., pointer that
// was skipped due missing address space, etc.).
More information about the llvm-commits
mailing list