https://github.com/mgcarrasco updated https://github.com/llvm/llvm-project/pull/229490
>From a56a106ef1ff8ec31a94b5977f630584f68e09a9 Mon Sep 17 00:00:00 2001 From: Manuel Carrasco <[email protected]> Date: Tue, 6 Oct 2026 11:59:19 -0500 Subject: [PATCH 1/2] [SPIRV] Forward-reference debug scopes with a reserved result id. Visiting a scope reserves its result id. With SPV_KHR_relaxed_extended_instruction a back edge uses that id through OpExtInstWithForwardRefsKHR, and a later failure defines it as DebugInfoNone when an emitted instruction already named it. Without the extension the edge is dropped and a later walk can retry the scope. --- llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp | 4 +- .../SPIRV/SPIRVNonSemanticDebugHandler.cpp | 106 +++++++++++++++--- .../SPIRV/SPIRVNonSemanticDebugHandler.h | 32 +++++- .../debug-composite-parent-debuginfonone.ll | 55 +++++++++ ...ype-composite-self-pointer-forward-refs.ll | 55 +++++++++ .../debug-typedef-base-debuginfonone.ll | 52 +++++++++ ...g-typedef-cycle-unsupported-forward-ref.ll | 42 +++++++ 7 files changed, 326 insertions(+), 20 deletions(-) create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-composite-parent-debuginfonone.ll create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-type-composite-self-pointer-forward-refs.ll create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-typedef-base-debuginfonone.ll create mode 100644 llvm/test/CodeGen/SPIRV/debug-info/debug-typedef-cycle-unsupported-forward-ref.ll diff --git a/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp b/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp index 15f1f2b1b05b3..5f8f91a9a3208 100644 --- a/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp @@ -180,7 +180,9 @@ static const StringMap<SPIRV::Extension::Extension> SPIRVExtensionMap = { SPIRV::Extension::Extension::SPV_KHR_poison_freeze}, {"SPV_KHR_untyped_pointers", SPIRV::Extension::Extension::SPV_KHR_untyped_pointers}, - {"SPV_EXT_long_vector", SPIRV::Extension::Extension::SPV_EXT_long_vector}}; + {"SPV_EXT_long_vector", SPIRV::Extension::Extension::SPV_EXT_long_vector}, + {"SPV_KHR_relaxed_extended_instruction", + SPIRV::Extension::Extension::SPV_KHR_relaxed_extended_instruction}}; bool SPIRVExtensionsParser::parse(cl::Option &O, StringRef ArgName, StringRef ArgValue, ExtensionSet &Vals) { diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp index 49757cbde9d01..33849434bc830 100644 --- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp @@ -340,6 +340,7 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) { LexicalBlocks.clear(); DebugScopeRegs.clear(); ScopesInProgress.clear(); + HasRelaxedExtInst = false; FailedScopes.clear(); DebugInlinedAtRegs.clear(); ScopeToPathOpStringReg.clear(); @@ -453,6 +454,12 @@ void SPIRVNonSemanticDebugHandler::prepareModuleOutput( // Add the extension to requirements so OpExtension is output. MAI.Reqs.addExtension(SPIRV::Extension::SPV_KHR_non_semantic_info); + HasRelaxedExtInst = ST.canUseExtension( + SPIRV::Extension::SPV_KHR_relaxed_extended_instruction); + if (HasRelaxedExtInst) + MAI.Reqs.addExtension( + SPIRV::Extension::SPV_KHR_relaxed_extended_instruction); + // Add the NonSemantic.Shader.DebugInfo.100 entry to ExtInstSetMap so that // outputOpExtInstImports() emits the OpExtInstImport instruction. Allocate a // fresh result ID for it now; the same ID is used in emitExtInst() operands. @@ -537,14 +544,39 @@ MCRegister SPIRVNonSemanticDebugHandler::emitOpConstantI32( /*Width=*/32, MAI); } +bool SPIRVNonSemanticDebugHandler::markForwardReferencedOperands( + ArrayRef<MCRegister> Operands) { + bool HasForwardRef = false; + for (auto &Entry : ScopesInProgress) { + InProgressScope &InProgress = Entry.second; + assert(InProgress.Id.isValid() && + "in-progress scope must have a reserved id"); + if (!is_contained(Operands, InProgress.Id)) + continue; + InProgress.ForwardReferenced = true; + HasForwardRef = true; + } + return HasForwardRef; +} + MCRegister SPIRVNonSemanticDebugHandler::emitExtInst( SPIRV::NonSemanticExtInst::NonSemanticExtInst Opcode, MCRegister VoidTypeReg, MCRegister ExtInstSetReg, - ArrayRef<MCRegister> Operands, SPIRV::ModuleAnalysisInfo &MAI) { - MCRegister Reg = MAI.getNextIDRegister(); + ArrayRef<MCRegister> Operands, SPIRV::ModuleAnalysisInfo &MAI, + std::optional<MCRegister> ResultReg) { + assert((!ResultReg || ResultReg->isValid()) && + "ResultReg must be a valid id when provided"); + MCRegister Reg = ResultReg ? *ResultReg : MAI.getNextIDRegister(); + + // An operand that is still an in-progress scope id is a forward reference. + // Tag the instruction with OpExtInstWithForwardRefsKHR and remember the id: + // if that scope later fails, the id still needs a definition. + bool HasForwardRef = + HasRelaxedExtInst && markForwardReferencedOperands(Operands); MCInst Inst; - Inst.setOpcode(SPIRV::OpExtInst); + Inst.setOpcode(HasForwardRef ? SPIRV::OpExtInstWithForwardRefsKHR + : SPIRV::OpExtInst); Inst.addOperand(MCOperand::createReg(Reg)); Inst.addOperand(MCOperand::createReg(VoidTypeReg)); Inst.addOperand(MCOperand::createReg(ExtInstSetReg)); @@ -760,8 +792,12 @@ MCRegister SPIRVNonSemanticDebugHandler::findOrEmitOpTypeInt32( } // Each node emits its dependencies first. E.g. for `void f() { struct L; }`, -// node L emits f, then L. For `struct S { S *p; }`, S* is a back edge, so S -// is emitted without member p. +// node L emits f, then L. Entering a scope reserves its result id. For +// `struct S { S *p; }`, S* is a back edge, and without +// SPV_KHR_relaxed_extended_instruction the edge is dropped and the reserved id +// stays unused. With it, that id is used with OpExtInstWithForwardRefsKHR. +// A later failure defines it as DebugInfoNone only when an emitted instruction +// already named it. auto SPIRVNonSemanticDebugHandler::getOrCreateDebugScope(const DIScope *S) -> EmitResult<MCRegister> { if (!S || FailedScopes.contains(S)) @@ -769,16 +805,55 @@ auto SPIRVNonSemanticDebugHandler::getOrCreateDebugScope(const DIScope *S) if (auto Reg = lookupOptReg(DebugScopeRegs, S)) return EmitResult<MCRegister>::emitted(*Reg); - // Mark the node as in progress while its dependencies are resolved/emitted. - // This also prevents cycles from happening. - if (!ScopesInProgress.insert(S).second) - return EmitResult<MCRegister>::inProgress(); + // Mark the scope node as in progress while its dependencies are + // resolved/emitted. + auto [InProgressIt, FirstVisit] = ScopesInProgress.try_emplace(S); + if (!FirstVisit) { // We hit a back edge, it is not the first insertion. + if (!HasRelaxedExtInst) + return EmitResult<MCRegister>::inProgress(); // No support for forward + // refs. + // Use the reserved id for the back edge when forward references are + // supported. + MCRegister ReservedId = InProgressIt->second.Id; + assert(ReservedId.isValid() && "in-progress scope must have a reserved id"); + return EmitResult<MCRegister>::emitted(ReservedId); + } + + // Reserve the result id first: a dependency may refer back to this scope. + assert(CurrentMAI && "debug scope id allocation requires CurrentMAI"); + MCRegister Id = CurrentMAI->getNextIDRegister(); + assert(Id.isValid() && "debug scope id must be a valid register"); + InProgressIt->second.Id = Id; EmitResult<PreparedScope> P = emitDebugScope(S); - ScopesInProgress.erase(S); - assert((!P || !GlobalNSDIEnabled) && - "debug type or scope created after module-scope NSDI emission"); + assert((!HasRelaxedExtInst || P.Status == EmitStatus::Emitted || + P.Status == EmitStatus::Unsupported) && + "InProgress is only returned when forward references are unsupported"); if (!P) { + // Emission during recursion update the container. Look the scope up again. + InProgressIt = ScopesInProgress.find(S); + assert(InProgressIt != ScopesInProgress.end() && + "scope must stay in progress until its instruction is emitted"); + bool ForwardReferenced = InProgressIt->second.ForwardReferenced; + ScopesInProgress.erase(InProgressIt); + // P here is either unsupported or in progress. When forward references are + // supported, P can only be unsupported. Define the id only when an + // already-emitted instruction named it. + if (ForwardReferenced) { + assert(HasRelaxedExtInst && + "a forward reference requires relaxed extended instructions"); + assert(P.Status == EmitStatus::Unsupported && + "a forward-referenced scope fails as unsupported"); + SPIRV::ModuleAnalysisInfo &MAI = *CurrentMAI; + emitExtInst(SPIRV::NonSemanticExtInst::DebugInfoNone, + getOrEmitOpTypeVoidReg(MAI), MAI.getExtInstSetReg(NSSet), {}, + MAI, Id); + } + + // InProgress is a dropped back edge (when forward references are not + // supported). Leave it out of FailedScopes so a later walk can retry the + // scope. Regardless of forward references, cache unsupported scopes to + // avoid retrying them. if (P.Status == EmitStatus::Unsupported) FailedScopes.insert(S); return P.Status == EmitStatus::InProgress @@ -790,7 +865,12 @@ auto SPIRVNonSemanticDebugHandler::getOrCreateDebugScope(const DIScope *S) SPIRV::ModuleAnalysisInfo &MAI = *CurrentMAI; MCRegister Reg = emitExtInst(P.Value->Opcode, getOrEmitOpTypeVoidReg(MAI), - MAI.getExtInstSetReg(NSSet), P.Value->Operands, MAI); + MAI.getExtInstSetReg(NSSet), P.Value->Operands, MAI, Id); + assert(Reg == Id && + "debug scope result id must reuse the id reserved at entry"); + ScopesInProgress.erase(S); + assert(!GlobalNSDIEnabled && + "debug type or scope created after module-scope NSDI emission"); DebugScopeRegs[S] = Reg; return EmitResult<MCRegister>::emitted(Reg); } diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h index 8e17d8cf0d39b..5a630a5a6dd8a 100644 --- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h +++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h @@ -133,8 +133,19 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase { } }; + // Used to track which scopes are in progress, and which operands are used by + // a forward references. Operands must be later defined even if the scope + // fails to emit. + struct InProgressScope { + MCRegister Id; + bool ForwardReferenced = false; + }; + // Types and scopes whose instruction is currently being built. - SmallPtrSet<const DIScope *, 8> ScopesInProgress; + SmallDenseMap<const DIScope *, InProgressScope, 8> ScopesInProgress; + + // True when prepareModuleOutput saw SPV_KHR_relaxed_extended_instruction. + bool HasRelaxedExtInst = false; // Types and scopes that are not supported. SmallPtrSet<const DIScope *, 8> FailedScopes; @@ -386,10 +397,16 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase { bool UseEmptyPathIfNullScope = false); MCRegister emitOpConstantI32(uint32_t Value, MCRegister I32TypeReg, SPIRV::ModuleAnalysisInfo &MAI); + + /// Mark in-progress scopes named by \p Operands. Return true when any + /// operand is such an id. A marked id needs a definition if that scope fails. + bool markForwardReferencedOperands(ArrayRef<MCRegister> Operands); + MCRegister emitExtInst(SPIRV::NonSemanticExtInst::NonSemanticExtInst Opcode, MCRegister VoidTypeReg, MCRegister ExtInstSetReg, ArrayRef<MCRegister> Operands, - SPIRV::ModuleAnalysisInfo &MAI); + SPIRV::ModuleAnalysisInfo &MAI, + std::optional<MCRegister> ResultReg = std::nullopt); /// Return OpTypeVoid id for this module (lazy lookup / emit, then cache). MCRegister getOrEmitOpTypeVoidReg(SPIRV::ModuleAnalysisInfo &MAI); @@ -407,10 +424,13 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase { /// Return the id for the type or scope \p S, emitting it and the types and /// scopes it names first. One already in \c DebugScopeRegs is Emitted. A null - /// \p S or one in \c FailedScopes is Unsupported. One already in - /// \c ScopesInProgress is the back edge of a cycle and is InProgress; nothing - /// is emitted for that edge. An Unsupported result is recorded in - /// \c FailedScopes, an InProgress one is not. + /// \p S or one in \c FailedScopes is Unsupported. Entering a scope reserves + /// its result id. A back edge (\p S already in \c ScopesInProgress) returns + /// that id when \c HasRelaxedExtInst is set, and InProgress otherwise. If + /// emission fails after an emitted instruction used that id, it is defined as + /// DebugInfoNone. An id that nothing named stays undefined. An Unsupported + /// result is recorded in \c FailedScopes; an InProgress one is not, so a + /// later walk can retry it. EmitResult<MCRegister> getOrCreateDebugScope(const DIScope *S); /// Build the instruction for \p S. Only \c getOrCreateDebugScope calls this diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-composite-parent-debuginfonone.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-composite-parent-debuginfonone.ll new file mode 100644 index 0000000000000..b088c6be238e8 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-composite-parent-debuginfonone.ll @@ -0,0 +1,55 @@ +; 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 %} +; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=FWD +; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | not spirv-val 2>&1 | FileCheck %s --check-prefix=VAL %} + +; struct S { int x; } is declared in namespace ns, and ns is declared in f. +; f is scoped in an unsupported enumeration. +; +; Namespaces are emitted before types, so ns is entered first and enters f +; while ns is still open. f's parameter type is S*, and S's Parent is ns, so +; S is emitted with ns's reserved id. f then fails, ns fails, and that id is +; defined as DebugInfoNone. Without the extension the back edge is dropped +; and S is not emitted. +; +; NonSemantic.Shader.DebugInfo says DebugTypeComposite's Parent must be a +; DebugCompilationUnit, DebugFunction, DebugLexicalBlock, or +; DebugTypeComposite. DebugInfoNone is not one of those. spirv-val rejects +; it, and that matches the spec. + +; CHECK-SPIRV-NOT: DebugTypeComposite +; CHECK-SPIRV-NOT: OpExtInstWithForwardRefsKHR + +; FWD: OpExtension "SPV_KHR_relaxed_extended_instruction" +; FWD: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +; FWD-DAG: [[void:%[0-9]+]] = OpTypeVoid +; FWD-DAG: [[name_s:%[0-9]+]] = OpString "S" +; FWD: [[s:%[0-9]+]] = OpExtInstWithForwardRefsKHR [[void]] [[ext]] DebugTypeComposite [[name_s]] {{%[0-9]+}} {{%[0-9]+}} {{%[0-9]+}} {{%[0-9]+}} [[parent:%[0-9]+]] +; FWD: [[parent]] = OpExtInst [[void]] [[ext]] DebugInfoNone + +; VAL: DebugTypeComposite: expected operand Parent must be a result id of a lexical scope + +define spir_func void @f(ptr addrspace(4) %p) !dbg !6 { +entry: + ret void +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!1, !2} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +!1 = !{i32 7, !"Dwarf Version", i32 5} +!2 = !{i32 2, !"Debug Info Version", i32 3} +!3 = !DIFile(filename: "ns-parent.cpp", directory: "/src") + +!6 = distinct !DISubprogram(name: "f", scope: !7, file: !3, line: 1, type: !8, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0) +!7 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "E", file: !3, line: 1, size: 32, elements: !12) +!8 = !DISubroutineType(types: !11) +!11 = !{null, !14} +!12 = !{} +!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!13 = !DINamespace(name: "ns", scope: !6) +!5 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "S", scope: !13, file: !3, line: 2, size: 32, elements: !15) +!15 = !{!16} +!16 = !DIDerivedType(tag: DW_TAG_member, name: "x", file: !3, line: 2, baseType: !9, size: 32) +!14 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !5, size: 64, dwarfAddressSpace: 4) diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-composite-self-pointer-forward-refs.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-composite-self-pointer-forward-refs.ll new file mode 100644 index 0000000000000..90e7c3f86145f --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-composite-self-pointer-forward-refs.ll @@ -0,0 +1,55 @@ +; 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 %} +; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=FWD +; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %} + +; struct S { int x; struct S *next; }; +; +; Without SPV_KHR_relaxed_extended_instruction the back edge is in progress +; while S is open, so S lists only member x. Once S and the pointer exist, the +; later walk emits member next. With the extension, the pointer is +; OpExtInstWithForwardRefsKHR naming S, and both members are emitted. + +; CHECK-SPIRV: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +; CHECK-SPIRV-DAG: [[void:%[0-9]+]] = OpTypeVoid +; CHECK-SPIRV-DAG: [[str_S:%[0-9]+]] = OpString "S" +; CHECK-SPIRV-DAG: [[str_x:%[0-9]+]] = OpString "x" +; CHECK-SPIRV-DAG: [[str_next:%[0-9]+]] = OpString "next" +; CHECK-SPIRV: [[mem_x:%[0-9]+]] = OpExtInst [[void]] [[ext]] DebugTypeMember [[str_x]] +; CHECK-SPIRV-NOT: DebugTypeMember [[str_next]] +; CHECK-SPIRV: [[comp:%[0-9]+]] = OpExtInst [[void]] [[ext]] DebugTypeComposite [[str_S]] {{.*}} [[mem_x]]{{$}} +; CHECK-SPIRV: OpExtInst [[void]] [[ext]] DebugTypePointer [[comp]] +; CHECK-SPIRV: OpExtInst [[void]] [[ext]] DebugTypeMember [[str_next]] + +; FWD: OpExtension "SPV_KHR_relaxed_extended_instruction" +; FWD: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +; FWD-DAG: [[void:%[0-9]+]] = OpTypeVoid +; FWD-DAG: [[str_S:%[0-9]+]] = OpString "S" +; FWD-DAG: [[str_next:%[0-9]+]] = OpString "next" +; FWD: [[ptr:%[0-9]+]] = OpExtInstWithForwardRefsKHR [[void]] [[ext]] DebugTypePointer [[comp:%[0-9]+]] +; FWD: [[mem_next:%[0-9]+]] = OpExtInst [[void]] [[ext]] DebugTypeMember [[str_next]] [[ptr]] +; FWD: [[comp]] = OpExtInst [[void]] [[ext]] DebugTypeComposite [[str_S]] {{.*}} [[mem_next]] + +define spir_func void @test() !dbg !6 { +entry: + ret void +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!1, !2} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !4) +!1 = !{i32 7, !"Dwarf Version", i32 5} +!2 = !{i32 2, !"Debug Info Version", i32 3} +!3 = !DIFile(filename: "self-pointer.c", directory: "/src") + +!4 = !{!5} +!5 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "S", file: !3, line: 1, size: 128, elements: !11) +!6 = distinct !DISubprogram(name: "test", scope: !3, file: !3, line: 5, type: !8, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0) +!8 = !DISubroutineType(types: !10) +!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!10 = !{null} +!11 = !{!12, !13} +!12 = !DIDerivedType(tag: DW_TAG_member, name: "x", file: !3, line: 2, baseType: !9, size: 32) +!13 = !DIDerivedType(tag: DW_TAG_member, name: "next", file: !3, line: 3, baseType: !14, size: 64, offset: 64) +!14 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !5, size: 64, dwarfAddressSpace: 4) diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-typedef-base-debuginfonone.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-typedef-base-debuginfonone.ll new file mode 100644 index 0000000000000..1883df3b35d1d --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-typedef-base-debuginfonone.ll @@ -0,0 +1,52 @@ +; 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 %} +; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=FWD +; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | not spirv-val 2>&1 | FileCheck %s --check-prefix=VAL %} + +; typedef U whose base is T, and T whose base is U. T is scoped in an +; unsupported enumeration. +; +; With SPV_KHR_relaxed_extended_instruction, U is emitted while T is open, so +; U's Base Type is T's reserved id. T then fails, and that id is defined as +; DebugInfoNone. Without the extension the back edge is dropped and neither +; typedef is emitted. +; +; NonSemantic.Shader.DebugInfo says DebugTypedef's Base Type is a debugging +; instruction for the named type, and DebugInfoNone is what other instructions +; refer to when the information is unknown. spirv-val still rejects this: it +; requires DebugTypeBasic. That check does not match the spec. + +; CHECK-SPIRV-NOT: DebugTypedef +; CHECK-SPIRV-NOT: OpExtInstWithForwardRefsKHR + +; FWD: OpExtension "SPV_KHR_relaxed_extended_instruction" +; FWD: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +; FWD-DAG: [[void:%[0-9]+]] = OpTypeVoid +; FWD-DAG: [[name_u:%[0-9]+]] = OpString "U" +; FWD: [[u:%[0-9]+]] = OpExtInstWithForwardRefsKHR [[void]] [[ext]] DebugTypedef [[name_u]] [[base:%[0-9]+]] +; FWD: [[base]] = OpExtInst [[void]] [[ext]] DebugInfoNone +; FWD-NOT: DebugTypedef + +; VAL: DebugTypedef: expected operand Base Type must be a result id of DebugTypeBasic + +define spir_func void @test() !dbg !6 { +entry: + ret void +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!1, !2} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !4) +!1 = !{i32 7, !"Dwarf Version", i32 5} +!2 = !{i32 2, !"Debug Info Version", i32 3} +!3 = !DIFile(filename: "typedef-none.c", directory: "/src") + +!4 = !{!5} +!5 = !DIDerivedType(tag: DW_TAG_typedef, name: "T", file: !3, line: 2, baseType: !8, scope: !7) +!6 = distinct !DISubprogram(name: "test", scope: !3, file: !3, line: 5, type: !10, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0) +!7 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "E", file: !3, line: 1, size: 32, elements: !12) +!8 = !DIDerivedType(tag: DW_TAG_typedef, name: "U", file: !3, line: 3, baseType: !5, scope: !3) +!10 = !DISubroutineType(types: !11) +!11 = !{null} +!12 = !{} diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-typedef-cycle-unsupported-forward-ref.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-typedef-cycle-unsupported-forward-ref.ll new file mode 100644 index 0000000000000..f7381dc35eb35 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-typedef-cycle-unsupported-forward-ref.ll @@ -0,0 +1,42 @@ +; 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 %} +; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=FWD +; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %} + +; Typedef T whose base is a pointer back to T, scoped in an enumeration type. +; The enumeration is unsupported, so T fails after the pointer has already named +; T's forward id. With SPV_KHR_relaxed_extended_instruction that id is defined +; as DebugInfoNone; without it both edges are dropped. + +; CHECK-SPIRV-NOT: DebugTypedef +; CHECK-SPIRV-NOT: DebugTypePointer +; CHECK-SPIRV-NOT: OpExtInstWithForwardRefsKHR + +; FWD: OpExtension "SPV_KHR_relaxed_extended_instruction" +; FWD: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +; FWD-DAG: [[void:%[0-9]+]] = OpTypeVoid +; FWD: OpExtInstWithForwardRefsKHR [[void]] [[ext]] DebugTypePointer [[none:%[0-9]+]] +; FWD: [[none]] = OpExtInst [[void]] [[ext]] DebugInfoNone +; FWD-NOT: DebugTypedef + +define spir_func void @test() !dbg !6 { +entry: + ret void +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!1, !2} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !4) +!1 = !{i32 7, !"Dwarf Version", i32 5} +!2 = !{i32 2, !"Debug Info Version", i32 3} +!3 = !DIFile(filename: "typedef-cycle.c", directory: "/src") + +!4 = !{!5, !8} +!5 = !DIDerivedType(tag: DW_TAG_typedef, name: "T", file: !3, line: 2, baseType: !8, scope: !7) +!6 = distinct !DISubprogram(name: "test", scope: !3, file: !3, line: 5, type: !10, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0) +!7 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "E", file: !3, line: 1, size: 32, elements: !12) +!8 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !5, size: 64, dwarfAddressSpace: 4) +!10 = !DISubroutineType(types: !11) +!11 = !{null} +!12 = !{} >From 687227f111e9f1c5c2dcc600b05ebc60b24a4b4f Mon Sep 17 00:00:00 2001 From: Manuel Carrasco <[email protected]> Date: Wed, 7 Oct 2026 05:24:50 -0500 Subject: [PATCH 2/2] [SPIRV] Gate debug-scope forward references behind a flag. -spirv-debug-scope-forward-refs defaults to off. --- .../SPIRV/SPIRVNonSemanticDebugHandler.cpp | 22 +++++++++++++------ .../SPIRV/SPIRVNonSemanticDebugHandler.h | 3 ++- .../debug-composite-parent-debuginfonone.ll | 10 +++++---- ...ype-composite-self-pointer-forward-refs.ll | 7 +++--- .../debug-typedef-base-debuginfonone.ll | 14 ++++-------- ...g-typedef-cycle-unsupported-forward-ref.ll | 7 +++--- 6 files changed, 35 insertions(+), 28 deletions(-) diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp index 33849434bc830..d0412e95ed213 100644 --- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp @@ -32,6 +32,7 @@ #include "llvm/IR/Module.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCStreamer.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/Path.h" @@ -39,6 +40,12 @@ using namespace llvm; +static cl::opt<bool> SPIRVDebugScopeForwardRefs( + "spirv-debug-scope-forward-refs", + cl::desc("Forward-reference debug scopes when " + "SPV_KHR_relaxed_extended_instruction is enabled"), + cl::init(false)); + namespace { /// Look up \p Key in a register map and return its value, or std::nullopt when @@ -454,8 +461,10 @@ void SPIRVNonSemanticDebugHandler::prepareModuleOutput( // Add the extension to requirements so OpExtension is output. MAI.Reqs.addExtension(SPIRV::Extension::SPV_KHR_non_semantic_info); - HasRelaxedExtInst = ST.canUseExtension( - SPIRV::Extension::SPV_KHR_relaxed_extended_instruction); + HasRelaxedExtInst = + SPIRVDebugScopeForwardRefs && + ST.canUseExtension( + SPIRV::Extension::SPV_KHR_relaxed_extended_instruction); if (HasRelaxedExtInst) MAI.Reqs.addExtension( SPIRV::Extension::SPV_KHR_relaxed_extended_instruction); @@ -793,11 +802,10 @@ MCRegister SPIRVNonSemanticDebugHandler::findOrEmitOpTypeInt32( // Each node emits its dependencies first. E.g. for `void f() { struct L; }`, // node L emits f, then L. Entering a scope reserves its result id. For -// `struct S { S *p; }`, S* is a back edge, and without -// SPV_KHR_relaxed_extended_instruction the edge is dropped and the reserved id -// stays unused. With it, that id is used with OpExtInstWithForwardRefsKHR. -// A later failure defines it as DebugInfoNone only when an emitted instruction -// already named it. +// `struct S { S *p; }`, S* is a back edge. When forward references are +// enabled, that id is used with OpExtInstWithForwardRefsKHR, and a later +// failure defines it as DebugInfoNone only when an emitted instruction already +// named it. Otherwise the edge is dropped and the reserved id stays unused. auto SPIRVNonSemanticDebugHandler::getOrCreateDebugScope(const DIScope *S) -> EmitResult<MCRegister> { if (!S || FailedScopes.contains(S)) diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h index 5a630a5a6dd8a..e0f351238bace 100644 --- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h +++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h @@ -144,7 +144,8 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase { // Types and scopes whose instruction is currently being built. SmallDenseMap<const DIScope *, InProgressScope, 8> ScopesInProgress; - // True when prepareModuleOutput saw SPV_KHR_relaxed_extended_instruction. + // True when -spirv-debug-scope-forward-refs is set and prepareModuleOutput + // saw SPV_KHR_relaxed_extended_instruction. bool HasRelaxedExtInst = false; // Types and scopes that are not supported. diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-composite-parent-debuginfonone.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-composite-parent-debuginfonone.ll index b088c6be238e8..c6bb31bac1d00 100644 --- a/llvm/test/CodeGen/SPIRV/debug-info/debug-composite-parent-debuginfonone.ll +++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-composite-parent-debuginfonone.ll @@ -1,7 +1,9 @@ ; 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 %} -; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=FWD -; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | not spirv-val 2>&1 | FileCheck %s --check-prefix=VAL %} +; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction --spirv-debug-scope-forward-refs=true -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=FWD +; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction --spirv-debug-scope-forward-refs=true -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | not spirv-val 2>&1 | FileCheck %s --check-prefix=VAL %} +; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction --spirv-debug-scope-forward-refs=0 -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,+SPV_KHR_relaxed_extended_instruction --spirv-debug-scope-forward-refs=0 -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %} ; struct S { int x; } is declared in namespace ns, and ns is declared in f. ; f is scoped in an unsupported enumeration. @@ -9,8 +11,8 @@ ; Namespaces are emitted before types, so ns is entered first and enters f ; while ns is still open. f's parameter type is S*, and S's Parent is ns, so ; S is emitted with ns's reserved id. f then fails, ns fails, and that id is -; defined as DebugInfoNone. Without the extension the back edge is dropped -; and S is not emitted. +; defined as DebugInfoNone. -spirv-debug-scope-forward-refs defaults to off, +; so without that flag the back edge is dropped and S is not emitted. ; ; NonSemantic.Shader.DebugInfo says DebugTypeComposite's Parent must be a ; DebugCompilationUnit, DebugFunction, DebugLexicalBlock, or diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-composite-self-pointer-forward-refs.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-composite-self-pointer-forward-refs.ll index 90e7c3f86145f..2e4e4402e5de6 100644 --- a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-composite-self-pointer-forward-refs.ll +++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-composite-self-pointer-forward-refs.ll @@ -1,13 +1,14 @@ ; 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 %} -; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=FWD -; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %} +; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction --spirv-debug-scope-forward-refs=true -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=FWD +; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction --spirv-debug-scope-forward-refs=true -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %} ; struct S { int x; struct S *next; }; ; ; Without SPV_KHR_relaxed_extended_instruction the back edge is in progress ; while S is open, so S lists only member x. Once S and the pointer exist, the -; later walk emits member next. With the extension, the pointer is +; later walk emits member next. With the extension and +; -spirv-debug-scope-forward-refs, the pointer is ; OpExtInstWithForwardRefsKHR naming S, and both members are emitted. ; CHECK-SPIRV: [[ext:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-typedef-base-debuginfonone.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-typedef-base-debuginfonone.ll index 1883df3b35d1d..b53835550a76a 100644 --- a/llvm/test/CodeGen/SPIRV/debug-info/debug-typedef-base-debuginfonone.ll +++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-typedef-base-debuginfonone.ll @@ -1,20 +1,16 @@ ; 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 %} -; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=FWD -; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | not spirv-val 2>&1 | FileCheck %s --check-prefix=VAL %} +; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction --spirv-debug-scope-forward-refs=true -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=FWD +; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction --spirv-debug-scope-forward-refs=true -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %} ; typedef U whose base is T, and T whose base is U. T is scoped in an ; unsupported enumeration. ; -; With SPV_KHR_relaxed_extended_instruction, U is emitted while T is open, so +; With SPV_KHR_relaxed_extended_instruction and +; -spirv-debug-scope-forward-refs, U is emitted while T is open, so ; U's Base Type is T's reserved id. T then fails, and that id is defined as ; DebugInfoNone. Without the extension the back edge is dropped and neither ; typedef is emitted. -; -; NonSemantic.Shader.DebugInfo says DebugTypedef's Base Type is a debugging -; instruction for the named type, and DebugInfoNone is what other instructions -; refer to when the information is unknown. spirv-val still rejects this: it -; requires DebugTypeBasic. That check does not match the spec. ; CHECK-SPIRV-NOT: DebugTypedef ; CHECK-SPIRV-NOT: OpExtInstWithForwardRefsKHR @@ -27,8 +23,6 @@ ; FWD: [[base]] = OpExtInst [[void]] [[ext]] DebugInfoNone ; FWD-NOT: DebugTypedef -; VAL: DebugTypedef: expected operand Base Type must be a result id of DebugTypeBasic - define spir_func void @test() !dbg !6 { entry: ret void diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-typedef-cycle-unsupported-forward-ref.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-typedef-cycle-unsupported-forward-ref.ll index f7381dc35eb35..5f4fb6653c4b4 100644 --- a/llvm/test/CodeGen/SPIRV/debug-info/debug-typedef-cycle-unsupported-forward-ref.ll +++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-typedef-cycle-unsupported-forward-ref.ll @@ -1,11 +1,12 @@ ; 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 %} -; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=FWD -; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %} +; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction --spirv-debug-scope-forward-refs=true -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=FWD +; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info,+SPV_KHR_relaxed_extended_instruction --spirv-debug-scope-forward-refs=true -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %} ; Typedef T whose base is a pointer back to T, scoped in an enumeration type. ; The enumeration is unsupported, so T fails after the pointer has already named -; T's forward id. With SPV_KHR_relaxed_extended_instruction that id is defined +; T's forward id. With SPV_KHR_relaxed_extended_instruction and +; -spirv-debug-scope-forward-refs that id is defined ; as DebugInfoNone; without it both edges are dropped. ; CHECK-SPIRV-NOT: DebugTypedef _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
