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

Issue:
Redundant loads not eliminated
Hello,

I have a set of compute shaders that broadcast components of a loaded vector to calculate outer product. I'm surprised to see that that each 'broadcast' ends up generating 4 loads:    

GLSL repro:
```glsl
#version 450
layout(binding=0) buffer InputA { vec4 x[]; } inputA;
layout(binding=1) buffer InputB { vec4 x[]; } inputB;
layout(binding=2) buffer Output { vec4 x[]; } outputO;

void main() {
  vec4 A = vec4(inputA.x[0].x,inputA.x[0].x, inputA.x[0].x, inputA.x[0].x);
  vec4 B = inputB.x[0];
  outputO.x[0] = A * B;
}
```

SPIR-V repro:
```
; SPIR-V
; Version: 1.4
; Generator: Khronos SPIR-V Tools Assembler; 0
; Bound: 36
; Schema: 0
               OpCapability Shader
          %1 = OpExtInstImport "GLSL.std.450"
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %2 "main"
               OpExecutionMode %2 LocalSize 1 1 1
               OpDecorate %_runtimearr_v4float ArrayStride 16
               OpMemberDecorate %_struct_4 0 Offset 0
               OpDecorate %_struct_4 BufferBlock
               OpDecorate %5 DescriptorSet 0
               OpDecorate %5 Binding 0
               OpDecorate %_runtimearr_v4float_0 ArrayStride 16
               OpMemberDecorate %_struct_7 0 Offset 0
               OpDecorate %_struct_7 BufferBlock
               OpDecorate %8 DescriptorSet 0
               OpDecorate %8 Binding 1
               OpDecorate %_runtimearr_v4float_1 ArrayStride 16
               OpMemberDecorate %_struct_10 0 Offset 0
               OpDecorate %_struct_10 BufferBlock
               OpDecorate %11 DescriptorSet 0
               OpDecorate %11 Binding 2
       %void = OpTypeVoid
         %13 = OpTypeFunction %void
      %float = OpTypeFloat 32
    %v4float = OpTypeVector %float 4
%_runtimearr_v4float = OpTypeRuntimeArray %v4float
  %_struct_4 = OpTypeStruct %_runtimearr_v4float
%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4
          %5 = OpVariable %_ptr_Uniform__struct_4 Uniform
        %int = OpTypeInt 32 1
      %int_0 = OpConstant %int 0
       %uint = OpTypeInt 32 0
     %uint_0 = OpConstant %uint 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
%_runtimearr_v4float_0 = OpTypeRuntimeArray %v4float
  %_struct_7 = OpTypeStruct %_runtimearr_v4float_0
%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
          %8 = OpVariable %_ptr_Uniform__struct_7 Uniform
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
%_runtimearr_v4float_1 = OpTypeRuntimeArray %v4float
 %_struct_10 = OpTypeStruct %_runtimearr_v4float_1
%_ptr_Uniform__struct_10 = OpTypePointer Uniform %_struct_10
         %11 = OpVariable %_ptr_Uniform__struct_10 Uniform
          %2 = OpFunction %void None %13
         %25 = OpLabel
         %26 = OpAccessChain %_ptr_Uniform_float %5 %int_0 %int_0 %uint_0
         %27 = OpLoad %float %26
         %28 = OpLoad %float %26
         %29 = OpLoad %float %26
         %30 = OpLoad %float %26
         %31 = OpCompositeConstruct %v4float %27 %28 %29 %30
         %32 = OpAccessChain %_ptr_Uniform_v4float %8 %int_0 %int_0
         %33 = OpLoad %v4float %32
         %34 = OpFMul %v4float %31 %33
         %35 = OpAccessChain %_ptr_Uniform_v4float %11 %int_0 %int_0
               OpStore %35 %34
               OpReturn
               OpFunctionEnd
```

Running `spirv-opt repro.spvasm -O -o -` does not eliminate loads `%28`-`%30` like I would expect. Is this simply a missing optimization, a more fundamental limitation, or am I holding it wrong? 


Discussion / hints:
This is surprising. I will investigate.
Thinking about this for three more minutes, this is not quite as surprising as I first thought.

FWIW, I wrote the initial implementation of store/load elimination, so I think I speak here with some authority.

In general, loads based on access chains require additional aliasing analysis to be sure their value is not changed by an intervening store. Buffers are a special case that do not require aliasing analysis because they are generally not both readable and writable in the same shader. But I don't recall writing or seeing such logic in the store/load elimination passes. I think in general only stores  and loads of OpVariables are currently eliminated.

I will finally add that I think that it is slightly unusual to see a reference like `inputA.x[0].x` replicated four times in a row in the same line. I know I would have captured `inputA.x[0].x` in a local temp and replicated the temp. The goal of spirv-opt was to optimize common code sequences. To spend time to improve such an uncommon code sequence would be very low priority in my mind unless your code base is full of such sequences.

I think I have answered your question, so will close. If you wish to re-open as a request for such sequences to be optimized, you are welcome, although I would consider such an issue as a request for enhancement rather than a bug fix.

Thanks for looking into this, @greg-lunarg.

In my actual code, I do have a local variable but still end up with 4 loads: https://github.com/google/uVkCompute/blob/5b1be93cf6af0b2b88b57ad851e13fe5a3b30015/benchmarks/matmul/matmul_tiled_i32.glsl#L68.

(You can compile this with `glslc -S -O -fshader-stage=compute matmul_tiled_i32.glsl -o - -DTILE_M=8 -DTILE_N=64 -DTILE_K=8 -DWG_X=16 -DWG_Y=1`)

Introducing a local variable in the repro attached to the issue does solve the problem, so I guess it depends on whether you would consider this a shaderc issue or a common code sequence.

Feel free to re-close the issue.
Here is the loop you refer to in your last comment:

```
[[unroll]] for (uint i = 0; i < C_ROWS; ++i) {
      uint gi = gID.y * 8 + laneId.y + i * 1;
      uint gk = k / 4;
      [[unroll]] for (uint kk = 0; kk < 8 / 4; ++kk) {
        i32vec4 A = inputA.x[coordToOffset(gi, gk + kk, strideA / 4)];
        [[unroll]] for (uint j = 0; j < C_COLS; ++j) {
          C[i][j] += i32vec4(A.x, A.x, A.x, A.x) * (B[0 + 4 * kk][j]);
          C[i][j] += i32vec4(A.y, A.y, A.y, A.y) * (B[1 + 4 * kk][j]);
          C[i][j] += i32vec4(A.z, A.z, A.z, A.z) * (B[2 + 4 * kk][j]);
          C[i][j] += i32vec4(A.w, A.w, A.w, A.w) * (B[3 + 4 * kk][j]);
        }
      }
    }
```

There are four loads of C[i][j], but they cannot be merged because there are intervening stores to C[i][j] that need to be respected.

Still, I agree that asking the optimizer to eliminate all but one load and store of C[i][j] here is reasonable. While one could again argue that C[i][j] here could be scalarized by hand, it is entirely possible and common for such sequences to arise in the SPIR-V from optimizer unrolling.

So I think this is a valid request for enhancement.

> There are four loads of C[i][j], but they cannot be merged because there are intervening stores to C[i][j] that need to be respected.

I meant the 4 loads of each `A.*` that feed into composite construct:
```
       %1779 = OpLoad %int %191                                                    
       %1781 = OpLoad %int %191      
       %1783 = OpLoad %int %191         
       %1785 = OpLoad %int %191      
       %1786 = OpCompositeConstruct %v4int %1779 %1781 %1783 %1785                 
       %1791 = OpIMul %v4int %1786 %714
       %1794 = OpIAdd %v4int %1758 %1791
       %1797 = OpLoad %int %213                                                    
       %1799 = OpLoad %int %213        
       %1801 = OpLoad %int %213         
       %1803 = OpLoad %int %213         
       %1804 = OpCompositeConstruct %v4int %1797 %1799 %1801 %1803
       %1809 = OpIMul %v4int %1804 %735                                            
       %1812 = OpIAdd %v4int %1794 %1809
       %1815 = OpLoad %int %235                                                    
       %1817 = OpLoad %int %235        
       %1819 = OpLoad %int %235         
       %1821 = OpLoad %int %235      
       %1822 = OpCompositeConstruct %v4int %1815 %1817 %1819 %1821                 
       %1827 = OpIMul %v4int %1822 %756
       %1830 = OpIAdd %v4int %1812 %1827
       %1833 = OpLoad %int %258                                                    
       %1835 = OpLoad %int %258        
       %1837 = OpLoad %int %258         
       %1839 = OpLoad %int %258         
       %1840 = OpCompositeConstruct %v4int %1833 %1835 %1837 %1839
       %1845 = OpIMul %v4int %1840 %777                                            
       %1848 = OpIAdd %v4int %1830 %1845
       %1864 = OpLoad %uint %143                                                   
       %1865 = OpIMul %uint %1864 %uint_8 
       %1867 = OpLoad %uint %146        
       %1868 = OpIAdd %uint %1865 %1867
       %1870 = OpIAdd %uint %1868 %uint_6                         
       %1877 = OpIMul %uint %172 %1870
       %1878 = OpIAdd %uint %1877 %154
       %1879 = OpAccessChain %_ptr_Uniform_v4int %168 %int_0 %1878
       %1880 = OpLoad %v4int %1879
               OpStore %164 %1880
```
If I compile your 2nd shader with glslangValidator -Os, the SPIR-V I get does not have these OpLoads. So I think the problem might be in the optimization recipe used by shaderc and/or spirv-opt for -O, rather than a defect in any of the passes.
Most passes that deal with removing redundant load (local-single-store, ssa-rewrite, local-single-block-elim) work on function scope variables, maybe private storage class. There are the redundancy elimination passes, but they only work for read-only storage, and the first example is not considered read-only.

The loads in the second example are cause by the memory model capability/extension not being in the allow list of some of the passes. This should be an easier fix.

Which one do you really care about?
> Which one do you really care about?

About what happens in the actual code with a local variable in a loop (https://github.com/KhronosGroup/SPIRV-Tools/issues/5086#issuecomment-1405411385). This would provide a workaround for the more general case.


Fix patch:
diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp
index 66e8813365..f045eb6600 100644
--- a/source/opt/local_access_chain_convert_pass.cpp
+++ b/source/opt/local_access_chain_convert_pass.cpp
@@ -398,60 +398,36 @@ Pass::Status LocalAccessChainConvertPass::Process() {
 
 void LocalAccessChainConvertPass::InitExtensions() {
   extensions_allowlist_.clear();
-  extensions_allowlist_.insert({
-      "SPV_AMD_shader_explicit_vertex_parameter",
-      "SPV_AMD_shader_trinary_minmax",
-      "SPV_AMD_gcn_shader",
-      "SPV_KHR_shader_ballot",
-      "SPV_AMD_shader_ballot",
-      "SPV_AMD_gpu_shader_half_float",
-      "SPV_KHR_shader_draw_parameters",
-      "SPV_KHR_subgroup_vote",
-      "SPV_KHR_8bit_storage",
-      "SPV_KHR_16bit_storage",
-      "SPV_KHR_device_group",
-      "SPV_KHR_multiview",
-      "SPV_NVX_multiview_per_view_attributes",
-      "SPV_NV_viewport_array2",
-      "SPV_NV_stereo_view_rendering",
-      "SPV_NV_sample_mask_override_coverage",
-      "SPV_NV_geometry_shader_passthrough",
-      "SPV_AMD_texture_gather_bias_lod",
-      "SPV_KHR_storage_buffer_storage_class",
-      // SPV_KHR_variable_pointers
-      //   Currently do not support extended pointer expressions
-      "SPV_AMD_gpu_shader_int16",
-      "SPV_KHR_post_depth_coverage",
-      "SPV_KHR_shader_atomic_counter_ops",
-      "SPV_EXT_shader_stencil_export",
-      "SPV_EXT_shader_viewport_index_layer",
-      "SPV_AMD_shader_image_load_store_lod",
-      "SPV_AMD_shader_fragment_mask",
-      "SPV_EXT_fragment_fully_covered",
-      "SPV_AMD_gpu_shader_half_float_fetch",
-      "SPV_GOOGLE_decorate_string",
-      "SPV_GOOGLE_hlsl_functionality1",
-      "SPV_GOOGLE_user_type",
-      "SPV_NV_shader_subgroup_partitioned",
-      "SPV_EXT_demote_to_helper_invocation",
-      "SPV_EXT_descriptor_indexing",
-      "SPV_NV_fragment_shader_barycentric",
-      "SPV_NV_compute_shader_derivatives",
-      "SPV_NV_shader_image_footprint",
-      "SPV_NV_shading_rate",
-      "SPV_NV_mesh_shader",
-      "SPV_NV_ray_tracing",
-      "SPV_KHR_ray_tracing",
-      "SPV_KHR_ray_query",
-      "SPV_EXT_fragment_invocation_density",
-      "SPV_KHR_terminate_invocation",
-      "SPV_KHR_subgroup_uniform_control_flow",
-      "SPV_KHR_integer_dot_product",
-      "SPV_EXT_shader_image_int64",
-      "SPV_KHR_non_semantic_info",
-      "SPV_KHR_uniform_group_instructions",
-      "SPV_KHR_fragment_shader_barycentric",
-  });
+  extensions_allowlist_.insert(
+      {"SPV_AMD_shader_explicit_vertex_parameter",
+       "SPV_AMD_shader_trinary_minmax", "SPV_AMD_gcn_shader",
+       "SPV_KHR_shader_ballot", "SPV_AMD_shader_ballot",
+       "SPV_AMD_gpu_shader_half_float", "SPV_KHR_shader_draw_parameters",
+       "SPV_KHR_subgroup_vote", "SPV_KHR_8bit_storage", "SPV_KHR_16bit_storage",
+       "SPV_KHR_device_group", "SPV_KHR_multiview",
+       "SPV_NVX_multiview_per_view_attributes", "SPV_NV_viewport_array2",
+       "SPV_NV_stereo_view_rendering", "SPV_NV_sample_mask_override_coverage",
+       "SPV_NV_geometry_shader_passthrough", "SPV_AMD_texture_gather_bias_lod",
+       "SPV_KHR_storage_buffer_storage_class",
+       // SPV_KHR_variable_pointers
+       //   Currently do not support extended pointer expressions
+       "SPV_AMD_gpu_shader_int16", "SPV_KHR_post_depth_coverage",
+       "SPV_KHR_shader_atomic_counter_ops", "SPV_EXT_shader_stencil_export",
+       "SPV_EXT_shader_viewport_index_layer",
+       "SPV_AMD_shader_image_load_store_lod", "SPV_AMD_shader_fragment_mask",
+       "SPV_EXT_fragment_fully_covered", "SPV_AMD_gpu_shader_half_float_fetch",
+       "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1",
+       "SPV_GOOGLE_user_type", "SPV_NV_shader_subgroup_partitioned",
+       "SPV_EXT_demote_to_helper_invocation", "SPV_EXT_descriptor_indexing",
+       "SPV_NV_fragment_shader_barycentric",
+       "SPV_NV_compute_shader_derivatives", "SPV_NV_shader_image_footprint",
+       "SPV_NV_shading_rate", "SPV_NV_mesh_shader", "SPV_NV_ray_tracing",
+       "SPV_KHR_ray_tracing", "SPV_KHR_ray_query",
+       "SPV_EXT_fragment_invocation_density", "SPV_KHR_terminate_invocation",
+       "SPV_KHR_subgroup_uniform_control_flow", "SPV_KHR_integer_dot_product",
+       "SPV_EXT_shader_image_int64", "SPV_KHR_non_semantic_info",
+       "SPV_KHR_uniform_group_instructions",
+       "SPV_KHR_fragment_shader_barycentric", "SPV_KHR_vulkan_memory_model"});
 }
 
 bool LocalAccessChainConvertPass::AnyIndexIsOutOfBounds(
diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp
index c1789c8851..05b2730048 100644
--- a/source/opt/local_single_block_elim_pass.cpp
+++ b/source/opt/local_single_block_elim_pass.cpp
@@ -234,60 +234,59 @@ Pass::Status LocalSingleBlockLoadStoreElimPass::Process() {
 
 void LocalSingleBlockLoadStoreElimPass::InitExtensions() {
   extensions_allowlist_.clear();
-  extensions_allowlist_.insert({
-      "SPV_AMD_shader_explicit_vertex_parameter",
-      "SPV_AMD_shader_trinary_minmax",
-      "SPV_AMD_gcn_shader",
-      "SPV_KHR_shader_ballot",
-      "SPV_AMD_shader_ballot",
-      "SPV_AMD_gpu_shader_half_float",
-      "SPV_KHR_shader_draw_parameters",
-      "SPV_KHR_subgroup_vote",
-      "SPV_KHR_8bit_storage",
-      "SPV_KHR_16bit_storage",
-      "SPV_KHR_device_group",
-      "SPV_KHR_multiview",
-      "SPV_NVX_multiview_per_view_attributes",
-      "SPV_NV_viewport_array2",
-      "SPV_NV_stereo_view_rendering",
-      "SPV_NV_sample_mask_override_coverage",
-      "SPV_NV_geometry_shader_passthrough",
-      "SPV_AMD_texture_gather_bias_lod",
-      "SPV_KHR_storage_buffer_storage_class",
-      "SPV_KHR_variable_pointers",
-      "SPV_AMD_gpu_shader_int16",
-      "SPV_KHR_post_depth_coverage",
-      "SPV_KHR_shader_atomic_counter_ops",
-      "SPV_EXT_shader_stencil_export",
-      "SPV_EXT_shader_viewport_index_layer",
-      "SPV_AMD_shader_image_load_store_lod",
-      "SPV_AMD_shader_fragment_mask",
-      "SPV_EXT_fragment_fully_covered",
-      "SPV_AMD_gpu_shader_half_float_fetch",
-      "SPV_GOOGLE_decorate_string",
-      "SPV_GOOGLE_hlsl_functionality1",
-      "SPV_GOOGLE_user_type",
-      "SPV_NV_shader_subgroup_partitioned",
-      "SPV_EXT_demote_to_helper_invocation",
-      "SPV_EXT_descriptor_indexing",
-      "SPV_NV_fragment_shader_barycentric",
-      "SPV_NV_compute_shader_derivatives",
-      "SPV_NV_shader_image_footprint",
-      "SPV_NV_shading_rate",
-      "SPV_NV_mesh_shader",
-      "SPV_NV_ray_tracing",
-      "SPV_KHR_ray_tracing",
-      "SPV_KHR_ray_query",
-      "SPV_EXT_fragment_invocation_density",
-      "SPV_EXT_physical_storage_buffer",
-      "SPV_KHR_terminate_invocation",
-      "SPV_KHR_subgroup_uniform_control_flow",
-      "SPV_KHR_integer_dot_product",
-      "SPV_EXT_shader_image_int64",
-      "SPV_KHR_non_semantic_info",
-      "SPV_KHR_uniform_group_instructions",
-      "SPV_KHR_fragment_shader_barycentric",
-  });
+  extensions_allowlist_.insert({"SPV_AMD_shader_explicit_vertex_parameter",
+                                "SPV_AMD_shader_trinary_minmax",
+                                "SPV_AMD_gcn_shader",
+                                "SPV_KHR_shader_ballot",
+                                "SPV_AMD_shader_ballot",
+                                "SPV_AMD_gpu_shader_half_float",
+                                "SPV_KHR_shader_draw_parameters",
+                                "SPV_KHR_subgroup_vote",
+                                "SPV_KHR_8bit_storage",
+                                "SPV_KHR_16bit_storage",
+                                "SPV_KHR_device_group",
+                                "SPV_KHR_multiview",
+                                "SPV_NVX_multiview_per_view_attributes",
+                                "SPV_NV_viewport_array2",
+                                "SPV_NV_stereo_view_rendering",
+                                "SPV_NV_sample_mask_override_coverage",
+                                "SPV_NV_geometry_shader_passthrough",
+                                "SPV_AMD_texture_gather_bias_lod",
+                                "SPV_KHR_storage_buffer_storage_class",
+                                "SPV_KHR_variable_pointers",
+                                "SPV_AMD_gpu_shader_int16",
+                                "SPV_KHR_post_depth_coverage",
+                                "SPV_KHR_shader_atomic_counter_ops",
+                                "SPV_EXT_shader_stencil_export",
+                                "SPV_EXT_shader_viewport_index_layer",
+                                "SPV_AMD_shader_image_load_store_lod",
+                                "SPV_AMD_shader_fragment_mask",
+                                "SPV_EXT_fragment_fully_covered",
+                                "SPV_AMD_gpu_shader_half_float_fetch",
+                                "SPV_GOOGLE_decorate_string",
+                                "SPV_GOOGLE_hlsl_functionality1",
+                                "SPV_GOOGLE_user_type",
+                                "SPV_NV_shader_subgroup_partitioned",
+                                "SPV_EXT_demote_to_helper_invocation",
+                                "SPV_EXT_descriptor_indexing",
+                                "SPV_NV_fragment_shader_barycentric",
+                                "SPV_NV_compute_shader_derivatives",
+                                "SPV_NV_shader_image_footprint",
+                                "SPV_NV_shading_rate",
+                                "SPV_NV_mesh_shader",
+                                "SPV_NV_ray_tracing",
+                                "SPV_KHR_ray_tracing",
+                                "SPV_KHR_ray_query",
+                                "SPV_EXT_fragment_invocation_density",
+                                "SPV_EXT_physical_storage_buffer",
+                                "SPV_KHR_terminate_invocation",
+                                "SPV_KHR_subgroup_uniform_control_flow",
+                                "SPV_KHR_integer_dot_product",
+                                "SPV_EXT_shader_image_int64",
+                                "SPV_KHR_non_semantic_info",
+                                "SPV_KHR_uniform_group_instructions",
+                                "SPV_KHR_fragment_shader_barycentric",
+                                "SPV_KHR_vulkan_memory_model"});
 }
 
 }  // namespace opt
diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp
index e494689fa6..906a6043f2 100644
--- a/source/opt/local_single_store_elim_pass.cpp
+++ b/source/opt/local_single_store_elim_pass.cpp
@@ -88,57 +88,56 @@ Pass::Status LocalSingleStoreElimPass::Process() {
 }
 
 void LocalSingleStoreElimPass::InitExtensionAllowList() {
-  extensions_allowlist_.insert({
-      "SPV_AMD_shader_explicit_vertex_parameter",
-      "SPV_AMD_shader_trinary_minmax",
-      "SPV_AMD_gcn_shader",
-      "SPV_KHR_shader_ballot",
-      "SPV_AMD_shader_ballot",
-      "SPV_AMD_gpu_shader_half_float",
-      "SPV_KHR_shader_draw_parameters",
-      "SPV_KHR_subgroup_vote",
-      "SPV_KHR_8bit_storage",
-      "SPV_KHR_16bit_storage",
-      "SPV_KHR_device_group",
-      "SPV_KHR_multiview",
-      "SPV_NVX_multiview_per_view_attributes",
-      "SPV_NV_viewport_array2",
-      "SPV_NV_stereo_view_rendering",
-      "SPV_NV_sample_mask_override_coverage",
-      "SPV_NV_geometry_shader_passthrough",
-      "SPV_AMD_texture_gather_bias_lod",
-      "SPV_KHR_storage_buffer_storage_class",
-      "SPV_KHR_variable_pointers",
-      "SPV_AMD_gpu_shader_int16",
-      "SPV_KHR_post_depth_coverage",
-      "SPV_KHR_shader_atomic_counter_ops",
-      "SPV_EXT_shader_stencil_export",
-      "SPV_EXT_shader_viewport_index_layer",
-      "SPV_AMD_shader_image_load_store_lod",
-      "SPV_AMD_shader_fragment_mask",
-      "SPV_EXT_fragment_fully_covered",
-      "SPV_AMD_gpu_shader_half_float_fetch",
-      "SPV_GOOGLE_decorate_string",
-      "SPV_GOOGLE_hlsl_functionality1",
-      "SPV_NV_shader_subgroup_partitioned",
-      "SPV_EXT_descriptor_indexing",
-      "SPV_NV_fragment_shader_barycentric",
-      "SPV_NV_compute_shader_derivatives",
-      "SPV_NV_shader_image_footprint",
-      "SPV_NV_shading_rate",
-      "SPV_NV_mesh_shader",
-      "SPV_NV_ray_tracing",
-      "SPV_KHR_ray_query",
-      "SPV_EXT_fragment_invocation_density",
-      "SPV_EXT_physical_storage_buffer",
-      "SPV_KHR_terminate_invocation",
-      "SPV_KHR_subgroup_uniform_control_flow",
-      "SPV_KHR_integer_dot_product",
-      "SPV_EXT_shader_image_int64",
-      "SPV_KHR_non_semantic_info",
-      "SPV_KHR_uniform_group_instructions",
-      "SPV_KHR_fragment_shader_barycentric",
-  });
+  extensions_allowlist_.insert({"SPV_AMD_shader_explicit_vertex_parameter",
+                                "SPV_AMD_shader_trinary_minmax",
+                                "SPV_AMD_gcn_shader",
+                                "SPV_KHR_shader_ballot",
+                                "SPV_AMD_shader_ballot",
+                                "SPV_AMD_gpu_shader_half_float",
+                                "SPV_KHR_shader_draw_parameters",
+                                "SPV_KHR_subgroup_vote",
+                                "SPV_KHR_8bit_storage",
+                                "SPV_KHR_16bit_storage",
+                                "SPV_KHR_device_group",
+                                "SPV_KHR_multiview",
+                                "SPV_NVX_multiview_per_view_attributes",
+                                "SPV_NV_viewport_array2",
+                                "SPV_NV_stereo_view_rendering",
+                                "SPV_NV_sample_mask_override_coverage",
+                                "SPV_NV_geometry_shader_passthrough",
+                                "SPV_AMD_texture_gather_bias_lod",
+                                "SPV_KHR_storage_buffer_storage_class",
+                                "SPV_KHR_variable_pointers",
+                                "SPV_AMD_gpu_shader_int16",
+                                "SPV_KHR_post_depth_coverage",
+                                "SPV_KHR_shader_atomic_counter_ops",
+                                "SPV_EXT_shader_stencil_export",
+                                "SPV_EXT_shader_viewport_index_layer",
+                                "SPV_AMD_shader_image_load_store_lod",
+                                "SPV_AMD_shader_fragment_mask",
+                                "SPV_EXT_fragment_fully_covered",
+                                "SPV_AMD_gpu_shader_half_float_fetch",
+                                "SPV_GOOGLE_decorate_string",
+                                "SPV_GOOGLE_hlsl_functionality1",
+                                "SPV_NV_shader_subgroup_partitioned",
+                                "SPV_EXT_descriptor_indexing",
+                                "SPV_NV_fragment_shader_barycentric",
+                                "SPV_NV_compute_shader_derivatives",
+                                "SPV_NV_shader_image_footprint",
+                                "SPV_NV_shading_rate",
+                                "SPV_NV_mesh_shader",
+                                "SPV_NV_ray_tracing",
+                                "SPV_KHR_ray_query",
+                                "SPV_EXT_fragment_invocation_density",
+                                "SPV_EXT_physical_storage_buffer",
+                                "SPV_KHR_terminate_invocation",
+                                "SPV_KHR_subgroup_uniform_control_flow",
+                                "SPV_KHR_integer_dot_product",
+                                "SPV_EXT_shader_image_int64",
+                                "SPV_KHR_non_semantic_info",
+                                "SPV_KHR_uniform_group_instructions",
+                                "SPV_KHR_fragment_shader_barycentric",
+                                "SPV_KHR_vulkan_memory_model"});
 }
 bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {
   std::vector<Instruction*> users;


Test patch:
diff --git a/test/opt/local_access_chain_convert_test.cpp b/test/opt/local_access_chain_convert_test.cpp
index 07fb537c2e..b35f3a3f86 100644
--- a/test/opt/local_access_chain_convert_test.cpp
+++ b/test/opt/local_access_chain_convert_test.cpp
@@ -1348,6 +1348,56 @@ OpFunctionEnd
                                                      true);
 }
 
+TEST_F(LocalAccessChainConvertTest, VkMemoryModelTest) {
+  const std::string text =
+      R"(
+; CHECK: OpCapability Shader
+; CHECK: OpCapability VulkanMemoryModel
+; CHECK: OpExtension "SPV_KHR_vulkan_memory_model"
+               OpCapability Shader
+               OpCapability VulkanMemoryModel
+               OpExtension "SPV_KHR_vulkan_memory_model"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical Vulkan
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 450
+               OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
+               OpSourceExtension "GL_GOOGLE_include_directive"
+               OpName %main "main"
+               OpName %a "a"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+%_ptr_Function_float = OpTypePointer Function %float
+    %float_1 = OpConstant %float 1
+; CHECK: OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[a:%\w+]] = OpVariable
+; Make sure the access chains were removed.
+; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[a]]
+; CHECK: [[ex:%\w+]] = OpCompositeExtract {{%\w+}} [[ld]] 0
+; CHECK: [[ld2:%\w+]] = OpLoad {{%\w+}} [[a]]
+; CHECK: [[v:%\w+]] = OpCompositeInsert {{%\w+}} [[ex]] [[ld2]] 0
+; CHECK: OpStore [[a]] [[v]]
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+          %a = OpVariable %_ptr_Function_v4float Function
+         %13 = OpAccessChain %_ptr_Function_float %a %uint_0
+         %14 = OpLoad %float %13
+         %17 = OpAccessChain %_ptr_Function_float %a %uint_0
+               OpStore %17 %14
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<LocalAccessChainConvertPass>(text, false);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Assorted vector and matrix types
diff --git a/test/opt/local_single_block_elim.cpp b/test/opt/local_single_block_elim.cpp
index 28b8a07d1f..7d19c227f6 100644
--- a/test/opt/local_single_block_elim.cpp
+++ b/test/opt/local_single_block_elim.cpp
@@ -1502,6 +1502,49 @@ TEST_F(LocalSingleBlockLoadStoreElimTest, DebugValueTest) {
   SinglePassRunAndMatch<LocalSingleBlockLoadStoreElimPass>(text, false);
 }
 
+TEST_F(LocalSingleBlockLoadStoreElimTest, VkMemoryModelTest) {
+  const std::string text =
+      R"(
+; CHECK: OpCapability Shader
+; CHECK: OpCapability VulkanMemoryModel
+; CHECK: OpExtension "SPV_KHR_vulkan_memory_model"
+               OpCapability Shader
+               OpCapability VulkanMemoryModel
+               OpExtension "SPV_KHR_vulkan_memory_model"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical Vulkan
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 450
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+       %bool = OpTypeBool
+      %false = OpConstantFalse %bool
+; CHECK: OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[a:%\w+]] = OpVariable
+; CHECK-NEXT: [[b:%\w+]] = OpVariable
+; CHECK: OpStore [[a]] [[v:%\w+]]
+; CHECK-NOT: OpLoad %int [[a]]
+; CHECK: OpStore [[b]] [[v]]
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+          %a = OpVariable %_ptr_Function_int Function
+          %b = OpVariable %_ptr_Function_int Function
+               OpStore %a %int_0
+         %16 = OpLoad %int %a
+               OpStore %b %16
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<LocalSingleBlockLoadStoreElimPass>(text, false);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Other target variable types
diff --git a/test/opt/local_single_store_elim_test.cpp b/test/opt/local_single_store_elim_test.cpp
index 8f43a11d41..ffe352ed11 100644
--- a/test/opt/local_single_store_elim_test.cpp
+++ b/test/opt/local_single_store_elim_test.cpp
@@ -1750,6 +1750,58 @@ TEST_F(LocalSingleStoreElimTest, DebugValuesForAllLocalsAndParams) {
   SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
 }
 
+TEST_F(LocalSingleStoreElimTest, VkMemoryModelTest) {
+  const std::string text =
+      R"(
+; CHECK: OpCapability Shader
+; CHECK: OpCapability VulkanMemoryModel
+; CHECK: OpExtension "SPV_KHR_vulkan_memory_model"
+               OpCapability Shader
+               OpCapability VulkanMemoryModel
+               OpExtension "SPV_KHR_vulkan_memory_model"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical Vulkan
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 450
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+       %bool = OpTypeBool
+      %false = OpConstantFalse %bool
+; CHECK: OpFunction
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[a:%\w+]] = OpVariable
+; CHECK-NEXT: [[b:%\w+]] = OpVariable
+; CHECK: OpStore [[a]] [[v:%\w+]]
+; CHECK: OpStore [[b]]
+; Make sure the load was removed.
+; CHECK: OpLabel
+; CHECK-NOT: OpLoad %int [[a]]
+; CHECK: OpStore [[b]] [[v]]
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+          %a = OpVariable %_ptr_Function_int Function
+          %b = OpVariable %_ptr_Function_int Function
+               OpStore %a %int_0
+               OpStore %b %int_1
+               OpSelectionMerge %15 None
+               OpBranchConditional %false %14 %15
+         %14 = OpLabel
+         %16 = OpLoad %int %a
+               OpStore %b %16
+               OpBranch %15
+         %15 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Other types