Preserve OpenCL.DebugInfo.100 through elim-local-single-store (#3498)

This pass basically follows the same process as ssa-rewrite: it adds a DebugValue after each Store and removes the DebugDeclare or DebugValue Deref. It only does this if all instructions that are dependent on the Store are Loads and are replaced.
diff --git a/source/opt/debug_info_manager.cpp b/source/opt/debug_info_manager.cpp
index 6f1aa2e..55e3f4a 100644
--- a/source/opt/debug_info_manager.cpp
+++ b/source/opt/debug_info_manager.cpp
@@ -374,6 +374,24 @@
   return dbg_decl_itr != var_id_to_dbg_decl_.end();
 }
 
+void DebugInfoManager::KillDebugDeclares(uint32_t variable_id) {
+  bool done = false;
+  while (!done) {
+    Instruction* kill_inst = nullptr;
+    auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
+    if (dbg_decl_itr != var_id_to_dbg_decl_.end()) {
+      for (auto dbg_decl : dbg_decl_itr->second) {
+        kill_inst = dbg_decl;
+        break;
+      }
+    }
+    if (kill_inst)
+      context()->KillInst(kill_inst);
+    else
+      done = true;
+  }
+}
+
 uint32_t DebugInfoManager::GetParentScope(uint32_t child_scope) {
   auto dbg_scope_itr = id_to_dbg_inst_.find(child_scope);
   assert(dbg_scope_itr != id_to_dbg_inst_.end());
@@ -485,6 +503,11 @@
     AnalyzeDebugInst(added_dbg_value);
     if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
       context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_value);
+    if (context()->AreAnalysesValid(
+            IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
+      auto insert_blk = context()->get_instr_block(insert_before);
+      context()->set_instr_block(added_dbg_value, insert_blk);
+    }
   }
 }
 
diff --git a/source/opt/debug_info_manager.h b/source/opt/debug_info_manager.h
index 438e7c6..d7c2700 100644
--- a/source/opt/debug_info_manager.h
+++ b/source/opt/debug_info_manager.h
@@ -136,6 +136,9 @@
   // Return true if |variable_id| has DebugDeclare or DebugVal.
   bool IsDebugDeclared(uint32_t variable_id);
 
+  // Kill all DebugDeclares for |variable_id|
+  void KillDebugDeclares(uint32_t variable_id);
+
   // Generates a DebugValue instruction with value |value_id| for every local
   // variable that is in the scope of |scope_and_line| and whose memory is
   // |variable_id| and inserts it after the instruction |insert_pos|.
diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp
index 3f61853..6626d87 100644
--- a/source/opt/local_single_store_elim_pass.cpp
+++ b/source/opt/local_single_store_elim_pass.cpp
@@ -133,7 +133,27 @@
     return false;
   }
 
-  return RewriteLoads(store_inst, users);
+  bool all_rewritten;
+  bool modified = RewriteLoads(store_inst, users, &all_rewritten);
+
+  // If all uses are rewritten and the variable has a DebugDeclare and the
+  // variable is not an aggregate, add a DebugValue after the store and remove
+  // the DebugDeclare.
+  uint32_t var_id = var_inst->result_id();
+  if (all_rewritten &&
+      context()->get_debug_info_mgr()->IsDebugDeclared(var_id)) {
+    const analysis::Type* var_type =
+        context()->get_type_mgr()->GetType(var_inst->type_id());
+    const analysis::Type* store_type = var_type->AsPointer()->pointee_type();
+    if (!(store_type->AsStruct() || store_type->AsArray())) {
+      context()->get_debug_info_mgr()->AddDebugValue(
+          store_inst, var_id, store_inst->GetSingleWordInOperand(1),
+          store_inst);
+      context()->get_debug_info_mgr()->KillDebugDeclares(var_id);
+    }
+  }
+
+  return modified;
 }
 
 Instruction* LocalSingleStoreElimPass::FindSingleStoreAndCheckUses(
@@ -172,6 +192,14 @@
       case SpvOpName:
       case SpvOpCopyObject:
         break;
+      case SpvOpExtInst: {
+        auto dbg_op = user->GetOpenCL100DebugOpcode();
+        if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
+            dbg_op == OpenCLDebugInfo100DebugValue) {
+          break;
+        }
+        return nullptr;
+      }
       default:
         if (!user->IsDecoration()) {
           // Don't know if this instruction modifies the variable.
@@ -218,7 +246,8 @@
 }
 
 bool LocalSingleStoreElimPass::RewriteLoads(
-    Instruction* store_inst, const std::vector<Instruction*>& uses) {
+    Instruction* store_inst, const std::vector<Instruction*>& uses,
+    bool* all_rewritten) {
   BasicBlock* store_block = context()->get_instr_block(store_inst);
   DominatorAnalysis* dominator_analysis =
       context()->GetDominatorAnalysis(store_block->GetParent());
@@ -229,16 +258,22 @@
   else
     stored_id = store_inst->GetSingleWordInOperand(kVariableInitIdInIdx);
 
-  std::vector<Instruction*> uses_in_store_block;
+  *all_rewritten = true;
   bool modified = false;
   for (Instruction* use : uses) {
-    if (use->opcode() == SpvOpLoad) {
-      if (dominator_analysis->Dominates(store_inst, use)) {
-        modified = true;
-        context()->KillNamesAndDecorates(use->result_id());
-        context()->ReplaceAllUsesWith(use->result_id(), stored_id);
-        context()->KillInst(use);
-      }
+    if (use->opcode() == SpvOpStore) continue;
+    auto dbg_op = use->GetOpenCL100DebugOpcode();
+    if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
+        dbg_op == OpenCLDebugInfo100DebugValue)
+      continue;
+    if (use->opcode() == SpvOpLoad &&
+        dominator_analysis->Dominates(store_inst, use)) {
+      modified = true;
+      context()->KillNamesAndDecorates(use->result_id());
+      context()->ReplaceAllUsesWith(use->result_id(), stored_id);
+      context()->KillInst(use);
+    } else {
+      *all_rewritten = false;
     }
   }
 
diff --git a/source/opt/local_single_store_elim_pass.h b/source/opt/local_single_store_elim_pass.h
index a7cdd19..d66a441 100644
--- a/source/opt/local_single_store_elim_pass.h
+++ b/source/opt/local_single_store_elim_pass.h
@@ -89,9 +89,10 @@
   bool FeedsAStore(Instruction* inst) const;
 
   // Replaces all of the loads in |uses| by the value stored in |store_inst|.
-  // The load instructions are then killed.
+  // The load instructions are then killed. |all_rewritten| is true iff all
+  // uses have been rewritten.
   bool RewriteLoads(Instruction* store_inst,
-                    const std::vector<Instruction*>& uses);
+                    const std::vector<Instruction*>& uses, bool* all_rewritten);
 
   // Extensions supported by this pass.
   std::unordered_set<std::string> extensions_allowlist_;
diff --git a/test/opt/local_single_store_elim_test.cpp b/test/opt/local_single_store_elim_test.cpp
index 5a1650b..ebc89a6 100644
--- a/test/opt/local_single_store_elim_test.cpp
+++ b/test/opt/local_single_store_elim_test.cpp
@@ -902,6 +902,315 @@
   )";
   SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
 }
+
+TEST_F(LocalSingleStoreElimTest, DebugDeclareTest) {
+  // If OpenCL.DebugInfo.100 enabled, check that store/load is still
+  // optimized, DebugValue placed after the store and the associated
+  // DebugDeclare is removed.
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0
+               OpExecutionMode %MainPs OriginUpperLeft
+         %20 = OpString "foo.frag"
+         %24 = OpString "PS_OUTPUT"
+         %28 = OpString "float"
+         %31 = OpString "vColor"
+         %33 = OpString "PS_INPUT"
+         %38 = OpString "vTextureCoords"
+         %40 = OpString "@type.2d.image"
+         %41 = OpString "type.2d.image"
+         %43 = OpString "Texture2D.TemplateParam"
+         %47 = OpString "src.MainPs"
+         %51 = OpString "tc"
+         %53 = OpString "ps_output"
+         %56 = OpString "i"
+         %58 = OpString "@type.sampler"
+         %59 = OpString "type.sampler"
+         %61 = OpString "g_sAniso"
+         %63 = OpString "g_tColor"
+               OpName %type_2d_image "type.2d.image"
+               OpName %g_tColor "g_tColor"
+               OpName %type_sampler "type.sampler"
+               OpName %g_sAniso "g_sAniso"
+               OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2"
+               OpName %out_var_SV_Target0 "out.var.SV_Target0"
+               OpName %MainPs "MainPs"
+               OpName %PS_INPUT "PS_INPUT"
+               OpMemberName %PS_INPUT 0 "vTextureCoords"
+               OpName %param_var_i "param.var.i"
+               OpName %PS_OUTPUT "PS_OUTPUT"
+               OpMemberName %PS_OUTPUT 0 "vColor"
+               OpName %type_sampled_image "type.sampled.image"
+               OpDecorate %in_var_TEXCOORD2 Location 0
+               OpDecorate %out_var_SV_Target0 Location 0
+               OpDecorate %g_tColor DescriptorSet 0
+               OpDecorate %g_tColor Binding 0
+               OpDecorate %g_sAniso DescriptorSet 0
+               OpDecorate %g_sAniso Binding 1
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+      %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+    %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+   %uint_128 = OpConstant %uint 128
+     %uint_0 = OpConstant %uint 0
+    %uint_64 = OpConstant %uint 64
+         %65 = OpTypeFunction %void
+   %PS_INPUT = OpTypeStruct %v2float
+%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
+  %PS_OUTPUT = OpTypeStruct %v4float
+         %75 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+   %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+   %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+         %39 = OpExtInst %void %1 DebugInfoNone
+         %55 = OpExtInst %void %1 DebugExpression
+         %22 = OpExtInst %void %1 DebugSource %20
+         %23 = OpExtInst %void %1 DebugCompilationUnit 1 4 %22 HLSL
+         %26 = OpExtInst %void %1 DebugTypeComposite %24 Structure %22 10 1 %23 %24 %uint_128 FlagIsProtected|FlagIsPrivate %27
+         %29 = OpExtInst %void %1 DebugTypeBasic %28 %uint_32 Float
+         %30 = OpExtInst %void %1 DebugTypeVector %29 4
+         %27 = OpExtInst %void %1 DebugTypeMember %31 %30 %22 12 5 %26 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+         %35 = OpExtInst %void %1 DebugTypeComposite %33 Structure %22 5 1 %23 %33 %uint_64 FlagIsProtected|FlagIsPrivate %36
+         %37 = OpExtInst %void %1 DebugTypeVector %29 2
+         %36 = OpExtInst %void %1 DebugTypeMember %38 %37 %22 7 5 %35 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate
+         %42 = OpExtInst %void %1 DebugTypeComposite %40 Class %22 0 0 %23 %41 %39 FlagIsProtected|FlagIsPrivate
+         %44 = OpExtInst %void %1 DebugTypeTemplateParameter %43 %29 %39 %22 0 0
+         %45 = OpExtInst %void %1 DebugTypeTemplate %42 %44
+         %46 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %26 %35
+         %48 = OpExtInst %void %1 DebugFunction %47 %46 %22 15 1 %23 %47 FlagIsProtected|FlagIsPrivate 16 %39
+         %50 = OpExtInst %void %1 DebugLexicalBlock %22 16 1 %48
+         %52 = OpExtInst %void %1 DebugLocalVariable %51 %37 %22 19 12 %50 FlagIsLocal
+         %54 = OpExtInst %void %1 DebugLocalVariable %53 %26 %22 17 15 %50 FlagIsLocal
+         %57 = OpExtInst %void %1 DebugLocalVariable %56 %35 %22 15 29 %48 FlagIsLocal 1
+         %60 = OpExtInst %void %1 DebugTypeComposite %58 Structure %22 0 0 %23 %59 %39 FlagIsProtected|FlagIsPrivate
+         %62 = OpExtInst %void %1 DebugGlobalVariable %61 %60 %22 3 14 %23 %61 %g_sAniso FlagIsDefinition
+         %64 = OpExtInst %void %1 DebugGlobalVariable %63 %42 %22 1 11 %23 %63 %g_tColor FlagIsDefinition
+     %MainPs = OpFunction %void None %65
+         %66 = OpLabel
+        %114 = OpExtInst %void %1 DebugScope %50
+         %98 = OpVariable %_ptr_Function_PS_OUTPUT Function
+         %99 = OpVariable %_ptr_Function_v2float Function
+        %115 = OpExtInst %void %1 DebugNoScope
+        %100 = OpVariable %_ptr_Function_PS_OUTPUT Function
+%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function
+         %70 = OpLoad %v2float %in_var_TEXCOORD2
+         %71 = OpCompositeConstruct %PS_INPUT %70
+               OpStore %param_var_i %71
+        %116 = OpExtInst %void %1 DebugScope %48
+        %102 = OpExtInst %void %1 DebugDeclare %57 %param_var_i %55
+        %117 = OpExtInst %void %1 DebugScope %50
+        %103 = OpExtInst %void %1 DebugDeclare %54 %98 %55
+               OpLine %20 19 17
+        %104 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0
+        %105 = OpLoad %v2float %104
+               OpLine %20 19 12
+               OpStore %99 %105
+        %106 = OpExtInst %void %1 DebugDeclare %52 %99 %55
+;CHECK-NOT: %106 = OpExtInst %void %1 DebugDeclare %52 %99 %55
+;CHECK:     %119 = OpExtInst %void %1 DebugValue %52 %105 %55
+               OpLine %20 20 26
+        %107 = OpLoad %type_2d_image %g_tColor
+               OpLine %20 20 46
+        %108 = OpLoad %type_sampler %g_sAniso
+               OpLine %20 20 57
+        %109 = OpLoad %v2float %99
+;CHECK-NOT: %109 = OpLoad %v2float %99
+               OpLine %20 20 26
+        %110 = OpSampledImage %type_sampled_image %107 %108
+        %111 = OpImageSampleImplicitLod %v4float %110 %109 None
+;CHECK-NOT: %111 = OpImageSampleImplicitLod %v4float %110 %109 None
+;CHECK:     %111 = OpImageSampleImplicitLod %v4float %110 %105 None
+               OpLine %20 20 5
+        %112 = OpAccessChain %_ptr_Function_v4float %98 %int_0
+               OpStore %112 %111
+               OpLine %20 21 12
+        %113 = OpLoad %PS_OUTPUT %98
+               OpLine %20 21 5
+               OpStore %100 %113
+        %118 = OpExtInst %void %1 DebugNoScope
+         %73 = OpLoad %PS_OUTPUT %100
+         %74 = OpCompositeExtract %v4float %73 0
+               OpStore %out_var_SV_Target0 %74
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
+}
+
+TEST_F(LocalSingleStoreElimTest, DebugValueTest) {
+  // If OpenCL.DebugInfo.100 enabled, check that store/load is still
+  // optimized, DebugValue placed after the store and the associated
+  // DebugValue Deref is removed.
+  const std::string text = R"(
+                 OpCapability Shader
+          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0
+               OpExecutionMode %MainPs OriginUpperLeft
+          %7 = OpString "foo.frag"
+          %8 = OpString "PS_OUTPUT"
+          %9 = OpString "float"
+         %10 = OpString "vColor"
+         %11 = OpString "PS_INPUT"
+         %12 = OpString "vTextureCoords"
+         %13 = OpString "@type.2d.image"
+         %14 = OpString "type.2d.image"
+         %15 = OpString "Texture2D.TemplateParam"
+         %16 = OpString "src.MainPs"
+         %17 = OpString "tc"
+         %18 = OpString "ps_output"
+         %19 = OpString "i"
+         %20 = OpString "@type.sampler"
+         %21 = OpString "type.sampler"
+         %22 = OpString "g_sAniso"
+         %23 = OpString "g_tColor"
+               OpName %type_2d_image "type.2d.image"
+               OpName %g_tColor "g_tColor"
+               OpName %type_sampler "type.sampler"
+               OpName %g_sAniso "g_sAniso"
+               OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2"
+               OpName %out_var_SV_Target0 "out.var.SV_Target0"
+               OpName %MainPs "MainPs"
+               OpName %PS_INPUT "PS_INPUT"
+               OpMemberName %PS_INPUT 0 "vTextureCoords"
+               OpName %param_var_i "param.var.i"
+               OpName %PS_OUTPUT "PS_OUTPUT"
+               OpMemberName %PS_OUTPUT 0 "vColor"
+               OpName %type_sampled_image "type.sampled.image"
+               OpDecorate %in_var_TEXCOORD2 Location 0
+               OpDecorate %out_var_SV_Target0 Location 0
+               OpDecorate %g_tColor DescriptorSet 0
+               OpDecorate %g_tColor Binding 0
+               OpDecorate %g_sAniso DescriptorSet 0
+               OpDecorate %g_sAniso Binding 1
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+      %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+    %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+   %uint_128 = OpConstant %uint 128
+     %uint_0 = OpConstant %uint 0
+    %uint_64 = OpConstant %uint 64
+         %45 = OpTypeFunction %void
+   %PS_INPUT = OpTypeStruct %v2float
+%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT
+  %PS_OUTPUT = OpTypeStruct %v4float
+         %47 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT
+%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+   %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant
+   %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input
+%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
+         %51 = OpExtInst %void %1 DebugInfoNone
+         %52 = OpExtInst %void %1 DebugExpression
+         %53 = OpExtInst %void %1 DebugOperation Deref
+         %54 = OpExtInst %void %1 DebugExpression %53
+         %55 = OpExtInst %void %1 DebugSource %7
+         %56 = OpExtInst %void %1 DebugCompilationUnit 1 4 %55 HLSL
+         %57 = OpExtInst %void %1 DebugTypeComposite %8 Structure %55 10 1 %56 %8 %uint_128 FlagIsProtected|FlagIsPrivate %58
+         %59 = OpExtInst %void %1 DebugTypeBasic %9 %uint_32 Float
+         %60 = OpExtInst %void %1 DebugTypeVector %59 4
+         %58 = OpExtInst %void %1 DebugTypeMember %10 %60 %55 12 5 %57 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+         %61 = OpExtInst %void %1 DebugTypeComposite %11 Structure %55 5 1 %56 %11 %uint_64 FlagIsProtected|FlagIsPrivate %62
+         %63 = OpExtInst %void %1 DebugTypeVector %59 2
+         %62 = OpExtInst %void %1 DebugTypeMember %12 %63 %55 7 5 %61 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate
+         %64 = OpExtInst %void %1 DebugTypeComposite %13 Class %55 0 0 %56 %14 %51 FlagIsProtected|FlagIsPrivate
+         %65 = OpExtInst %void %1 DebugTypeTemplateParameter %15 %59 %51 %55 0 0
+         %66 = OpExtInst %void %1 DebugTypeTemplate %64 %65
+         %67 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %57 %61
+         %68 = OpExtInst %void %1 DebugFunction %16 %67 %55 15 1 %56 %16 FlagIsProtected|FlagIsPrivate 16 %51
+         %69 = OpExtInst %void %1 DebugLexicalBlock %55 16 1 %68
+         %70 = OpExtInst %void %1 DebugLocalVariable %17 %63 %55 19 12 %69 FlagIsLocal
+         %71 = OpExtInst %void %1 DebugLocalVariable %18 %57 %55 17 15 %69 FlagIsLocal
+         %72 = OpExtInst %void %1 DebugLocalVariable %19 %61 %55 15 29 %68 FlagIsLocal 1
+         %73 = OpExtInst %void %1 DebugTypeComposite %20 Structure %55 0 0 %56 %21 %51 FlagIsProtected|FlagIsPrivate
+         %74 = OpExtInst %void %1 DebugGlobalVariable %22 %73 %55 3 14 %56 %22 %g_sAniso FlagIsDefinition
+         %75 = OpExtInst %void %1 DebugGlobalVariable %23 %64 %55 1 11 %56 %23 %g_tColor FlagIsDefinition
+     %MainPs = OpFunction %void None %45
+         %76 = OpLabel
+        %101 = OpExtInst %void %1 DebugScope %69
+         %78 = OpVariable %_ptr_Function_PS_OUTPUT Function
+         %79 = OpVariable %_ptr_Function_v2float Function
+        %102 = OpExtInst %void %1 DebugNoScope
+         %81 = OpVariable %_ptr_Function_PS_OUTPUT Function
+%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function
+         %82 = OpLoad %v2float %in_var_TEXCOORD2
+         %83 = OpCompositeConstruct %PS_INPUT %82
+               OpStore %param_var_i %83
+        %103 = OpExtInst %void %1 DebugScope %68
+         %85 = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52
+        %104 = OpExtInst %void %1 DebugScope %69
+         %87 = OpExtInst %void %1 DebugDeclare %71 %78 %52
+               OpLine %7 19 17
+         %88 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0
+         %89 = OpLoad %v2float %88
+               OpLine %7 19 12
+               OpStore %79 %89
+         %90 = OpExtInst %void %1 DebugValue %70 %79 %54
+;CHECK-NOT: %90 = OpExtInst %void %1 DebugValue %70 %79 %54
+;CHECK:    %106 = OpExtInst %void %1 DebugValue %70 %89 %52
+               OpLine %7 20 26
+         %91 = OpLoad %type_2d_image %g_tColor
+               OpLine %7 20 46
+         %92 = OpLoad %type_sampler %g_sAniso
+               OpLine %7 20 57
+         %93 = OpLoad %v2float %79
+;CHECK-NOT: %93 = OpLoad %v2float %79
+               OpLine %7 20 26
+         %94 = OpSampledImage %type_sampled_image %91 %92
+         %95 = OpImageSampleImplicitLod %v4float %94 %93 None
+;CHECK-NOT: %95 = OpImageSampleImplicitLod %v4float %94 %93 None
+;CHECK:     %95 = OpImageSampleImplicitLod %v4float %94 %89 None
+               OpLine %7 20 5
+         %96 = OpAccessChain %_ptr_Function_v4float %78 %int_0
+               OpStore %96 %95
+               OpLine %7 21 12
+         %97 = OpLoad %PS_OUTPUT %78
+               OpLine %7 21 5
+               OpStore %81 %97
+        %105 = OpExtInst %void %1 DebugNoScope
+         %99 = OpLoad %PS_OUTPUT %81
+        %100 = OpCompositeExtract %v4float %99 0
+               OpStore %out_var_SV_Target0 %100
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Other types