[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