Squashed 'third_party/SPIRV-Tools/' changes from 0741f4273..9668d2e4e

9668d2e4e spirv-val: Label and add test for PSB Aligned (#4756)
c6d5474f4 BUILD.gn: Enable independent builds within the Chromium tree (#4762)
9d1b57288 spirv-opt: (WIP) Eliminate Dead Input Component Pass (#4720)
f8cd51431 spirv-val: Add better error code for invalid operand (#4753)
3820c4f6e Qualify std::move. (#4741)
0ab57c2c4 spirv-val: Small Cleanup of validate image (#4752)
7963fa13c spirv-val: Add Vulkan Dref not allowed 3D dim VUID (#4751)
5c9d55a59 spirv-val: Add Vulkan Image VUID 06214 (#4750)
970070857 spirv-val: Label Vulkan RuntimeArray VUID (#4749)
ca59914c7 spirv-val: Disallow array of push constants (#4742)

git-subtree-dir: third_party/SPIRV-Tools
git-subtree-split: 9668d2e4e4b7e089fd8f25c50c373df07e05d4a9
diff --git a/Android.mk b/Android.mk
index 3e836c5..e7616f9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -106,6 +106,7 @@
 		source/opt/eliminate_dead_constant_pass.cpp \
 		source/opt/eliminate_dead_functions_pass.cpp \
 		source/opt/eliminate_dead_functions_util.cpp \
+		source/opt/eliminate_dead_input_components_pass.cpp \
 		source/opt/eliminate_dead_members_pass.cpp \
 		source/opt/feature_manager.cpp \
 		source/opt/fix_storage_class.cpp \
diff --git a/BUILD.gn b/BUILD.gn
index 0f58884..5910e5e 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -19,6 +19,12 @@
   import("//third_party/protobuf/proto_library.gni")
 }
 
+# SPIRV-Tools may be part of multiple projects in the Chromium tree.
+# Only enable building executables if this is the main copy, or standalone.
+abspath = get_path_info(".", "abspath")
+spvtools_chromium_third_party = (abspath == "//third_party/SPIRV-Tools/")
+spvtools_build_executables = !build_with_chromium || spvtools_chromium_third_party
+
 spirv_headers = spirv_tools_spirv_headers_dir
 spirv_is_winuwp = is_win && target_os == "winuwp"
 
@@ -611,6 +617,8 @@
     "source/opt/eliminate_dead_functions_pass.h",
     "source/opt/eliminate_dead_functions_util.cpp",
     "source/opt/eliminate_dead_functions_util.h",
+    "source/opt/eliminate_dead_input_components_pass.cpp",
+    "source/opt/eliminate_dead_input_components_pass.h",
     "source/opt/eliminate_dead_members_pass.cpp",
     "source/opt/eliminate_dead_members_pass.h",
     "source/opt/empty_pass.h",
@@ -875,7 +883,7 @@
   configs += [ ":spvtools_internal_config" ]
 }
 
-if (build_with_chromium) {
+if (build_with_chromium && spvtools_build_executables) {
   # The spirv-fuzz library is only built when in a Chromium checkout
   # due to its dependency on protobuf.
 
@@ -1304,7 +1312,7 @@
 
 # The tests are scoped to Chromium to avoid needing to write gtest integration.
 # See Chromium's third_party/googletest/BUILD.gn for a complete integration.
-if (build_with_chromium) {
+if (build_with_chromium && spvtools_build_executables) {
   test("spvtools_test") {
     sources = [
       "test/assembly_context_test.cpp",
@@ -1413,73 +1421,75 @@
   configs += [ ":spvtools_internal_config" ]
 }
 
-executable("spirv-as") {
-  sources = [ "tools/as/as.cpp" ]
-  deps = [
-    ":spvtools",
-    ":spvtools_software_version",
-  ]
-  configs += [ ":spvtools_internal_config" ]
+if (spvtools_build_executables) {
+  executable("spirv-as") {
+    sources = [ "tools/as/as.cpp" ]
+    deps = [
+      ":spvtools",
+      ":spvtools_software_version",
+    ]
+    configs += [ ":spvtools_internal_config" ]
+  }
+
+  executable("spirv-dis") {
+    sources = [ "tools/dis/dis.cpp" ]
+    deps = [
+      ":spvtools",
+      ":spvtools_software_version",
+    ]
+    configs += [ ":spvtools_internal_config" ]
+  }
+
+  executable("spirv-val") {
+    sources = [ "tools/val/val.cpp" ]
+    deps = [
+      ":spvtools",
+      ":spvtools_software_version",
+      ":spvtools_util_cli_consumer",
+      ":spvtools_val",
+    ]
+    configs += [ ":spvtools_internal_config" ]
+  }
+
+  executable("spirv-cfg") {
+    sources = [
+      "tools/cfg/bin_to_dot.cpp",
+      "tools/cfg/bin_to_dot.h",
+      "tools/cfg/cfg.cpp",
+    ]
+    deps = [
+      ":spvtools",
+      ":spvtools_software_version",
+    ]
+    configs += [ ":spvtools_internal_config" ]
+  }
+
+  executable("spirv-opt") {
+    sources = [ "tools/opt/opt.cpp" ]
+    deps = [
+      ":spvtools",
+      ":spvtools_opt",
+      ":spvtools_software_version",
+      ":spvtools_util_cli_consumer",
+      ":spvtools_val",
+    ]
+    configs += [ ":spvtools_internal_config" ]
+  }
+
+  executable("spirv-link") {
+    sources = [ "tools/link/linker.cpp" ]
+    deps = [
+      ":spvtools",
+      ":spvtools_link",
+      ":spvtools_opt",
+      ":spvtools_software_version",
+      ":spvtools_val",
+    ]
+    configs += [ ":spvtools_internal_config" ]
+  }
 }
 
-executable("spirv-dis") {
-  sources = [ "tools/dis/dis.cpp" ]
-  deps = [
-    ":spvtools",
-    ":spvtools_software_version",
-  ]
-  configs += [ ":spvtools_internal_config" ]
-}
-
-executable("spirv-val") {
-  sources = [ "tools/val/val.cpp" ]
-  deps = [
-    ":spvtools",
-    ":spvtools_software_version",
-    ":spvtools_util_cli_consumer",
-    ":spvtools_val",
-  ]
-  configs += [ ":spvtools_internal_config" ]
-}
-
-executable("spirv-cfg") {
-  sources = [
-    "tools/cfg/bin_to_dot.cpp",
-    "tools/cfg/bin_to_dot.h",
-    "tools/cfg/cfg.cpp",
-  ]
-  deps = [
-    ":spvtools",
-    ":spvtools_software_version",
-  ]
-  configs += [ ":spvtools_internal_config" ]
-}
-
-executable("spirv-opt") {
-  sources = [ "tools/opt/opt.cpp" ]
-  deps = [
-    ":spvtools",
-    ":spvtools_opt",
-    ":spvtools_software_version",
-    ":spvtools_util_cli_consumer",
-    ":spvtools_val",
-  ]
-  configs += [ ":spvtools_internal_config" ]
-}
-
-executable("spirv-link") {
-  sources = [ "tools/link/linker.cpp" ]
-  deps = [
-    ":spvtools",
-    ":spvtools_link",
-    ":spvtools_opt",
-    ":spvtools_software_version",
-    ":spvtools_val",
-  ]
-  configs += [ ":spvtools_internal_config" ]
-}
-
-if (!is_ios && !spirv_is_winuwp && build_with_chromium) {
+if (!is_ios && !spirv_is_winuwp && build_with_chromium && spvtools_build_executables) {
   # iOS and UWP do not allow std::system calls which spirv-fuzz
   # requires. Additionally, spirv-fuzz is only built when in a
   # Chromium checkout due to its dependency on protobuf.
@@ -1500,7 +1510,7 @@
   }
 }
 
-if (!is_ios && !spirv_is_winuwp) {
+if (!is_ios && !spirv_is_winuwp && spvtools_build_executables) {
   # iOS and UWP do not allow std::system calls which spirv-reduce
   # requires.
 
@@ -1518,19 +1528,21 @@
   }
 }
 
-group("all_spirv_tools") {
-  deps = [
-    ":spirv-as",
-    ":spirv-cfg",
-    ":spirv-dis",
-    ":spirv-link",
-    ":spirv-opt",
-    ":spirv-val",
-  ]
-  if (!is_ios && !spirv_is_winuwp && build_with_chromium) {
-    deps += [ ":spirv-fuzz" ]
-  }
-  if (!is_ios && !spirv_is_winuwp) {
-    deps += [ ":spirv-reduce" ]
+if (spvtools_build_executables){
+  group("all_spirv_tools") {
+    deps = [
+      ":spirv-as",
+      ":spirv-cfg",
+      ":spirv-dis",
+      ":spirv-link",
+      ":spirv-opt",
+      ":spirv-val",
+    ]
+    if (!is_ios && !spirv_is_winuwp && build_with_chromium) {
+      deps += [ ":spirv-fuzz" ]
+    }
+    if (!is_ios && !spirv_is_winuwp) {
+      deps += [ ":spirv-reduce" ]
+    }
   }
 }
diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp
index 2273e85..fbbd9bc 100644
--- a/include/spirv-tools/optimizer.hpp
+++ b/include/spirv-tools/optimizer.hpp
@@ -886,6 +886,13 @@
 // propagated into their final positions.
 Optimizer::PassToken CreateInterpolateFixupPass();
 
+// Removes unused components from composite input variables. Current
+// implementation just removes trailing unused components from input arrays.
+// The pass performs best after maximizing dead code removal. A subsequent dead
+// code elimination pass would be beneficial in removing newly unused component
+// types.
+Optimizer::PassToken CreateEliminateDeadInputComponentsPass();
+
 // Creates a convert-to-sampled-image pass to convert images and/or
 // samplers with given pairs of descriptor set and binding to sampled image.
 // If a pair of an image and a sampler have the same pair of descriptor set and
diff --git a/source/assembly_grammar.cpp b/source/assembly_grammar.cpp
index 79f18ee..4f5942a 100644
--- a/source/assembly_grammar.cpp
+++ b/source/assembly_grammar.cpp
@@ -62,9 +62,9 @@
     end = std::find(begin, text_end, separator);
 
     spv_operand_desc entry = nullptr;
-    if (spvOperandTableNameLookup(env, operandTable, type, begin, end - begin,
-                                  &entry)) {
-      return SPV_ERROR_INVALID_TEXT;
+    if (auto error = spvOperandTableNameLookup(env, operandTable, type, begin,
+                                               end - begin, &entry)) {
+      return error;
     }
     value |= entry->value;
 
diff --git a/source/operand.cpp b/source/operand.cpp
index 6d83e81..0c255a3 100644
--- a/source/operand.cpp
+++ b/source/operand.cpp
@@ -72,12 +72,16 @@
       // Note that the second rule assumes the extension enabling this operand
       // is indeed requested in the SPIR-V code; checking that should be
       // validator's work.
-      if (((version >= entry.minVersion && version <= entry.lastVersion) ||
-           entry.numExtensions > 0u || entry.numCapabilities > 0u) &&
-          nameLength == strlen(entry.name) &&
+      if (nameLength == strlen(entry.name) &&
           !strncmp(entry.name, name, nameLength)) {
-        *pEntry = &entry;
-        return SPV_SUCCESS;
+        if ((version >= entry.minVersion && version <= entry.lastVersion) ||
+            entry.numExtensions > 0u || entry.numCapabilities > 0u) {
+          *pEntry = &entry;
+          return SPV_SUCCESS;
+        } else {
+          // if there is no extension/capability then the version is wrong
+          return SPV_ERROR_WRONG_VERSION;
+        }
       }
     }
   }
diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt
index 05c02fc..61e7a98 100644
--- a/source/opt/CMakeLists.txt
+++ b/source/opt/CMakeLists.txt
@@ -45,6 +45,7 @@
   eliminate_dead_constant_pass.h
   eliminate_dead_functions_pass.h
   eliminate_dead_functions_util.h
+  eliminate_dead_input_components_pass.h
   eliminate_dead_members_pass.h
   empty_pass.h
   feature_manager.h
@@ -158,6 +159,7 @@
   eliminate_dead_constant_pass.cpp
   eliminate_dead_functions_pass.cpp
   eliminate_dead_functions_util.cpp
+  eliminate_dead_input_components_pass.cpp
   eliminate_dead_members_pass.cpp
   feature_manager.cpp
   fix_storage_class.cpp
diff --git a/source/opt/dead_branch_elim_pass.cpp b/source/opt/dead_branch_elim_pass.cpp
index 356dbcb..cc616ca 100644
--- a/source/opt/dead_branch_elim_pass.cpp
+++ b/source/opt/dead_branch_elim_pass.cpp
@@ -207,7 +207,7 @@
       Instruction::OperandList new_operands;
       new_operands.push_back(terminator->GetInOperand(0));
       new_operands.push_back({SPV_OPERAND_TYPE_ID, {live_lab_id}});
-      terminator->SetInOperands(move(new_operands));
+      terminator->SetInOperands(std::move(new_operands));
       context()->UpdateDefUse(terminator);
     } else {
       // Check if the merge instruction is still needed because of a
diff --git a/source/opt/eliminate_dead_input_components_pass.cpp b/source/opt/eliminate_dead_input_components_pass.cpp
new file mode 100644
index 0000000..f383136
--- /dev/null
+++ b/source/opt/eliminate_dead_input_components_pass.cpp
@@ -0,0 +1,146 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+// Copyright (c) 2022 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/opt/eliminate_dead_input_components_pass.h"
+
+#include <set>
+#include <vector>
+
+#include "source/opt/instruction.h"
+#include "source/opt/ir_builder.h"
+#include "source/opt/ir_context.h"
+#include "source/util/bit_vector.h"
+
+namespace {
+
+const uint32_t kAccessChainBaseInIdx = 0;
+const uint32_t kAccessChainIndex0InIdx = 1;
+const uint32_t kConstantValueInIdx = 0;
+const uint32_t kVariableStorageClassInIdx = 0;
+
+}  // namespace
+
+namespace spvtools {
+namespace opt {
+
+Pass::Status EliminateDeadInputComponentsPass::Process() {
+  // Current functionality assumes shader capability
+  if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
+    return Status::SuccessWithoutChange;
+  analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  bool modified = false;
+  std::vector<std::pair<Instruction*, unsigned>> arrays_to_change;
+  for (auto& var : context()->types_values()) {
+    if (var.opcode() != SpvOpVariable) {
+      continue;
+    }
+    analysis::Type* var_type = type_mgr->GetType(var.type_id());
+    analysis::Pointer* ptr_type = var_type->AsPointer();
+    if (ptr_type == nullptr) {
+      continue;
+    }
+    if (ptr_type->storage_class() != SpvStorageClassInput) {
+      continue;
+    }
+    const analysis::Array* arr_type = ptr_type->pointee_type()->AsArray();
+    if (arr_type == nullptr) {
+      continue;
+    }
+    unsigned arr_len_id = arr_type->LengthId();
+    Instruction* arr_len_inst = def_use_mgr->GetDef(arr_len_id);
+    if (arr_len_inst->opcode() != SpvOpConstant) {
+      continue;
+    }
+    // SPIR-V requires array size is >= 1, so this works for signed or
+    // unsigned size
+    unsigned original_max =
+        arr_len_inst->GetSingleWordInOperand(kConstantValueInIdx) - 1;
+    unsigned max_idx = FindMaxIndex(var, original_max);
+    if (max_idx != original_max) {
+      ChangeArrayLength(var, max_idx + 1);
+      modified = true;
+    }
+  }
+
+  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
+}
+
+unsigned EliminateDeadInputComponentsPass::FindMaxIndex(Instruction& var,
+                                                        unsigned original_max) {
+  unsigned max = 0;
+  bool seen_non_const_ac = false;
+  assert(var.opcode() == SpvOpVariable && "must be variable");
+  context()->get_def_use_mgr()->WhileEachUser(
+      var.result_id(), [&max, &seen_non_const_ac, var, this](Instruction* use) {
+        auto use_opcode = use->opcode();
+        if (use_opcode == SpvOpLoad || use_opcode == SpvOpCopyMemory ||
+            use_opcode == SpvOpCopyMemorySized ||
+            use_opcode == SpvOpCopyObject) {
+          seen_non_const_ac = true;
+          return false;
+        }
+        if (use->opcode() != SpvOpAccessChain &&
+            use->opcode() != SpvOpInBoundsAccessChain) {
+          return true;
+        }
+        // OpAccessChain with no indices currently not optimized
+        if (use->NumInOperands() == 1) {
+          seen_non_const_ac = true;
+          return false;
+        }
+        unsigned base_id = use->GetSingleWordInOperand(kAccessChainBaseInIdx);
+        USE_ASSERT(base_id == var.result_id() && "unexpected base");
+        unsigned idx_id = use->GetSingleWordInOperand(kAccessChainIndex0InIdx);
+        Instruction* idx_inst = context()->get_def_use_mgr()->GetDef(idx_id);
+        if (idx_inst->opcode() != SpvOpConstant) {
+          seen_non_const_ac = true;
+          return false;
+        }
+        unsigned value = idx_inst->GetSingleWordInOperand(kConstantValueInIdx);
+        if (value > max) max = value;
+        return true;
+      });
+  return seen_non_const_ac ? original_max : max;
+}
+
+void EliminateDeadInputComponentsPass::ChangeArrayLength(Instruction& arr,
+                                                         unsigned length) {
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
+  analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+  analysis::Pointer* ptr_type = type_mgr->GetType(arr.type_id())->AsPointer();
+  const analysis::Array* arr_ty = ptr_type->pointee_type()->AsArray();
+  assert(arr_ty && "expecting array type");
+  uint32_t length_id = const_mgr->GetUIntConst(length);
+  analysis::Array new_arr_ty(arr_ty->element_type(),
+                             arr_ty->GetConstantLengthInfo(length_id, length));
+  analysis::Type* reg_new_arr_ty = type_mgr->GetRegisteredType(&new_arr_ty);
+  analysis::Pointer new_ptr_ty(reg_new_arr_ty, SpvStorageClassInput);
+  analysis::Type* reg_new_ptr_ty = type_mgr->GetRegisteredType(&new_ptr_ty);
+  uint32_t new_ptr_ty_id = type_mgr->GetTypeInstruction(reg_new_ptr_ty);
+  arr.SetResultType(new_ptr_ty_id);
+  def_use_mgr->AnalyzeInstUse(&arr);
+  // Move array OpVariable instruction after its new type to preserve order
+  USE_ASSERT(arr.GetSingleWordInOperand(kVariableStorageClassInIdx) !=
+                 SpvStorageClassFunction &&
+             "cannot move Function variable");
+  Instruction* new_ptr_ty_inst = def_use_mgr->GetDef(new_ptr_ty_id);
+  arr.RemoveFromList();
+  arr.InsertAfter(new_ptr_ty_inst);
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/eliminate_dead_input_components_pass.h b/source/opt/eliminate_dead_input_components_pass.h
new file mode 100644
index 0000000..b77857f
--- /dev/null
+++ b/source/opt/eliminate_dead_input_components_pass.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+// Copyright (c) 2022 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_OPT_ELIMINATE_DEAD_INPUT_COMPONENTS_H_
+#define SOURCE_OPT_ELIMINATE_DEAD_INPUT_COMPONENTS_H_
+
+#include <unordered_map>
+
+#include "source/opt/ir_context.h"
+#include "source/opt/module.h"
+#include "source/opt/pass.h"
+
+namespace spvtools {
+namespace opt {
+
+// See optimizer.hpp for documentation.
+class EliminateDeadInputComponentsPass : public Pass {
+ public:
+  explicit EliminateDeadInputComponentsPass() {}
+
+  const char* name() const override { return "reduce-load-size"; }
+  Status Process() override;
+
+  // Return the mask of preserved Analyses.
+  IRContext::Analysis GetPreservedAnalyses() override {
+    return IRContext::kAnalysisDefUse |
+           IRContext::kAnalysisInstrToBlockMapping |
+           IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG |
+           IRContext::kAnalysisDominatorAnalysis |
+           IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap |
+           IRContext::kAnalysisConstants | IRContext::kAnalysisTypes;
+  }
+
+ private:
+  // Find the max constant used to index the variable declared by |var|
+  // through OpAccessChain or OpInBoundsAccessChain. If any non-constant
+  // indices or non-Op*AccessChain use of |var|, return |original_max|.
+  unsigned FindMaxIndex(Instruction& var, unsigned original_max);
+
+  // Change the length of the array |inst| to |length|
+  void ChangeArrayLength(Instruction& inst, unsigned length);
+};
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // SOURCE_OPT_ELIMINATE_DEAD_INPUT_COMPONENTS_H_
diff --git a/source/opt/inst_bindless_check_pass.cpp b/source/opt/inst_bindless_check_pass.cpp
index 5607239..c2c5d6c 100644
--- a/source/opt/inst_bindless_check_pass.cpp
+++ b/source/opt/inst_bindless_check_pass.cpp
@@ -39,13 +39,6 @@
 static const int kSpvTypeImageSampled = 5;
 }  // anonymous namespace
 
-// Avoid unused variable warning/error on Linux
-#ifndef NDEBUG
-#define USE_ASSERT(x) assert(x)
-#else
-#define USE_ASSERT(x) ((void)(x))
-#endif
-
 namespace spvtools {
 namespace opt {
 
diff --git a/source/opt/interp_fixup_pass.cpp b/source/opt/interp_fixup_pass.cpp
index ad29e6a..e8cdd99 100644
--- a/source/opt/interp_fixup_pass.cpp
+++ b/source/opt/interp_fixup_pass.cpp
@@ -31,13 +31,6 @@
 // Input Operand Indices
 static const int kSpvVariableStorageClassInIdx = 0;
 
-// Avoid unused variable warning/error on Linux
-#ifndef NDEBUG
-#define USE_ASSERT(x) assert(x)
-#else
-#define USE_ASSERT(x) ((void)(x))
-#endif
-
 // Folding rule function which attempts to replace |op(OpLoad(a),...)|
 // by |op(a,...)|, where |op| is one of the GLSLstd450 InterpolateAt*
 // instructions. Returns true if replaced, false otherwise.
diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp
index ec2c8ea..f28b1ba 100644
--- a/source/opt/optimizer.cpp
+++ b/source/opt/optimizer.cpp
@@ -523,6 +523,8 @@
     RegisterPass(CreateInterpolateFixupPass());
   } else if (pass_name == "remove-dont-inline") {
     RegisterPass(CreateRemoveDontInlinePass());
+  } else if (pass_name == "eliminate-dead-input-components") {
+    RegisterPass(CreateEliminateDeadInputComponentsPass());
   } else if (pass_name == "convert-to-sampled-image") {
     if (pass_args.size() > 0) {
       auto descriptor_set_binding_pairs =
@@ -1004,6 +1006,11 @@
       MakeUnique<opt::InterpFixupPass>());
 }
 
+Optimizer::PassToken CreateEliminateDeadInputComponentsPass() {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::EliminateDeadInputComponentsPass>());
+}
+
 Optimizer::PassToken CreateConvertToSampledImagePass(
     const std::vector<opt::DescriptorSetAndBinding>&
         descriptor_set_binding_pairs) {
diff --git a/source/opt/pass.h b/source/opt/pass.h
index 4a8ea67..b2303e2 100644
--- a/source/opt/pass.h
+++ b/source/opt/pass.h
@@ -28,6 +28,13 @@
 #include "spirv-tools/libspirv.hpp"
 #include "types.h"
 
+// Avoid unused variable warning/error on Linux
+#ifndef NDEBUG
+#define USE_ASSERT(x) assert(x)
+#else
+#define USE_ASSERT(x) ((void)(x))
+#endif
+
 namespace spvtools {
 namespace opt {
 
diff --git a/source/opt/passes.h b/source/opt/passes.h
index 26739cd..a12c76b 100644
--- a/source/opt/passes.h
+++ b/source/opt/passes.h
@@ -34,6 +34,7 @@
 #include "source/opt/desc_sroa.h"
 #include "source/opt/eliminate_dead_constant_pass.h"
 #include "source/opt/eliminate_dead_functions_pass.h"
+#include "source/opt/eliminate_dead_input_components_pass.h"
 #include "source/opt/eliminate_dead_members_pass.h"
 #include "source/opt/empty_pass.h"
 #include "source/opt/fix_storage_class.h"
diff --git a/source/opt/private_to_local_pass.cpp b/source/opt/private_to_local_pass.cpp
index 12a226d..80fb4c5 100644
--- a/source/opt/private_to_local_pass.cpp
+++ b/source/opt/private_to_local_pass.cpp
@@ -135,7 +135,7 @@
   // Place the variable at the start of the first basic block.
   context()->AnalyzeUses(variable);
   context()->set_instr_block(variable, &*function->begin());
-  function->begin()->begin()->InsertBefore(move(var));
+  function->begin()->begin()->InsertBefore(std::move(var));
 
   // Update uses where the type may have changed.
   return UpdateUses(variable);
diff --git a/source/opt/types.cpp b/source/opt/types.cpp
index 47abd41..ebbdc36 100644
--- a/source/opt/types.cpp
+++ b/source/opt/types.cpp
@@ -419,6 +419,12 @@
 
 void Array::ReplaceElementType(const Type* type) { element_type_ = type; }
 
+Array::LengthInfo Array::GetConstantLengthInfo(uint32_t const_id,
+                                               uint32_t length) const {
+  std::vector<uint32_t> extra_words{LengthInfo::Case::kConstant, length};
+  return {const_id, extra_words};
+}
+
 RuntimeArray::RuntimeArray(const Type* type)
     : Type(kRuntimeArray), element_type_(type) {
   assert(!type->AsVoid());
diff --git a/source/opt/types.h b/source/opt/types.h
index 01e8f3c..f5a4a6b 100644
--- a/source/opt/types.h
+++ b/source/opt/types.h
@@ -390,6 +390,7 @@
   size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
 
   void ReplaceElementType(const Type* element_type);
+  LengthInfo GetConstantLengthInfo(uint32_t const_id, uint32_t length) const;
 
  private:
   bool IsSameImpl(const Type* that, IsSameCache*) const override;
diff --git a/source/text.cpp b/source/text.cpp
index 415c059..97d00cb 100644
--- a/source/text.cpp
+++ b/source/text.cpp
@@ -403,9 +403,10 @@
     case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS:
     case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: {
       uint32_t value;
-      if (grammar.parseMaskOperand(type, textValue, &value)) {
-        return context->diagnostic() << "Invalid " << spvOperandTypeStr(type)
-                                     << " operand '" << textValue << "'.";
+      if (auto error = grammar.parseMaskOperand(type, textValue, &value)) {
+        return context->diagnostic(error)
+               << "Invalid " << spvOperandTypeStr(type) << " operand '"
+               << textValue << "'.";
       }
       if (auto error = context->binaryEncodeU32(value, pInst)) return error;
       // Prepare to parse the operands for this logical operand.
@@ -769,8 +770,8 @@
     instructions.push_back({});
     spv_instruction_t& inst = instructions.back();
 
-    if (spvTextEncodeOpcode(grammar, &context, &inst)) {
-      return SPV_ERROR_INVALID_TEXT;
+    if (auto error = spvTextEncodeOpcode(grammar, &context, &inst)) {
+      return error;
     }
 
     if (context.advance()) break;
diff --git a/source/val/validate_atomics.cpp b/source/val/validate_atomics.cpp
index cfa15d9..3b0a7fa 100644
--- a/source/val/validate_atomics.cpp
+++ b/source/val/validate_atomics.cpp
@@ -39,7 +39,7 @@
     case SpvStorageClassAtomicCounter:
     case SpvStorageClassImage:
     case SpvStorageClassFunction:
-    case SpvStorageClassPhysicalStorageBufferEXT:
+    case SpvStorageClassPhysicalStorageBuffer:
       return true;
       break;
     default:
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp
index eb6caf0..e712936 100644
--- a/source/val/validate_decorations.cpp
+++ b/source/val/validate_decorations.cpp
@@ -1017,7 +1017,7 @@
       }
 
       const bool phys_storage_buffer =
-          storageClass == SpvStorageClassPhysicalStorageBufferEXT;
+          storageClass == SpvStorageClassPhysicalStorageBuffer;
       const bool workgroup =
           storageClass == SpvStorageClassWorkgroup &&
           vstate.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayoutKHR);
@@ -1407,11 +1407,11 @@
         storage != SpvStorageClassUniform &&
         storage != SpvStorageClassPushConstant &&
         storage != SpvStorageClassInput && storage != SpvStorageClassOutput &&
-        storage != SpvStorageClassPhysicalStorageBufferEXT) {
+        storage != SpvStorageClassPhysicalStorageBuffer) {
       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
              << "FPRoundingMode decoration can be applied only to the "
                 "Object operand of an OpStore in the StorageBuffer, "
-                "PhysicalStorageBufferEXT, Uniform, PushConstant, Input, or "
+                "PhysicalStorageBuffer, Uniform, PushConstant, Input, or "
                 "Output Storage Classes.";
     }
   }
diff --git a/source/val/validate_function.cpp b/source/val/validate_function.cpp
index 596186b..656893f 100644
--- a/source/val/validate_function.cpp
+++ b/source/val/validate_function.cpp
@@ -147,8 +147,8 @@
               "type of the same index.";
   }
 
-  // Validate that PhysicalStorageBufferEXT have one of Restrict, Aliased,
-  // RestrictPointerEXT, or AliasedPointerEXT.
+  // Validate that PhysicalStorageBuffer have one of Restrict, Aliased,
+  // RestrictPointer, or AliasedPointer.
   auto param_nonarray_type_id = param_type->id();
   while (_.GetIdOpcode(param_nonarray_type_id) == SpvOpTypeArray) {
     param_nonarray_type_id =
@@ -157,7 +157,7 @@
   if (_.GetIdOpcode(param_nonarray_type_id) == SpvOpTypePointer) {
     auto param_nonarray_type = _.FindDef(param_nonarray_type_id);
     if (param_nonarray_type->GetOperandAs<uint32_t>(1u) ==
-        SpvStorageClassPhysicalStorageBufferEXT) {
+        SpvStorageClassPhysicalStorageBuffer) {
       // check for Aliased or Restrict
       const auto& decorations = _.id_decorations(inst->id());
 
@@ -174,14 +174,14 @@
       if (!foundAliased && !foundRestrict) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << "OpFunctionParameter " << inst->id()
-               << ": expected Aliased or Restrict for PhysicalStorageBufferEXT "
+               << ": expected Aliased or Restrict for PhysicalStorageBuffer "
                   "pointer.";
       }
       if (foundAliased && foundRestrict) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << "OpFunctionParameter " << inst->id()
                << ": can't specify both Aliased and Restrict for "
-                  "PhysicalStorageBufferEXT pointer.";
+                  "PhysicalStorageBuffer pointer.";
       }
     } else {
       const auto pointee_type_id =
@@ -189,31 +189,31 @@
       const auto pointee_type = _.FindDef(pointee_type_id);
       if (SpvOpTypePointer == pointee_type->opcode() &&
           pointee_type->GetOperandAs<uint32_t>(1u) ==
-              SpvStorageClassPhysicalStorageBufferEXT) {
-        // check for AliasedPointerEXT/RestrictPointerEXT
+              SpvStorageClassPhysicalStorageBuffer) {
+        // check for AliasedPointer/RestrictPointer
         const auto& decorations = _.id_decorations(inst->id());
 
         bool foundAliased = std::any_of(
             decorations.begin(), decorations.end(), [](const Decoration& d) {
-              return SpvDecorationAliasedPointerEXT == d.dec_type();
+              return SpvDecorationAliasedPointer == d.dec_type();
             });
 
         bool foundRestrict = std::any_of(
             decorations.begin(), decorations.end(), [](const Decoration& d) {
-              return SpvDecorationRestrictPointerEXT == d.dec_type();
+              return SpvDecorationRestrictPointer == d.dec_type();
             });
 
         if (!foundAliased && !foundRestrict) {
           return _.diag(SPV_ERROR_INVALID_ID, inst)
                  << "OpFunctionParameter " << inst->id()
-                 << ": expected AliasedPointerEXT or RestrictPointerEXT for "
-                    "PhysicalStorageBufferEXT pointer.";
+                 << ": expected AliasedPointer or RestrictPointer for "
+                    "PhysicalStorageBuffer pointer.";
         }
         if (foundAliased && foundRestrict) {
           return _.diag(SPV_ERROR_INVALID_ID, inst)
                  << "OpFunctionParameter " << inst->id()
-                 << ": can't specify both AliasedPointerEXT and "
-                    "RestrictPointerEXT for PhysicalStorageBufferEXT pointer.";
+                 << ": can't specify both AliasedPointer and "
+                    "RestrictPointer for PhysicalStorageBuffer pointer.";
         }
       }
     }
diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp
index b12d1e8..e9164af 100644
--- a/source/val/validate_image.cpp
+++ b/source/val/validate_image.cpp
@@ -640,65 +640,64 @@
   return SPV_SUCCESS;
 }
 
-// Checks some of the validation rules which are common to multiple opcodes.
-spv_result_t ValidateImageCommon(ValidationState_t& _, const Instruction* inst,
-                                 const ImageTypeInfo& info) {
-  const SpvOp opcode = inst->opcode();
-  if (IsProj(opcode)) {
-    if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D &&
-        info.dim != SpvDimRect) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Expected Image 'Dim' parameter to be 1D, 2D, 3D or Rect";
-    }
-
-    if (info.multisampled != 0) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Expected Image 'MS' parameter to be 0";
-    }
-
-    if (info.arrayed != 0) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Expected Image 'arrayed' parameter to be 0";
-    }
+// Validate OpImage*Proj* instructions
+spv_result_t ValidateImageProj(ValidationState_t& _, const Instruction* inst,
+                               const ImageTypeInfo& info) {
+  if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D &&
+      info.dim != SpvDimRect) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Image 'Dim' parameter to be 1D, 2D, 3D or Rect";
   }
 
-  if (opcode == SpvOpImageRead || opcode == SpvOpImageSparseRead ||
-      opcode == SpvOpImageWrite) {
-    if (info.sampled == 0) {
-    } else if (info.sampled == 2) {
-      if (info.dim == SpvDim1D && !_.HasCapability(SpvCapabilityImage1D)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Capability Image1D is required to access storage image";
-      } else if (info.dim == SpvDimRect &&
-                 !_.HasCapability(SpvCapabilityImageRect)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Capability ImageRect is required to access storage image";
-      } else if (info.dim == SpvDimBuffer &&
-                 !_.HasCapability(SpvCapabilityImageBuffer)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Capability ImageBuffer is required to access storage image";
-      } else if (info.dim == SpvDimCube && info.arrayed == 1 &&
-                 !_.HasCapability(SpvCapabilityImageCubeArray)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Capability ImageCubeArray is required to access "
-               << "storage image";
-      }
+  if (info.multisampled != 0) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Image 'MS' parameter to be 0";
+  }
 
-      if (info.multisampled == 1 &&
-          !_.HasCapability(SpvCapabilityImageMSArray)) {
-#if 0
-        // TODO(atgoo@github.com) The description of this rule in the spec
-        // is unclear and Glslang doesn't declare ImageMSArray. Need to clarify
-        // and reenable.
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-            << "Capability ImageMSArray is required to access storage "
-            << "image";
-#endif
-      }
-    } else {
+  if (info.arrayed != 0) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Image 'arrayed' parameter to be 0";
+  }
+
+  return SPV_SUCCESS;
+}
+
+// Validate OpImage*Read and OpImage*Write instructions
+spv_result_t ValidateImageReadWrite(ValidationState_t& _,
+                                    const Instruction* inst,
+                                    const ImageTypeInfo& info) {
+  if (info.sampled == 2) {
+    if (info.dim == SpvDim1D && !_.HasCapability(SpvCapabilityImage1D)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Expected Image 'Sampled' parameter to be 0 or 2";
+             << "Capability Image1D is required to access storage image";
+    } else if (info.dim == SpvDimRect &&
+               !_.HasCapability(SpvCapabilityImageRect)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Capability ImageRect is required to access storage image";
+    } else if (info.dim == SpvDimBuffer &&
+               !_.HasCapability(SpvCapabilityImageBuffer)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Capability ImageBuffer is required to access storage image";
+    } else if (info.dim == SpvDimCube && info.arrayed == 1 &&
+               !_.HasCapability(SpvCapabilityImageCubeArray)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Capability ImageCubeArray is required to access "
+             << "storage image";
     }
+
+    if (info.multisampled == 1 && !_.HasCapability(SpvCapabilityImageMSArray)) {
+#if 0
+      // TODO(atgoo@github.com) The description of this rule in the spec
+      // is unclear and Glslang doesn't declare ImageMSArray. Need to clarify
+      // and reenable.
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+          << "Capability ImageMSArray is required to access storage "
+          << "image";
+#endif
+    }
+  } else if (info.sampled != 0) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Image 'Sampled' parameter to be 0 or 2";
   }
 
   return SPV_SUCCESS;
@@ -813,7 +812,8 @@
     }
   }
 
-  // Dim is checked elsewhere.
+  // Universal checks on image type operands
+  // Dim and Format and Access Qualifier are checked elsewhere.
 
   if (info.depth > 2) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -825,6 +825,35 @@
            << "Invalid Arrayed " << info.arrayed << " (must be 0 or 1)";
   }
 
+  if (info.multisampled > 1) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Invalid MS " << info.multisampled << " (must be 0 or 1)";
+  }
+
+  if (info.sampled > 2) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Invalid Sampled " << info.sampled << " (must be 0, 1 or 2)";
+  }
+
+  if (info.dim == SpvDimSubpassData) {
+    if (info.sampled != 2) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << _.VkErrorID(6214) << "Dim SubpassData requires Sampled to be 2";
+    }
+
+    if (info.format != SpvImageFormatUnknown) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Dim SubpassData requires format Unknown";
+    }
+  } else {
+    if (info.multisampled && (info.sampled == 2) &&
+        !_.HasCapability(SpvCapabilityStorageImageMultisample)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Capability StorageImageMultisample is required when using "
+                "multisampled storage image";
+    }
+  }
+
   if (spvIsOpenCLEnv(target_env)) {
     if ((info.arrayed == 1) && (info.dim != SpvDim1D) &&
         (info.dim != SpvDim2D)) {
@@ -832,23 +861,22 @@
              << "In the OpenCL environment, Arrayed may only be set to 1 "
              << "when Dim is either 1D or 2D.";
     }
-  }
 
-  if (info.multisampled > 1) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Invalid MS " << info.multisampled << " (must be 0 or 1)";
-  }
-
-  if (spvIsOpenCLEnv(target_env)) {
     if (info.multisampled != 0) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "MS must be 0 in the OpenCL environment.";
     }
-  }
 
-  if (info.sampled > 2) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Invalid Sampled " << info.sampled << " (must be 0, 1 or 2)";
+    if (info.sampled != 0) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Sampled must be 0 in the OpenCL environment.";
+    }
+
+    if (info.access_qualifier == SpvAccessQualifierMax) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "In the OpenCL environment, the optional Access Qualifier"
+             << " must be present.";
+    }
   }
 
   if (spvIsVulkanEnv(target_env)) {
@@ -857,43 +885,10 @@
              << _.VkErrorID(4657)
              << "Sampled must be 1 or 2 in the Vulkan environment.";
     }
-  }
 
-  if (spvIsOpenCLEnv(_.context()->target_env)) {
-    if (info.sampled != 0) {
+    if (info.dim == SpvDimSubpassData && info.arrayed != 0) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Sampled must be 0 in the OpenCL environment.";
-    }
-  }
-
-  if (info.dim == SpvDimSubpassData) {
-    if (info.sampled != 2) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Dim SubpassData requires Sampled to be 2";
-    }
-
-    if (info.format != SpvImageFormatUnknown) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Dim SubpassData requires format Unknown";
-    }
-  }
-
-  // Format and Access Qualifier are also checked elsewhere.
-
-  if (spvIsOpenCLEnv(_.context()->target_env)) {
-    if (info.access_qualifier == SpvAccessQualifierMax) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "In the OpenCL environment, the optional Access Qualifier"
-             << " must be present.";
-    }
-  }
-
-  if (info.multisampled && (info.sampled == 2) &&
-      (info.dim != SpvDimSubpassData)) {
-    if (!_.HasCapability(SpvCapabilityStorageImageMultisample)) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Capability StorageImageMultisample is required when using "
-                "multisampled storage image";
+             << _.VkErrorID(6214) << "Dim SubpassData requires Arrayed to be 0";
     }
   }
 
@@ -1205,7 +1200,9 @@
            << "Corrupt image type definition";
   }
 
-  if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result;
+  if (IsProj(opcode)) {
+    if (spv_result_t result = ValidateImageProj(_, inst, info)) return result;
+  }
 
   if (info.multisampled) {
     // When using image operands, the Sample image operand is required if and
@@ -1268,6 +1265,27 @@
   return SPV_SUCCESS;
 }
 
+// Validates anything OpImage*Dref* instruction
+spv_result_t ValidateImageDref(ValidationState_t& _, const Instruction* inst,
+                               const ImageTypeInfo& info) {
+  const uint32_t dref_type = _.GetOperandTypeId(inst, 4);
+  if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Dref to be of 32-bit float type";
+  }
+
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    if (info.dim == SpvDim3D) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << _.VkErrorID(4777)
+             << "In Vulkan, OpImage*Dref* instructions must not use images "
+                "with a 3D Dim";
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
 spv_result_t ValidateImageDrefLod(ValidationState_t& _,
                                   const Instruction* inst) {
   const SpvOp opcode = inst->opcode();
@@ -1295,7 +1313,9 @@
            << "Corrupt image type definition";
   }
 
-  if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result;
+  if (IsProj(opcode)) {
+    if (spv_result_t result = ValidateImageProj(_, inst, info)) return result;
+  }
 
   if (info.multisampled) {
     // When using image operands, the Sample image operand is required if and
@@ -1325,11 +1345,7 @@
            << " components, but given only " << actual_coord_size;
   }
 
-  const uint32_t dref_type = _.GetOperandTypeId(inst, 4);
-  if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Expected Dref to be of 32-bit float type";
-  }
+  if (spv_result_t result = ValidateImageDref(_, inst, info)) return result;
 
   if (spv_result_t result =
           ValidateImageOperands(_, inst, info, /* word_index = */ 7))
@@ -1464,7 +1480,8 @@
   if (info.dim != SpvDim2D && info.dim != SpvDimCube &&
       info.dim != SpvDimRect) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Expected Image 'Dim' cannot be Cube";
+           << _.VkErrorID(4777)
+           << "Expected Image 'Dim' to be 2D, Cube, or Rect";
   }
 
   const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
@@ -1500,11 +1517,7 @@
   } else {
     assert(opcode == SpvOpImageDrefGather ||
            opcode == SpvOpImageSparseDrefGather);
-    const uint32_t dref_type = _.GetOperandTypeId(inst, 4);
-    if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Expected Dref to be of 32-bit float type";
-    }
+    if (spv_result_t result = ValidateImageDref(_, inst, info)) return result;
   }
 
   if (spv_result_t result =
@@ -1572,6 +1585,13 @@
                << " to have 4 components";
       }
     }
+
+    const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5);
+    if (mask & SpvImageOperandsConstOffsetMask) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "ConstOffset image operand not allowed "
+             << "in the OpenCL environment.";
+    }
   }
 
   if (info.dim == SpvDimSubpassData) {
@@ -1597,7 +1617,8 @@
     }
   }
 
-  if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result;
+  if (spv_result_t result = ValidateImageReadWrite(_, inst, info))
+    return result;
 
   const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
   if (!_.IsIntScalarOrVectorType(coord_type)) {
@@ -1622,16 +1643,6 @@
     }
   }
 
-  const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5);
-
-  if (mask & SpvImageOperandsConstOffsetMask) {
-    if (spvIsOpenCLEnv(_.context()->target_env)) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "ConstOffset image operand not allowed "
-             << "in the OpenCL environment.";
-    }
-  }
-
   if (spv_result_t result =
           ValidateImageOperands(_, inst, info, /* word_index = */ 6))
     return result;
@@ -1657,7 +1668,8 @@
            << "Image 'Dim' cannot be SubpassData";
   }
 
-  if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result;
+  if (spv_result_t result = ValidateImageReadWrite(_, inst, info))
+    return result;
 
   const uint32_t coord_type = _.GetOperandTypeId(inst, 1);
   if (!_.IsIntScalarOrVectorType(coord_type)) {
diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp
index 4f3d9cd..0b23126 100644
--- a/source/val/validate_memory.cpp
+++ b/source/val/validate_memory.cpp
@@ -315,11 +315,12 @@
   SpvStorageClass dst_sc, src_sc;
   std::tie(dst_sc, src_sc) = GetStorageClass(_, inst);
   if (inst->operands().size() <= index) {
-    if (src_sc == SpvStorageClassPhysicalStorageBufferEXT ||
-        dst_sc == SpvStorageClassPhysicalStorageBufferEXT) {
+    // Cases where lack of some operand is invalid
+    if (src_sc == SpvStorageClassPhysicalStorageBuffer ||
+        dst_sc == SpvStorageClassPhysicalStorageBuffer) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
-             << "Memory accesses with PhysicalStorageBufferEXT must use "
-                "Aligned.";
+             << _.VkErrorID(4708)
+             << "Memory accesses with PhysicalStorageBuffer must use Aligned.";
     }
     return SPV_SUCCESS;
   }
@@ -368,7 +369,7 @@
         dst_sc != SpvStorageClassCrossWorkgroup &&
         dst_sc != SpvStorageClassGeneric && dst_sc != SpvStorageClassImage &&
         dst_sc != SpvStorageClassStorageBuffer &&
-        dst_sc != SpvStorageClassPhysicalStorageBufferEXT) {
+        dst_sc != SpvStorageClassPhysicalStorageBuffer) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "NonPrivatePointerKHR requires a pointer in Uniform, "
              << "Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer "
@@ -379,7 +380,7 @@
         src_sc != SpvStorageClassCrossWorkgroup &&
         src_sc != SpvStorageClassGeneric && src_sc != SpvStorageClassImage &&
         src_sc != SpvStorageClassStorageBuffer &&
-        src_sc != SpvStorageClassPhysicalStorageBufferEXT) {
+        src_sc != SpvStorageClassPhysicalStorageBuffer) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "NonPrivatePointerKHR requires a pointer in Uniform, "
              << "Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer "
@@ -388,11 +389,11 @@
   }
 
   if (!(mask & SpvMemoryAccessAlignedMask)) {
-    if (src_sc == SpvStorageClassPhysicalStorageBufferEXT ||
-        dst_sc == SpvStorageClassPhysicalStorageBufferEXT) {
+    if (src_sc == SpvStorageClassPhysicalStorageBuffer ||
+        dst_sc == SpvStorageClassPhysicalStorageBuffer) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
-             << "Memory accesses with PhysicalStorageBufferEXT must use "
-                "Aligned.";
+             << _.VkErrorID(4708)
+             << "Memory accesses with PhysicalStorageBuffer must use Aligned.";
     }
   }
 
@@ -519,20 +520,21 @@
     }
   }
 
-  // Vulkan 14.5.1: Check type of PushConstant variables.
-  // Vulkan 14.5.2: Check type of UniformConstant and Uniform variables.
   if (spvIsVulkanEnv(_.context()->target_env)) {
+    // Vulkan Push Constant Interface section: Check type of PushConstant
+    // variables.
     if (storage_class == SpvStorageClassPushConstant) {
-      if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) {
+      if (pointee->opcode() != SpvOpTypeStruct) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << "PushConstant OpVariable <id> '" << _.getIdName(inst->id())
                << "' has illegal type.\n"
-               << "From Vulkan spec, section 14.5.1:\n"
-               << "Such variables must be typed as OpTypeStruct, "
-               << "or an array of this type";
+               << "From Vulkan spec, Push Constant Interface section:\n"
+               << "Such variables must be typed as OpTypeStruct";
       }
     }
 
+    // Vulkan Descriptor Set Interface: Check type of UniformConstant and
+    // Uniform variables.
     if (storage_class == SpvStorageClassUniformConstant) {
       if (!IsAllowedTypeOrArrayOfSame(
               _, pointee,
@@ -627,9 +629,9 @@
     }
   }
 
-  if (storage_class == SpvStorageClassPhysicalStorageBufferEXT) {
+  if (storage_class == SpvStorageClassPhysicalStorageBuffer) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "PhysicalStorageBufferEXT must not be used with OpVariable.";
+           << "PhysicalStorageBuffer must not be used with OpVariable.";
   }
 
   auto pointee_base = pointee;
@@ -638,23 +640,23 @@
   }
   if (pointee_base->opcode() == SpvOpTypePointer) {
     if (pointee_base->GetOperandAs<uint32_t>(1u) ==
-        SpvStorageClassPhysicalStorageBufferEXT) {
-      // check for AliasedPointerEXT/RestrictPointerEXT
+        SpvStorageClassPhysicalStorageBuffer) {
+      // check for AliasedPointer/RestrictPointer
       bool foundAliased =
-          _.HasDecoration(inst->id(), SpvDecorationAliasedPointerEXT);
+          _.HasDecoration(inst->id(), SpvDecorationAliasedPointer);
       bool foundRestrict =
-          _.HasDecoration(inst->id(), SpvDecorationRestrictPointerEXT);
+          _.HasDecoration(inst->id(), SpvDecorationRestrictPointer);
       if (!foundAliased && !foundRestrict) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << "OpVariable " << inst->id()
-               << ": expected AliasedPointerEXT or RestrictPointerEXT for "
-               << "PhysicalStorageBufferEXT pointer.";
+               << ": expected AliasedPointer or RestrictPointer for "
+               << "PhysicalStorageBuffer pointer.";
       }
       if (foundAliased && foundRestrict) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << "OpVariable " << inst->id()
-               << ": can't specify both AliasedPointerEXT and "
-               << "RestrictPointerEXT for PhysicalStorageBufferEXT pointer.";
+               << ": can't specify both AliasedPointer and "
+               << "RestrictPointer for PhysicalStorageBuffer pointer.";
       }
     }
   }
@@ -667,7 +669,8 @@
     if (value_type && value_type->opcode() == SpvOpTypeRuntimeArray) {
       if (!_.HasCapability(SpvCapabilityRuntimeDescriptorArrayEXT)) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << "OpVariable, <id> '" << _.getIdName(inst->id())
+               << _.VkErrorID(4680) << "OpVariable, <id> '"
+               << _.getIdName(inst->id())
                << "', is attempting to create memory for an illegal type, "
                << "OpTypeRuntimeArray.\nFor Vulkan OpTypeRuntimeArray can only "
                << "appear as the final member of an OpTypeStruct, thus cannot "
@@ -679,6 +682,7 @@
             storage_class != SpvStorageClassUniform &&
             storage_class != SpvStorageClassUniformConstant) {
           return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << _.VkErrorID(4680)
                  << "For Vulkan with RuntimeDescriptorArrayEXT, a variable "
                  << "containing OpTypeRuntimeArray must have storage class of "
                  << "StorageBuffer, Uniform, or UniformConstant.";
@@ -692,25 +696,30 @@
     // as BufferBlock.
     if (value_type && value_type->opcode() == SpvOpTypeStruct) {
       if (DoesStructContainRTA(_, value_type)) {
-        if (storage_class == SpvStorageClassStorageBuffer) {
+        if (storage_class == SpvStorageClassStorageBuffer ||
+            storage_class == SpvStorageClassPhysicalStorageBuffer) {
           if (!_.HasDecoration(value_id, SpvDecorationBlock)) {
             return _.diag(SPV_ERROR_INVALID_ID, inst)
+                   << _.VkErrorID(4680)
                    << "For Vulkan, an OpTypeStruct variable containing an "
                    << "OpTypeRuntimeArray must be decorated with Block if it "
-                   << "has storage class StorageBuffer.";
+                   << "has storage class StorageBuffer or "
+                      "PhysicalStorageBuffer.";
           }
         } else if (storage_class == SpvStorageClassUniform) {
           if (!_.HasDecoration(value_id, SpvDecorationBufferBlock)) {
             return _.diag(SPV_ERROR_INVALID_ID, inst)
+                   << _.VkErrorID(4680)
                    << "For Vulkan, an OpTypeStruct variable containing an "
                    << "OpTypeRuntimeArray must be decorated with BufferBlock "
                    << "if it has storage class Uniform.";
           }
         } else {
           return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << _.VkErrorID(4680)
                  << "For Vulkan, OpTypeStruct variables containing "
                  << "OpTypeRuntimeArray must have storage class of "
-                 << "StorageBuffer or Uniform.";
+                 << "StorageBuffer, PhysicalStorageBuffer, or Uniform.";
         }
       }
     }
@@ -745,7 +754,7 @@
           SPV_OPERAND_TYPE_STORAGE_CLASS, storage_class);
       switch (storage_class) {
         case SpvStorageClassStorageBuffer:
-        case SpvStorageClassPhysicalStorageBufferEXT:
+        case SpvStorageClassPhysicalStorageBuffer:
           if (!_.HasCapability(SpvCapabilityStorageBuffer16BitAccess)) {
             storage_class_ok = false;
           }
@@ -807,7 +816,7 @@
           SPV_OPERAND_TYPE_STORAGE_CLASS, storage_class);
       switch (storage_class) {
         case SpvStorageClassStorageBuffer:
-        case SpvStorageClassPhysicalStorageBufferEXT:
+        case SpvStorageClassPhysicalStorageBuffer:
           if (!_.HasCapability(SpvCapabilityStorageBuffer8BitAccess)) {
             storage_class_ok = false;
           }
@@ -1138,8 +1147,6 @@
              << "'s type does not match Source <id> '"
              << _.getIdName(source_type->id()) << "'s type.";
     }
-
-    if (auto error = CheckMemoryAccess(_, inst, 2)) return error;
   } else {
     const auto size_id = inst->GetOperandAs<uint32_t>(2);
     const auto size = _.FindDef(size_id);
@@ -1182,8 +1189,6 @@
         // Cannot infer any other opcodes.
         break;
     }
-
-    if (auto error = CheckMemoryAccess(_, inst, 3)) return error;
   }
   if (auto error = ValidateCopyMemoryMemoryAccess(_, inst)) return error;
 
@@ -1508,7 +1513,7 @@
 
   if (storage_class != SpvStorageClassWorkgroup &&
       storage_class != SpvStorageClassStorageBuffer &&
-      storage_class != SpvStorageClassPhysicalStorageBufferEXT) {
+      storage_class != SpvStorageClassPhysicalStorageBuffer) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << opname << " storage class for pointer type <id> '"
            << _.getIdName(pointer_type_id)
diff --git a/source/val/validate_type.cpp b/source/val/validate_type.cpp
index 4376b52..2aded61 100644
--- a/source/val/validate_type.cpp
+++ b/source/val/validate_type.cpp
@@ -228,8 +228,8 @@
   if (spvIsVulkanEnv(_.context()->target_env) &&
       element_type->opcode() == SpvOpTypeRuntimeArray) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "OpTypeArray Element Type <id> '" << _.getIdName(element_type_id)
-           << "' is not valid in "
+           << _.VkErrorID(4680) << "OpTypeArray Element Type <id> '"
+           << _.getIdName(element_type_id) << "' is not valid in "
            << spvLogStringForEnv(_.context()->target_env) << " environments.";
   }
 
@@ -298,7 +298,7 @@
   if (spvIsVulkanEnv(_.context()->target_env) &&
       element_type->opcode() == SpvOpTypeRuntimeArray) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "OpTypeRuntimeArray Element Type <id> '"
+           << _.VkErrorID(4680) << "OpTypeRuntimeArray Element Type <id> '"
            << _.getIdName(element_id) << "' is not valid in "
            << spvLogStringForEnv(_.context()->target_env) << " environments.";
   }
@@ -373,7 +373,8 @@
           member_type_index == inst->operands().size() - 1;
       if (!is_last_member) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << "In " << spvLogStringForEnv(_.context()->target_env)
+               << _.VkErrorID(4680) << "In "
+               << spvLogStringForEnv(_.context()->target_env)
                << ", OpTypeRuntimeArray must only be used for the last member "
                   "of an OpTypeStruct";
       }
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index 977a160..65c1dd6 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -460,7 +460,7 @@
     default:
     // fall through
     case SpvAddressingModelPhysical64:
-    case SpvAddressingModelPhysicalStorageBuffer64EXT:
+    case SpvAddressingModelPhysicalStorageBuffer64:
       pointer_size_and_alignment_ = 8;
       break;
   }
@@ -1862,6 +1862,8 @@
       return VUID_WRAP(VUID-StandaloneSpirv-FPRoundingMode-04675);
     case 4677:
       return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677);
+    case 4680:
+      return VUID_WRAP( VUID-StandaloneSpirv-OpTypeRuntimeArray-04680);
     case 4682:
       return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682);
     case 6426:
@@ -1870,6 +1872,8 @@
       return VUID_WRAP(VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685);
     case 4686:
       return VUID_WRAP(VUID-StandaloneSpirv-None-04686);
+    case 4708:
+      return VUID_WRAP(VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708);
     case 4710:
       return VUID_WRAP(VUID-StandaloneSpirv-PhysicalStorageBuffer64-04710);
     case 4711:
@@ -1884,6 +1888,8 @@
       return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04733);
     case 4734:
       return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-04734);
+    case 4777:
+      return VUID_WRAP(VUID-StandaloneSpirv-OpImage-04777);
     case 4780:
       return VUID_WRAP(VUID-StandaloneSpirv-Result-04780);
     case 4915:
@@ -1896,6 +1902,8 @@
       return VUID_WRAP(VUID-StandaloneSpirv-Location-04918);
     case 4919:
       return VUID_WRAP(VUID-StandaloneSpirv-Location-04919);
+    case 6214:
+      return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-06214);
     case 6491:
       return VUID_WRAP(VUID-StandaloneSpirv-DescriptorSet-06491);
     default:
diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt
index f50fdda..6dfb1b7 100644
--- a/test/opt/CMakeLists.txt
+++ b/test/opt/CMakeLists.txt
@@ -42,6 +42,7 @@
        desc_sroa_test.cpp
        eliminate_dead_const_test.cpp
        eliminate_dead_functions_test.cpp
+       eliminate_dead_input_components_test.cpp
        eliminate_dead_member_test.cpp
        feature_manager_test.cpp
        fix_storage_class_test.cpp
diff --git a/test/opt/eliminate_dead_input_components_test.cpp b/test/opt/eliminate_dead_input_components_test.cpp
new file mode 100644
index 0000000..b0098f7
--- /dev/null
+++ b/test/opt/eliminate_dead_input_components_test.cpp
@@ -0,0 +1,404 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+// Copyright (c) 2022 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using ElimDeadInputComponentsTest = PassTest<::testing::Test>;
+
+TEST_F(ElimDeadInputComponentsTest, ElimOneConstantIndex) {
+  // Should reduce to uv[2]
+  //
+  // #version 450
+  //
+  // layout(location = 0) in vec4 uv[8];
+  //
+  // out gl_PerVertex {
+  //     vec4 gl_Position;
+  // };
+  //
+  // void main()
+  // {
+  //     gl_Position = uv[1];
+  // }
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %_ %uv
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpName %_ ""
+               OpName %uv "uv"
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpDecorate %gl_PerVertex Block
+               OpDecorate %uv Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%gl_PerVertex = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
+           %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+      %int_1 = OpConstant %int 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+;CHECK:     %uv = OpVariable %_ptr_Input__arr_v4float_uint_2 Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1
+         %21 = OpLoad %v4float %20
+         %23 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+               OpStore %23 %21
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
+}
+
+TEST_F(ElimDeadInputComponentsTest, ElimOneConstantIndexInBounds) {
+  // Same as ElimOneConstantIndex but with OpInBoundsAccessChain
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %_ %uv
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpName %_ ""
+               OpName %uv "uv"
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpDecorate %gl_PerVertex Block
+               OpDecorate %uv Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%gl_PerVertex = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
+           %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+      %int_1 = OpConstant %int 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+;CHECK:     %uv = OpVariable %_ptr_Input__arr_v4float_uint_2 Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %20 = OpInBoundsAccessChain %_ptr_Input_v4float %uv %int_1
+         %21 = OpLoad %v4float %20
+         %23 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+               OpStore %23 %21
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
+}
+
+TEST_F(ElimDeadInputComponentsTest, ElimTwoConstantIndices) {
+  // Should reduce to uv[4]
+  //
+  // #version 450
+  //
+  // layout(location = 0) in vec4 uv[8];
+  //
+  // out gl_PerVertex {
+  //     vec4 gl_Position;
+  // };
+  //
+  // void main()
+  // {
+  //     gl_Position = uv[1] + uv[3];
+  // }
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %_ %uv
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpName %_ ""
+               OpName %uv "uv"
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpDecorate %gl_PerVertex Block
+               OpDecorate %uv Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%gl_PerVertex = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
+         %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+      %int_1 = OpConstant %int 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+      %int_3 = OpConstant %int 3
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+;CHECK:     %uv = OpVariable %_ptr_Input__arr_v4float_uint_4 Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1
+         %21 = OpLoad %v4float %20
+         %23 = OpAccessChain %_ptr_Input_v4float %uv %int_3
+         %24 = OpLoad %v4float %23
+         %25 = OpFAdd %v4float %21 %24
+         %27 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+               OpStore %27 %25
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
+}
+
+TEST_F(ElimDeadInputComponentsTest, NoElimMaxConstantIndex) {
+  // Should not reduce uv[8] because of max index of 7
+  //
+  // #version 450
+  //
+  // layout(location = 0) in vec4 uv[8];
+  //
+  // out gl_PerVertex {
+  //     vec4 gl_Position;
+  // };
+  //
+  // void main()
+  // {
+  //     gl_Position = uv[1] + uv[7];
+  // }
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %_ %uv
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpName %_ ""
+               OpName %uv "uv"
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpDecorate %gl_PerVertex Block
+               OpDecorate %uv Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%gl_PerVertex = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
+         %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+      %int_1 = OpConstant %int 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+      %int_7 = OpConstant %int 7
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+;CHECK:     %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1
+         %21 = OpLoad %v4float %20
+         %23 = OpAccessChain %_ptr_Input_v4float %uv %int_7
+         %24 = OpLoad %v4float %23
+         %25 = OpFAdd %v4float %21 %24
+         %27 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+               OpStore %27 %25
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
+}
+
+TEST_F(ElimDeadInputComponentsTest, NoElimNonConstantIndex) {
+  // Should not reduce uv[8] because of non-constant index of ui
+  //
+  // #version 450
+  //
+  // layout(location = 0) in vec4 uv[8];
+  //
+  // out gl_PerVertex {
+  //     vec4 gl_Position;
+  // };
+  //
+  // uniform ubname {
+  //     int ui;
+  // } ubinst;
+  //
+  // void main()
+  // {
+  //     gl_Position = uv[1] + uv[ubinst.ui];
+  // }
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %_ %uv %ubinst
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpName %_ ""
+               OpName %uv "uv"
+               OpName %ubname "ubname"
+               OpMemberName %ubname 0 "ui"
+               OpName %ubinst "ubinst"
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpDecorate %gl_PerVertex Block
+               OpDecorate %uv Location 0
+               OpMemberDecorate %ubname 0 Offset 0
+               OpDecorate %ubname Block
+               OpDecorate %ubinst DescriptorSet 0
+               OpDecorate %ubinst Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%gl_PerVertex = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
+         %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+      %int_1 = OpConstant %int 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+     %ubname = OpTypeStruct %int
+%_ptr_Uniform_ubname = OpTypePointer Uniform %ubname
+     %ubinst = OpVariable %_ptr_Uniform_ubname Uniform
+%_ptr_Uniform_int = OpTypePointer Uniform %int
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+;CHECK:  %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1
+         %21 = OpLoad %v4float %20
+         %26 = OpAccessChain %_ptr_Uniform_int %ubinst %int_0
+         %27 = OpLoad %int %26
+         %28 = OpAccessChain %_ptr_Input_v4float %uv %27
+         %29 = OpLoad %v4float %28
+         %30 = OpFAdd %v4float %21 %29
+         %32 = OpAccessChain %_ptr_Output_v4float %_ %int_0
+               OpStore %32 %30
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
+}
+
+TEST_F(ElimDeadInputComponentsTest, NoElimNonIndexedAccessChain) {
+  // Should not change due to non-indexed access chain
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %_ %uv
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %gl_PerVertex "gl_PerVertex"
+               OpMemberName %gl_PerVertex 0 "gl_Position"
+               OpName %_ ""
+               OpName %uv "uv"
+               OpMemberDecorate %gl_PerVertex 0 BuiltIn Position
+               OpDecorate %gl_PerVertex Block
+               OpDecorate %uv Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%gl_PerVertex = OpTypeStruct %v4float
+%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex
+          %_ = OpVariable %_ptr_Output_gl_PerVertex Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8
+           %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+      %int_1 = OpConstant %int 1
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+;CHECK:  %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %20 = OpAccessChain %_ptr_Input__arr_v4float_uint_8 %uv
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_3);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true);
+}
+
+}  // namespace
+}  // namespace opt
+}  // namespace spvtools
diff --git a/test/val/val_capability_test.cpp b/test/val/val_capability_test.cpp
index c432c3c..0d84caa 100644
--- a/test/val/val_capability_test.cpp
+++ b/test/val/val_capability_test.cpp
@@ -2918,8 +2918,8 @@
            "StorageTexelBufferArrayNonUniformIndexing",
            "SPV_EXT_descriptor_indexing"),
       // SPV_EXT_physical_storage_buffer
-      IN15("PhysicalStorageBufferAddressesEXT",
-           "PhysicalStorageBufferAddresses", "SPV_EXT_physical_storage_buffer"),
+      IN15("PhysicalStorageBufferAddresses", "PhysicalStorageBufferAddresses",
+           "SPV_EXT_physical_storage_buffer"),
       // SPV_KHR_vulkan_memory_model
       IN15("VulkanMemoryModelKHR", "VulkanMemoryModel",
            "SPV_KHR_vulkan_memory_model"),
diff --git a/test/val/val_conversion_test.cpp b/test/val/val_conversion_test.cpp
index b9802ec..94bd27d 100644
--- a/test/val/val_conversion_test.cpp
+++ b/test/val/val_conversion_test.cpp
@@ -1557,7 +1557,7 @@
 OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint Fragment %main "main"
 OpExecutionMode %main OriginUpperLeft
-OpDecorate %val1 RestrictPointerEXT
+OpDecorate %val1 RestrictPointer
 %uint64 = OpTypeInt 64 0
 %u64_1 = OpConstant %uint64 1
 %ptr = OpTypePointer PhysicalStorageBuffer %uint64
@@ -1615,7 +1615,7 @@
 OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint Fragment %main "main"
 OpExecutionMode %main OriginUpperLeft
-OpDecorate %val1 RestrictPointerEXT
+OpDecorate %val1 RestrictPointer
 %uint32 = OpTypeInt 32 0
 %uint64 = OpTypeInt 64 0
 %ptr = OpTypePointer PhysicalStorageBuffer %uint64
diff --git a/test/val/val_data_test.cpp b/test/val/val_data_test.cpp
index 1d4c0e0..eef08a1 100644
--- a/test/val/val_data_test.cpp
+++ b/test/val/val_data_test.cpp
@@ -767,6 +767,8 @@
   CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_1);
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
   EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
+  EXPECT_THAT(getDiagnosticString(),
               HasSubstr("In Vulkan, OpTypeRuntimeArray must only be used for "
                         "the last member of an OpTypeStruct\n  %_struct_3 = "
                         "OpTypeStruct %_runtimearr_uint %uint\n"));
diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp
index 6f5a246..9eaf825 100644
--- a/test/val/val_decoration_test.cpp
+++ b/test/val/val_decoration_test.cpp
@@ -5943,16 +5943,16 @@
 
 TEST_F(ValidateDecorations, PSBAliasedRestrictPointerSuccess) {
   const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
 OpCapability Int64
 OpCapability Shader
 OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint Fragment %main "main"
 OpExecutionMode %main OriginUpperLeft
-OpDecorate %val1 RestrictPointerEXT
+OpDecorate %val1 RestrictPointer
 %uint64 = OpTypeInt 64 0
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
 %pptr_f = OpTypePointer Function %ptr
 %void = OpTypeVoid
 %voidfn = OpTypeFunction %void
@@ -5969,15 +5969,15 @@
 
 TEST_F(ValidateDecorations, PSBAliasedRestrictPointerMissing) {
   const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
 OpCapability Int64
 OpCapability Shader
 OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint Fragment %main "main"
 OpExecutionMode %main OriginUpperLeft
 %uint64 = OpTypeInt 64 0
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
 %pptr_f = OpTypePointer Function %ptr
 %void = OpTypeVoid
 %voidfn = OpTypeFunction %void
@@ -5991,23 +5991,23 @@
   CompileSuccessfully(body.c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("expected AliasedPointerEXT or RestrictPointerEXT for "
-                        "PhysicalStorageBufferEXT pointer"));
+              HasSubstr("expected AliasedPointer or RestrictPointer for "
+                        "PhysicalStorageBuffer pointer"));
 }
 
 TEST_F(ValidateDecorations, PSBAliasedRestrictPointerBoth) {
   const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
 OpCapability Int64
 OpCapability Shader
 OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint Fragment %main "main"
 OpExecutionMode %main OriginUpperLeft
-OpDecorate %val1 RestrictPointerEXT
-OpDecorate %val1 AliasedPointerEXT
+OpDecorate %val1 RestrictPointer
+OpDecorate %val1 AliasedPointer
 %uint64 = OpTypeInt 64 0
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
 %pptr_f = OpTypePointer Function %ptr
 %void = OpTypeVoid
 %voidfn = OpTypeFunction %void
@@ -6020,24 +6020,23 @@
 
   CompileSuccessfully(body.c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("can't specify both AliasedPointerEXT and RestrictPointerEXT "
-                "for PhysicalStorageBufferEXT pointer"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("can't specify both AliasedPointer and RestrictPointer "
+                        "for PhysicalStorageBuffer pointer"));
 }
 
 TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamSuccess) {
   const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
 OpCapability Int64
 OpCapability Shader
 OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint Fragment %main "main"
 OpExecutionMode %main OriginUpperLeft
 OpDecorate %fparam Restrict
 %uint64 = OpTypeInt 64 0
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
 %void = OpTypeVoid
 %voidfn = OpTypeFunction %void
 %fnptr = OpTypeFunction %void %ptr
@@ -6058,15 +6057,15 @@
 
 TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamMissing) {
   const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
 OpCapability Int64
 OpCapability Shader
 OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint Fragment %main "main"
 OpExecutionMode %main OriginUpperLeft
 %uint64 = OpTypeInt 64 0
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
 %void = OpTypeVoid
 %voidfn = OpTypeFunction %void
 %fnptr = OpTypeFunction %void %ptr
@@ -6085,22 +6084,22 @@
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("expected Aliased or Restrict for "
-                        "PhysicalStorageBufferEXT pointer"));
+                        "PhysicalStorageBuffer pointer"));
 }
 
 TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamBoth) {
   const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
 OpCapability Int64
 OpCapability Shader
 OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint Fragment %main "main"
 OpExecutionMode %main OriginUpperLeft
 OpDecorate %fparam Restrict
 OpDecorate %fparam Aliased
 %uint64 = OpTypeInt 64 0
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
 %void = OpTypeVoid
 %voidfn = OpTypeFunction %void
 %fnptr = OpTypeFunction %void %ptr
@@ -6119,12 +6118,12 @@
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("can't specify both Aliased and Restrict for "
-                        "PhysicalStorageBufferEXT pointer"));
+                        "PhysicalStorageBuffer pointer"));
 }
 
 TEST_F(ValidateDecorations, PSBFPRoundingModeSuccess) {
   std::string spirv = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
 OpCapability Shader
 OpCapability Linkage
 OpCapability StorageBuffer16BitAccess
@@ -6132,14 +6131,14 @@
 OpExtension "SPV_KHR_storage_buffer_storage_class"
 OpExtension "SPV_KHR_variable_pointers"
 OpExtension "SPV_KHR_16bit_storage"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint GLCompute %main "main"
 OpDecorate %_ FPRoundingMode RTE
-OpDecorate %half_ptr_var AliasedPointerEXT
+OpDecorate %half_ptr_var AliasedPointer
 %half = OpTypeFloat 16
 %float = OpTypeFloat 32
 %float_1_25 = OpConstant %float 1.25
-%half_ptr = OpTypePointer PhysicalStorageBufferEXT %half
+%half_ptr = OpTypePointer PhysicalStorageBuffer %half
 %half_pptr_f = OpTypePointer Function %half_ptr
 %void = OpTypeVoid
 %func = OpTypeFunction %void
diff --git a/test/val/val_fixtures.h b/test/val/val_fixtures.h
index acbe0e5..98d8d32 100644
--- a/test/val/val_fixtures.h
+++ b/test/val/val_fixtures.h
@@ -40,8 +40,10 @@
 
   // Assembles the given SPIR-V text, checks that it fails to assemble,
   // and returns resulting diagnostic.  No internal state is updated.
+  // Setting the desired_result to SPV_SUCCESS is used to allow all results
   std::string CompileFailure(std::string code,
-                             spv_target_env env = SPV_ENV_UNIVERSAL_1_0);
+                             spv_target_env env = SPV_ENV_UNIVERSAL_1_0,
+                             spv_result_t desired_result = SPV_SUCCESS);
 
   // Checks that 'code' is valid SPIR-V text representation and stores the
   // binary version for further method calls.
@@ -108,11 +110,17 @@
 
 template <typename T>
 std::string ValidateBase<T>::CompileFailure(std::string code,
-                                            spv_target_env env) {
+                                            spv_target_env env,
+                                            spv_result_t desired_result) {
   spv_diagnostic diagnostic = nullptr;
-  EXPECT_NE(SPV_SUCCESS,
-            spvTextToBinary(ScopedContext(env).context, code.c_str(),
-                            code.size(), &binary_, &diagnostic));
+  spv_result_t actual_result =
+      spvTextToBinary(ScopedContext(env).context, code.c_str(), code.size(),
+                      &binary_, &diagnostic);
+  EXPECT_NE(SPV_SUCCESS, actual_result);
+  // optional check for exact result
+  if (desired_result != SPV_SUCCESS) {
+    EXPECT_EQ(actual_result, desired_result);
+  }
   std::string result(diagnostic->error);
   spvDiagnosticDestroy(diagnostic);
   return result;
diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp
index a11d07c..d8f6b44 100644
--- a/test/val/val_image_test.cpp
+++ b/test/val/val_image_test.cpp
@@ -246,6 +246,11 @@
 %uniform_image_u32_2d_0001 = OpVariable %ptr_image_u32_2d_0001 UniformConstant
 %type_sampled_image_u32_2d_0001 = OpTypeSampledImage %type_image_u32_2d_0001
 
+%type_image_u32_3d_0001 = OpTypeImage %u32 3D 0 0 0 1 Unknown
+%ptr_image_u32_3d_0001 = OpTypePointer UniformConstant %type_image_u32_3d_0001
+%uniform_image_u32_3d_0001 = OpVariable %ptr_image_u32_3d_0001 UniformConstant
+%type_sampled_image_u32_3d_0001 = OpTypeSampledImage %type_image_u32_3d_0001
+
 %type_image_u32_2d_0002 = OpTypeImage %u32 2D 0 0 0 2 Unknown
 %ptr_image_u32_2d_0002 = OpTypePointer UniformConstant %type_image_u32_2d_0002
 %uniform_image_u32_2d_0002 = OpVariable %ptr_image_u32_2d_0002 UniformConstant
@@ -272,6 +277,11 @@
 %uniform_image_f32_3d_0111 = OpVariable %ptr_image_f32_3d_0111 UniformConstant
 %type_sampled_image_f32_3d_0111 = OpTypeSampledImage %type_image_f32_3d_0111
 
+%type_image_f32_3d_0001 = OpTypeImage %f32 3D 0 0 0 1 Unknown
+%ptr_image_f32_3d_0001 = OpTypePointer UniformConstant %type_image_f32_3d_0001
+%uniform_image_f32_3d_0001 = OpVariable %ptr_image_f32_3d_0001 UniformConstant
+%type_sampled_image_f32_3d_0001 = OpTypeSampledImage %type_image_f32_3d_0001
+
 %type_image_f32_cube_0101 = OpTypeImage %f32 Cube 0 1 0 1 Unknown
 %ptr_image_f32_cube_0101 = OpTypePointer UniformConstant %type_image_f32_cube_0101
 %uniform_image_f32_cube_0101 = OpVariable %ptr_image_f32_cube_0101 UniformConstant
@@ -738,6 +748,34 @@
               HasSubstr("Dim SubpassData requires Sampled to be 2"));
 }
 
+TEST_F(ValidateImage, TypeImageWrongSampledForSubpassDataVulkan) {
+  const std::string code = GetShaderHeader("OpCapability InputAttachment\n") +
+                           R"(
+%img_type = OpTypeImage %f32 SubpassData 0 0 0 1 Unknown
+)" + TrivialMain();
+
+  CompileSuccessfully(code.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeImage-06214"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Dim SubpassData requires Sampled to be 2"));
+}
+
+TEST_F(ValidateImage, TypeImageWrongArrayForSubpassDataVulkan) {
+  const std::string code = GetShaderHeader("OpCapability InputAttachment\n") +
+                           R"(
+%img_type = OpTypeImage %f32 SubpassData 0 1 0 2 Unknown
+)" + TrivialMain();
+
+  CompileSuccessfully(code.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeImage-06214"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Dim SubpassData requires Arrayed to be 0"));
+}
+
 TEST_F(ValidateImage, TypeImage_OpenCL_Sampled0_OK) {
   const std::string code = GetKernelHeader() + R"(
 %img_type = OpTypeImage %void 2D 0 0 0 0 Unknown ReadOnly
@@ -1062,7 +1100,7 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 137[%137] cannot be a "
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 145[%145] cannot be a "
                                                "type"));
 }
 
@@ -2293,6 +2331,24 @@
               HasSubstr("Expected Dref to be of 32-bit float type"));
 }
 
+TEST_F(ValidateImage, SampleDrefImplicitLodWrongDimVulkan) {
+  const std::string body = R"(
+%img = OpLoad %type_image_u32_3d_0001 %uniform_image_u32_3d_0001
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_u32_3d_0001 %img %sampler
+%res1 = OpImageSampleDrefImplicitLod %u32 %simg %f32vec3_hhh %f32_1
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "", "Fragment", "", SPV_ENV_VULKAN_1_0).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpImage-04777"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("In Vulkan, OpImage*Dref* instructions must not use "
+                        "images with a 3D Dim"));
+}
+
 TEST_F(ValidateImage, SampleDrefExplicitLodSuccess) {
   const std::string body = R"(
 %img = OpLoad %type_image_s32_3d_0001 %uniform_image_s32_3d_0001
@@ -3249,6 +3305,23 @@
               HasSubstr("Expected Dref to be of 32-bit float type"));
 }
 
+TEST_F(ValidateImage, DrefGatherWrongDimVulkan) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_3d_0001 %uniform_image_f32_3d_0001
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_3d_0001 %img %sampler
+%res1 = OpImageDrefGather %f32vec4 %simg %f32vec4_0000 %f32_0_5
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "", "Fragment", "", SPV_ENV_VULKAN_1_0).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpImage-04777"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Image 'Dim' to be 2D, Cube, or Rect"));
+}
+
 TEST_F(ValidateImage, ReadSuccess1) {
   const std::string body = R"(
 %img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002
@@ -5467,7 +5540,8 @@
 )";
 
   EXPECT_THAT(CompileFailure(GenerateShaderCode(body, "", "Fragment", "",
-                                                SPV_ENV_UNIVERSAL_1_3)),
+                                                SPV_ENV_UNIVERSAL_1_3),
+                             SPV_ENV_UNIVERSAL_1_3, SPV_ERROR_WRONG_VERSION),
               HasSubstr("Invalid image operand 'SignExtend'"));
 }
 
@@ -5478,7 +5552,8 @@
 )";
 
   EXPECT_THAT(CompileFailure(GenerateShaderCode(body, "", "Fragment", "",
-                                                SPV_ENV_UNIVERSAL_1_3)),
+                                                SPV_ENV_UNIVERSAL_1_3),
+                             SPV_ENV_UNIVERSAL_1_3, SPV_ERROR_WRONG_VERSION),
               HasSubstr("Invalid image operand 'ZeroExtend'"));
 }
 
diff --git a/test/val/val_logicals_test.cpp b/test/val/val_logicals_test.cpp
index b57c743..1b76c85 100644
--- a/test/val/val_logicals_test.cpp
+++ b/test/val/val_logicals_test.cpp
@@ -1053,18 +1053,18 @@
 
 TEST_F(ValidateLogicals, PSBSelectSuccess) {
   const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
 OpCapability Int64
 OpCapability Shader
 OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint Fragment %main "main"
 OpExecutionMode %main OriginUpperLeft
-OpDecorate %val1 AliasedPointerEXT
+OpDecorate %val1 AliasedPointer
 %uint64 = OpTypeInt 64 0
 %bool = OpTypeBool
 %true = OpConstantTrue %bool
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
 %pptr_f = OpTypePointer Function %ptr
 %void = OpTypeVoid
 %voidfn = OpTypeFunction %void
diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp
index 7215974..f4369c6 100644
--- a/test/val/val_memory_test.cpp
+++ b/test/val/val_memory_test.cpp
@@ -833,11 +833,45 @@
 )";
   CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("PushConstant OpVariable <id> '6[%6]' has illegal "
-                        "type.\nFrom Vulkan spec, section 14.5.1:\n"
-                        "Such variables must be typed as OpTypeStruct, "
-                        "or an array of this type"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("PushConstant OpVariable <id> '6[%6]' has illegal "
+                "type.\nFrom Vulkan spec, Push Constant Interface section:\n"
+                "Such variables must be typed as OpTypeStruct"));
+}
+
+TEST_F(ValidateMemory, VulkanPushConstantArrayOfStructBad) {
+  std::string spirv = R"(
+            OpCapability Shader
+            OpMemoryModel Logical GLSL450
+            OpEntryPoint Fragment %1 "main"
+            OpExecutionMode %1 OriginUpperLeft
+
+            OpDecorate %struct Block
+            OpMemberDecorate %struct 0 Offset 0
+
+    %void = OpTypeVoid
+  %voidfn = OpTypeFunction %void
+   %float = OpTypeFloat 32
+     %int = OpTypeInt 32 0
+   %int_1 = OpConstant %int 1
+  %struct = OpTypeStruct %float
+   %array = OpTypeArray %struct %int_1
+     %ptr = OpTypePointer PushConstant %array
+      %pc = OpVariable %ptr PushConstant
+
+       %1 = OpFunction %void None %voidfn
+   %label = OpLabel
+            OpReturn
+            OpFunctionEnd
+)";
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("PushConstant OpVariable <id> '10[%10]' has illegal "
+                "type.\nFrom Vulkan spec, Push Constant Interface section:\n"
+                "Such variables must be typed as OpTypeStruct"));
 }
 
 TEST_F(ValidateMemory, VulkanPushConstant) {
@@ -1563,16 +1597,16 @@
 
 TEST_F(ValidateMemory, PSBLoadAlignedSuccess) {
   const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
 OpCapability Int64
 OpCapability Shader
 OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint Fragment %main "main"
 OpExecutionMode %main OriginUpperLeft
-OpDecorate %val1 AliasedPointerEXT
+OpDecorate %val1 AliasedPointer
 %uint64 = OpTypeInt 64 0
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
 %pptr_f = OpTypePointer Function %ptr
 %void = OpTypeVoid
 %voidfn = OpTypeFunction %void
@@ -1585,22 +1619,22 @@
 OpFunctionEnd
 )";
 
-  CompileSuccessfully(body.c_str());
-  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+  CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
 }
 
 TEST_F(ValidateMemory, PSBLoadAlignedMissing) {
   const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
 OpCapability Int64
 OpCapability Shader
 OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint Fragment %main "main"
 OpExecutionMode %main OriginUpperLeft
-OpDecorate %val1 AliasedPointerEXT
+OpDecorate %val1 AliasedPointer
 %uint64 = OpTypeInt 64 0
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
 %pptr_f = OpTypePointer Function %ptr
 %void = OpTypeVoid
 %voidfn = OpTypeFunction %void
@@ -1613,27 +1647,61 @@
 OpFunctionEnd
 )";
 
-  CompileSuccessfully(body.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708"));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr(
-          "Memory accesses with PhysicalStorageBufferEXT must use Aligned"));
+      HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned"));
+}
+
+TEST_F(ValidateMemory, PSBLoadAlignedMissingWithOtherOperand) {
+  const std::string body = R"(
+OpCapability PhysicalStorageBufferAddresses
+OpCapability Int64
+OpCapability Shader
+OpExtension "SPV_EXT_physical_storage_buffer"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %val1 AliasedPointer
+%uint64 = OpTypeInt 64 0
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
+%pptr_f = OpTypePointer Function %ptr
+%void = OpTypeVoid
+%voidfn = OpTypeFunction %void
+%main = OpFunction %void None %voidfn
+%entry = OpLabel
+%val1 = OpVariable %pptr_f Function
+%val2 = OpLoad %ptr %val1
+%val3 = OpLoad %uint64 %val2 Volatile
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned"));
 }
 
 TEST_F(ValidateMemory, PSBStoreAlignedSuccess) {
   const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
 OpCapability Int64
 OpCapability Shader
 OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint Fragment %main "main"
 OpExecutionMode %main OriginUpperLeft
-OpDecorate %val1 AliasedPointerEXT
+OpDecorate %val1 AliasedPointer
 %uint64 = OpTypeInt 64 0
 %u64_1 = OpConstant %uint64 1
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
 %pptr_f = OpTypePointer Function %ptr
 %void = OpTypeVoid
 %voidfn = OpTypeFunction %void
@@ -1646,23 +1714,23 @@
 OpFunctionEnd
 )";
 
-  CompileSuccessfully(body.c_str());
-  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+  CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
 }
 
 TEST_F(ValidateMemory, PSBStoreAlignedMissing) {
   const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
 OpCapability Int64
 OpCapability Shader
 OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint Fragment %main "main"
 OpExecutionMode %main OriginUpperLeft
-OpDecorate %val1 AliasedPointerEXT
+OpDecorate %val1 AliasedPointer
 %uint64 = OpTypeInt 64 0
 %u64_1 = OpConstant %uint64 1
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
 %pptr_f = OpTypePointer Function %ptr
 %void = OpTypeVoid
 %voidfn = OpTypeFunction %void
@@ -1675,27 +1743,168 @@
 OpFunctionEnd
 )";
 
-  CompileSuccessfully(body.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708"));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr(
-          "Memory accesses with PhysicalStorageBufferEXT must use Aligned"));
+      HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned"));
+}
+
+TEST_F(ValidateMemory, PSBCopyMemoryAlignedSuccess) {
+  const std::string body = R"(
+OpCapability PhysicalStorageBufferAddresses
+OpCapability Int64
+OpCapability Shader
+OpExtension "SPV_EXT_physical_storage_buffer"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %val1 AliasedPointer
+%int = OpTypeInt 32 0
+%uint64 = OpTypeInt 64 0
+%u64_1 = OpConstant %uint64 1
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
+%pptr_f = OpTypePointer Function %ptr
+%void = OpTypeVoid
+%voidfn = OpTypeFunction %void
+%main = OpFunction %void None %voidfn
+%entry = OpLabel
+%val1 = OpVariable %pptr_f Function
+%val2 = OpLoad %ptr %val1
+%val3 = OpLoad %ptr %val1
+OpCopyMemory %val2 %val3 Aligned 4
+OpCopyMemory %val3 %val2 Aligned 4 Aligned 4
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateMemory, PSBCopyMemoryAlignedMissingTarget) {
+  const std::string body = R"(
+OpCapability PhysicalStorageBufferAddresses
+OpCapability Int64
+OpCapability Shader
+OpExtension "SPV_EXT_physical_storage_buffer"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %val1 AliasedPointer
+%int = OpTypeInt 32 0
+%uint64 = OpTypeInt 64 0
+%u64_1 = OpConstant %uint64 1
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
+%pptr_f = OpTypePointer Function %ptr
+%void = OpTypeVoid
+%voidfn = OpTypeFunction %void
+%main = OpFunction %void None %voidfn
+%entry = OpLabel
+%val1 = OpVariable %pptr_f Function
+%val2 = OpLoad %ptr %val1
+%val3 = OpLoad %ptr %val1
+OpCopyMemory %val2 %val3 Volatile Aligned 4
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned"));
+}
+
+TEST_F(ValidateMemory, PSBCopyMemoryAlignedMissingSource) {
+  const std::string body = R"(
+OpCapability PhysicalStorageBufferAddresses
+OpCapability Int64
+OpCapability Shader
+OpExtension "SPV_EXT_physical_storage_buffer"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %val1 AliasedPointer
+%int = OpTypeInt 32 0
+%uint64 = OpTypeInt 64 0
+%u64_1 = OpConstant %uint64 1
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
+%pptr_f = OpTypePointer Function %ptr
+%void = OpTypeVoid
+%voidfn = OpTypeFunction %void
+%main = OpFunction %void None %voidfn
+%entry = OpLabel
+%val1 = OpVariable %pptr_f Function
+%val2 = OpLoad %ptr %val1
+%val3 = OpLoad %ptr %val1
+OpCopyMemory %val2 %val3 Aligned 4 Volatile
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned"));
+}
+
+TEST_F(ValidateMemory, PSBCopyMemoryAlignedMissingBoth) {
+  const std::string body = R"(
+OpCapability PhysicalStorageBufferAddresses
+OpCapability Int64
+OpCapability Shader
+OpExtension "SPV_EXT_physical_storage_buffer"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %val1 AliasedPointer
+%int = OpTypeInt 32 0
+%uint64 = OpTypeInt 64 0
+%u64_1 = OpConstant %uint64 1
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
+%pptr_f = OpTypePointer Function %ptr
+%void = OpTypeVoid
+%voidfn = OpTypeFunction %void
+%main = OpFunction %void None %voidfn
+%entry = OpLabel
+%val1 = OpVariable %pptr_f Function
+%val2 = OpLoad %ptr %val1
+%val3 = OpLoad %ptr %val1
+OpCopyMemory %val2 %val3 Volatile
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned"));
 }
 
 TEST_F(ValidateMemory, PSBVariable) {
   const std::string body = R"(
-OpCapability PhysicalStorageBufferAddressesEXT
+OpCapability PhysicalStorageBufferAddresses
 OpCapability Int64
 OpCapability Shader
 OpExtension "SPV_EXT_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64EXT GLSL450
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint Fragment %main "main"
 OpExecutionMode %main OriginUpperLeft
-OpDecorate %val1 AliasedPointerEXT
+OpDecorate %val1 AliasedPointer
 %uint64 = OpTypeInt 64 0
-%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64
-%val1 = OpVariable %ptr PhysicalStorageBufferEXT
+%ptr = OpTypePointer PhysicalStorageBuffer %uint64
+%val1 = OpVariable %ptr PhysicalStorageBuffer
 %void = OpTypeVoid
 %voidfn = OpTypeFunction %void
 %main = OpFunction %void None %voidfn
@@ -1708,7 +1917,7 @@
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("PhysicalStorageBufferEXT must not be used with OpVariable"));
+      HasSubstr("PhysicalStorageBuffer must not be used with OpVariable"));
 }
 
 std::string GenCoopMatLoadStoreShader(const std::string& storeMemoryAccess,
@@ -2097,6 +2306,8 @@
 
   CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -2162,6 +2373,8 @@
 
   CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("For Vulkan with RuntimeDescriptorArrayEXT, a variable "
@@ -2217,11 +2430,14 @@
 
   CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
           "For Vulkan, OpTypeStruct variables containing OpTypeRuntimeArray "
-          "must have storage class of StorageBuffer or Uniform.\n  %6 = "
+          "must have storage class of StorageBuffer, PhysicalStorageBuffer, or "
+          "Uniform.\n  %6 = "
           "OpVariable %_ptr_Workgroup__struct_4 Workgroup\n"));
 }
 
@@ -2247,9 +2463,12 @@
   CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
   EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
+  EXPECT_THAT(getDiagnosticString(),
               HasSubstr("For Vulkan, an OpTypeStruct variable containing an "
                         "OpTypeRuntimeArray must be decorated with Block if it "
-                        "has storage class StorageBuffer.\n  %6 = OpVariable "
+                        "has storage class StorageBuffer or "
+                        "PhysicalStorageBuffer.\n  %6 = OpVariable "
                         "%_ptr_StorageBuffer__struct_4 StorageBuffer\n"));
 }
 
@@ -2301,6 +2520,8 @@
   CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
   EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
+  EXPECT_THAT(getDiagnosticString(),
               HasSubstr("For Vulkan, an OpTypeStruct variable containing an "
                         "OpTypeRuntimeArray must be decorated with BufferBlock "
                         "if it has storage class Uniform.\n  %6 = OpVariable "
@@ -2328,6 +2549,8 @@
 
   CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -2361,6 +2584,8 @@
 
   CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -2423,6 +2648,8 @@
 
   CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -2459,6 +2686,8 @@
 
   CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -2490,6 +2719,8 @@
 
   CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("OpTypeArray Element Type <id> '5[%_runtimearr_4]' is not "
@@ -2524,6 +2755,8 @@
 
   CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -2558,6 +2791,8 @@
 
   CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -2595,6 +2830,8 @@
 
   CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp
index 63511a6..0129478 100644
--- a/tools/opt/opt.cpp
+++ b/tools/opt/opt.cpp
@@ -208,6 +208,10 @@
                unused stores to vector components, that are not removed by
                aggressive dead code elimination.)");
   printf(R"(
+  --eliminate-dead-input-components
+               Deletes unused components from input variables. Currently
+               deletes trailing unused elements from input arrays.)");
+  printf(R"(
   --eliminate-dead-variables
                Deletes module scope variables that are not referenced.)");
   printf(R"(