Preserve OpenCL.100.DebugInfo in reduce-load-size pass (#3492)

The decision to reduce the load must be not affected by debug
instructions. For example, even when a DebugValue references a
result id of a loaded composite value, this change lets the
reduce-load-size pass reduce the load if the full composite value is not
used anywhere other than the DebugValue.
diff --git a/source/opt/copy_prop_arrays.cpp b/source/opt/copy_prop_arrays.cpp
index ba7cea7..67a97b6 100644
--- a/source/opt/copy_prop_arrays.cpp
+++ b/source/opt/copy_prop_arrays.cpp
@@ -592,7 +592,7 @@
     Instruction* use = pair.first;
     uint32_t index = pair.second;
 
-    if (use->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100InstructionsMax) {
+    if (use->IsOpenCL100DebugInstr()) {
       switch (use->GetOpenCL100DebugOpcode()) {
         case OpenCLDebugInfo100DebugDeclare: {
           if (new_ptr_inst->opcode() == SpvOpVariable ||
diff --git a/source/opt/debug_info_manager.cpp b/source/opt/debug_info_manager.cpp
index 60ad71b..e0c946e 100644
--- a/source/opt/debug_info_manager.cpp
+++ b/source/opt/debug_info_manager.cpp
@@ -518,8 +518,7 @@
 }
 
 void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
-  if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100InstructionsMax)
-    return;
+  if (!dbg_inst->IsOpenCL100DebugInstr()) return;
 
   RegisterDbgInst(dbg_inst);
 
@@ -567,8 +566,7 @@
   // list.
   if (empty_debug_expr_inst_ != nullptr &&
       empty_debug_expr_inst_->PreviousNode() != nullptr &&
-      empty_debug_expr_inst_->PreviousNode()->GetOpenCL100DebugOpcode() !=
-          OpenCLDebugInfo100InstructionsMax) {
+      empty_debug_expr_inst_->PreviousNode()->IsOpenCL100DebugInstr()) {
     empty_debug_expr_inst_->InsertBefore(
         &*context()->module()->ext_inst_debuginfo_begin());
   }
@@ -577,16 +575,14 @@
   // list.
   if (debug_info_none_inst_ != nullptr &&
       debug_info_none_inst_->PreviousNode() != nullptr &&
-      debug_info_none_inst_->PreviousNode()->GetOpenCL100DebugOpcode() !=
-          OpenCLDebugInfo100InstructionsMax) {
+      debug_info_none_inst_->PreviousNode()->IsOpenCL100DebugInstr()) {
     debug_info_none_inst_->InsertBefore(
         &*context()->module()->ext_inst_debuginfo_begin());
   }
 }
 
 void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
-  if (instr == nullptr ||
-      instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100InstructionsMax) {
+  if (instr == nullptr || !instr->IsOpenCL100DebugInstr()) {
     return;
   }
 
diff --git a/source/opt/instruction.h b/source/opt/instruction.h
index 870a225..e99a5ae 100644
--- a/source/opt/instruction.h
+++ b/source/opt/instruction.h
@@ -539,6 +539,11 @@
   // OpenCLDebugInfo100InstructionsMax.
   OpenCLDebugInfo100Instructions GetOpenCL100DebugOpcode() const;
 
+  // Returns true if it is an OpenCL.DebugInfo.100 instruction.
+  bool IsOpenCL100DebugInstr() const {
+    return GetOpenCL100DebugOpcode() != OpenCLDebugInfo100InstructionsMax;
+  }
+
   // Dump this instruction on stderr.  Useful when running interactive
   // debuggers.
   void Dump() const;
diff --git a/source/opt/reduce_load_size.cpp b/source/opt/reduce_load_size.cpp
index 7b5a015..c58adf4 100644
--- a/source/opt/reduce_load_size.cpp
+++ b/source/opt/reduce_load_size.cpp
@@ -112,10 +112,10 @@
   Instruction* new_access_chain = ir_builder.AddAccessChain(
       pointer_to_result_type_id,
       composite_inst->GetSingleWordInOperand(kLoadPointerInIdx), ids);
-  Instruction* new_laod =
+  Instruction* new_load =
       ir_builder.AddLoad(inst->type_id(), new_access_chain->result_id());
 
-  context()->ReplaceAllUsesWith(inst->result_id(), new_laod->result_id());
+  context()->ReplaceAllUsesWith(inst->result_id(), new_load->result_id());
   context()->KillInst(inst);
   return true;
 }
@@ -139,6 +139,7 @@
 
   all_elements_used =
       !def_use_mgr->WhileEachUser(op_inst, [&elements_used](Instruction* use) {
+        if (use->IsOpenCL100DebugInstr()) return true;
         if (use->opcode() != SpvOpCompositeExtract ||
             use->NumInOperands() == 1) {
           return false;
diff --git a/test/opt/reduce_load_size_test.cpp b/test/opt/reduce_load_size_test.cpp
index 50dc501..7672e8f 100644
--- a/test/opt/reduce_load_size_test.cpp
+++ b/test/opt/reduce_load_size_test.cpp
@@ -107,6 +107,104 @@
   SinglePassRunAndMatch<ReduceLoadSize>(test, false);
 }
 
+TEST_F(ReduceLoadSizeTest, cbuffer_load_extract_not_affected_by_debug_instr) {
+  // Originally from the following HLSL:
+  //   struct S {
+  //     uint f;
+  //   };
+  //
+  //
+  //   cbuffer gBuffer { uint a[32]; };
+  //
+  //   RWStructuredBuffer<S> gRWSBuffer;
+  //
+  //   uint foo(uint p[32]) {
+  //     return p[1];
+  //   }
+  //
+  //   [numthreads(1,1,1)]
+  //   void main() {
+  //      gRWSBuffer[0].f = foo(a);
+  //   }
+  const std::string test =
+      R"(
+               OpCapability Shader
+        %ext = OpExtInstImport "OpenCL.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource HLSL 600
+  %file_name = OpString "test"
+ %float_name = OpString "float"
+  %main_name = OpString "main"
+     %f_name = OpString "f"
+               OpName %type_gBuffer "type.gBuffer"
+               OpMemberName %type_gBuffer 0 "a"
+               OpName %gBuffer "gBuffer"
+               OpName %S "S"
+               OpMemberName %S 0 "f"
+               OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S"
+               OpName %gRWSBuffer "gRWSBuffer"
+               OpName %main "main"
+               OpDecorate %_arr_uint_uint_32 ArrayStride 16
+               OpMemberDecorate %type_gBuffer 0 Offset 0
+               OpDecorate %type_gBuffer Block
+               OpMemberDecorate %S 0 Offset 0
+               OpDecorate %_runtimearr_S ArrayStride 4
+               OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0
+               OpDecorate %type_RWStructuredBuffer_S BufferBlock
+               OpDecorate %gBuffer DescriptorSet 0
+               OpDecorate %gBuffer Binding 0
+               OpDecorate %gRWSBuffer DescriptorSet 0
+               OpDecorate %gRWSBuffer Binding 1
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+%_arr_uint_uint_32 = OpTypeArray %uint %uint_32
+%type_gBuffer = OpTypeStruct %_arr_uint_uint_32
+%_ptr_Uniform_type_gBuffer = OpTypePointer Uniform %type_gBuffer
+          %S = OpTypeStruct %uint
+%_runtimearr_S = OpTypeRuntimeArray %S
+%type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S
+%_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S
+        %int = OpTypeInt 32 1
+       %void = OpTypeVoid
+         %15 = OpTypeFunction %void
+      %int_0 = OpConstant %int 0
+%_ptr_Uniform__arr_uint_uint_32 = OpTypePointer Uniform %_arr_uint_uint_32
+     %uint_0 = OpConstant %uint 0
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+    %gBuffer = OpVariable %_ptr_Uniform_type_gBuffer Uniform
+ %gRWSBuffer = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform
+  %null_expr = OpExtInst %void %ext DebugExpression
+        %src = OpExtInst %void %ext DebugSource %file_name
+         %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+     %dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+    %main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
+   %dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+      %dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+       %main = OpFunction %void None %15
+         %20 = OpLabel
+          %s = OpExtInst %void %ext DebugScope %dbg_main
+; CHECK: [[ac1:%\w+]] = OpAccessChain {{%\w+}} %gBuffer %int_0
+; CHECK: [[ac2:%\w+]] = OpAccessChain {{%\w+}} [[ac1]] %uint_1
+; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[ac2]]
+; CHECK: OpStore {{%\w+}} [[ld]]
+         %21 = OpAccessChain %_ptr_Uniform__arr_uint_uint_32 %gBuffer %int_0
+         %22 = OpLoad %_arr_uint_uint_32 %21    ; Load of 32-element array.
+      %value = OpExtInst %void %ext DebugValue %dbg_f %22 %null_expr
+         %23 = OpCompositeExtract %uint %22 1
+         %24 = OpAccessChain %_ptr_Uniform_uint %gRWSBuffer %int_0 %uint_0 %int_0
+               OpStore %24 %23
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+                        SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+  SinglePassRunAndMatch<ReduceLoadSize>(test, false);
+}
+
 TEST_F(ReduceLoadSizeTest, cbuffer_load_extract_vector) {
   // Originally from the following HLSL:
   //   struct S {