Skip to content

Emit debug type vector#200056

Open
dnovillo wants to merge 3 commits into
llvm:mainfrom
dnovillo:emit-debug-type-vector
Open

Emit debug type vector#200056
dnovillo wants to merge 3 commits into
llvm:mainfrom
dnovillo:emit-debug-type-vector

Conversation

@dnovillo

Copy link
Copy Markdown
Contributor

NOTE: This PR depends on #200016 (which is included here so the bots don't fail)

This emits DebugTypeVector for HLSL float4-style vectors. It works by collecting those nodes in collectType() and emitting DebugTypeVector after the DebugTypeBasic loop. The collection recurses into getBaseType() so the element DIBasicType is reachable from a vector-only module.

Added a new test in test/CodeGen/SPIRV/debug-info/debug-type-vector.ll.

@dnovillo

Copy link
Copy Markdown
Contributor Author

@mgcarrasco this is the next one in the debug type series.

@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-backend-spir-v

Author: Diego Novillo (dnovillo)

Changes

NOTE: This PR depends on #200016 (which is included here so the bots don't fail)

This emits DebugTypeVector for HLSL float4-style vectors. It works by collecting those nodes in collectType() and emitting DebugTypeVector after the DebugTypeBasic loop. The collection recurses into getBaseType() so the element DIBasicType is reachable from a vector-only module.

Added a new test in test/CodeGen/SPIRV/debug-info/debug-type-vector.ll.


Full diff: https://github.com/llvm/llvm-project/pull/200056.diff

3 Files Affected:

  • (modified) llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp (+54-12)
  • (modified) llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h (+8)
  • (added) llvm/test/CodeGen/SPIRV/debug-info/debug-type-vector.ll (+66)
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index a6c647eddcd04..59a0c7d682b3f 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -87,29 +87,42 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
     }
   }
 
-  // Collect basic and pointer types referenced by debug variable records.
+  // Collect 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);
-            }
-          }
+          collectType(DVR.getVariable()->getType());
         }
       }
     }
   }
 }
 
+void SPIRVNonSemanticDebugHandler::collectType(const DIType *Ty) {
+  if (!Ty)
+    return;
+  if (const auto *BT = dyn_cast<DIBasicType>(Ty)) {
+    BasicTypes.insert(BT);
+    return;
+  }
+  if (const auto *DT = dyn_cast<DIDerivedType>(Ty)) {
+    if (DT->getTag() == dwarf::DW_TAG_pointer_type) {
+      PointerTypes.insert(DT);
+      collectType(DT->getBaseType());
+    }
+    return;
+  }
+  if (const auto *CT = dyn_cast<DICompositeType>(Ty)) {
+    if (CT->getTag() == dwarf::DW_TAG_array_type && CT->isVector()) {
+      VectorTypes.insert(CT);
+      collectType(CT->getBaseType());
+    }
+    return;
+  }
+}
+
 void SPIRVNonSemanticDebugHandler::prepareModuleOutput(
     const SPIRVSubtarget &ST, SPIRV::ModuleAnalysisInfo &MAI) {
   if (CompileUnits.empty())
@@ -370,6 +383,35 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
     BasicTypeRegs[BT] = BTReg;
   }
 
+  // Emit DebugTypeVector for each collected vector type. Skip entries that
+  // would produce a module rejected by spirv-val: Base Type must resolve to
+  // a DebugTypeBasic, the subrange count must be a constant, and the Vulkan
+  // flavor of NSDI requires Component Count in [1, 4].
+  for (const DICompositeType *VT : VectorTypes) {
+    const auto *BaseTy = dyn_cast_or_null<DIBasicType>(VT->getBaseType());
+    if (!BaseTy)
+      continue;
+    auto BTIt = BasicTypeRegs.find(BaseTy);
+    if (BTIt == BasicTypeRegs.end())
+      continue;
+
+    DINodeArray Elements = VT->getElements();
+    if (Elements.size() != 1)
+      continue;
+    const auto *SR = cast<DISubrange>(Elements[0]);
+    const auto *CI = dyn_cast_if_present<ConstantInt *>(SR->getCount());
+    if (!CI)
+      continue;
+    uint64_t Count = CI->getZExtValue();
+    if (Count == 0 || Count > 4)
+      continue;
+
+    MCRegister CountReg =
+        emitOpConstantI32(static_cast<uint32_t>(Count), I32TypeReg, MAI);
+    emitExtInst(SPIRV::NonSemanticExtInst::DebugTypeVector, VoidTypeReg,
+                ExtInstSetReg, {BTIt->second, CountReg}, MAI);
+  }
+
   // Emit DebugTypePointer for each referenced pointer type.
   for (const DIDerivedType *PT : PointerTypes)
     emitDebugTypePointer(PT, VoidTypeReg, I32TypeReg, ExtInstSetReg, I32ZeroReg,
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index 7d8bac0c5ab5a..8efd778ce6222 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -61,6 +61,9 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   // Types referenced by debug variable records, collected in beginModule().
   SetVector<const DIBasicType *> BasicTypes;
   SetVector<const DIDerivedType *> PointerTypes;
+  // DICompositeType nodes with DW_TAG_array_type and DINode::FlagVector,
+  // populated by collectType() during the beginModule() scan.
+  SetVector<const DICompositeType *> VectorTypes;
 
   // Cache of already-emitted i32 constants, keyed by value. Prevents
   // duplicate OpConstant instructions for the same integer value.
@@ -166,6 +169,11 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   /// Map a DWARF source language code to a NonSemantic.Shader.DebugInfo.100
   /// source language code.
   static unsigned toNSDISrcLang(unsigned DwarfSrcLang);
+
+  /// Insert Ty into the appropriate type-collection set, recursing into any
+  /// referenced types. Called from beginModule() for every DIType reached
+  /// through a DbgVariableRecord. Idempotent on SetVector duplicates.
+  void collectType(const DIType *Ty);
 };
 
 } // namespace llvm
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-vector.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-vector.ll
new file mode 100644
index 0000000000000..4e9dee89e3fe0
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-vector.ll
@@ -0,0 +1,66 @@
+; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %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 %}
+
+; Verify that DICompositeType nodes with tag DW_TAG_array_type and the
+; DIFlagVector flag lower to DebugTypeVector. Three element widths exercise
+; the OpConstant cache: a 4-component float vector and a 3-component float
+; vector share the float DebugTypeBasic but have distinct component-count
+; constants, while a 4-component int vector reuses the OpConstant 4 already
+; emitted for the float4.
+;
+; The OpConstant cache merges values across uses. OpConstant 3 is emitted
+; once and shared between the float DebugTypeBasic's Encoding operand (3 =
+; Float, NSDI 4.5) and the 3-component vector's ComponentCount operand.
+; Likewise OpConstant 4 is shared between the int DebugTypeBasic's Encoding
+; (4 = Signed) and the 4-component vector's ComponentCount.
+
+; 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: [[str_float:%[0-9]+]] = OpString "float"
+; CHECK-SPIRV-DAG: [[str_int:%[0-9]+]] = OpString "int"
+; CHECK-SPIRV-DAG: [[size_32bit:%[0-9]+]] = OpConstant [[type_int32]] 32{{$}}
+; CHECK-SPIRV-DAG: [[flag_zero:%[0-9]+]] = OpConstant [[type_int32]] 0{{$}}
+; CHECK-SPIRV-DAG: [[const_3:%[0-9]+]] = OpConstant [[type_int32]] 3{{$}}
+; CHECK-SPIRV-DAG: [[const_4:%[0-9]+]] = OpConstant [[type_int32]] 4{{$}}
+; CHECK-SPIRV-DAG: [[basic_float:%[0-9]+]] = OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeBasic [[str_float]] [[size_32bit]] [[const_3]] [[flag_zero]]
+; CHECK-SPIRV-DAG: [[basic_int:%[0-9]+]] = OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeBasic [[str_int]] [[size_32bit]] [[const_4]] [[flag_zero]]
+; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeVector [[basic_float]] [[const_4]]
+; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeVector [[basic_float]] [[const_3]]
+; CHECK-SPIRV-DAG: OpExtInst [[type_void]] [[ext_inst_non_semantic]] DebugTypeVector [[basic_int]] [[const_4]]
+
+define spir_func void @test() !dbg !6 {
+entry:
+  %f4 = alloca <4 x float>, align 16
+  %f3 = alloca <3 x float>, align 16
+  %i4 = alloca <4 x i32>, align 16
+    #dbg_declare(ptr %f4, !10, !DIExpression(), !14)
+    #dbg_declare(ptr %f3, !15, !DIExpression(), !17)
+    #dbg_declare(ptr %i4, !18, !DIExpression(), !22)
+  ret void
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_HLSL, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+!1 = !DIFile(filename: "vector.hlsl", directory: "/src")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!6 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 1, type: !7, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9)
+!7 = !DISubroutineType(types: !8)
+!8 = !{null}
+!9 = !{}
+!10 = !DILocalVariable(name: "f4", scope: !6, file: !1, line: 2, type: !11)
+!11 = !DICompositeType(tag: DW_TAG_array_type, baseType: !12, size: 128, flags: DIFlagVector, elements: !13)
+!12 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float)
+!13 = !{!DISubrange(count: 4)}
+!14 = !DILocation(line: 2, column: 10, scope: !6)
+!15 = !DILocalVariable(name: "f3", scope: !6, file: !1, line: 3, type: !16)
+!16 = !DICompositeType(tag: DW_TAG_array_type, baseType: !12, size: 96, flags: DIFlagVector, elements: !20)
+!17 = !DILocation(line: 3, column: 10, scope: !6)
+!18 = !DILocalVariable(name: "i4", scope: !6, file: !1, line: 4, type: !19)
+!19 = !DICompositeType(tag: DW_TAG_array_type, baseType: !21, size: 128, flags: DIFlagVector, elements: !13)
+!20 = !{!DISubrange(count: 3)}
+!21 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!22 = !DILocation(line: 4, column: 8, scope: !6)

Comment thread llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp Outdated
Comment thread llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp Outdated
@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown

🐧 Linux x64 Test Results

  • 196355 tests passed
  • 5334 tests skipped

✅ The build succeeded and all tests passed.

// non-constant subrange count (not yet supported).
for (const DICompositeType *VT : VectorTypes) {
const auto *BaseTy = dyn_cast_or_null<DIBasicType>(VT->getBaseType());
if (!BaseTy)

@mgcarrasco mgcarrasco Jun 5, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the null check necessary if it is later checked in the container?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the comment also say that this is for vectors with DIBasicType?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the null check necessary if it is later checked in the container?

Well, the thing is that dyn_cast_or_null will return null in two cases: when the base type is absent and when it's not a DIBasicType. I'd rather be able to separate the reasons why we're skipping. Skipping non-basic types is an intentional skip. As opposed to the map guard that handles valid types that are not yet emitted.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the comment also say that this is for vectors with DIBasicType?

Will do. Thanks.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

dnovillo added 2 commits June 8, 2026 10:43
This emits `DebugTypeVector` for HLSL `float4`-style vectors. It works by collecting those nodes in `collectType()` and emitting `DebugTypeVector` after the `DebugTypeBasic` loop. The collection recurses into `getBaseType()` so the element `DIBasicType` is reachable from a vector-only module.

Added a new test in `test/CodeGen/SPIRV/debug-info/debug-type-vector.ll`.
@dnovillo dnovillo force-pushed the emit-debug-type-vector branch from 4fe042c to 27c1493 Compare June 8, 2026 14:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants