[llvm-branch-commits] [llvm] c941964 - [SPIRV] Extend NSDI debug handling for DebugTypeFunction. (#197003)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Jun 5 09:27:27 PDT 2026
Author: Manuel Carrasco
Date: 2026-06-05T17:06:06+01:00
New Revision: c941964c58cf8ba2b65c1358f42576ab1eff67b0
URL: https://github.com/llvm/llvm-project/commit/c941964c58cf8ba2b65c1358f42576ab1eff67b0
DIFF: https://github.com/llvm/llvm-project/commit/c941964c58cf8ba2b65c1358f42576ab1eff67b0.diff
LOG: [SPIRV] Extend NSDI debug handling for DebugTypeFunction. (#197003)
Extend NSDI handling for
[DebugTypeFunction](https://github.khronos.org/SPIRV-Registry/nonsemantic/NonSemantic.Shader.DebugInfo.html#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.
Added:
llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-int-string-dedup.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-multi-scalar-params.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-omit.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-debug-none-base.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-param.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-scalar-returns.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-subroutine-type-flags.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll
llvm/test/CodeGen/SPIRV/debug-info/debug-type-pointer-composite-pointee.ll
Modified:
llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
Removed:
################################################################################
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index a6c647eddcd04..de2dc8d17a4ad 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -14,15 +14,126 @@
#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 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,
+ SmallVector<const DISubroutineType *> &SubroutineTypes) {
+ if (const auto *BT = dyn_cast<DIBasicType>(Ty)) {
+ BasicTypes.push_back(BT);
+ return;
+ }
+ if (const auto *ST = dyn_cast<DISubroutineType>(Ty)) {
+ SubroutineTypes.push_back(ST);
+ return;
+ }
+ const auto *DT = dyn_cast<DIDerivedType>(Ty);
+ if (DT && DT->getTag() == dwarf::DW_TAG_pointer_type)
+ PointerTypes.push_back(DT);
+}
+
+enum : uint32_t {
+ NSDIFlagIsProtected = 1u << 0,
+ NSDIFlagIsPrivate = 1u << 1,
+ NSDIFlagIsPublic = NSDIFlagIsPrivate | NSDIFlagIsProtected,
+ NSDIFlagIsLocal = 1u << 2,
+ NSDIFlagIsDefinition = 1u << 3,
+ 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) {
+ 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 |= NSDIFlagFwdDecl;
+ if (DFlags & DINode::FlagArtificial)
+ Flags |= NSDIFlagArtificial;
+ if (DFlags & DINode::FlagExplicit)
+ Flags |= NSDIFlagExplicit;
+ if (DFlags & DINode::FlagPrototyped)
+ Flags |= NSDIFlagPrototyped;
+ if (DFlags & DINode::FlagObjectPointer)
+ Flags |= NSDIFlagObjectPointer;
+ if (DFlags & DINode::FlagStaticMember)
+ Flags |= NSDIFlagStaticMember;
+ if (DFlags & DINode::FlagLValueReference)
+ Flags |= NSDIFlagLValueReference;
+ if (DFlags & DINode::FlagRValueReference)
+ Flags |= NSDIFlagRValueReference;
+ 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 |= NSDIFlagLValueReference;
+ if (DN->getTag() == dwarf::DW_TAG_rvalue_reference_type)
+ Flags |= NSDIFlagRValueReference;
+ 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 +169,22 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
if (!Asm)
return;
+ CompileUnits.clear();
+ BasicTypes.clear();
+ PointerTypes.clear();
+ SubroutineTypes.clear();
+ DebugTypeRegs.clear();
+ OpStringContentCache.clear();
+ I32ConstantCache.clear();
+ DebugTypeFunctionCache.clear();
+ GlobalDIEmitted = false;
+#ifndef NDEBUG
+ NonSemanticOpStringsSectionEmitted = false;
+#endif
+ 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 +214,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 +257,31 @@ SPIRVNonSemanticDebugHandler::emitOpString(StringRef S,
return Reg;
}
+void 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 = emitOpString(S, MAI);
+}
+
+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 "
+ "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 +316,34 @@ 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())
+ 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 +375,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 +395,69 @@ 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);
+ // Unsupported type, no DebugType* id available.
+ 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>
+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 (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)
+ return std::nullopt;
+ Ops.push_back(*OptReg);
+ }
}
+ return getOrEmitDebugTypeFunction(Ops, VoidTypeReg, ExtInstSetReg, MAI);
+}
+
+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;
+
+ return std::nullopt;
}
void SPIRVNonSemanticDebugHandler::emitNonSemanticDebugStrings(
@@ -258,10 +473,14 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticDebugStrings(
return;
for (const CompileUnitInfo &Info : CompileUnits)
- FileStringRegs.push_back(emitOpString(Info.FilePath, MAI));
+ emitOpStringIfNew(Info.FilePath, MAI);
for (const DIBasicType *BT : BasicTypes)
- BasicTypeNameRegs.push_back(emitOpString(BT->getName(), MAI));
+ emitOpStringIfNew(BT->getName(), MAI);
+
+#ifndef NDEBUG
+ NonSemanticOpStringsSectionEmitted = true;
+#endif
}
void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
@@ -277,8 +496,17 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
if (!ExtInstSetReg.isValid())
return; // Extension not available.
- MCRegister VoidTypeReg = findOrEmitOpTypeVoid(MAI);
- MCRegister I32TypeReg = findOrEmitOpTypeInt32(MAI);
+#ifndef NDEBUG
+ assert(NonSemanticOpStringsSectionEmitted &&
+ "emitNonSemanticDebugStrings() must run before "
+ "emitNonSemanticGlobalDebugInfo()");
+#endif
+
+ 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 +530,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 +546,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 +584,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..b07e3f23a71af 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -21,13 +21,14 @@
#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"
#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 +41,10 @@ class SPIRVSubtarget;
/// Call sequence:
/// beginModule() -- collect compile-unit metadata.
/// prepareModuleOutput() -- add extension + ext inst set to MAI.
+/// emitNonSemanticDebugStrings() -- OpString for NSDI strings (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,21 +60,40 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
SmallVector<CompileUnitInfo> CompileUnits;
int64_t DwarfVersion = 0;
- // Types referenced by debug variable records, collected in beginModule().
- SetVector<const DIBasicType *> BasicTypes;
- SetVector<const DIDerivedType *> PointerTypes;
+ // DI types partitioned from DebugInfoFinder.types() in beginModule()
+ // (basics, pointers, subroutine types NSDI v1 may emit).
+ SmallVector<const DIBasicType *> BasicTypes;
+ SmallVector<const DIDerivedType *> PointerTypes;
+ SmallVector<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;
+
+#ifndef NDEBUG // Only declare the variable for debugging purposes.
+ // True after emitNonSemanticDebugStrings() emitted the NSDI OpStrings for
+ // this module. SPIRVAsmPrinter calls that before
+ // emitNonSemanticGlobalDebugInfo().
+ bool NonSemanticOpStringsSectionEmitted = false;
+#endif
+
+ 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
+ // 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
@@ -91,9 +112,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 +125,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 +159,14 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
private:
void emitMCInst(MCInst &Inst);
MCRegister emitOpString(StringRef S, SPIRV::ModuleAnalysisInfo &MAI);
+
+ /// Section 7 only: emit OpString and cache it if not already present. Must
+ /// not be called after NonSemanticOpStringsSectionEmitted is set.
+ void emitOpStringIfNew(StringRef S, SPIRV::ModuleAnalysisInfo &MAI);
+
+ /// 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 +174,19 @@ 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);
+
+ /// 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 +195,41 @@ 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 \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);
+
+ /// 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 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,
+ bool ReturnType);
/// 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..c28e2b062dfcc
--- /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
+; 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: [[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"
+
+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..a49eaede1f374
--- /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
+; 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..c49af8964e0f0
--- /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
+; 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..3eb3774a317af
--- /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
+; 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..808d90efec896
--- /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
+; 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: [[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"
+
+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..37e002a21f2ab
--- /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
+; 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: [[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"
+
+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..b6d6b429bbd72
--- /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
+; 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..d9f41f4cf0bab
--- /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
+; 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: [[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-COUNT-1: OpExtInst [[type_void]] [[ext]] 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)
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)
More information about the llvm-branch-commits
mailing list