From: Viktoria Maximova Date: Mon, 18 May 2026 12:29:20 +0200 Subject: [07/13] [Backport to 22] [DebugInfo] Fix DebugFunction Parent when DISubprogram is scoped in DIModule (#3753) Origin: upstream, https://github.com/KhronosGroup/SPIRV-LLVM-Translator/commit/d7f4a19bbdadc81b6cb4c30fa0a2e7576f45c768 NonSemantic.Shader.DebugInfo requires DebugFunction's Parent operand to be a lexical scope, defined by the spec as one of DebugCompilationUnit, DebugFunction, DebugLexicalBlock, or DebugTypeComposite. DebugModule (introduced in .200) is not a lexical scope, and spirv-val rejects it. When a DISubprogram's scope is a DIModule, fall back to the enclosing DebugCompilationUnit, matching the existing handling for DIFile. AI-assisted: Claude Opus 4.7 (commercial SaaS) --- lib/SPIRV/LLVMToSPIRVDbgTran.cpp | 9 ++++++++- .../NonSemantic/Shader200/DebugLineOnlyOnce.ll | 11 ++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/SPIRV/LLVMToSPIRVDbgTran.cpp b/lib/SPIRV/LLVMToSPIRVDbgTran.cpp index 1f65ef12..7462da8d 100644 --- a/lib/SPIRV/LLVMToSPIRVDbgTran.cpp +++ b/lib/SPIRV/LLVMToSPIRVDbgTran.cpp @@ -1247,7 +1247,14 @@ SPIRVEntry *LLVMToSPIRVDbgTran::transDbgFunction(const DISubprogram *Func) { Ops[LineIdx] = Func->getLine(); Ops[ColumnIdx] = 0; // This version of DISubprogram has no column number auto *Scope = Func->getScope(); - if (Scope && !isa(Scope)) { + // NonSemantic.Shader.DebugInfo requires DebugFunction Parent to be a + // lexical scope: DebugCompilationUnit, DebugFunction, DebugLexicalBlock or + // DebugTypeComposite. + // DebugModule (introduced in .200) is not a lexical scope, so fall back to + // the compile unit when the DISubprogram is scoped inside a DIModule. + bool ScopeIsLexical = Scope && !isa(Scope) && + !(isNonSemanticDebugInfo() && isa(Scope)); + if (ScopeIsLexical) { Ops[ParentIdx] = getScope(Scope)->getId(); } else { if (auto *Unit = Func->getUnit()) diff --git a/test/DebugInfo/NonSemantic/Shader200/DebugLineOnlyOnce.ll b/test/DebugInfo/NonSemantic/Shader200/DebugLineOnlyOnce.ll index 3de5644a..e7631d82 100644 --- a/test/DebugInfo/NonSemantic/Shader200/DebugLineOnlyOnce.ll +++ b/test/DebugInfo/NonSemantic/Shader200/DebugLineOnlyOnce.ll @@ -1,4 +1,13 @@ ; This test verifies that there are no duplicates of any DebugLine extended instructions. +; +; It also checks that when a DISubprogram is scoped inside a DIModule (typical +; for Fortran module subroutines), DebugFunction.Parent falls back to the +; enclosing DebugCompilationUnit. NonSemantic.Shader.DebugInfo requires Parent +; to be a lexical scope, and DebugModule (introduced in .200) is not one. +; +; TODO: DILocations with line: 0 (artificial locations, allowed by LLVM IR) +; are replaced with line: 1 here so spirv-val accepts the output. Remove this +; workaround once the translator maps line-0 DILocations to DebugNoLine. ; RUN: llvm-as %s -o %t.bc ; RUN: llvm-spirv %t.bc -o %t.spv --spirv-debug-info-version=nonsemantic-shader-200 @@ -56,7 +65,7 @@ attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memo !3406 = !DILocalVariable(name: "k", scope: !3336, file: !52, line: 40, type: !15) !3409 = !DILocalVariable(name: "jj", scope: !3336, file: !52, line: 40, type: !15) !3411 = !DILocalVariable(name: "ii", scope: !3336, file: !52, line: 40, type: !15) -!3447 = !DILocation(line: 0, scope: !3336) +!3447 = !DILocation(line: 1, scope: !3336) !3578 = !DILocation(line: 182, column: 2, scope: !3336) !3579 = !DILocation(line: 183, column: 2, scope: !3336) !3583 = !DILocation(line: 227, column: 2, scope: !3336) -- 2.55.0