Emit debug type vector (#200056)
This emits `DebugTypeVector` for HLSL `float4`-style vectors. `partitionTypes()` separates vector `DICompositeType` nodes from basic types so both can be visited in a single pass over the debug metadata. A new `emitDebugTypeVector()` helper builds the `DebugTypeVector` instruction and looks up the base-type register in `DebugTypeRegs`. The helper skips four cases silently: 1. Absent or non-`DIBasicType` base type: only scalar element types are supported for now. 2. Base type not yet emitted: the type was not reached during the `DebugTypeBasic` pass. 3. Multiple subranges: `DebugTypeVector` models one-dimensional vectors only (NSDI cannot encode multi-subrange types). 4. Non-constant subrange count: NSDI cannot represent variable-length counts. Added a new test in `test/CodeGen/SPIRV/debug-info/debug-type-vector.ll`.
This commit is contained in:
parent
274331053c
commit
056b4a71c0
4 changed files with 195 additions and 8 deletions
|
|
@ -27,15 +27,17 @@ using namespace llvm;
|
|||
|
||||
namespace {
|
||||
|
||||
/// Partition \p Ty into \p BasicTypes, \p PointerTypes, and \p SubroutineTypes
|
||||
/// for NSDI emission. Used when iterating DebugInfoFinder.types(); each DI
|
||||
/// node is seen once, so no recursion into pointer bases. Other composites and
|
||||
/// non-pointer derived kinds are ignored because they are not yet supported.
|
||||
/// Only types that are supported (later used) are partitioned.
|
||||
/// Partition \p Ty into \p BasicTypes, \p PointerTypes, \p SubroutineTypes,
|
||||
/// and \p VectorTypes for NSDI emission. Used when iterating
|
||||
/// DebugInfoFinder.types(); each DI node is seen once, so no recursion into
|
||||
/// pointer bases. Other composites and non-pointer derived kinds are ignored
|
||||
/// because they are not yet supported. Only types that are supported (later
|
||||
/// used) are partitioned.
|
||||
static void
|
||||
partitionTypes(const DIType *Ty, SmallVector<const DIBasicType *> &BasicTypes,
|
||||
SmallVector<const DIDerivedType *> &PointerTypes,
|
||||
SmallVector<const DISubroutineType *> &SubroutineTypes) {
|
||||
SmallVector<const DISubroutineType *> &SubroutineTypes,
|
||||
SmallVector<const DICompositeType *> &VectorTypes) {
|
||||
if (const auto *BT = dyn_cast<DIBasicType>(Ty)) {
|
||||
BasicTypes.push_back(BT);
|
||||
return;
|
||||
|
|
@ -44,6 +46,11 @@ partitionTypes(const DIType *Ty, SmallVector<const DIBasicType *> &BasicTypes,
|
|||
SubroutineTypes.push_back(ST);
|
||||
return;
|
||||
}
|
||||
if (const auto *CT = dyn_cast<DICompositeType>(Ty)) {
|
||||
if (CT->getTag() == dwarf::DW_TAG_array_type && CT->isVector())
|
||||
VectorTypes.push_back(CT);
|
||||
return;
|
||||
}
|
||||
const auto *DT = dyn_cast<DIDerivedType>(Ty);
|
||||
if (DT && DT->getTag() == dwarf::DW_TAG_pointer_type)
|
||||
PointerTypes.push_back(DT);
|
||||
|
|
@ -173,6 +180,7 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
|
|||
BasicTypes.clear();
|
||||
PointerTypes.clear();
|
||||
SubroutineTypes.clear();
|
||||
VectorTypes.clear();
|
||||
DebugTypeRegs.clear();
|
||||
OpStringContentCache.clear();
|
||||
I32ConstantCache.clear();
|
||||
|
|
@ -218,7 +226,7 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
|
|||
DebugInfoFinder Finder;
|
||||
Finder.processModule(*M);
|
||||
llvm::for_each(Finder.types(), [&](DIType *Ty) {
|
||||
partitionTypes(Ty, BasicTypes, PointerTypes, SubroutineTypes);
|
||||
partitionTypes(Ty, BasicTypes, PointerTypes, SubroutineTypes, VectorTypes);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -460,6 +468,34 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::mapDISignatureTypeToReg(
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugTypeVector(
|
||||
const DICompositeType *VT, MCRegister ExtInstSetReg,
|
||||
SPIRV::ModuleAnalysisInfo &MAI) {
|
||||
const auto *BaseTy = dyn_cast_or_null<DIBasicType>(VT->getBaseType());
|
||||
if (!BaseTy)
|
||||
return std::nullopt;
|
||||
auto BTIt = DebugTypeRegs.find(BaseTy);
|
||||
if (BTIt == DebugTypeRegs.end())
|
||||
return std::nullopt;
|
||||
|
||||
// DebugTypeVector models only 1D vectors (multi-subrange types cannot be
|
||||
// encoded).
|
||||
DINodeArray Elements = VT->getElements();
|
||||
if (Elements.size() != 1)
|
||||
return std::nullopt;
|
||||
const auto *SR = cast<DISubrange>(Elements[0]);
|
||||
const auto *CI = dyn_cast_if_present<ConstantInt *>(SR->getCount());
|
||||
if (!CI)
|
||||
return std::nullopt;
|
||||
|
||||
MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
|
||||
MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI);
|
||||
MCRegister CountReg = emitOpConstantI32(
|
||||
static_cast<uint32_t>(CI->getZExtValue()), I32TypeReg, MAI);
|
||||
return emitExtInst(SPIRV::NonSemanticExtInst::DebugTypeVector, VoidTypeReg,
|
||||
ExtInstSetReg, {BTIt->second, CountReg}, MAI);
|
||||
}
|
||||
|
||||
void SPIRVNonSemanticDebugHandler::emitNonSemanticDebugStrings(
|
||||
SPIRV::ModuleAnalysisInfo &MAI) {
|
||||
if (CompileUnits.empty())
|
||||
|
|
@ -587,6 +623,12 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
|
|||
DebugTypeRegs[BT] = BTReg;
|
||||
}
|
||||
|
||||
// Emit DebugTypeVector for each collected vector type.
|
||||
for (const DICompositeType *VT : VectorTypes) {
|
||||
if (auto VecReg = emitDebugTypeVector(VT, ExtInstSetReg, MAI))
|
||||
DebugTypeRegs[VT] = *VecReg;
|
||||
}
|
||||
|
||||
// Emit DebugTypePointer for each referenced pointer type.
|
||||
for (const DIDerivedType *PT : PointerTypes) {
|
||||
if (auto PtrReg = emitDebugTypePointer(PT, ExtInstSetReg, MAI))
|
||||
|
|
|
|||
|
|
@ -61,10 +61,13 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
|
|||
int64_t DwarfVersion = 0;
|
||||
|
||||
// DI types partitioned from DebugInfoFinder.types() in beginModule()
|
||||
// (basics, pointers, subroutine types NSDI v1 may emit).
|
||||
// (basics, pointers, vectors, subroutine types NSDI v1 may emit).
|
||||
SmallVector<const DIBasicType *> BasicTypes;
|
||||
SmallVector<const DIDerivedType *> PointerTypes;
|
||||
SmallVector<const DISubroutineType *> SubroutineTypes;
|
||||
// DICompositeType nodes with DW_TAG_array_type and DINode::FlagVector,
|
||||
// partitioned from DebugInfoFinder.types() in beginModule().
|
||||
SmallVector<const DICompositeType *> VectorTypes;
|
||||
|
||||
// Filled in emitNonSemanticGlobalDebugInfo(): DI types to their result
|
||||
// registers.
|
||||
|
|
@ -216,6 +219,16 @@ private:
|
|||
MCRegister ExtInstSetReg,
|
||||
SPIRV::ModuleAnalysisInfo &MAI);
|
||||
|
||||
/// Emit \c DebugTypeVector for the vector composite type \p VT.
|
||||
///
|
||||
/// \returns The result id register on success. Returns \c std::nullopt and
|
||||
/// emits nothing if \p VT has no \c DIBasicType base type, if the base type
|
||||
/// has not been emitted yet, if \p VT has more than one \c DISubrange
|
||||
/// element, or if the component count is not a compile-time constant.
|
||||
std::optional<MCRegister> emitDebugTypeVector(const DICompositeType *VT,
|
||||
MCRegister ExtInstSetReg,
|
||||
SPIRV::ModuleAnalysisInfo &MAI);
|
||||
|
||||
/// Map a \c DISubroutineType::getTypeArray() element to an operand register
|
||||
/// for
|
||||
/// \c DebugTypeFunction. Non-null \p Ty resolves via \c DebugTypeRegs; if the
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
; 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 %}
|
||||
|
||||
; Verify that DICompositeType vector nodes that cannot be lowered to
|
||||
; DebugTypeVector are silently skipped. Four conditions trigger the skip:
|
||||
; 1. No base type (getBaseType() returns null).
|
||||
; 2. Non-DIBasicType base type (struct type as element type).
|
||||
; 3. More than one DISubrange element (multi-dimensional array shape).
|
||||
; 4. Non-constant subrange count (DILocalVariable used as component count).
|
||||
;
|
||||
; None of the four vectors below should produce a DebugTypeVector instruction.
|
||||
|
||||
; CHECK-NOT: DebugTypeVector
|
||||
|
||||
define spir_func void @test() !dbg !6 {
|
||||
entry:
|
||||
%v1 = alloca <4 x float>, align 16
|
||||
%v2 = alloca <4 x float>, align 16
|
||||
%v3 = alloca <4 x i32>, align 16
|
||||
%v4 = alloca <4 x i64>, align 32
|
||||
#dbg_declare(ptr %v1, !10, !DIExpression(), !30)
|
||||
#dbg_declare(ptr %v2, !12, !DIExpression(), !30)
|
||||
#dbg_declare(ptr %v3, !15, !DIExpression(), !30)
|
||||
#dbg_declare(ptr %v4, !20, !DIExpression(), !30)
|
||||
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: "skip.hlsl", directory: "/src")
|
||||
!2 = !{i32 7, !"Dwarf Version", i32 5}
|
||||
!3 = !{i32 2, !"Debug Info Version", i32 3}
|
||||
!4 = !DISubroutineType(types: !5)
|
||||
!5 = !{null}
|
||||
!6 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 1, type: !4, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9)
|
||||
!9 = !{}
|
||||
|
||||
; Case 1: vector with no base type -- skipped at dyn_cast_or_null<DIBasicType>.
|
||||
!10 = !DILocalVariable(name: "v1", scope: !6, file: !1, line: 2, type: !11)
|
||||
!11 = !DICompositeType(tag: DW_TAG_array_type, size: 128, flags: DIFlagVector, elements: !29)
|
||||
|
||||
; Case 2: vector with non-DIBasicType base (struct) -- dyn_cast_or_null<DIBasicType>
|
||||
; returns null, so skipped at the same check as case 1.
|
||||
!12 = !DILocalVariable(name: "v2", scope: !6, file: !1, line: 3, type: !13)
|
||||
!13 = !DICompositeType(tag: DW_TAG_array_type, baseType: !14, size: 128, flags: DIFlagVector, elements: !29)
|
||||
!14 = !DICompositeType(tag: DW_TAG_structure_type, name: "S", file: !1, size: 32)
|
||||
|
||||
; Case 3: vector with multiple subranges -- skipped at Elements.size() != 1.
|
||||
!15 = !DILocalVariable(name: "v3", scope: !6, file: !1, line: 4, type: !16)
|
||||
!16 = !DICompositeType(tag: DW_TAG_array_type, baseType: !17, size: 128, flags: DIFlagVector, elements: !18)
|
||||
!17 = !DIBasicType(name: "uint", size: 32, encoding: DW_ATE_unsigned)
|
||||
!18 = !{!DISubrange(count: 4), !DISubrange(count: 4)}
|
||||
|
||||
; Case 4: vector with a non-constant subrange count (DILocalVariable) --
|
||||
; skipped at dyn_cast_if_present<ConstantInt *>.
|
||||
!20 = !DILocalVariable(name: "v4", scope: !6, file: !1, line: 5, type: !21)
|
||||
!21 = !DICompositeType(tag: DW_TAG_array_type, baseType: !22, size: 256, flags: DIFlagVector, elements: !27)
|
||||
!22 = !DIBasicType(name: "int64", size: 64, encoding: DW_ATE_signed)
|
||||
!23 = !DILocalVariable(name: "n", scope: !6, file: !1, line: 1, type: !24)
|
||||
!24 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
|
||||
!27 = !{!DISubrange(count: !23)}
|
||||
|
||||
!29 = !{!DISubrange(count: 4)}
|
||||
!30 = !DILocation(line: 2, column: 1, scope: !6)
|
||||
66
llvm/test/CodeGen/SPIRV/debug-info/debug-type-vector.ll
Normal file
66
llvm/test/CodeGen/SPIRV/debug-info/debug-type-vector.ll
Normal file
|
|
@ -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)
|
||||
Loading…
Reference in a new issue