Emit debug type vector#200056
Conversation
|
@mgcarrasco this is the next one in the debug type series. |
|
@llvm/pr-subscribers-backend-spir-v Author: Diego Novillo (dnovillo) ChangesNOTE: This PR depends on #200016 (which is included here so the bots don't fail) This emits Added a new test in Full diff: https://github.com/llvm/llvm-project/pull/200056.diff 3 Files Affected:
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)
|
🐧 Linux x64 Test Results
✅ 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) |
There was a problem hiding this comment.
Is the null check necessary if it is later checked in the container?
There was a problem hiding this comment.
Should the comment also say that this is for vectors with DIBasicType?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Should the comment also say that this is for vectors with DIBasicType?
Will do. Thanks.
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`.
4fe042c to
27c1493
Compare
NOTE: This PR depends on #200016 (which is included here so the bots don't fail)
This emits
DebugTypeVectorfor HLSLfloat4-style vectors. It works by collecting those nodes incollectType()and emittingDebugTypeVectorafter theDebugTypeBasicloop. The collection recurses intogetBaseType()so the elementDIBasicTypeis reachable from a vector-only module.Added a new test in
test/CodeGen/SPIRV/debug-info/debug-type-vector.ll.