Repository: KhronosGroup/SPIRV-Tools (language: python)

Issue:
Eliminate dead functions producing inconsistent DefUse manager
I ran into this from an OpenCL-CTS kernel running through clspv on top of swiftshader. Here is a reasonably small test file:
```
OpCapability Shader
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpExtension "SPV_KHR_non_semantic_info"
%587 = OpExtInstImport "GLSL.std.450"
%737 = OpExtInstImport "NonSemantic.ClspvReflection.5"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %542 "test"
%738 = OpString "test"
%741 = OpString "out"
%744 = OpString "in"
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%void = OpTypeVoid
%541 = OpTypeFunction %void
%628 = OpTypeFunction %float %float
%uint_0 = OpConstant %uint 0
%uint_1 = OpConstant %uint 1
%uint_2 = OpConstant %uint 2
%float_0 = OpConstant %float 0
%542 = OpFunction %void None %541
%543 = OpLabel
OpReturn
OpFunctionEnd
%629 = OpFunction %float None %628
%630 = OpFunctionParameter %float
%631 = OpLabel
OpReturnValue %float_0
OpFunctionEnd
%740 = OpExtInst %void %737 Kernel %542 %738 %uint_2
%742 = OpExtInst %void %737 ArgumentInfo %741
%743 = OpExtInst %void %737 ArgumentStorageBuffer %740 %uint_0 %uint_0 %uint_0 %742
```

Run:
```
spirv-opt test.spv -o tmp.spv --eliminate-dead-functions
```

There is an assertion:
> Assertion failed: (unique_id_ != 0), function unique_id, file instruction.h, line 251.

It comes from attempting to print the differences after detecting an inconsistency in the DefUseManager.


Discussion / hints:

Fix patch:
diff --git a/source/opt/eliminate_dead_functions_util.cpp b/source/opt/eliminate_dead_functions_util.cpp
index cf7f92f550..e95b7f6a86 100644
--- a/source/opt/eliminate_dead_functions_util.cpp
+++ b/source/opt/eliminate_dead_functions_util.cpp
@@ -37,7 +37,9 @@ Module::iterator EliminateFunction(IRContext* context,
               assert(inst->IsNonSemanticInstruction());
               if (to_kill.find(inst) != to_kill.end()) return;
               std::unique_ptr<Instruction> clone(inst->Clone(context));
-              context->ForgetUses(inst);
+              // Clear uses of "inst" to in case this moves a dependent chain of
+              // instructions.
+              context->get_def_use_mgr()->ClearInst(inst);
               context->AnalyzeDefUse(clone.get());
               if (first_func) {
                 context->AddGlobalValue(std::move(clone));


Test patch:
diff --git a/test/opt/eliminate_dead_functions_test.cpp b/test/opt/eliminate_dead_functions_test.cpp
index 96deb2a68c..e9f79a10f2 100644
--- a/test/opt/eliminate_dead_functions_test.cpp
+++ b/test/opt/eliminate_dead_functions_test.cpp
@@ -517,6 +517,39 @@ OpFunctionEnd
   SinglePassRunAndMatch<EliminateDeadFunctionsPass>(text, true);
 }
 
+TEST_F(EliminateDeadFunctionsBasicTest, DependentNonSemanticChain) {
+  const std::string text = R"(
+; CHECK: OpEntryPoint GLCompute [[main:%\w+]]
+; CHECK: [[main]] = OpFunction
+; CHECK-NOT: = OpFunction
+; CHECK: [[ext1:%\w+]] = OpExtInst %void {{%\w+}} 1 [[main]]
+; CHECK: [[ext2:%\w+]] = OpExtInst %void {{%\w+}} 2 [[ext1]]
+; CHECK: [[ext3:%\w+]] = OpExtInst %void {{%\w+}} 3 [[ext1]] [[ext2]]
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Test"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%main_entry = OpLabel
+OpReturn
+OpFunctionEnd
+%dead = OpFunction %void None %void_fn
+%dead_entry = OpLabel
+OpReturn
+OpFunctionEnd
+%2 = OpExtInst %void %1 1 %main
+%3 = OpExtInst %void %1 2 %2
+%4 = OpExtInst %void %1 3 %2 %3
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
+  SinglePassRunAndMatch<EliminateDeadFunctionsPass>(text, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools