opt: allow aggressive DCE to optimize untyped_pointers (#6602)

This will be required to allow DXC generating descriptor heaps code
which uses untyped pointers/descriptor_heap
diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp
index b8f239d..479ba79 100644
--- a/source/opt/aggressive_dead_code_elim_pass.cpp
+++ b/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -128,6 +128,7 @@
     switch (user->opcode()) {
       case spv::Op::OpAccessChain:
       case spv::Op::OpInBoundsAccessChain:
+      case spv::Op::OpUntypedAccessChainKHR:
       case spv::Op::OpCopyObject:
         this->AddStores(func, user->result_id());
         break;
@@ -1123,6 +1124,7 @@
       "SPV_EXT_demote_to_helper_invocation",
       "SPV_EXT_descriptor_indexing",
       "SPV_EXT_descriptor_heap",
+      "SPV_KHR_untyped_pointers",
       "SPV_NV_fragment_shader_barycentric",
       "SPV_NV_compute_shader_derivatives",
       "SPV_NV_shader_image_footprint",
diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp
index 6ec4c65..2bfd491 100644
--- a/source/opt/instruction.cpp
+++ b/source/opt/instruction.cpp
@@ -252,6 +252,7 @@
     switch (base_inst->opcode()) {
       case spv::Op::OpAccessChain:
       case spv::Op::OpInBoundsAccessChain:
+      case spv::Op::OpUntypedAccessChainKHR:
       case spv::Op::OpPtrAccessChain:
       case spv::Op::OpInBoundsPtrAccessChain:
       case spv::Op::OpImageTexelPointer:
diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp
index 7327a2c..6a66d2b 100644
--- a/source/opt/ir_context.cpp
+++ b/source/opt/ir_context.cpp
@@ -580,6 +580,7 @@
          (uint32_t)spv::Op::OpTypeStruct,
          (uint32_t)spv::Op::OpTypeOpaque,
          (uint32_t)spv::Op::OpTypePointer,
+         (uint32_t)spv::Op::OpTypeUntypedPointerKHR,
          (uint32_t)spv::Op::OpTypeFunction,
          (uint32_t)spv::Op::OpTypeEvent,
          (uint32_t)spv::Op::OpTypeDeviceEvent,
@@ -588,10 +589,12 @@
          (uint32_t)spv::Op::OpTypePipe,
          (uint32_t)spv::Op::OpTypeForwardPointer,
          (uint32_t)spv::Op::OpVariable,
+         (uint32_t)spv::Op::OpUntypedVariableKHR,
          (uint32_t)spv::Op::OpImageTexelPointer,
          (uint32_t)spv::Op::OpLoad,
          (uint32_t)spv::Op::OpAccessChain,
          (uint32_t)spv::Op::OpInBoundsAccessChain,
+         (uint32_t)spv::Op::OpUntypedAccessChainKHR,
          (uint32_t)spv::Op::OpArrayLength,
          (uint32_t)spv::Op::OpVectorExtractDynamic,
          (uint32_t)spv::Op::OpVectorInsertDynamic,
diff --git a/source/opt/mem_pass.cpp b/source/opt/mem_pass.cpp
index 8da0668..4d061ff 100644
--- a/source/opt/mem_pass.cpp
+++ b/source/opt/mem_pass.cpp
@@ -72,7 +72,8 @@
 
 bool MemPass::IsNonPtrAccessChain(const spv::Op opcode) const {
   return opcode == spv::Op::OpAccessChain ||
-         opcode == spv::Op::OpInBoundsAccessChain;
+         opcode == spv::Op::OpInBoundsAccessChain ||
+         opcode == spv::Op::OpUntypedAccessChainKHR;
 }
 
 bool MemPass::IsPtr(uint32_t ptrId) {
@@ -88,11 +89,14 @@
     ptrInst = get_def_use_mgr()->GetDef(varId);
   }
   const spv::Op op = ptrInst->opcode();
-  if (op == spv::Op::OpVariable || IsNonPtrAccessChain(op)) return true;
+  if (op == spv::Op::OpVariable || op == spv::Op::OpUntypedVariableKHR ||
+      IsNonPtrAccessChain(op))
+    return true;
   const uint32_t varTypeId = ptrInst->type_id();
   if (varTypeId == 0) return false;
   const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
-  return varTypeInst->opcode() == spv::Op::OpTypePointer;
+  return varTypeInst->opcode() == spv::Op::OpTypePointer ||
+         varTypeInst->opcode() == spv::Op::OpTypeUntypedPointerKHR;
 }
 
 Instruction* MemPass::GetPtr(uint32_t ptrId, uint32_t* varId) {
@@ -102,11 +106,13 @@
 
   switch (ptrInst->opcode()) {
     case spv::Op::OpVariable:
+    case spv::Op::OpUntypedVariableKHR:
     case spv::Op::OpFunctionParameter:
       varInst = ptrInst;
       break;
     case spv::Op::OpAccessChain:
     case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpUntypedAccessChainKHR:
     case spv::Op::OpPtrAccessChain:
     case spv::Op::OpInBoundsPtrAccessChain:
     case spv::Op::OpImageTexelPointer:
@@ -119,7 +125,8 @@
       break;
   }
 
-  if (varInst->opcode() == spv::Op::OpVariable) {
+  if (varInst->opcode() == spv::Op::OpVariable ||
+      varInst->opcode() == spv::Op::OpUntypedVariableKHR) {
     *varId = varInst->result_id();
   } else {
     *varId = 0;
diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp
index e5990d4..a3a75e3 100644
--- a/test/opt/aggressive_dead_code_elim_test.cpp
+++ b/test/opt/aggressive_dead_code_elim_test.cpp
@@ -9025,6 +9025,199 @@
   SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
 }
 
+TEST_F(AggressiveDCETest, EliminateUntypedAccessChain) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability Sampled1D
+               OpCapability DescriptorHeapEXT
+               OpCapability UntypedPointersKHR
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpName %main "main"
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+%type_untyped_pointer = OpTypeUntypedPointerKHR Uniform
+       %void = OpTypeVoid
+      %float = OpTypeFloat 32
+         %10 = OpTypeFunction %void
+%type_1d_image = OpTypeImage %float 1D 2 0 0 1 Unknown
+%_ptr_Function_type_1d_image = OpTypePointer Function %type_1d_image
+%type_buffer_ext = OpTypeBufferEXT StorageBuffer
+%_runtimearr_type_buffer_ext = OpTypeRuntimeArray %type_buffer_ext
+%resource_heap = OpUntypedVariableKHR %type_untyped_pointer Uniform
+       %main = OpFunction %void None %10
+         %20 = OpLabel
+        %t1d = OpVariable %_ptr_Function_type_1d_image Function
+; CHECK-NOT: OpUntypedAccessChainKHR
+         %21 = OpUntypedAccessChainKHR %type_untyped_pointer %_runtimearr_type_buffer_ext %resource_heap %uint_0
+; CHECK-NOT: OpLoad %type_1d_image
+         %22 = OpLoad %type_1d_image %21
+; CHECK-NOT: OpStore %t1d
+               OpStore %t1d %22
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
+}
+
+TEST_F(AggressiveDCETest, NoEliminateLiveUntypedAccessChain) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability DescriptorHeapEXT
+               OpCapability UntypedPointersKHR
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %outColor
+               OpExecutionMode %main OriginUpperLeft
+               OpName %main "main"
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+%type_untyped_pointer = OpTypeUntypedPointerKHR Uniform
+       %void = OpTypeVoid
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+   %outColor = OpVariable %_ptr_Output_v4float Output
+         %10 = OpTypeFunction %void
+%type_buffer_ext = OpTypeBufferEXT StorageBuffer
+%_runtimearr_type_buffer_ext = OpTypeRuntimeArray %type_buffer_ext
+%resource_heap = OpUntypedVariableKHR %type_untyped_pointer Uniform
+       %main = OpFunction %void None %10
+         %20 = OpLabel
+; CHECK: OpUntypedAccessChainKHR
+         %21 = OpUntypedAccessChainKHR %type_untyped_pointer %_runtimearr_type_buffer_ext %resource_heap %uint_0
+; CHECK: OpLoad
+         %22 = OpLoad %v4float %21
+               OpStore %outColor %22
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
+}
+
+TEST_F(AggressiveDCETest, EliminateUntypedAccessChainWithCopyObject) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability Sampled1D
+               OpCapability DescriptorHeapEXT
+               OpCapability UntypedPointersKHR
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpName %main "main"
+               OpName %type_1d_image "type_1d_image"
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+%type_untyped_pointer = OpTypeUntypedPointerKHR Uniform
+       %void = OpTypeVoid
+      %float = OpTypeFloat 32
+         %10 = OpTypeFunction %void
+%type_1d_image = OpTypeImage %float 1D 2 0 0 1 Unknown
+%_ptr_Function_type_1d_image = OpTypePointer Function %type_1d_image
+%type_buffer_ext = OpTypeBufferEXT StorageBuffer
+%_runtimearr_type_buffer_ext = OpTypeRuntimeArray %type_buffer_ext
+%resource_heap = OpUntypedVariableKHR %type_untyped_pointer Uniform
+       %main = OpFunction %void None %10
+         %20 = OpLabel
+        %t1d = OpVariable %_ptr_Function_type_1d_image Function
+; CHECK-NOT: OpUntypedAccessChainKHR
+         %21 = OpUntypedAccessChainKHR %type_untyped_pointer %_runtimearr_type_buffer_ext %resource_heap %uint_0
+; CHECK-NOT: OpCopyObject
+         %22 = OpCopyObject %type_untyped_pointer %21
+; CHECK-NOT: OpLoad %type_1d_image
+         %23 = OpLoad %type_1d_image %22
+; CHECK-NOT: OpStore %t1d
+               OpStore %t1d %23
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
+}
+
+// For now, aggressive DCE does not optimizes this pattern. If you implement
+// it, remove this test.
+TEST_F(AggressiveDCETest, EliminateUntypedAtomic) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability Int64
+               OpCapability DescriptorHeapEXT
+               OpCapability UntypedPointersKHR
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+%type_untyped_pointer = OpTypeUntypedPointerKHR Uniform
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+%type_buffer_ext = OpTypeBufferEXT StorageBuffer
+%_runtimearr_type_buffer_ext = OpTypeRuntimeArray %type_buffer_ext
+%resource_heap = OpUntypedVariableKHR %type_untyped_pointer Uniform
+       %main = OpFunction %void None %10
+         %20 = OpLabel
+         %21 = OpUntypedAccessChainKHR %type_untyped_pointer %_runtimearr_type_buffer_ext %resource_heap %uint_0
+; CHECK: [[ptr:%\w+]] = OpUntypedAccessChainKHR
+         %22 = OpAtomicLoad %uint %21 %uint_0 %uint_0
+; CHECK:       OpAtomicLoad %uint [[ptr]]
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
+}
+
+TEST_F(AggressiveDCETest, EliminateUntypedAccessChainLoop) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability Sampled1D
+               OpCapability DescriptorHeapEXT
+               OpCapability UntypedPointersKHR
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+     %uint_10 = OpConstant %uint 10
+%type_untyped_pointer = OpTypeUntypedPointerKHR Uniform
+       %void = OpTypeVoid
+      %bool = OpTypeBool
+         %10 = OpTypeFunction %void
+%type_1d_image = OpTypeImage %uint 1D 2 0 0 1 Unknown
+%type_buffer_ext = OpTypeBufferEXT StorageBuffer
+%_runtimearr_type_buffer_ext = OpTypeRuntimeArray %type_buffer_ext
+%resource_heap = OpUntypedVariableKHR %type_untyped_pointer Uniform
+       %main = OpFunction %void None %10
+         %20 = OpLabel
+               OpBranch %header
+     %header = OpLabel
+      %count = OpPhi %uint %uint_0 %20 %next %loop
+       %cond = OpULessThan %bool %count %uint_10
+               OpLoopMerge %exit %loop None
+               OpBranchConditional %cond %loop %exit
+       %loop = OpLabel
+; CHECK-NOT: OpUntypedAccessChainKHR
+         %21 = OpUntypedAccessChainKHR %type_untyped_pointer %_runtimearr_type_buffer_ext %resource_heap %count
+; CHECK-NOT: OpLoad
+         %22 = OpLoad %type_1d_image %21
+       %next = OpIAdd %uint %count %uint_1
+               OpBranch %header
+       %exit = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools