Add DebugBuildIdentifier to the live worklist during initialization (#6626)
DebugBuildIdentifier was missing from the set of NonSemantic debug
instructions added to the worklist in
InitializeModuleScopeLiveInstructions. Instead it was handled in
ProcessGlobalValues (after the worklist is exhausted) using
live_insts_.Set() on its direct operands.
live_insts_.Set() marks an instruction live but does not enqueue it, so
transitive operand dependencies are never visited. In shaders where the
only use of a type (e.g. OpTypeInt) is through a constant that is itself
only referenced by DebugBuildIdentifier's flags argument, the type
instruction is not marked live and is incorrectly killed by ADCE.
The surviving constant then references a deleted type, producing invalid
SPIR-V. Any subsequent pass that rebuilds the DefUseManager (e.g. CCP)
will fail with "Definition is not registered."
Fix: include NonSemanticShaderDebugInfo100DebugBuildIdentifier in the
worklist loop alongside the other always-live debug instructions. The
worklist's transitive closure correctly marks all operand dependencies
live before global-value cleanup runs. The existing special-case code in
ProcessGlobalValues becomes unreachable for this opcode (IsLive returns
true, so the early continue fires) and is now dead; it is left in
placefor safety.
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp
index 698d4e6..8ca2518 100644
--- a/source/opt/aggressive_dead_code_elim_pass.cpp
+++ b/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -768,7 +768,8 @@
op == NonSemanticShaderDebugInfoDebugSourceContinued ||
op == NonSemanticShaderDebugInfoDebugLocalVariable ||
op == NonSemanticShaderDebugInfoDebugExpression ||
- op == NonSemanticShaderDebugInfoDebugOperation) {
+ op == NonSemanticShaderDebugInfoDebugOperation ||
+ op == NonSemanticShaderDebugInfoDebugBuildIdentifier) {
AddToWorklist(&dbg);
}
}
diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp
index 9665a18..2763afb 100644
--- a/test/opt/aggressive_dead_code_elim_test.cpp
+++ b/test/opt/aggressive_dead_code_elim_test.cpp
@@ -9218,6 +9218,45 @@
SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
}
+TEST_F(AggressiveDCETest, KeepDebugBuildIdentifier) {
+ // Regression test for https://github.com/KhronosGroup/SPIRV-Tools/issues/6619
+ //
+ // DebugBuildIdentifier was not added to the live-instruction worklist during
+ // initialization, so its operand dependencies (e.g. OpTypeInt used only by
+ // a constant that is only referenced by DebugBuildIdentifier) were never
+ // visited and were incorrectly eliminated. The surviving constant then
+ // referenced a deleted type, producing invalid SPIR-V.
+ //
+ // After the fix, DebugBuildIdentifier is enqueued in the worklist so its
+ // transitive operands (OpTypeInt 32 0, OpConstant %uint 0) are marked live.
+
+ const std::string spirv = R"(
+; CHECK: [[ext:%\w+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK: [[str:%\w+]] = OpString
+; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
+; CHECK: [[uint_0:%\w+]] = OpConstant [[uint]] 0
+; CHECK: OpExtInst %void [[ext]] DebugBuildIdentifier [[str]] [[uint_0]]
+ OpCapability Shader
+ OpExtension "SPV_KHR_non_semantic_info"
+ %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ %2 = OpString "01cfb4b77c321225f096da8ac72f29d42f0632a7"
+ %void = OpTypeVoid
+ %uint = OpTypeInt 32 0
+ %uint_0 = OpConstant %uint 0
+ %3 = OpTypeFunction %void
+ %4 = OpExtInst %void %1 DebugBuildIdentifier %2 %uint_0
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools