[llvm] [SPIRV] Extend NSDI debug handling for DebugTypeFunction. (PR #197003)

Manuel Carrasco via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 5 06:45:37 PDT 2026


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

>From aaabe242cf31b08c7e624975b21eed1def525f27 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Mon, 11 May 2026 12:14:16 -0500
Subject: [PATCH 01/21] [SPIRV] Extend NSDI debug handling for
 DebugTypeFunction.

Changes:

- Collect required types with DebugInfoFinder instead of walking DbgVariableRecords only. This allows processing types that might not be available in these records, and avoids code duplication for the traversals.

- Emit DebugTypeFunction for DISubroutineType when every signature slot maps to an
emitted debug type.

- Reset per-module state in beginModule().

- Replace parallel FileStringRegs/BasicTypeNameRegs with a StringMap OpString
cache, deduplicating identical strings.

- Add LLVM DINode flag lowering for NSDI.

- Cache DebugInfoNone, OpTypeVoid and OpTypeInt32 registers to avoid duplicate instructions.
---
 .../SPIRV/SPIRVNonSemanticDebugHandler.cpp    | 323 ++++++++++++++----
 .../SPIRV/SPIRVNonSemanticDebugHandler.h      |  93 +++--
 .../debug-type-function-int-string-dedup.ll   |  44 +++
 ...debug-type-function-multi-scalar-params.ll |  39 +++
 .../debug-info/debug-type-function-omit.ll    |  44 +++
 ...g-type-function-pointer-debug-none-base.ll |  38 +++
 .../debug-type-function-pointer-param.ll      |  35 ++
 .../debug-type-function-scalar-returns.ll     |  32 ++
 ...bug-type-function-subroutine-type-flags.ll |  36 ++
 .../debug-type-function-void-prototypes.ll    |  42 +++
 10 files changed, 635 insertions(+), 91 deletions(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-int-string-dedup.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-multi-scalar-params.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-omit.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-debug-none-base.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-param.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-scalar-returns.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-subroutine-type-flags.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index a6c647eddcd04..a25368863e1a7 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -14,15 +14,123 @@
 #include "llvm/ADT/SmallVectorExtras.h"
 #include "llvm/BinaryFormat/Dwarf.h"
 #include "llvm/CodeGen/AsmPrinter.h"
+#include "llvm/IR/DebugInfo.h"
 #include "llvm/IR/DebugInfoMetadata.h"
-#include "llvm/IR/DebugProgramInstruction.h"
 #include "llvm/IR/Module.h"
 #include "llvm/MC/MCInst.h"
 #include "llvm/MC/MCStreamer.h"
+#include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/Path.h"
+#include <cassert>
 
 using namespace llvm;
 
+namespace {
+
+/// Partition \p Ty into \p BasicTypes, \p PointerTypes, and \p SubroutineTypes
+/// for NSDI emission. Used when iterating DebugInfoFinder.types(); each DI
+/// node is seen once, so no recursion into pointer bases. Other composites and
+/// non-pointer derived kinds are ignored.
+static void
+partitionTypes(const DIType *Ty, SetVector<const DIBasicType *> &BasicTypes,
+               SetVector<const DIDerivedType *> &PointerTypes,
+               SetVector<const DISubroutineType *> &SubroutineTypes) {
+  if (const auto *BT = dyn_cast<DIBasicType>(Ty)) {
+    BasicTypes.insert(BT);
+    return;
+  }
+  if (const auto *ST = dyn_cast<DISubroutineType>(Ty)) {
+    SubroutineTypes.insert(ST);
+    return;
+  }
+  const auto *DT = dyn_cast<DIDerivedType>(Ty);
+  if (DT && DT->getTag() == dwarf::DW_TAG_pointer_type)
+    PointerTypes.insert(DT);
+}
+
+enum : uint32_t {
+  NSDIFlagIsProtected = 1u << 0,
+  NSDIFlagIsPrivate = 1u << 1,
+  NSDIFlagIsPublic = NSDIFlagIsPrivate | NSDIFlagIsProtected,
+  NSDIFlagIsLocal = 1u << 2,
+  NSDIFlagIsDefinition = 1u << 3,
+  NSDIFlagIsFwdDecl = 1u << 4,
+  NSDIFlagIsArtificial = 1u << 5,
+  NSDIFlagIsExplicit = 1u << 6,
+  NSDIFlagIsPrototyped = 1u << 7,
+  NSDIFlagIsObjectPointer = 1u << 8,
+  NSDIFlagIsStaticMember = 1u << 9,
+  NSDIFlagIsLValueReference = 1u << 11,
+  NSDIFlagIsRValueReference = 1u << 12,
+  NSDIFlagIsOptimized = 1u << 13,
+  NSDIFlagIsEnumClass = 1u << 14,
+  NSDIFlagTypePassByValue = 1u << 15,
+  NSDIFlagTypePassByReference = 1u << 16,
+};
+
+static uint32_t mapDIFlagsToNonSemantic(DINode::DIFlags DFlags) {
+  uint32_t Flags = 0;
+  if ((DFlags & DINode::FlagAccessibility) == DINode::FlagPublic)
+    Flags |= NSDIFlagIsPublic;
+  if ((DFlags & DINode::FlagAccessibility) == DINode::FlagProtected)
+    Flags |= NSDIFlagIsProtected;
+  if ((DFlags & DINode::FlagAccessibility) == DINode::FlagPrivate)
+    Flags |= NSDIFlagIsPrivate;
+  if (DFlags & DINode::FlagFwdDecl)
+    Flags |= NSDIFlagIsFwdDecl;
+  if (DFlags & DINode::FlagArtificial)
+    Flags |= NSDIFlagIsArtificial;
+  if (DFlags & DINode::FlagExplicit)
+    Flags |= NSDIFlagIsExplicit;
+  if (DFlags & DINode::FlagPrototyped)
+    Flags |= NSDIFlagIsPrototyped;
+  if (DFlags & DINode::FlagObjectPointer)
+    Flags |= NSDIFlagIsObjectPointer;
+  if (DFlags & DINode::FlagStaticMember)
+    Flags |= NSDIFlagIsStaticMember;
+  if (DFlags & DINode::FlagLValueReference)
+    Flags |= NSDIFlagIsLValueReference;
+  if (DFlags & DINode::FlagRValueReference)
+    Flags |= NSDIFlagIsRValueReference;
+  if (DFlags & DINode::FlagTypePassByValue)
+    Flags |= NSDIFlagTypePassByValue;
+  if (DFlags & DINode::FlagTypePassByReference)
+    Flags |= NSDIFlagTypePassByReference;
+  if (DFlags & DINode::FlagEnumClass)
+    Flags |= NSDIFlagIsEnumClass;
+  return Flags;
+}
+
+static uint32_t transDebugFlags(const DINode *DN) {
+  uint32_t Flags = 0;
+  if (const auto *GV = dyn_cast<DIGlobalVariable>(DN)) {
+    if (GV->isLocalToUnit())
+      Flags |= NSDIFlagIsLocal;
+    if (GV->isDefinition())
+      Flags |= NSDIFlagIsDefinition;
+  }
+  if (const auto *SP = dyn_cast<DISubprogram>(DN)) {
+    if (SP->isLocalToUnit())
+      Flags |= NSDIFlagIsLocal;
+    if (SP->isOptimized())
+      Flags |= NSDIFlagIsOptimized;
+    if (SP->isDefinition())
+      Flags |= NSDIFlagIsDefinition;
+    Flags |= mapDIFlagsToNonSemantic(SP->getFlags());
+  }
+  if (DN->getTag() == dwarf::DW_TAG_reference_type)
+    Flags |= NSDIFlagIsLValueReference;
+  if (DN->getTag() == dwarf::DW_TAG_rvalue_reference_type)
+    Flags |= NSDIFlagIsRValueReference;
+  if (const auto *Ty = dyn_cast<DIType>(DN))
+    Flags |= mapDIFlagsToNonSemantic(Ty->getFlags());
+  if (const auto *LV = dyn_cast<DILocalVariable>(DN))
+    Flags |= mapDIFlagsToNonSemantic(LV->getFlags());
+  return Flags;
+}
+
+} // namespace
+
 SPIRVNonSemanticDebugHandler::SPIRVNonSemanticDebugHandler(AsmPrinter &AP)
     : DebugHandlerBase(&AP) {}
 
@@ -58,6 +166,17 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
   if (!Asm)
     return;
 
+  CompileUnits.clear();
+  BasicTypes.clear();
+  PointerTypes.clear();
+  SubroutineTypes.clear();
+  DebugTypeRegs.clear();
+  OpStringContentCache.clear();
+  NonSemanticOpStringsSectionEmitted = false;
+  CachedDebugInfoNoneReg = MCRegister();
+  CachedOpTypeVoidReg = MCRegister();
+  CachedOpTypeInt32Reg = MCRegister();
+
   // Collect compile-unit info: file paths and source languages.
   for (const DICompileUnit *CU : M->debug_compile_units()) {
     const DIFile *File = CU->getFile();
@@ -87,27 +206,12 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
     }
   }
 
-  // Collect basic and pointer types referenced by debug variable records.
-  for (const auto &F : *M) {
-    for (const auto &BB : F) {
-      for (const auto &I : BB) {
-        for (const DbgVariableRecord &DVR :
-             filterDbgVars(I.getDbgRecordRange())) {
-          const DIType *Ty = DVR.getVariable()->getType();
-          if (const auto *BT = dyn_cast<DIBasicType>(Ty)) {
-            BasicTypes.insert(BT);
-          } else if (const auto *DT = dyn_cast<DIDerivedType>(Ty)) {
-            if (DT->getTag() == dwarf::DW_TAG_pointer_type) {
-              PointerTypes.insert(DT);
-              if (const auto *BT =
-                      dyn_cast_or_null<DIBasicType>(DT->getBaseType()))
-                BasicTypes.insert(BT);
-            }
-          }
-        }
-      }
-    }
-  }
+  // Find all debug info types that may be referenced by NSDI instructions.
+  DebugInfoFinder Finder;
+  Finder.processModule(*M);
+  llvm::for_each(Finder.types(), [&](DIType *Ty) {
+    partitionTypes(Ty, BasicTypes, PointerTypes, SubroutineTypes);
+  });
 }
 
 void SPIRVNonSemanticDebugHandler::prepareModuleOutput(
@@ -145,6 +249,29 @@ SPIRVNonSemanticDebugHandler::emitOpString(StringRef S,
   return Reg;
 }
 
+MCRegister SPIRVNonSemanticDebugHandler::emitOpStringIfNew(
+    StringRef S, SPIRV::ModuleAnalysisInfo &MAI) {
+  assert(!NonSemanticOpStringsSectionEmitted &&
+         "emitOpStringIfNew is only valid while emitting SPIR-V section 7");
+  auto It = OpStringContentCache.find(S);
+  if (It != OpStringContentCache.end())
+    return It->second;
+  MCRegister Reg = emitOpString(S, MAI);
+  OpStringContentCache.insert(std::make_pair(S, Reg));
+  return Reg;
+}
+
+MCRegister SPIRVNonSemanticDebugHandler::getCachedOpStringReg(StringRef S) {
+  assert(NonSemanticOpStringsSectionEmitted &&
+         "getCachedOpStringReg requires emitNonSemanticDebugStrings() first");
+  auto It = OpStringContentCache.find(S);
+  if (It == OpStringContentCache.end())
+    report_fatal_error(
+        "NSDI OpString missing from cache; emitNonSemanticDebugStrings must "
+        "cache every string used in section 10");
+  return It->second;
+}
+
 MCRegister SPIRVNonSemanticDebugHandler::emitOpConstantI32(
     uint32_t Value, MCRegister I32TypeReg, SPIRV::ModuleAnalysisInfo &MAI) {
   auto [It, Inserted] = I32ConstantCache.try_emplace(Value);
@@ -179,6 +306,20 @@ MCRegister SPIRVNonSemanticDebugHandler::emitExtInst(
   return Reg;
 }
 
+MCRegister SPIRVNonSemanticDebugHandler::getOrEmitOpTypeVoidReg(
+    SPIRV::ModuleAnalysisInfo &MAI) {
+  if (!CachedOpTypeVoidReg.isValid())
+    CachedOpTypeVoidReg = findOrEmitOpTypeVoid(MAI);
+  return CachedOpTypeVoidReg;
+}
+
+MCRegister SPIRVNonSemanticDebugHandler::getOrEmitOpTypeInt32Reg(
+    SPIRV::ModuleAnalysisInfo &MAI) {
+  if (!CachedOpTypeInt32Reg.isValid())
+    CachedOpTypeInt32Reg = findOrEmitOpTypeInt32(MAI);
+  return CachedOpTypeInt32Reg;
+}
+
 MCRegister SPIRVNonSemanticDebugHandler::findOrEmitOpTypeVoid(
     SPIRV::ModuleAnalysisInfo &MAI) {
   for (const MachineInstr *MI : MAI.getMSInstrs(SPIRV::MB_TypeConstVars)) {
@@ -210,15 +351,18 @@ MCRegister SPIRVNonSemanticDebugHandler::findOrEmitOpTypeInt32(
   return Reg;
 }
 
-void SPIRVNonSemanticDebugHandler::emitDebugTypePointer(
-    const DIDerivedType *PT, MCRegister VoidTypeReg, MCRegister I32TypeReg,
-    MCRegister ExtInstSetReg, MCRegister I32ZeroReg,
-    const DenseMap<const DIBasicType *, MCRegister> &BasicTypeRegs,
+std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugTypePointer(
+    const DIDerivedType *PT, MCRegister ExtInstSetReg,
     SPIRV::ModuleAnalysisInfo &MAI) {
   // A DWARF address space is required to determine the SPIR-V storage class.
   // Skip pointer types that do not carry one.
   if (!PT->getDWARFAddressSpace().has_value())
-    return;
+    return std::nullopt;
+
+  MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
+  MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI);
+  MCRegister DebugTypePointerFlagsReg =
+      emitOpConstantI32(transDebugFlags(PT), I32TypeReg, MAI);
 
   // For SPIR-V targets, Clang sets DwarfAddressSpace to the LLVM IR address
   // space, which addressSpaceToStorageClass expects.
@@ -227,22 +371,67 @@ void SPIRVNonSemanticDebugHandler::emitDebugTypePointer(
       addressSpaceToStorageClass(PT->getDWARFAddressSpace().value(), ST),
       I32TypeReg, MAI);
 
-  if (const auto *BaseType = dyn_cast_or_null<DIBasicType>(PT->getBaseType())) {
-    auto BTIt = BasicTypeRegs.find(BaseType);
-    if (BTIt != BasicTypeRegs.end())
-      emitExtInst(SPIRV::NonSemanticExtInst::DebugTypePointer, VoidTypeReg,
-                  ExtInstSetReg, {BTIt->second, StorageClassReg, I32ZeroReg},
-                  MAI);
+  if (const DIType *BaseTy = PT->getBaseType()) {
+    auto BaseIt = DebugTypeRegs.find(BaseTy);
+    if (BaseIt != DebugTypeRegs.end())
+      return emitExtInst(
+          SPIRV::NonSemanticExtInst::DebugTypePointer, VoidTypeReg,
+          ExtInstSetReg,
+          {BaseIt->second, StorageClassReg, DebugTypePointerFlagsReg}, MAI);
+  } else {
+    // No getBaseType() (typical for void*): use DebugInfoNone as Base Type,
+    // same as SPIRV-LLVM-Translator (see issue #109287 and the DISABLED
+    // spirv-val run in debug-type-pointer.ll). spirv-val may still reject this
+    // encoding; see https://github.com/KhronosGroup/SPIRV-Registry/pull/287.
+    return emitExtInst(
+        SPIRV::NonSemanticExtInst::DebugTypePointer, VoidTypeReg, ExtInstSetReg,
+        {CachedDebugInfoNoneReg, StorageClassReg, DebugTypePointerFlagsReg},
+        MAI);
+  }
+
+  return std::nullopt;
+}
+
+std::optional<MCRegister>
+SPIRVNonSemanticDebugHandler::emitDebugTypeFunctionForSubroutineType(
+    const DISubroutineType *ST, MCRegister ExtInstSetReg,
+    SPIRV::ModuleAnalysisInfo &MAI) {
+  MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
+  MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI);
+  MCRegister DebugTypeFunctionFlagsReg =
+      emitOpConstantI32(transDebugFlags(ST), I32TypeReg, MAI);
+  DITypeArray TA = ST->getTypeArray();
+  SmallVector<MCRegister, 8> Ops;
+  Ops.push_back(DebugTypeFunctionFlagsReg);
+  // Empty DI type tuple: no explicit return or parameter slots (hand-written IR
+  // may use !{}). Emit void-only prototype. Same as SPIRV-LLVM-Translator when
+  // DISubroutineType::getTypeArray() has zero elements.
+  if (TA.empty()) {
+    Ops.push_back(VoidTypeReg);
   } else {
-    // Void pointer: use DebugInfoNone for the base type. Note that
-    // spirv-val currently rejects DebugInfoNone as the base type of
-    // DebugTypePointer; see issue #109287 and the DISABLED spirv-val run
-    // in debug-type-pointer.ll.
-    MCRegister NoneReg = emitExtInst(SPIRV::NonSemanticExtInst::DebugInfoNone,
-                                     VoidTypeReg, ExtInstSetReg, {}, MAI);
-    emitExtInst(SPIRV::NonSemanticExtInst::DebugTypePointer, VoidTypeReg,
-                ExtInstSetReg, {NoneReg, StorageClassReg, I32ZeroReg}, MAI);
+    for (DIType *Elem : TA) {
+      auto OptReg = mapDISignatureTypeToReg(Elem, VoidTypeReg);
+      // No emitted DebugType* id for this slot (e.g., pointer that
+      // was skipped due missing address space, etc.).
+      if (!OptReg)
+        return std::nullopt;
+      Ops.push_back(*OptReg);
+    }
   }
+  return emitExtInst(SPIRV::NonSemanticExtInst::DebugTypeFunction, VoidTypeReg,
+                     ExtInstSetReg, Ops, MAI);
+}
+
+std::optional<MCRegister>
+SPIRVNonSemanticDebugHandler::mapDISignatureTypeToReg(const DIType *Ty,
+                                                      MCRegister VoidTypeReg) {
+  if (!Ty)
+    return VoidTypeReg;
+  auto It = DebugTypeRegs.find(Ty);
+  if (It != DebugTypeRegs.end())
+    return It->second;
+
+  return std::nullopt;
 }
 
 void SPIRVNonSemanticDebugHandler::emitNonSemanticDebugStrings(
@@ -258,10 +447,12 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticDebugStrings(
     return;
 
   for (const CompileUnitInfo &Info : CompileUnits)
-    FileStringRegs.push_back(emitOpString(Info.FilePath, MAI));
+    (void)emitOpStringIfNew(Info.FilePath, MAI);
 
   for (const DIBasicType *BT : BasicTypes)
-    BasicTypeNameRegs.push_back(emitOpString(BT->getName(), MAI));
+    (void)emitOpStringIfNew(BT->getName(), MAI);
+
+  NonSemanticOpStringsSectionEmitted = true;
 }
 
 void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
@@ -277,8 +468,15 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
   if (!ExtInstSetReg.isValid())
     return; // Extension not available.
 
-  MCRegister VoidTypeReg = findOrEmitOpTypeVoid(MAI);
-  MCRegister I32TypeReg = findOrEmitOpTypeInt32(MAI);
+  assert(NonSemanticOpStringsSectionEmitted &&
+         "emitNonSemanticDebugStrings() must run before "
+         "emitNonSemanticGlobalDebugInfo()");
+
+  MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
+  MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI);
+
+  CachedDebugInfoNoneReg = emitExtInst(SPIRV::NonSemanticExtInst::DebugInfoNone,
+                                       VoidTypeReg, ExtInstSetReg, {}, MAI);
 
   // Emit integer constants shared across all NSDI instructions. The constant
   // cache ensures each value is emitted at most once even when referenced from
@@ -302,11 +500,8 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
       });
 
   // Emit DebugSource and DebugCompilationUnit for each compile unit.
-  // FileStringRegs was populated by emitNonSemanticDebugStrings() in section 7.
-  assert(FileStringRegs.size() == CompileUnits.size() &&
-         "FileStringRegs must be populated by emitNonSemanticDebugStrings()");
-  for (auto [Info, FileStrReg, SrcLangReg] :
-       llvm::zip(CompileUnits, FileStringRegs, SrcLangRegs)) {
+  for (auto [Info, SrcLangReg] : llvm::zip(CompileUnits, SrcLangRegs)) {
+    MCRegister FileStrReg = getCachedOpStringReg(Info.FilePath);
     MCRegister DebugSourceReg =
         emitExtInst(SPIRV::NonSemanticExtInst::DebugSource, VoidTypeReg,
                     ExtInstSetReg, {FileStrReg}, MAI);
@@ -321,18 +516,10 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
   // DebugTypePointer. Cached with other i32 constants.
   MCRegister I32ZeroReg = emitOpConstantI32(0, I32TypeReg, MAI);
 
-  // Maps each DIBasicType to its DebugTypeBasic result register for use as
-  // operands in DebugTypePointer instructions.
-  DenseMap<const DIBasicType *, MCRegister> BasicTypeRegs;
+  DebugTypeRegs.clear();
 
-  // BasicTypeNameRegs was populated by emitNonSemanticDebugStrings() in
-  // section 7.
-  assert(
-      BasicTypeNameRegs.size() == BasicTypes.size() &&
-      "BasicTypeNameRegs must be populated by emitNonSemanticDebugStrings()");
-  unsigned BTIdx = 0;
   for (const DIBasicType *BT : BasicTypes) {
-    MCRegister NameReg = BasicTypeNameRegs[BTIdx++];
+    MCRegister NameReg = getCachedOpStringReg(BT->getName());
     MCRegister SizeReg = emitOpConstantI32(
         static_cast<uint32_t>(BT->getSizeInBits()), I32TypeReg, MAI);
 
@@ -367,11 +554,19 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
     MCRegister BTReg = emitExtInst(
         SPIRV::NonSemanticExtInst::DebugTypeBasic, VoidTypeReg, ExtInstSetReg,
         {NameReg, SizeReg, EncodingReg, I32ZeroReg}, MAI);
-    BasicTypeRegs[BT] = BTReg;
+    DebugTypeRegs[BT] = BTReg;
   }
 
   // Emit DebugTypePointer for each referenced pointer type.
-  for (const DIDerivedType *PT : PointerTypes)
-    emitDebugTypePointer(PT, VoidTypeReg, I32TypeReg, ExtInstSetReg, I32ZeroReg,
-                         BasicTypeRegs, MAI);
+  for (const DIDerivedType *PT : PointerTypes) {
+    if (auto PtrReg = emitDebugTypePointer(PT, ExtInstSetReg, MAI))
+      DebugTypeRegs[PT] = *PtrReg;
+  }
+
+  // Emit DebugTypeFunction for each distinct DISubroutineType.
+  for (const DISubroutineType *ST : SubroutineTypes) {
+    if (auto FnTyReg =
+            emitDebugTypeFunctionForSubroutineType(ST, ExtInstSetReg, MAI))
+      DebugTypeRegs[ST] = *FnTyReg;
+  }
 }
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index 7d8bac0c5ab5a..2e98f54760e29 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -24,10 +24,12 @@
 #include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
 #include "llvm/CodeGen/DebugHandlerBase.h"
 #include "llvm/IR/DebugInfoMetadata.h"
 #include "llvm/MC/MCInst.h"
 #include "llvm/MC/MCRegister.h"
+#include <optional>
 
 namespace llvm {
 
@@ -40,9 +42,10 @@ class SPIRVSubtarget;
 /// Call sequence:
 ///   beginModule()                    -- collect compile-unit metadata.
 ///   prepareModuleOutput()            -- add extension + ext inst set to MAI.
-///   emitNonSemanticGlobalDebugInfo() -- emit DebugSource,
+///   emitNonSemanticDebugStrings()    -- OpString for NSDI paths/names (sec.
+///   7). emitNonSemanticGlobalDebugInfo() -- emit DebugSource,
 ///                                       DebugCompilationUnit, DebugTypeBasic,
-///                                       DebugTypePointer.
+///                                       DebugTypePointer, DebugTypeFunction.
 ///   beginFunctionImpl()              -- no-op (no per-function DI yet).
 ///   endFunctionImpl()                -- no-op.
 class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
@@ -58,22 +61,35 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   SmallVector<CompileUnitInfo> CompileUnits;
   int64_t DwarfVersion = 0;
 
-  // Types referenced by debug variable records, collected in beginModule().
+  // DI types partitioned from DebugInfoFinder.types() in beginModule()
+  // (basics, pointers, subroutine types NSDI v1 may emit).
   SetVector<const DIBasicType *> BasicTypes;
   SetVector<const DIDerivedType *> PointerTypes;
+  SetVector<const DISubroutineType *> SubroutineTypes;
+
+  // Filled in emitNonSemanticGlobalDebugInfo(): DI types to their result
+  // registers.
+  DenseMap<const DIType *, MCRegister> DebugTypeRegs;
+
+  // Maps OpString contents to result id. Populated only by emitOpStringIfNew()
+  // during section 7; section 10 uses getCachedOpStringReg() (lookup only).
+  StringMap<MCRegister> OpStringContentCache;
+
+  // True after emitNonSemanticDebugStrings() emitted the NSDI OpStrings for
+  // this module. SPIRVAsmPrinter calls that before
+  // emitNonSemanticGlobalDebugInfo().
+  bool NonSemanticOpStringsSectionEmitted = false;
+
+  MCRegister CachedDebugInfoNoneReg;
+
+  MCRegister CachedOpTypeVoidReg;
+
+  MCRegister CachedOpTypeInt32Reg;
 
   // Cache of already-emitted i32 constants, keyed by value. Prevents
   // duplicate OpConstant instructions for the same integer value.
   DenseMap<uint32_t, MCRegister> I32ConstantCache;
 
-  // OpString registers for NSDI instructions, populated by
-  // emitNonSemanticDebugStrings() (section 7) and consumed by
-  // emitNonSemanticGlobalDebugInfo() (section 10). OpString must appear in
-  // section 7 per the SPIR-V module layout; it cannot be emitted alongside the
-  // OpExtInst instructions in section 10.
-  SmallVector<MCRegister> FileStringRegs;    // one per CompileUnits entry
-  SmallVector<MCRegister> BasicTypeNameRegs; // one per BasicTypes entry
-
   // True once emitNonSemanticGlobalDebugInfo() has run. Both
   // SPIRVAsmPrinter::emitFunctionHeader() and emitEndOfAsmFile() may call
   // outputModuleSections(), each guarded by ModuleSectionsEmitted, so only
@@ -91,9 +107,9 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   /// Emit OpString instructions for all NSDI file paths and basic type names
   /// into the debug section (section 7 of the SPIR-V module layout). Must be
   /// called from SPIRVAsmPrinter::outputDebugSourceAndStrings(), after
-  /// prepareModuleOutput() has registered the ext inst set. The resulting
-  /// registers are cached in FileStringRegs and BasicTypeNameRegs for use by
-  /// emitNonSemanticGlobalDebugInfo().
+  /// prepareModuleOutput() has registered the ext inst set. Registers are
+  /// stored in OpStringContentCache; emitNonSemanticGlobalDebugInfo() resolves
+  /// them via getCachedOpStringReg().
   void emitNonSemanticDebugStrings(SPIRV::ModuleAnalysisInfo &MAI);
 
   /// Add SPV_KHR_non_semantic_info extension and
@@ -104,9 +120,10 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
                            SPIRV::ModuleAnalysisInfo &MAI);
 
   /// Emit module-scope NSDI instructions (DebugSource, DebugCompilationUnit,
-  /// DebugTypeBasic, DebugTypePointer). Called by SPIRVAsmPrinter::
-  /// outputModuleSections() at section 10 in place of
-  /// outputModuleSection(MB_NonSemanticGlobalDI).
+  /// DebugTypeBasic, DebugTypePointer, DebugTypeFunction). Called by
+  /// SPIRVAsmPrinter::outputModuleSections() at section 10 in place of
+  /// outputModuleSection(MB_NonSemanticGlobalDI). Requires
+  /// emitNonSemanticDebugStrings() to have run first when NSDI strings apply.
   void emitNonSemanticGlobalDebugInfo(SPIRV::ModuleAnalysisInfo &MAI);
 
 protected:
@@ -137,6 +154,14 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
 private:
   void emitMCInst(MCInst &Inst);
   MCRegister emitOpString(StringRef S, SPIRV::ModuleAnalysisInfo &MAI);
+
+  /// Section 7 only: return cached id or emit OpString and cache it. Must not
+  /// be called after NonSemanticOpStringsSectionEmitted is set.
+  MCRegister emitOpStringIfNew(StringRef S, SPIRV::ModuleAnalysisInfo &MAI);
+
+  /// Section 10 only: lookup OpString id from cache; asserts if missing or if
+  /// section 7 did not complete.
+  MCRegister getCachedOpStringReg(StringRef S);
   MCRegister emitOpConstantI32(uint32_t Value, MCRegister I32TypeReg,
                                SPIRV::ModuleAnalysisInfo &MAI);
   MCRegister emitExtInst(SPIRV::NonSemanticExtInst::NonSemanticExtInst Opcode,
@@ -144,6 +169,12 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
                          ArrayRef<MCRegister> Operands,
                          SPIRV::ModuleAnalysisInfo &MAI);
 
+  /// Return OpTypeVoid id for this module (lazy lookup / emit, then cache).
+  MCRegister getOrEmitOpTypeVoidReg(SPIRV::ModuleAnalysisInfo &MAI);
+
+  /// Return OpTypeInt 32 0 id for this module (lazy lookup / emit, then cache).
+  MCRegister getOrEmitOpTypeInt32Reg(SPIRV::ModuleAnalysisInfo &MAI);
+
   /// Find OpTypeVoid in the already-emitted TypeConstVars section, or emit one
   /// if the module does not contain it (e.g. no void-returning functions).
   MCRegister findOrEmitOpTypeVoid(SPIRV::ModuleAnalysisInfo &MAI);
@@ -152,16 +183,24 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   /// one if the module does not contain it.
   MCRegister findOrEmitOpTypeInt32(SPIRV::ModuleAnalysisInfo &MAI);
 
-  /// Emit a DebugTypePointer instruction for PT. Skips pointer types that do
-  /// not carry a DWARF address space. For pointers whose base type is a
-  /// DIBasicType, looks up the base type's DebugTypeBasic register in
-  /// BasicTypeRegs. All other pointers (void pointers and pointers whose base
-  /// type is not a DIBasicType) use DebugInfoNone as the base type operand.
-  void emitDebugTypePointer(
-      const DIDerivedType *PT, MCRegister VoidTypeReg, MCRegister I32TypeReg,
-      MCRegister ExtInstSetReg, MCRegister I32ZeroReg,
-      const DenseMap<const DIBasicType *, MCRegister> &BasicTypeRegs,
-      SPIRV::ModuleAnalysisInfo &MAI);
+  /// Emit a DebugTypePointer instruction for PT. Returns std::nullopt when the
+  /// pointer is skipped (no DWARF address space). If the base DI type has an
+  /// entry in DebugTypeRegs, use it as the Base Type operand; otherwise use
+  /// DebugInfoNone.
+  std::optional<MCRegister>
+  emitDebugTypePointer(const DIDerivedType *PT, MCRegister ExtInstSetReg,
+                       SPIRV::ModuleAnalysisInfo &MAI);
+
+  /// Emit one DebugTypeFunction for ST when every DI operand maps to a debug
+  /// type id; otherwise emit nothing and return std::nullopt.
+  std::optional<MCRegister>
+  emitDebugTypeFunctionForSubroutineType(const DISubroutineType *ST,
+                                         MCRegister ExtInstSetReg,
+                                         SPIRV::ModuleAnalysisInfo &MAI);
+
+  /// Map DISubroutineType slot DI types using DebugTypeRegs.
+  std::optional<MCRegister> mapDISignatureTypeToReg(const DIType *Ty,
+                                                    MCRegister VoidTypeReg);
 
   /// Map a DWARF source language code to a NonSemantic.Shader.DebugInfo.100
   /// source language code.
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-int-string-dedup.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-int-string-dedup.ll
new file mode 100644
index 0000000000000..1f67a7001fd5d
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-int-string-dedup.ll
@@ -0,0 +1,44 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Two distinct !DIBasicType nodes named "int" share one OpString "int".
+
+; CHECK-SPIRV: [[ext_inst_non_semantic:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-SPIRV-COUNT-1: OpString "int"
+; CHECK-SPIRV-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-SPIRV-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-SPIRV-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeFunction [[flag_zero]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func i32 @dedupe_a() !dbg !10 {
+entry:
+  ret i32 0
+}
+
+define spir_func i32 @dedupe_b() !dbg !11 {
+entry:
+  ret i32 0
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version XX.X", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-type-function-int-string-dedup.c", directory: "/AAAAAAAAAA/BBBBBBBB/CCCCCCCCC", checksumkind: CSK_MD5, checksum: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 7, !"frame-pointer", i32 2}
+
+!6 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !7)
+!7 = !{!8}
+!8 = distinct !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!9 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !12)
+!12 = !{!13}
+!13 = distinct !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!10 = distinct !DISubprogram(name: "dedupe_a", linkageName: "dedupe_a", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!11 = distinct !DISubprogram(name: "dedupe_b", linkageName: "dedupe_b", scope: !1, file: !1, line: 2, type: !9, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-multi-scalar-params.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-multi-scalar-params.ll
new file mode 100644
index 0000000000000..ab5fc00c0f978
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-multi-scalar-params.ll
@@ -0,0 +1,39 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+;
+; Multi-slot signature mapping order: DebugTypeFunction must keep operand order
+; for void(int, float, int).
+
+; CHECK: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-DAG: [[str_int:%[0-9]+]] = OpString "int"
+; CHECK-DAG: [[str_float:%[0-9]+]] = OpString "float"
+; CHECK-DAG: [[dbg_int:%[0-9]+]] = OpExtInst [[type_void]] [[ext]] DebugTypeBasic [[str_int]]
+; CHECK-DAG: [[dbg_float:%[0-9]+]] = OpExtInst [[type_void]] [[ext]] DebugTypeBasic [[str_float]]
+; CHECK: OpExtInst [[type_void]] [[ext]] DebugTypeFunction [[flag_zero]] [[type_void]] [[dbg_int]] [[dbg_float]] [[dbg_int]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @multi_params(i32 %a, float %b, i32 %c) !dbg !10 {
+entry:
+  ret void
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version XX.X", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-type-function-multi-scalar-params.c", directory: "/AAAAAAAAAA/BBBBBBBB/CCCCCCCCC", checksumkind: CSK_MD5, checksum: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 7, !"frame-pointer", i32 2}
+
+!6 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !7)
+!7 = !{null, !8, !9, !8}
+!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!9 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float)
+
+!10 = distinct !DISubprogram(name: "multi_params", linkageName: "multi_params", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-omit.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-omit.ll
new file mode 100644
index 0000000000000..62d2e8afd51e5
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-omit.ll
@@ -0,0 +1,44 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --implicit-check-not=DebugTypeFunction --check-prefix=CHECK
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Unsupported composite parameter and pointer without dwarfAddressSpace — DISubroutineType not lowered to DebugTypeFunction.
+
+; CHECK: OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK: DebugCompilationUnit
+; CHECK-NOT: DebugTypePointer
+; CHECK-NOT: DebugTypeFunction
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @opaque_struct_param(i32 %x) !dbg !10 {
+entry:
+  ret void
+}
+
+define spir_func void @ptr_no_as(ptr %p) !dbg !11 {
+entry:
+  ret void
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version XX.X", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-type-function-omit.c", directory: "/AAAAAAAAAA/BBBBBBBB/CCCCCCCCC", checksumkind: CSK_MD5, checksum: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 7, !"frame-pointer", i32 2}
+
+!6 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !7)
+!7 = !{null, !8}
+!8 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "opaque_sig", file: !1, line: 50, size: 32, elements: !9, identifier: "opaque_sig")
+!9 = !{}
+
+!12 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !13)
+!13 = !{null, !14}
+!14 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !15, size: 64)
+!15 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!10 = distinct !DISubprogram(name: "opaque_struct_param", linkageName: "opaque_struct_param", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!11 = distinct !DISubprogram(name: "ptr_no_as", linkageName: "ptr_no_as", scope: !1, file: !1, line: 2, type: !12, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-debug-none-base.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-debug-none-base.ll
new file mode 100644
index 0000000000000..9e7d148e40c15
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-debug-none-base.ll
@@ -0,0 +1,38 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK
+; TODO(#109287): spirv-val coverage remains disabled for DebugTypePointer with
+; DebugInfoNone as the base type.
+;
+; Pointer parameter with null baseType should lower to DebugTypePointer using
+; DebugInfoNone as Base Type, and still be consumed by DebugTypeFunction.
+
+; CHECK: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-DAG: [[debug_none:%[0-9]+]] = OpExtInst [[type_void]] [[ext]] DebugInfoNone
+; CHECK-DAG: [[storage_class:%[0-9]+]] = OpConstant [[type_int32]] 8{{$}}
+; CHECK: [[dbg_ptr:%[0-9]+]] = OpExtInst [[type_void]] [[ext]] DebugTypePointer [[debug_none]] [[storage_class]] [[flag_zero]]
+; CHECK: OpExtInst [[type_void]] [[ext]] DebugTypeFunction [[flag_zero]] [[type_void]] [[dbg_ptr]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @ptr_null_base(ptr addrspace(4) %p) !dbg !10 {
+entry:
+  ret void
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version XX.X", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-type-function-pointer-debug-none-base.c", directory: "/AAAAAAAAAA/BBBBBBBB/CCCCCCCCC", checksumkind: CSK_MD5, checksum: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 7, !"frame-pointer", i32 2}
+
+!6 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !7)
+!7 = !{null, !8}
+!8 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64, dwarfAddressSpace: 4)
+
+!10 = distinct !DISubprogram(name: "ptr_null_base", linkageName: "ptr_null_base", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-param.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-param.ll
new file mode 100644
index 0000000000000..854b73ebed017
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-param.ll
@@ -0,0 +1,35 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; void (*)(addrspace(4) i32*) — DebugTypePointer + DebugTypeFunction includes the pointer parameter operand.
+
+; CHECK-SPIRV: [[ext_inst_non_semantic:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-SPIRV-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-SPIRV-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-SPIRV-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypePointer
+; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeFunction [[flag_zero]] [[type_void]] {{%[0-9]+}}
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @ptr_param(ptr addrspace(4) %p) !dbg !10 {
+entry:
+  ret void
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version XX.X", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-type-function-pointer-param.c", directory: "/AAAAAAAAAA/BBBBBBBB/CCCCCCCCC", checksumkind: CSK_MD5, checksum: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 7, !"frame-pointer", i32 2}
+
+!6 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !7)
+!7 = !{null, !8}
+!8 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !9, size: 64, dwarfAddressSpace: 4)
+!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!10 = distinct !DISubprogram(name: "ptr_param", linkageName: "ptr_param", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-scalar-returns.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-scalar-returns.ll
new file mode 100644
index 0000000000000..6770af122b35b
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-scalar-returns.ll
@@ -0,0 +1,32 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-SPIRV: [[ext_inst_non_semantic:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-SPIRV-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-SPIRV-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-SPIRV-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-SPIRV-DAG: OpString "float"
+; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeFunction [[flag_zero]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func float @float_sig() !dbg !9 {
+entry:
+  ret float 0.000000e+00
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version XX.X", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-type-function-scalar-returns.c", directory: "/AAAAAAAAAA/BBBBBBBB/CCCCCCCCC", checksumkind: CSK_MD5, checksum: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 7, !"frame-pointer", i32 2}
+
+!6 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !7)
+!7 = !{!8}
+!8 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float)
+
+!9 = distinct !DISubprogram(name: "float_sig", linkageName: "float_sig", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-subroutine-type-flags.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-subroutine-type-flags.ll
new file mode 100644
index 0000000000000..9ef42724a42ea
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-subroutine-type-flags.ll
@@ -0,0 +1,36 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Test the encoding of the flags in the DISubroutineType
+
+; CHECK: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-DAG: [[flag_prototyped:%[0-9]+]] = OpConstant [[type_int32]] 128{{$}}
+; CHECK-DAG: [[dbg_ptr:%[0-9]+]] = OpExtInst [[type_void]] [[ext]] DebugTypePointer {{.*}} {{.*}} [[flag_zero]]
+; CHECK: OpExtInst [[type_void]] [[ext]] DebugTypeFunction [[flag_prototyped]] [[type_void]] [[dbg_ptr]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @ptr_param(ptr addrspace(4) %p) !dbg !10 {
+entry:
+  ret void
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version XX.X", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-type-function-subroutine-type-flags.c", directory: "/AAAAAAAAAA/BBBBBBBB/CCCCCCCCC", checksumkind: CSK_MD5, checksum: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 7, !"frame-pointer", i32 2}
+
+!6 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, flags: DIFlagPrototyped, types: !7)
+!7 = !{null, !8}
+!8 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !9, size: 64, dwarfAddressSpace: 4)
+!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!10 = distinct !DISubprogram(name: "ptr_param", linkageName: "ptr_param", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll
new file mode 100644
index 0000000000000..c46dc74e03ef9
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll
@@ -0,0 +1,42 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Void function types in LLVM metadata may be written as an empty type list (!{}) or with an explicit
+; null return slot (!{null}); both should produce the same SPIR-V DebugTypeFunction (flags + void only).
+
+; CHECK-SPIRV: [[ext_inst_non_semantic:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-SPIRV-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-SPIRV-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-SPIRV-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeFunction [[flag_zero]] [[type_void]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @void_fn() !dbg !10 {
+entry:
+  ret void
+}
+
+define spir_func void @void_explicit_null() !dbg !11 {
+entry:
+  ret void
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version XX.X", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-type-function-void-prototypes.c", directory: "/AAAAAAAAAA/BBBBBBBB/CCCCCCCCC", checksumkind: CSK_MD5, checksum: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 7, !"frame-pointer", i32 2}
+
+!6 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !7)
+!7 = !{}
+
+!8 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !9)
+!9 = !{null}
+
+!10 = distinct !DISubprogram(name: "void_fn", linkageName: "void_fn", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!11 = distinct !DISubprogram(name: "void_explicit_null", linkageName: "void_explicit_null", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)

>From cdb0bbc1e7e87a72ff38de83b5d4a93f96ccdef8 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 12:25:20 -0500
Subject: [PATCH 02/21] [reviews] Fix docs.

---
 .../Target/SPIRV/SPIRVNonSemanticDebugHandler.h    | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index 2e98f54760e29..ed9c1af1263a6 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -183,10 +183,16 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   /// one if the module does not contain it.
   MCRegister findOrEmitOpTypeInt32(SPIRV::ModuleAnalysisInfo &MAI);
 
-  /// Emit a DebugTypePointer instruction for PT. Returns std::nullopt when the
-  /// pointer is skipped (no DWARF address space). If the base DI type has an
-  /// entry in DebugTypeRegs, use it as the Base Type operand; otherwise use
-  /// DebugInfoNone.
+  /// Emit \c DebugTypePointer for pointer metadata \p PT.
+  ///
+  /// \returns The result id register on success. Returns \c std::nullopt and
+  /// emits nothing if \p PT has no DWARF address space (needed to pick the
+  /// SPIR-V storage class), or if \p PT has a non-null base DI type that is not
+  /// yet in \c DebugTypeRegs (the pointee was not emitted as a debug type).
+  ///
+  /// Base Type operand: the register from \c DebugTypeRegs for \p PT's base
+  /// type when it is set and mapped; \c DebugInfoNone when there is no base
+  /// type (e.g. \c void * in IR), consistent with SPIRV-LLVM-Translator.
   std::optional<MCRegister>
   emitDebugTypePointer(const DIDerivedType *PT, MCRegister ExtInstSetReg,
                        SPIRV::ModuleAnalysisInfo &MAI);

>From ba67fa218abbf8a994fcb5335a630796a7df678c Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 12:28:15 -0500
Subject: [PATCH 03/21] [reviews] Initialize missing member variables.

---
 llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index a25368863e1a7..4a0669b0162c9 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -172,6 +172,8 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
   SubroutineTypes.clear();
   DebugTypeRegs.clear();
   OpStringContentCache.clear();
+  I32ConstantCache.clear();
+  GlobalDIEmitted = false;
   NonSemanticOpStringsSectionEmitted = false;
   CachedDebugInfoNoneReg = MCRegister();
   CachedOpTypeVoidReg = MCRegister();

>From 243cae036372df7f6fe1aed91e7d8e24e117951a Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 12:29:21 -0500
Subject: [PATCH 04/21] [reviews] Use reportFatalInternalError to report
 internal invariant violation.

---
 llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 4a0669b0162c9..efdc2ca0bd2a8 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -268,7 +268,7 @@ MCRegister SPIRVNonSemanticDebugHandler::getCachedOpStringReg(StringRef S) {
          "getCachedOpStringReg requires emitNonSemanticDebugStrings() first");
   auto It = OpStringContentCache.find(S);
   if (It == OpStringContentCache.end())
-    report_fatal_error(
+    reportFatalInternalError(
         "NSDI OpString missing from cache; emitNonSemanticDebugStrings must "
         "cache every string used in section 10");
   return It->second;

>From 32637f370bf607e14b94a767f43150ecebe17840 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 12:34:46 -0500
Subject: [PATCH 05/21] [reviews] Refactor

---
 llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index efdc2ca0bd2a8..a9ccd8e8b8a8e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -255,11 +255,12 @@ MCRegister SPIRVNonSemanticDebugHandler::emitOpStringIfNew(
     StringRef S, SPIRV::ModuleAnalysisInfo &MAI) {
   assert(!NonSemanticOpStringsSectionEmitted &&
          "emitOpStringIfNew is only valid while emitting SPIR-V section 7");
-  auto It = OpStringContentCache.find(S);
-  if (It != OpStringContentCache.end())
+  auto [It, Inserted] = OpStringContentCache.try_emplace(S, MCRegister());
+  if (!Inserted)
     return It->second;
+
   MCRegister Reg = emitOpString(S, MAI);
-  OpStringContentCache.insert(std::make_pair(S, Reg));
+  It->second = Reg;
   return Reg;
 }
 

>From 941b4e566dbae149a5fded7643adcba3f35fb9d7 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 12:41:56 -0500
Subject: [PATCH 06/21] [review] Switch to assert.

---
 llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index a9ccd8e8b8a8e..0e7fa8e8d8eee 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -268,10 +268,9 @@ MCRegister SPIRVNonSemanticDebugHandler::getCachedOpStringReg(StringRef S) {
   assert(NonSemanticOpStringsSectionEmitted &&
          "getCachedOpStringReg requires emitNonSemanticDebugStrings() first");
   auto It = OpStringContentCache.find(S);
-  if (It == OpStringContentCache.end())
-    reportFatalInternalError(
-        "NSDI OpString missing from cache; emitNonSemanticDebugStrings must "
-        "cache every string used in section 10");
+  assert(It == OpStringContentCache.end() &&
+         "NSDI OpString missing from cache; emitNonSemanticDebugStrings must "
+         "cache every string used in section 10");
   return It->second;
 }
 

>From 667432612dcf92272c3aee3334d26146cacaf29d Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 12:46:36 -0500
Subject: [PATCH 07/21] [reviews] Fix docs after clang-format.

---
 llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index ed9c1af1263a6..50ce503af3201 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -42,10 +42,10 @@ class SPIRVSubtarget;
 /// Call sequence:
 ///   beginModule()                    -- collect compile-unit metadata.
 ///   prepareModuleOutput()            -- add extension + ext inst set to MAI.
-///   emitNonSemanticDebugStrings()    -- OpString for NSDI paths/names (sec.
-///   7). emitNonSemanticGlobalDebugInfo() -- emit DebugSource,
-///                                       DebugCompilationUnit, DebugTypeBasic,
-///                                       DebugTypePointer, DebugTypeFunction.
+///   emitNonSemanticDebugStrings()    -- OpString for NSDI strings (sec. 7).
+///   emitNonSemanticGlobalDebugInfo() -- emit DebugSource, DebugTypeBasic,
+///                                       DebugTypePointer, DebugTypeFunction,
+///                                       DebugCompilationUnit.
 ///   beginFunctionImpl()              -- no-op (no per-function DI yet).
 ///   endFunctionImpl()                -- no-op.
 class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {

>From 606e1725c7060c1a0f4d2d44222925f5cb70b444 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 12:58:24 -0500
Subject: [PATCH 08/21] [reviews] Use vectors.

---
 .../Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp    | 12 ++++++------
 llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h |  7 +++----
 2 files changed, 9 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 0e7fa8e8d8eee..bbc8133e53694 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -32,20 +32,20 @@ namespace {
 /// node is seen once, so no recursion into pointer bases. Other composites and
 /// non-pointer derived kinds are ignored.
 static void
-partitionTypes(const DIType *Ty, SetVector<const DIBasicType *> &BasicTypes,
-               SetVector<const DIDerivedType *> &PointerTypes,
-               SetVector<const DISubroutineType *> &SubroutineTypes) {
+partitionTypes(const DIType *Ty, SmallVector<const DIBasicType *> &BasicTypes,
+               SmallVector<const DIDerivedType *> &PointerTypes,
+               SmallVector<const DISubroutineType *> &SubroutineTypes) {
   if (const auto *BT = dyn_cast<DIBasicType>(Ty)) {
-    BasicTypes.insert(BT);
+    BasicTypes.push_back(BT);
     return;
   }
   if (const auto *ST = dyn_cast<DISubroutineType>(Ty)) {
-    SubroutineTypes.insert(ST);
+    SubroutineTypes.push_back(ST);
     return;
   }
   const auto *DT = dyn_cast<DIDerivedType>(Ty);
   if (DT && DT->getTag() == dwarf::DW_TAG_pointer_type)
-    PointerTypes.insert(DT);
+    PointerTypes.push_back(DT);
 }
 
 enum : uint32_t {
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index 50ce503af3201..57704d64e8158 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -21,7 +21,6 @@
 #include "MCTargetDesc/SPIRVBaseInfo.h"
 #include "SPIRVModuleAnalysis.h"
 #include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
@@ -63,9 +62,9 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
 
   // DI types partitioned from DebugInfoFinder.types() in beginModule()
   // (basics, pointers, subroutine types NSDI v1 may emit).
-  SetVector<const DIBasicType *> BasicTypes;
-  SetVector<const DIDerivedType *> PointerTypes;
-  SetVector<const DISubroutineType *> SubroutineTypes;
+  SmallVector<const DIBasicType *> BasicTypes;
+  SmallVector<const DIDerivedType *> PointerTypes;
+  SmallVector<const DISubroutineType *> SubroutineTypes;
 
   // Filled in emitNonSemanticGlobalDebugInfo(): DI types to their result
   // registers.

>From 3bdcaa7469f00543a9d9b47ece8a3d852fb4a02f Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 13:03:16 -0500
Subject: [PATCH 09/21] Fix typo.

---
 llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index bbc8133e53694..7ddfe039e5349 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -268,7 +268,7 @@ MCRegister SPIRVNonSemanticDebugHandler::getCachedOpStringReg(StringRef S) {
   assert(NonSemanticOpStringsSectionEmitted &&
          "getCachedOpStringReg requires emitNonSemanticDebugStrings() first");
   auto It = OpStringContentCache.find(S);
-  assert(It == OpStringContentCache.end() &&
+  assert(It != OpStringContentCache.end() &&
          "NSDI OpString missing from cache; emitNonSemanticDebugStrings must "
          "cache every string used in section 10");
   return It->second;

>From 57f966b38899e860f8d139fb0d28b429aba3ad72 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 14:00:19 -0500
Subject: [PATCH 10/21] Fix handling for DebugTypeFunction.

---
 .../SPIRV/SPIRVNonSemanticDebugHandler.cpp    | 19 ++++++++++++-------
 .../SPIRV/SPIRVNonSemanticDebugHandler.h      | 15 +++++++++++++--
 2 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 7ddfe039e5349..b488326d0c6ba 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -411,8 +411,9 @@ SPIRVNonSemanticDebugHandler::emitDebugTypeFunctionForSubroutineType(
   if (TA.empty()) {
     Ops.push_back(VoidTypeReg);
   } else {
-    for (DIType *Elem : TA) {
-      auto OptReg = mapDISignatureTypeToReg(Elem, VoidTypeReg);
+    for (unsigned I = 0, E = TA.size(); I != E; ++I) {
+      bool IsReturnType = I == 0;
+      auto OptReg = mapDISignatureTypeToReg(TA[I], VoidTypeReg, IsReturnType);
       // No emitted DebugType* id for this slot (e.g., pointer that
       // was skipped due missing address space, etc.).
       if (!OptReg)
@@ -424,11 +425,15 @@ SPIRVNonSemanticDebugHandler::emitDebugTypeFunctionForSubroutineType(
                      ExtInstSetReg, Ops, MAI);
 }
 
-std::optional<MCRegister>
-SPIRVNonSemanticDebugHandler::mapDISignatureTypeToReg(const DIType *Ty,
-                                                      MCRegister VoidTypeReg) {
-  if (!Ty)
-    return VoidTypeReg;
+std::optional<MCRegister> SPIRVNonSemanticDebugHandler::mapDISignatureTypeToReg(
+    const DIType *Ty, MCRegister VoidTypeReg, bool ReturnType) {
+  if (!Ty) {
+    if (ReturnType)
+      return VoidTypeReg;
+    assert(CachedDebugInfoNoneReg.isValid() &&
+           "DebugInfoNone must be emitted before DISubroutineType operands");
+    return CachedDebugInfoNoneReg;
+  }
   auto It = DebugTypeRegs.find(Ty);
   if (It != DebugTypeRegs.end())
     return It->second;
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index 57704d64e8158..91e8bcde43679 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -203,9 +203,20 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
                                          MCRegister ExtInstSetReg,
                                          SPIRV::ModuleAnalysisInfo &MAI);
 
-  /// Map DISubroutineType slot DI types using DebugTypeRegs.
+  /// Map a \c DISubroutineType::getTypeArray() element to an operand register
+  /// for
+  /// \c DebugTypeFunction. Non-null \p Ty resolves via \c DebugTypeRegs; if the
+  /// type was never emitted, returns \c std::nullopt.
+  ///
+  /// LLVM encodes a void return as a null first element (and may use null in
+  /// later slots). NonSemantic \c DebugTypeFunction
+  /// requires a concrete return-type operand, so when \p ReturnType is true and
+  /// \p Ty is null, this returns \p VoidTypeReg (\c OpTypeVoid). When
+  /// \p ReturnType is false and \p Ty is null, this returns
+  /// \c CachedDebugInfoNoneReg (\c DebugInfoNone).
   std::optional<MCRegister> mapDISignatureTypeToReg(const DIType *Ty,
-                                                    MCRegister VoidTypeReg);
+                                                    MCRegister VoidTypeReg,
+                                                    bool ReturnType);
 
   /// Map a DWARF source language code to a NonSemantic.Shader.DebugInfo.100
   /// source language code.

>From 072f27425c77991cf3c2c01ed17fc888f9de953c Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 14:33:30 -0500
Subject: [PATCH 11/21] [reviews] Simplify code and add missing test.

---
 .../SPIRV/SPIRVNonSemanticDebugHandler.cpp    | 20 +++++-----
 .../debug-type-pointer-composite-pointee.ll   | 38 +++++++++++++++++++
 2 files changed, 47 insertions(+), 11 deletions(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-type-pointer-composite-pointee.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index b488326d0c6ba..61601cbb1af40 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -380,18 +380,16 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugTypePointer(
           SPIRV::NonSemanticExtInst::DebugTypePointer, VoidTypeReg,
           ExtInstSetReg,
           {BaseIt->second, StorageClassReg, DebugTypePointerFlagsReg}, MAI);
-  } else {
-    // No getBaseType() (typical for void*): use DebugInfoNone as Base Type,
-    // same as SPIRV-LLVM-Translator (see issue #109287 and the DISABLED
-    // spirv-val run in debug-type-pointer.ll). spirv-val may still reject this
-    // encoding; see https://github.com/KhronosGroup/SPIRV-Registry/pull/287.
-    return emitExtInst(
-        SPIRV::NonSemanticExtInst::DebugTypePointer, VoidTypeReg, ExtInstSetReg,
-        {CachedDebugInfoNoneReg, StorageClassReg, DebugTypePointerFlagsReg},
-        MAI);
+    // Unsupported type, no DebugType* id available.
+    return std::nullopt;
   }
-
-  return std::nullopt;
+  // No getBaseType() (typical for void*): use DebugInfoNone as Base Type,
+  // same as SPIRV-LLVM-Translator (see issue #109287 and the DISABLED
+  // spirv-val run in debug-type-pointer.ll). spirv-val may still reject this
+  // encoding; see https://github.com/KhronosGroup/SPIRV-Registry/pull/287.
+  return emitExtInst(
+      SPIRV::NonSemanticExtInst::DebugTypePointer, VoidTypeReg, ExtInstSetReg,
+      {CachedDebugInfoNoneReg, StorageClassReg, DebugTypePointerFlagsReg}, MAI);
 }
 
 std::optional<MCRegister>
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-pointer-composite-pointee.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-pointer-composite-pointee.ll
new file mode 100644
index 0000000000000..ce0a45452f6e4
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-pointer-composite-pointee.ll
@@ -0,0 +1,38 @@
+; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; DW_TAG_array_type composite not yet supported
+
+; CHECK: OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK: OpExtInst {{.*}} DebugCompilationUnit
+; CHECK-NOT: DebugTypePointer
+
+define spir_func void @ptr_to_array() !dbg !10 {
+entry:
+  %p = alloca ptr addrspace(4), align 8
+    #dbg_declare(ptr %p, !11, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !14)
+  ret void
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-type-pointer-composite-pointee.c", directory: "/src", checksumkind: CSK_MD5, checksum: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 7, !"frame-pointer", i32 2}
+
+!6 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !7)
+!7 = !{null}
+!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!9 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, size: 256, elements: !12)
+!12 = !{!13}
+!13 = !DISubrange(count: 8)
+
+!10 = distinct !DISubprogram(name: "ptr_to_array", linkageName: "ptr_to_array", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !15)
+!15 = !{}
+!11 = !DILocalVariable(name: "pa", scope: !10, file: !1, line: 2, type: !16)
+!16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !9, size: 64, dwarfAddressSpace: 4)
+!14 = !DILocation(line: 2, column: 5, scope: !10)

>From f7d08e920ed2752ed53c23b2586d74cf2b4198bb Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 15 May 2026 14:45:20 -0500
Subject: [PATCH 12/21] [reviews] Simplify tests.

---
 .../debug-type-function-int-string-dedup.ll        | 14 +++++++-------
 .../debug-type-function-multi-scalar-params.ll     |  2 +-
 .../SPIRV/debug-info/debug-type-function-omit.ll   |  2 +-
 .../debug-type-function-pointer-debug-none-base.ll |  2 +-
 .../debug-type-function-pointer-param.ll           | 14 +++++++-------
 .../debug-type-function-scalar-returns.ll          | 14 +++++++-------
 .../debug-type-function-subroutine-type-flags.ll   |  2 +-
 .../debug-type-function-void-prototypes.ll         | 12 ++++++------
 8 files changed, 31 insertions(+), 31 deletions(-)

diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-int-string-dedup.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-int-string-dedup.ll
index 1f67a7001fd5d..c28e2b062dfcc 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-int-string-dedup.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-int-string-dedup.ll
@@ -1,14 +1,14 @@
-; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
 ; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
 
 ; Two distinct !DIBasicType nodes named "int" share one OpString "int".
 
-; CHECK-SPIRV: [[ext_inst_non_semantic:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
-; CHECK-SPIRV-COUNT-1: OpString "int"
-; CHECK-SPIRV-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
-; CHECK-SPIRV-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
-; CHECK-SPIRV-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
-; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeFunction [[flag_zero]]
+; CHECK: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-COUNT-1: OpString "int"
+; CHECK-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-DAG: OpExtInst [[type_void]] [[ext]] DebugTypeFunction [[flag_zero]]
 
 target triple = "spirv64-unknown-unknown"
 
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-multi-scalar-params.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-multi-scalar-params.ll
index ab5fc00c0f978..a49eaede1f374 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-multi-scalar-params.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-multi-scalar-params.ll
@@ -1,4 +1,4 @@
-; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
 ; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
 ;
 ; Multi-slot signature mapping order: DebugTypeFunction must keep operand order
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-omit.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-omit.ll
index 62d2e8afd51e5..c49af8964e0f0 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-omit.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-omit.ll
@@ -1,4 +1,4 @@
-; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --implicit-check-not=DebugTypeFunction --check-prefix=CHECK
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
 ; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
 
 ; Unsupported composite parameter and pointer without dwarfAddressSpace — DISubroutineType not lowered to DebugTypeFunction.
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-debug-none-base.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-debug-none-base.ll
index 9e7d148e40c15..3eb3774a317af 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-debug-none-base.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-debug-none-base.ll
@@ -1,4 +1,4 @@
-; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
 ; TODO(#109287): spirv-val coverage remains disabled for DebugTypePointer with
 ; DebugInfoNone as the base type.
 ;
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-param.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-param.ll
index 854b73ebed017..808d90efec896 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-param.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-pointer-param.ll
@@ -1,14 +1,14 @@
-; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
 ; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
 
 ; void (*)(addrspace(4) i32*) — DebugTypePointer + DebugTypeFunction includes the pointer parameter operand.
 
-; CHECK-SPIRV: [[ext_inst_non_semantic:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
-; CHECK-SPIRV-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
-; CHECK-SPIRV-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
-; CHECK-SPIRV-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
-; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypePointer
-; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeFunction [[flag_zero]] [[type_void]] {{%[0-9]+}}
+; CHECK: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-DAG: OpExtInst [[type_void]] [[ext]] DebugTypePointer
+; CHECK-DAG: OpExtInst [[type_void]] [[ext]] DebugTypeFunction [[flag_zero]] [[type_void]] {{%[0-9]+}}
 
 target triple = "spirv64-unknown-unknown"
 
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-scalar-returns.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-scalar-returns.ll
index 6770af122b35b..37e002a21f2ab 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-scalar-returns.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-scalar-returns.ll
@@ -1,12 +1,12 @@
-; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
 ; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
 
-; CHECK-SPIRV: [[ext_inst_non_semantic:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
-; CHECK-SPIRV-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
-; CHECK-SPIRV-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
-; CHECK-SPIRV-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
-; CHECK-SPIRV-DAG: OpString "float"
-; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeFunction [[flag_zero]]
+; CHECK: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-DAG: OpString "float"
+; CHECK-DAG: OpExtInst [[type_void]] [[ext]] DebugTypeFunction [[flag_zero]]
 
 target triple = "spirv64-unknown-unknown"
 
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-subroutine-type-flags.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-subroutine-type-flags.ll
index 9ef42724a42ea..b6d6b429bbd72 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-subroutine-type-flags.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-subroutine-type-flags.ll
@@ -1,4 +1,4 @@
-; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
 ; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
 
 ; Test the encoding of the flags in the DISubroutineType
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll
index c46dc74e03ef9..13e31d3af057f 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll
@@ -1,14 +1,14 @@
-; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
 ; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
 
 ; Void function types in LLVM metadata may be written as an empty type list (!{}) or with an explicit
 ; null return slot (!{null}); both should produce the same SPIR-V DebugTypeFunction (flags + void only).
 
-; CHECK-SPIRV: [[ext_inst_non_semantic:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
-; CHECK-SPIRV-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
-; CHECK-SPIRV-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
-; CHECK-SPIRV-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
-; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeFunction [[flag_zero]] [[type_void]]
+; CHECK: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-DAG: OpExtInst [[type_void]] [[ext]] DebugTypeFunction [[flag_zero]] [[type_void]]
 
 target triple = "spirv64-unknown-unknown"
 

>From 46f1d7ac94d0e560cee3bdf6f338fa55b9c832c9 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 29 May 2026 04:53:02 -0500
Subject: [PATCH 13/21] Improve comment.

---
 llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 61601cbb1af40..9cee76b28b352 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -30,7 +30,7 @@ namespace {
 /// Partition \p Ty into \p BasicTypes, \p PointerTypes, and \p SubroutineTypes
 /// for NSDI emission. Used when iterating DebugInfoFinder.types(); each DI
 /// node is seen once, so no recursion into pointer bases. Other composites and
-/// non-pointer derived kinds are ignored.
+/// non-pointer derived kinds are ignored because they are not yet supported.
 static void
 partitionTypes(const DIType *Ty, SmallVector<const DIBasicType *> &BasicTypes,
                SmallVector<const DIDerivedType *> &PointerTypes,

>From 0971592362f8ed9a581115d45d44224d7508d4f7 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 29 May 2026 05:03:59 -0500
Subject: [PATCH 14/21] [reviews] Improve enum value names and add missing
 ones.

---
 .../SPIRV/SPIRVNonSemanticDebugHandler.cpp    | 38 ++++++++++---------
 1 file changed, 20 insertions(+), 18 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 9cee76b28b352..943012350714c 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -54,18 +54,20 @@ enum : uint32_t {
   NSDIFlagIsPublic = NSDIFlagIsPrivate | NSDIFlagIsProtected,
   NSDIFlagIsLocal = 1u << 2,
   NSDIFlagIsDefinition = 1u << 3,
-  NSDIFlagIsFwdDecl = 1u << 4,
-  NSDIFlagIsArtificial = 1u << 5,
-  NSDIFlagIsExplicit = 1u << 6,
-  NSDIFlagIsPrototyped = 1u << 7,
-  NSDIFlagIsObjectPointer = 1u << 8,
-  NSDIFlagIsStaticMember = 1u << 9,
-  NSDIFlagIsLValueReference = 1u << 11,
-  NSDIFlagIsRValueReference = 1u << 12,
+  NSDIFlagFwdDecl = 1u << 4,
+  NSDIFlagArtificial = 1u << 5,
+  NSDIFlagExplicit = 1u << 6,
+  NSDIFlagPrototyped = 1u << 7,
+  NSDIFlagObjectPointer = 1u << 8,
+  NSDIFlagStaticMember = 1u << 9,
+  NSDIFlagIndirectVariable = 1u << 10,
+  NSDIFlagLValueReference = 1u << 11,
+  NSDIFlagRValueReference = 1u << 12,
   NSDIFlagIsOptimized = 1u << 13,
   NSDIFlagIsEnumClass = 1u << 14,
   NSDIFlagTypePassByValue = 1u << 15,
   NSDIFlagTypePassByReference = 1u << 16,
+  NSDIFlagUnknownPhysicalLayout = 1u << 17,
 };
 
 static uint32_t mapDIFlagsToNonSemantic(DINode::DIFlags DFlags) {
@@ -77,21 +79,21 @@ static uint32_t mapDIFlagsToNonSemantic(DINode::DIFlags DFlags) {
   if ((DFlags & DINode::FlagAccessibility) == DINode::FlagPrivate)
     Flags |= NSDIFlagIsPrivate;
   if (DFlags & DINode::FlagFwdDecl)
-    Flags |= NSDIFlagIsFwdDecl;
+    Flags |= NSDIFlagFwdDecl;
   if (DFlags & DINode::FlagArtificial)
-    Flags |= NSDIFlagIsArtificial;
+    Flags |= NSDIFlagArtificial;
   if (DFlags & DINode::FlagExplicit)
-    Flags |= NSDIFlagIsExplicit;
+    Flags |= NSDIFlagExplicit;
   if (DFlags & DINode::FlagPrototyped)
-    Flags |= NSDIFlagIsPrototyped;
+    Flags |= NSDIFlagPrototyped;
   if (DFlags & DINode::FlagObjectPointer)
-    Flags |= NSDIFlagIsObjectPointer;
+    Flags |= NSDIFlagObjectPointer;
   if (DFlags & DINode::FlagStaticMember)
-    Flags |= NSDIFlagIsStaticMember;
+    Flags |= NSDIFlagStaticMember;
   if (DFlags & DINode::FlagLValueReference)
-    Flags |= NSDIFlagIsLValueReference;
+    Flags |= NSDIFlagLValueReference;
   if (DFlags & DINode::FlagRValueReference)
-    Flags |= NSDIFlagIsRValueReference;
+    Flags |= NSDIFlagRValueReference;
   if (DFlags & DINode::FlagTypePassByValue)
     Flags |= NSDIFlagTypePassByValue;
   if (DFlags & DINode::FlagTypePassByReference)
@@ -119,9 +121,9 @@ static uint32_t transDebugFlags(const DINode *DN) {
     Flags |= mapDIFlagsToNonSemantic(SP->getFlags());
   }
   if (DN->getTag() == dwarf::DW_TAG_reference_type)
-    Flags |= NSDIFlagIsLValueReference;
+    Flags |= NSDIFlagLValueReference;
   if (DN->getTag() == dwarf::DW_TAG_rvalue_reference_type)
-    Flags |= NSDIFlagIsRValueReference;
+    Flags |= NSDIFlagRValueReference;
   if (const auto *Ty = dyn_cast<DIType>(DN))
     Flags |= mapDIFlagsToNonSemantic(Ty->getFlags());
   if (const auto *LV = dyn_cast<DILocalVariable>(DN))

>From 98f19137f425ef479f736dbf3e92efe6849dcc76 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 29 May 2026 05:38:39 -0500
Subject: [PATCH 15/21] [reviews] Deduplicate DebugTypeFunction by operand ids

Add getOrEmitDebugTypeFunction() to reuse identical DebugTypeFunction
instructions when distinct DISubroutineType nodes lower to the same
operands (e.g. !{} vs !{null} void prototypes). Update the lit test to
assert a single emission.
---
 .../SPIRV/SPIRVNonSemanticDebugHandler.cpp    | 19 +++++++++++++++++--
 .../SPIRV/SPIRVNonSemanticDebugHandler.h      | 11 +++++++++++
 .../debug-type-function-void-prototypes.ll    |  2 +-
 3 files changed, 29 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 943012350714c..bdc5b3787e10f 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -175,6 +175,7 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
   DebugTypeRegs.clear();
   OpStringContentCache.clear();
   I32ConstantCache.clear();
+  DebugTypeFunctionCache.clear();
   GlobalDIEmitted = false;
   NonSemanticOpStringsSectionEmitted = false;
   CachedDebugInfoNoneReg = MCRegister();
@@ -310,6 +311,21 @@ MCRegister SPIRVNonSemanticDebugHandler::emitExtInst(
   return Reg;
 }
 
+MCRegister SPIRVNonSemanticDebugHandler::getOrEmitDebugTypeFunction(
+    ArrayRef<MCRegister> Ops, MCRegister VoidTypeReg, MCRegister ExtInstSetReg,
+    SPIRV::ModuleAnalysisInfo &MAI) {
+  auto [It, Inserted] = DebugTypeFunctionCache.try_emplace(
+      SmallVector<MCRegister, 8>(Ops));
+  if (!Inserted)
+    return It->second;
+
+  MCRegister Reg = emitExtInst(
+      SPIRV::NonSemanticExtInst::DebugTypeFunction, VoidTypeReg, ExtInstSetReg,
+      Ops, MAI);
+  It->second = Reg;
+  return Reg;
+}
+
 MCRegister SPIRVNonSemanticDebugHandler::getOrEmitOpTypeVoidReg(
     SPIRV::ModuleAnalysisInfo &MAI) {
   if (!CachedOpTypeVoidReg.isValid())
@@ -421,8 +437,7 @@ SPIRVNonSemanticDebugHandler::emitDebugTypeFunctionForSubroutineType(
       Ops.push_back(*OptReg);
     }
   }
-  return emitExtInst(SPIRV::NonSemanticExtInst::DebugTypeFunction, VoidTypeReg,
-                     ExtInstSetReg, Ops, MAI);
+  return getOrEmitDebugTypeFunction(Ops, VoidTypeReg, ExtInstSetReg, MAI);
 }
 
 std::optional<MCRegister> SPIRVNonSemanticDebugHandler::mapDISignatureTypeToReg(
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index 91e8bcde43679..8ab8031bc8ff7 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -89,6 +89,10 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   // duplicate OpConstant instructions for the same integer value.
   DenseMap<uint32_t, MCRegister> I32ConstantCache;
 
+  // Cache of already-emitted DebugTypeFunction instructions, keyed by operand
+  // ids (flags, return type, parameters).
+  DenseMap<SmallVector<MCRegister, 8>, MCRegister> DebugTypeFunctionCache;
+
   // True once emitNonSemanticGlobalDebugInfo() has run. Both
   // SPIRVAsmPrinter::emitFunctionHeader() and emitEndOfAsmFile() may call
   // outputModuleSections(), each guarded by ModuleSectionsEmitted, so only
@@ -168,6 +172,13 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
                          ArrayRef<MCRegister> Operands,
                          SPIRV::ModuleAnalysisInfo &MAI);
 
+  /// Return a cached DebugTypeFunction id when \p Ops matches a prior emission,
+  /// otherwise emit and cache a new instruction.
+  MCRegister getOrEmitDebugTypeFunction(ArrayRef<MCRegister> Ops,
+                                        MCRegister VoidTypeReg,
+                                        MCRegister ExtInstSetReg,
+                                        SPIRV::ModuleAnalysisInfo &MAI);
+
   /// Return OpTypeVoid id for this module (lazy lookup / emit, then cache).
   MCRegister getOrEmitOpTypeVoidReg(SPIRV::ModuleAnalysisInfo &MAI);
 
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll
index 13e31d3af057f..d9f41f4cf0bab 100644
--- a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-function-void-prototypes.ll
@@ -8,7 +8,7 @@
 ; CHECK-DAG: [[type_void:%[0-9]+]] = OpTypeVoid
 ; CHECK-DAG: [[type_int32:%[0-9]+]] = OpTypeInt 32 0
 ; CHECK-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
-; CHECK-DAG: OpExtInst [[type_void]] [[ext]] DebugTypeFunction [[flag_zero]] [[type_void]]
+; CHECK-COUNT-1: OpExtInst [[type_void]] [[ext]] DebugTypeFunction [[flag_zero]] [[type_void]]
 
 target triple = "spirv64-unknown-unknown"
 

>From 83cd9783ca3a9c60fbd3fa91635de911e3a3f70e Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 29 May 2026 07:19:05 -0500
Subject: [PATCH 16/21] [reviews] Improve assertions.

---
 llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 10 ++++++++++
 llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h   |  2 ++
 2 files changed, 12 insertions(+)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index bdc5b3787e10f..38abd374d30b1 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -177,7 +177,9 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
   I32ConstantCache.clear();
   DebugTypeFunctionCache.clear();
   GlobalDIEmitted = false;
+#ifndef NDEBUG
   NonSemanticOpStringsSectionEmitted = false;
+#endif
   CachedDebugInfoNoneReg = MCRegister();
   CachedOpTypeVoidReg = MCRegister();
   CachedOpTypeInt32Reg = MCRegister();
@@ -256,8 +258,10 @@ SPIRVNonSemanticDebugHandler::emitOpString(StringRef S,
 
 MCRegister SPIRVNonSemanticDebugHandler::emitOpStringIfNew(
     StringRef S, SPIRV::ModuleAnalysisInfo &MAI) {
+#ifndef NDEBUG
   assert(!NonSemanticOpStringsSectionEmitted &&
          "emitOpStringIfNew is only valid while emitting SPIR-V section 7");
+#endif
   auto [It, Inserted] = OpStringContentCache.try_emplace(S, MCRegister());
   if (!Inserted)
     return It->second;
@@ -268,8 +272,10 @@ MCRegister SPIRVNonSemanticDebugHandler::emitOpStringIfNew(
 }
 
 MCRegister SPIRVNonSemanticDebugHandler::getCachedOpStringReg(StringRef S) {
+#ifndef NDEBUG
   assert(NonSemanticOpStringsSectionEmitted &&
          "getCachedOpStringReg requires emitNonSemanticDebugStrings() first");
+#endif
   auto It = OpStringContentCache.find(S);
   assert(It != OpStringContentCache.end() &&
          "NSDI OpString missing from cache; emitNonSemanticDebugStrings must "
@@ -474,7 +480,9 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticDebugStrings(
   for (const DIBasicType *BT : BasicTypes)
     (void)emitOpStringIfNew(BT->getName(), MAI);
 
+#ifndef NDEBUG
   NonSemanticOpStringsSectionEmitted = true;
+#endif
 }
 
 void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
@@ -490,9 +498,11 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
   if (!ExtInstSetReg.isValid())
     return; // Extension not available.
 
+#ifndef NDEBUG
   assert(NonSemanticOpStringsSectionEmitted &&
          "emitNonSemanticDebugStrings() must run before "
          "emitNonSemanticGlobalDebugInfo()");
+#endif
 
   MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
   MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI);
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index 8ab8031bc8ff7..fbf40d2a0e1ac 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -74,10 +74,12 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   // during section 7; section 10 uses getCachedOpStringReg() (lookup only).
   StringMap<MCRegister> OpStringContentCache;
 
+#ifndef NDEBUG
   // True after emitNonSemanticDebugStrings() emitted the NSDI OpStrings for
   // this module. SPIRVAsmPrinter calls that before
   // emitNonSemanticGlobalDebugInfo().
   bool NonSemanticOpStringsSectionEmitted = false;
+#endif
 
   MCRegister CachedDebugInfoNoneReg;
 

>From 33d3654e370f7f26644953ac58b91ab5a9eba58e Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 29 May 2026 07:28:41 -0500
Subject: [PATCH 17/21] Fix format.

---
 llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 38abd374d30b1..60f97cbf07074 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -320,14 +320,13 @@ MCRegister SPIRVNonSemanticDebugHandler::emitExtInst(
 MCRegister SPIRVNonSemanticDebugHandler::getOrEmitDebugTypeFunction(
     ArrayRef<MCRegister> Ops, MCRegister VoidTypeReg, MCRegister ExtInstSetReg,
     SPIRV::ModuleAnalysisInfo &MAI) {
-  auto [It, Inserted] = DebugTypeFunctionCache.try_emplace(
-      SmallVector<MCRegister, 8>(Ops));
+  auto [It, Inserted] =
+      DebugTypeFunctionCache.try_emplace(SmallVector<MCRegister, 8>(Ops));
   if (!Inserted)
     return It->second;
 
-  MCRegister Reg = emitExtInst(
-      SPIRV::NonSemanticExtInst::DebugTypeFunction, VoidTypeReg, ExtInstSetReg,
-      Ops, MAI);
+  MCRegister Reg = emitExtInst(SPIRV::NonSemanticExtInst::DebugTypeFunction,
+                               VoidTypeReg, ExtInstSetReg, Ops, MAI);
   It->second = Reg;
   return Reg;
 }

>From 8b7f9918d881a6dda1526a90f3495dc6f867ce95 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 5 Jun 2026 05:38:07 -0500
Subject: [PATCH 18/21] Add comment.

---
 llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 60f97cbf07074..e30abd26727e9 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -31,6 +31,7 @@ namespace {
 /// for NSDI emission. Used when iterating DebugInfoFinder.types(); each DI
 /// node is seen once, so no recursion into pointer bases. Other composites and
 /// non-pointer derived kinds are ignored because they are not yet supported.
+/// Only types that are supported (later used) are partitioned.
 static void
 partitionTypes(const DIType *Ty, SmallVector<const DIBasicType *> &BasicTypes,
                SmallVector<const DIDerivedType *> &PointerTypes,

>From a8f9f255bc19608b4042503b0228b6ed044bd32e Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 5 Jun 2026 05:39:59 -0500
Subject: [PATCH 19/21] Add comment.

---
 llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index fbf40d2a0e1ac..3f586a8eb976b 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -74,7 +74,7 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   // during section 7; section 10 uses getCachedOpStringReg() (lookup only).
   StringMap<MCRegister> OpStringContentCache;
 
-#ifndef NDEBUG
+#ifndef NDEBUG // Only declare the variable for debugging purposes.
   // True after emitNonSemanticDebugStrings() emitted the NSDI OpStrings for
   // this module. SPIRVAsmPrinter calls that before
   // emitNonSemanticGlobalDebugInfo().

>From 52bfa6de567453c97145a397889bc3dae55720c8 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 5 Jun 2026 05:43:39 -0500
Subject: [PATCH 20/21] Use [[maybe_unused]].

---
 llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index e30abd26727e9..5f45c5597e489 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -474,11 +474,13 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticDebugStrings(
   if (!MAI.getExtInstSetReg(NSSet).isValid())
     return;
 
-  for (const CompileUnitInfo &Info : CompileUnits)
-    (void)emitOpStringIfNew(Info.FilePath, MAI);
+  for (const CompileUnitInfo &Info : CompileUnits) {
+    [[maybe_unused]] MCRegister Reg = emitOpStringIfNew(Info.FilePath, MAI);
+  }
 
-  for (const DIBasicType *BT : BasicTypes)
-    (void)emitOpStringIfNew(BT->getName(), MAI);
+  for (const DIBasicType *BT : BasicTypes) {
+    [[maybe_unused]] MCRegister Reg = emitOpStringIfNew(BT->getName(), MAI);
+  }
 
 #ifndef NDEBUG
   NonSemanticOpStringsSectionEmitted = true;

>From a47854fe2f11a0285946e7cac5933f78a9e087e9 Mon Sep 17 00:00:00 2001
From: Manuel Carrasco <Manuel.Carrasco at amd.com>
Date: Fri, 5 Jun 2026 05:51:40 -0500
Subject: [PATCH 21/21] Minor update.

---
 llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index 5f45c5597e489..06704702a1378 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -434,7 +434,7 @@ SPIRVNonSemanticDebugHandler::emitDebugTypeFunctionForSubroutineType(
     Ops.push_back(VoidTypeReg);
   } else {
     for (unsigned I = 0, E = TA.size(); I != E; ++I) {
-      bool IsReturnType = I == 0;
+      bool IsReturnType = (I == 0);
       auto OptReg = mapDISignatureTypeToReg(TA[I], VoidTypeReg, IsReturnType);
       // No emitted DebugType* id for this slot (e.g., pointer that
       // was skipped due missing address space, etc.).



More information about the llvm-commits mailing list