Reland "Merge tag 'vulkan-sdk-1.4.350.1' into main"

This is a reland of commit c6c58028313c7a46c9ad7661f21543e788b9cf57

Original change's description:
> Merge tag 'vulkan-sdk-1.4.350.1' into main
>
> Bug: 510484520
> Change-Id: I14ecf0679e1a1468f39748261f2618fc365e2686
> Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/spirv-tools/+/1708910
> Reviewed-by: Craig Stout <cstout@google.com>

Bug: 510484520
Change-Id: I9c143b1baac07e49a6e59f340fb3c4eda7588028
Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/spirv-tools/+/1721692
Reviewed-by: Craig Stout <cstout@google.com>
SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com>
diff --git a/.bazelversion b/.bazelversion
index a8907c0..ba7f754 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-7.0.2
+7.4.0
diff --git a/.github/workflows/autoroll.yml b/.github/workflows/autoroll.yml
index eb0b0fe..7494ac8 100644
--- a/.github/workflows/autoroll.yml
+++ b/.github/workflows/autoroll.yml
@@ -9,6 +9,7 @@
 
 jobs:
   update-dependencies:
+    if: github.repository == 'KhronosGroup/SPIRV-Tools'
     permissions:
       contents: write
       pull-requests: write
@@ -16,7 +17,7 @@
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
 
       # Checkout the depot tools they are needed by roll_deps.sh
       - name: Checkout depot tools
diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml
index 38dd1b3..8e52db3 100644
--- a/.github/workflows/bazel.yml
+++ b/.github/workflows/bazel.yml
@@ -7,31 +7,33 @@
     branches:
       - 'main'
   pull_request:
+    types: [opened, synchronize, reopened, unlabeled]
 
 jobs:
   build:
+    if: github.event.action != 'unlabeled' || github.event.label.name == 'kokoro:run'
     timeout-minutes: 120
     strategy:
       matrix:
-        os: [ubuntu-latest, windows-2019]
+        os: [ubuntu-latest, windows-2025]
 
     runs-on: ${{matrix.os}}
 
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
         with:
           fetch-depth: '0'
       - name: Download dependencies
         run: python3 utils/git-sync-deps
       - name: Mount Bazel cache
-        uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
+        uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
         with:
           path: ~/.bazel/cache
           key: bazel-cache-${{ runner.os }}
       - name: Build All
         run: bazel --output_user_root=~/.bazel/cache build //...
       - name: Test All
-        run: bazel --output_user_root=~/.bazel/cache test //...
+        run: bazel --output_user_root=~/.bazel/cache test --test_output=errors //...
 
   # iOS is 10x expensive to run on GitHub machines, so only run if we know something else passed
   # The steps are unfortunately duplicated because github actions requires 2 jobs for a dependency
@@ -41,17 +43,17 @@
     runs-on: macos-latest
 
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
         with:
           fetch-depth: '0'
       - name: Download dependencies
         run: python3 utils/git-sync-deps
       - name: Mount Bazel cache
-        uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
+        uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
         with:
           path: ~/.bazel/cache
           key: bazel-cache-${{ runner.os }}
       - name: Build All
         run: bazel --output_user_root=~/.bazel/cache build //...
       - name: Test All
-        run: bazel --output_user_root=~/.bazel/cache test //...
+        run: bazel --output_user_root=~/.bazel/cache test --test_output=errors //...
diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml
index b856e72..22c87b2 100644
--- a/.github/workflows/ios.yml
+++ b/.github/workflows/ios.yml
@@ -14,8 +14,8 @@
     runs-on: macos-latest
     if: ${{ github.event.workflow_run.conclusion == 'success' }}
     steps:
-        - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
-        - uses: lukka/get-cmake@71b7adfe2603f48bb9ed50d2b01a72499ae94885 # v3.31.0
+        - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+        - uses: lukka/get-cmake@7bfc9baacbbdcb5e37957ad05c3546b3e222be3c # v4.02
         - name: Download dependencies
           run: python3 utils/git-sync-deps
         # NOTE: The MacOS SDK ships universal binaries. CI should reflect this.
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 1e0d865..c52b3cc 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -13,7 +13,7 @@
   prepare-release-job:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
       - name: Prepare CHANGELOG for version
         run: |
           python utils/generate_changelog.py CHANGES "${{ github.ref_name }}" VERSION_CHANGELOG
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index 8a2c9ec..decd4ed 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -23,12 +23,12 @@
 
     steps:
       - name: "Checkout code"
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
         with:
           persist-credentials: false
 
       - name: "Run analysis"
-        uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
+        uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
         with:
           results_file: results.sarif
           results_format: sarif
@@ -40,7 +40,7 @@
       # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
       # format to the repository Actions tab.
       - name: "Upload artifact"
-        uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
+        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
         with:
           name: SARIF file
           path: results.sarif
@@ -48,6 +48,6 @@
 
       # Upload the results to GitHub's code scanning dashboard.
       - name: "Upload to code-scanning"
-        uses: github/codeql-action/upload-sarif@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
+        uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
         with:
           sarif_file: results.sarif
diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml
index cbe2143..7aa9679 100644
--- a/.github/workflows/wasm.yml
+++ b/.github/workflows/wasm.yml
@@ -2,14 +2,20 @@
 permissions:
   contents: read
 
-on: [push, pull_request]
+on:
+  push:
+    branches:
+      - 'main'
+  pull_request:
+    types: [opened, synchronize, reopened, unlabeled]
 
 jobs:
   build:
+    if: github.event.action != 'unlabeled' || github.event.label.name == 'kokoro:run'
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
         with:
           fetch-depth: '0'
       - name: Build web
diff --git a/.gitignore b/.gitignore
index e85cea9..29e83c9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,8 @@
 .clang_complete
 .ycm_extra_conf.py*
 *.pyc
+android_test/lib
+android_test/app
 compile_commands.json
 /build*/
 /buildtools/
@@ -11,6 +13,7 @@
 /external/effcee
 /external/re2
 /external/protobuf
+/external/mimalloc
 /out
 /TAGS
 /third_party/llvm-build/
diff --git a/Android.mk b/Android.mk
index 1414b52..6c2ecfa 100644
--- a/Android.mk
+++ b/Android.mk
@@ -11,7 +11,6 @@
 		source/diagnostic.cpp \
 		source/disassemble.cpp \
 		source/ext_inst.cpp \
-		source/enum_string_mapping.cpp \
 		source/extensions.cpp \
 		source/libspirv.cpp \
 		source/name_mapper.cpp \
@@ -25,6 +24,7 @@
 		source/spirv_target_env.cpp \
 		source/spirv_validator_options.cpp \
 		source/table.cpp \
+		source/table2.cpp \
 		source/text.cpp \
 		source/text_handler.cpp \
 		source/to_string.cpp \
@@ -53,9 +53,12 @@
 		source/val/validate_debug.cpp \
 		source/val/validate_decorations.cpp \
 		source/val/validate_derivatives.cpp \
+		source/val/validate_dot_product.cpp \
 		source/val/validate_extensions.cpp \
 		source/val/validate_execution_limitations.cpp \
 		source/val/validate_function.cpp \
+		source/val/validate_graph.cpp \
+		source/val/validate_group.cpp \
 		source/val/validate_id.cpp \
 		source/val/validate_image.cpp \
 		source/val/validate_interfaces.cpp \
@@ -67,16 +70,20 @@
 		source/val/validate_mode_setting.cpp \
 		source/val/validate_layout.cpp \
 		source/val/validate_literals.cpp \
+		source/val/validate_logical_pointers.cpp \
 		source/val/validate_logicals.cpp \
 		source/val/validate_non_uniform.cpp \
+		source/val/validate_pipe.cpp \
 		source/val/validate_primitives.cpp \
 		source/val/validate_ray_query.cpp \
 		source/val/validate_ray_tracing.cpp \
 		source/val/validate_ray_tracing_reorder.cpp \
 		source/val/validate_scopes.cpp \
 		source/val/validate_small_type_uses.cpp \
+		source/val/validate_tensor.cpp \
 		source/val/validate_tensor_layout.cpp \
-		source/val/validate_type.cpp
+		source/val/validate_type.cpp\
+		source/val/validate_invalid_type.cpp
 
 SPVTOOLS_OPT_SRC_FILES := \
 		source/opt/aggressive_dead_code_elim_pass.cpp \
@@ -125,6 +132,7 @@
 		source/opt/fold_spec_constant_op_and_composite_pass.cpp \
 		source/opt/freeze_spec_constant_value_pass.cpp \
 		source/opt/function.cpp \
+		source/opt/graph.cpp \
 		source/opt/graphics_robust_access_pass.cpp \
 		source/opt/if_conversion.cpp \
 		source/opt/inline_pass.cpp \
@@ -137,6 +145,7 @@
 		source/opt/invocation_interlock_placement_pass.cpp \
 		source/opt/ir_context.cpp \
 		source/opt/ir_loader.cpp \
+		source/opt/legalize_multidim_array_pass.cpp \
 		source/opt/licm_pass.cpp \
 		source/opt/liveness.cpp \
 		source/opt/local_access_chain_convert_pass.cpp \
@@ -167,16 +176,19 @@
 		source/opt/redundancy_elimination.cpp \
 		source/opt/register_pressure.cpp \
 		source/opt/relax_float_ops_pass.cpp \
+		source/opt/canonicalize_ids_pass.cpp \
 		source/opt/remove_dontinline_pass.cpp \
 		source/opt/remove_duplicates_pass.cpp \
 		source/opt/remove_unused_interface_variables_pass.cpp \
 		source/opt/replace_desc_array_access_using_var_index.cpp \
 		source/opt/replace_invalid_opc.cpp \
+		source/opt/resolve_binding_conflicts_pass.cpp \
 		source/opt/scalar_analysis.cpp \
 		source/opt/scalar_analysis_simplification.cpp \
 		source/opt/scalar_replacement_pass.cpp \
 		source/opt/set_spec_constant_default_value_pass.cpp \
 		source/opt/simplification_pass.cpp \
+		source/opt/split_combined_image_sampler_pass.cpp \
 		source/opt/spread_volatile_semantics.cpp \
 		source/opt/ssa_rewrite_pass.cpp \
 		source/opt/strength_reduction_pass.cpp \
@@ -196,52 +208,49 @@
 		source/opt/wrap_opkill.cpp
 
 # Locations of grammar files.
-#
-SPV_COREUNIFIED1_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/spirv.core.grammar.json
-SPV_GLSL_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.glsl.std.450.grammar.json
-SPV_OPENCL_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.opencl.std.100.grammar.json
-SPV_DEBUGINFO_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.debuginfo.grammar.json
-SPV_CLDEBUGINFO100_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json
-SPV_VKDEBUGINFO100_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json
+GRAMMAR_DIR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1
 
 define gen_spvtools_grammar_tables
-$(call generate-file-dir,$(1)/core.insts-unified1.inc)
-$(1)/core.insts-unified1.inc $(1)/operand.kinds-unified1.inc \
-$(1)/glsl.std.450.insts.inc \
-$(1)/opencl.std.insts.inc \
+# $1 is the output directory, which is unique per ABI.
+# Rules for creating grammar tables. They are statically compiled
+# into the SPIRV-Tools code.
+$(call generate-file-dir,$(1)/core_tables_body.inc)
+$(1)/core_tables_body.inc \
+$(1)/core_tables_header.inc \
 : \
-        $(LOCAL_PATH)/utils/generate_grammar_tables.py \
-        $(SPV_COREUNIFIED1_GRAMMAR) \
-        $(SPV_GLSL_GRAMMAR) \
-        $(SPV_OpenCL_GRAMMAR) \
-        $(SPV_DEBUGINFO_GRAMMAR) \
-        $(SPV_CLDEBUGINFO100_GRAMMAR)
-		@$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \
-		                --spirv-core-grammar=$(SPV_COREUNIFIED1_GRAMMAR) \
-		                --extinst-glsl-grammar=$(SPV_GLSL_GRAMMAR) \
-		                --extinst-opencl-grammar=$(SPV_OPENCL_GRAMMAR) \
-		                --extinst-debuginfo-grammar=$(SPV_DEBUGINFO_GRAMMAR) \
-		                --extinst-cldebuginfo100-grammar=$(SPV_CLDEBUGINFO100_GRAMMAR) \
-		                --core-insts-output=$(1)/core.insts-unified1.inc \
-		                --glsl-insts-output=$(1)/glsl.std.450.insts.inc \
-		                --opencl-insts-output=$(1)/opencl.std.insts.inc \
-		                --operand-kinds-output=$(1)/operand.kinds-unified1.inc \
-										--output-language=c++
-		@echo "[$(TARGET_ARCH_ABI)] Grammar (from unified1)  : instructions & operands <= grammar JSON files"
-$(LOCAL_PATH)/source/opcode.cpp: $(1)/core.insts-unified1.inc
-$(LOCAL_PATH)/source/operand.cpp: $(1)/operand.kinds-unified1.inc
-$(LOCAL_PATH)/source/ext_inst.cpp: \
-	$(1)/glsl.std.450.insts.inc \
-	$(1)/opencl.std.insts.inc \
-	$(1)/debuginfo.insts.inc \
-	$(1)/opencl.debuginfo.100.insts.inc \
-	$(1)/nonsemantic.shader.debuginfo.100.insts.inc \
-	$(1)/spv-amd-gcn-shader.insts.inc \
-	$(1)/spv-amd-shader-ballot.insts.inc \
-	$(1)/spv-amd-shader-explicit-vertex-parameter.insts.inc \
-	$(1)/spv-amd-shader-trinary-minmax.insts.inc
-$(LOCAL_PATH)/source/opt/amd_ext_to_khr.cpp: \
-	$(1)/spv-amd-shader-ballot.insts.inc
+        $(LOCAL_PATH)/utils/ggt.py \
+	$(GRAMMAR_DIR)/extinst.debuginfo.grammar.json \
+	$(GRAMMAR_DIR)/extinst.glsl.std.450.grammar.json \
+	$(GRAMMAR_DIR)/extinst.nonsemantic.clspvreflection.grammar.json \
+	$(GRAMMAR_DIR)/extinst.nonsemantic.shader.debuginfo.100.grammar.json \
+	$(GRAMMAR_DIR)/extinst.nonsemantic.vkspreflection.grammar.json \
+	$(GRAMMAR_DIR)/extinst.opencl.debuginfo.100.grammar.json \
+	$(GRAMMAR_DIR)/extinst.opencl.std.100.grammar.json \
+	$(GRAMMAR_DIR)/extinst.spv-amd-gcn-shader.grammar.json \
+	$(GRAMMAR_DIR)/extinst.spv-amd-shader-ballot.grammar.json \
+	$(GRAMMAR_DIR)/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json \
+	$(GRAMMAR_DIR)/extinst.spv-amd-shader-trinary-minmax.grammar.json \
+	$(GRAMMAR_DIR)/spirv.core.grammar.json
+	@$(HOST_PYTHON) $(LOCAL_PATH)/utils/ggt.py \
+		--core-tables-body-output=$(1)/core_tables_body.inc \
+		--core-tables-header-output=$(1)/core_tables_header.inc \
+		--spirv-core-grammar=$(GRAMMAR_DIR)/spirv.core.grammar.json \
+		--extinst=,$(GRAMMAR_DIR)/extinst.debuginfo.grammar.json \
+		--extinst=,$(GRAMMAR_DIR)/extinst.glsl.std.450.grammar.json \
+		--extinst=,$(GRAMMAR_DIR)/extinst.nonsemantic.clspvreflection.grammar.json \
+		--extinst=SHDEBUG100_,$(GRAMMAR_DIR)/extinst.nonsemantic.shader.debuginfo.100.grammar.json \
+		--extinst=,$(GRAMMAR_DIR)/extinst.nonsemantic.vkspreflection.grammar.json \
+		--extinst=CLDEBUG100_,$(GRAMMAR_DIR)/extinst.opencl.debuginfo.100.grammar.json \
+		--extinst=,$(GRAMMAR_DIR)/extinst.opencl.std.100.grammar.json \
+		--extinst=,$(GRAMMAR_DIR)/extinst.spv-amd-gcn-shader.grammar.json \
+		--extinst=,$(GRAMMAR_DIR)/extinst.spv-amd-shader-ballot.grammar.json \
+		--extinst=,$(GRAMMAR_DIR)/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json \
+		--extinst=,$(GRAMMAR_DIR)/extinst.spv-amd-shader-trinary-minmax.grammar.json
+	@echo "[$(TARGET_ARCH_ABI)] Grammar tables <= grammar JSON files"
+# Make all source files depend on the generated core tables
+$(foreach F,$(SPVTOOLS_SRC_FILES) $(SPVTOOLS_OPT_SRC_FILES),$(LOCAL_PATH)/$F ) \
+  : $(1)/core_tables_body.inc \
+    $(1)/core_tables_header.inc
 endef
 $(eval $(call gen_spvtools_grammar_tables,$(SPVTOOLS_OUT_PATH)))
 
@@ -262,55 +271,13 @@
 $(foreach F,$(SPVTOOLS_SRC_FILES) $(SPVTOOLS_OPT_SRC_FILES),$(LOCAL_PATH)/$F ) \
 	: $(1)/$(2).h
 endef
-# We generate language-specific headers for DebugInfo and OpenCL.DebugInfo.100
-$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),DebugInfo,$(SPV_DEBUGINFO_GRAMMAR)))
-$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),OpenCLDebugInfo100,$(SPV_CLDEBUGINFO100_GRAMMAR)))
-$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),NonSemanticShaderDebugInfo100,$(SPV_VKDEBUGINFO100_GRAMMAR)))
 
 
-define gen_spvtools_vendor_tables
-$(call generate-file-dir,$(1)/$(2).insts.inc)
-$(1)/$(2).insts.inc : \
-        $(LOCAL_PATH)/utils/generate_grammar_tables.py \
-        $(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.$(2).grammar.json
-		@$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \
-		    --extinst-vendor-grammar=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.$(2).grammar.json \
-		    --vendor-insts-output=$(1)/$(2).insts.inc \
-		    --vendor-operand-kind-prefix=$(3)
-		@echo "[$(TARGET_ARCH_ABI)] Vendor extended instruction set: $(2) tables <= grammar"
-$(LOCAL_PATH)/source/ext_inst.cpp: $(1)/$(2).insts.inc
-endef
-# Vendor and debug extended instruction sets, with grammars from SPIRV-Tools source tree.
-$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),debuginfo,""))
-$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),opencl.debuginfo.100,"CLDEBUG100_"))
-$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),nonsemantic.shader.debuginfo.100,"SHDEBUG100_"))
-$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-gcn-shader,""))
-$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-ballot,""))
-$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-explicit-vertex-parameter,""))
-$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-trinary-minmax,""))
-$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),nonsemantic.clspvreflection,""))
-$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),nonsemantic.vkspreflection,""))
+# Generate C++ headers for some extended instruction sets.
+$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),DebugInfo,$(GRAMMAR_DIR)/extinst.debuginfo.grammar.json))
+$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),OpenCLDebugInfo100,$(GRAMMAR_DIR)/extinst.opencl.debuginfo.100.grammar.json))
+$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),NonSemanticShaderDebugInfo100,$(GRAMMAR_DIR)/extinst.nonsemantic.shader.debuginfo.100.grammar.json))
 
-define gen_spvtools_enum_string_mapping
-$(call generate-file-dir,$(1)/extension_enum.inc.inc)
-$(1)/extension_enum.inc $(1)/enum_string_mapping.inc: \
-        $(LOCAL_PATH)/utils/generate_grammar_tables.py \
-        $(SPV_COREUNIFIED1_GRAMMAR)
-		@$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \
-		                --spirv-core-grammar=$(SPV_COREUNIFIED1_GRAMMAR) \
-		                --extinst-debuginfo-grammar=$(SPV_DEBUGINFO_GRAMMAR) \
-		                --extinst-cldebuginfo100-grammar=$(SPV_CLDEBUGINFO100_GRAMMAR) \
-		                --extension-enum-output=$(1)/extension_enum.inc \
-		                --enum-string-mapping-output=$(1)/enum_string_mapping.inc \
-										--output-language=c++
-		@echo "[$(TARGET_ARCH_ABI)] Generate enum<->string mapping <= grammar JSON files"
-# Generated header extension_enum.inc is transitively included by table.h, which is
-# used pervasively.  Capture the pervasive dependency.
-$(foreach F,$(SPVTOOLS_SRC_FILES) $(SPVTOOLS_OPT_SRC_FILES),$(LOCAL_PATH)/$F ) \
-  : $(1)/extension_enum.inc
-$(LOCAL_PATH)/source/enum_string_mapping.cpp: $(1)/enum_string_mapping.inc
-endef
-$(eval $(call gen_spvtools_enum_string_mapping,$(SPVTOOLS_OUT_PATH)))
 
 define gen_spvtools_build_version_inc
 $(call generate-file-dir,$(1)/dummy_filename)
diff --git a/BUILD.bazel b/BUILD.bazel
index 526d08e..d1a9d42 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -5,14 +5,12 @@
     "DEBUGINFO_GRAMMAR_JSON_FILE",
     "SHDEBUGINFO100_GRAMMAR_JSON_FILE",
     "TEST_COPTS",
-    "generate_core_tables",
-    "generate_enum_string_mapping",
+    "create_grammar_tables_target",
+    "ExtInst",
     "generate_extinst_lang_headers",
-    "generate_glsl_tables",
-    "generate_opencl_tables",
-    "generate_vendor_tables",
     "incompatible_with",
 )
+load("@rules_python//python:defs.bzl", "py_binary")
 
 package(
     default_visibility = ["//visibility:private"],
@@ -29,8 +27,18 @@
 ])
 
 py_binary(
-    name = "generate_grammar_tables",
-    srcs = ["utils/generate_grammar_tables.py"],
+    # The script that generates compressed grammar tables for
+    # instructions and operands.
+    name = "ggt",
+    main = "utils/ggt.py", # The file found by $(location :ggt)
+    srcs = [
+        "utils/ggt.py",
+        "utils/Table/__init__.py",
+        "utils/Table/Context.py",
+        "utils/Table/IndexRange.py",
+        "utils/Table/Operand.py",
+        "utils/Table/StringList.py",
+    ],
 )
 
 py_binary(
@@ -38,36 +46,25 @@
     srcs = ["utils/generate_language_headers.py"],
 )
 
-generate_core_tables(version = "unified1")
-
-generate_enum_string_mapping(version = "unified1")
-
-generate_opencl_tables(version = "unified1")
-
-generate_glsl_tables(version = "unified1")
-
-generate_vendor_tables(extension = "spv-amd-shader-explicit-vertex-parameter")
-
-generate_vendor_tables(extension = "spv-amd-shader-trinary-minmax")
-
-generate_vendor_tables(extension = "spv-amd-gcn-shader")
-
-generate_vendor_tables(extension = "spv-amd-shader-ballot")
-
-generate_vendor_tables(extension = "debuginfo")
-
-generate_vendor_tables(extension = "nonsemantic.clspvreflection")
-
-generate_vendor_tables(extension = "nonsemantic.vkspreflection")
-
-generate_vendor_tables(
-    extension = "opencl.debuginfo.100",
-    operand_kind_prefix = "CLDEBUG100_",
-)
-
-generate_vendor_tables(
-    extension = "nonsemantic.shader.debuginfo.100",
-    operand_kind_prefix = "SHDEBUG100_",
+create_grammar_tables_target(
+    name="core", # unused
+    extinsts = [
+        ExtInst("glsl.std.450", target="spirv_glsl_grammar_unified1"),
+        ExtInst("opencl.std.100", target="spirv_opencl_grammar_unified1"),
+        ExtInst("opencl.debuginfo.100", prefix="CLDEBUG100_"),
+        ExtInst("nonsemantic.shader.debuginfo", prefix="SHDEBUG100_"),
+        ExtInst("tosa.001000.1", target="spirv_ext_inst_tosa_001000_1", prefix="TOSA_"),
+        ExtInst("arm.motion-engine.100", target="spirv_ext_inst_arm_motion_engine_100"),
+    ] + [ExtInst(e) for e in [
+            "spv-amd-shader-explicit-vertex-parameter",
+            "spv-amd-shader-trinary-minmax",
+            "spv-amd-gcn-shader",
+            "spv-amd-shader-ballot",
+            "debuginfo",
+            "nonsemantic.clspvreflection",
+            "nonsemantic.vkspreflection",
+        ]
+    ]
 )
 
 generate_extinst_lang_headers(
@@ -136,24 +133,14 @@
         "source/*.cpp",
         "source/util/*.cpp",
         "source/val/*.cpp",
+    ], exclude = [
+        "source/mimalloc.cpp"
     ]) + [
         ":build_version_inc",
-        ":gen_core_tables_unified1",
-        ":gen_enum_string_mapping",
+        ":gen_compressed_tables",
         ":gen_extinst_lang_headers_DebugInfo",
         ":gen_extinst_lang_headers_NonSemanticShaderDebugInfo100",
         ":gen_extinst_lang_headers_OpenCLDebugInfo100",
-        ":gen_glsl_tables_unified1",
-        ":gen_opencl_tables_unified1",
-        ":gen_vendor_tables_debuginfo",
-        ":gen_vendor_tables_nonsemantic_clspvreflection",
-        ":gen_vendor_tables_nonsemantic_vkspreflection",
-        ":gen_vendor_tables_nonsemantic_shader_debuginfo_100",
-        ":gen_vendor_tables_opencl_debuginfo_100",
-        ":gen_vendor_tables_spv_amd_gcn_shader",
-        ":gen_vendor_tables_spv_amd_shader_ballot",
-        ":gen_vendor_tables_spv_amd_shader_explicit_vertex_parameter",
-        ":gen_vendor_tables_spv_amd_shader_trinary_minmax",
         ":generators_inc",
     ],
     hdrs = [
@@ -191,9 +178,7 @@
 
 cc_library(
     name = "spirv_tools_opt_internal",
-    srcs = glob(["source/opt/*.cpp"]) + [
-        ":gen_vendor_tables_spv_amd_shader_ballot",
-    ],
+    srcs = glob(["source/opt/*.cpp"]),
     hdrs = glob(["source/opt/*.h"]) + [
         "include/spirv-tools/optimizer.hpp",
     ],
@@ -201,6 +186,7 @@
     deps = [
         ":spirv_tools_internal",
         "@spirv_headers//:spirv_common_headers",
+        "@spirv_headers//:spirv_c_headers",
     ],
 )
 
@@ -218,7 +204,7 @@
 cc_library(
     name = "spirv_tools_link",
     srcs = glob(["source/link/*.cpp"]),
-    hdrs = ["include/spirv-tools/linker.hpp"],
+    hdrs = ["include/spirv-tools/linker.hpp", "source/link/fnvar.h"],
     copts = COMMON_COPTS,
     linkstatic = 1,
     visibility = ["//visibility:public"],
@@ -450,6 +436,7 @@
         ":tools_io",
         "@googletest//:gtest",
         "@googletest//:gtest_main",
+        "@spirv_headers//:spirv_common_headers",
     ],
 ) for f in glob(
     [
@@ -559,6 +546,7 @@
 [cc_test(
     name = "opt_{testcase}_test".format(testcase = f[len("test/opt/"):-len("_test.cpp")]),
     size = "small",
+    timeout = "moderate" if f[len("test/opt/"):-len("_test.cpp")] == "fold" else "short",
     srcs = [f],
     copts = TEST_COPTS,
     linkstatic = 1,
@@ -684,16 +672,35 @@
         ":val_test_lib",
         "@googletest//:gtest",
         "@googletest//:gtest_main",
+        "@spirv_headers//:spirv_cpp11_headers",
     ],
 ) for f in glob(
     ["test/val/val_*_test.cpp"],
     exclude = [
         "test/val/val_capability_test.cpp",
+        "test/val/val_ext_inst_debug_test.cpp",
         "test/val/val_limits_test.cpp",
     ],
 )]
 
 cc_test(
+    name = "val_ext_inst_debug_test",
+    size = "small",
+    srcs = ["test/val/val_ext_inst_debug_test.cpp"],
+    copts = TEST_COPTS,
+    linkstatic = 1,
+    deps = [
+        ":spirv_tools_internal",
+        ":test_lib",
+        ":val_test_lib",
+        "@googletest//:gtest",
+        "@googletest//:gtest_main",
+        "@spirv_headers//:spirv_common_headers",
+        "@spirv_headers//:spirv_cpp11_headers",
+    ],
+)
+
+cc_test(
     name = "val_capability_test",
     size = "large",
     timeout = "long",
diff --git a/BUILD.gn b/BUILD.gn
index 3997b6f..9726251 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -33,169 +33,68 @@
 spirv_headers = spirv_tools_spirv_headers_dir
 spirv_is_winuwp = is_win && target_os == "winuwp"
 
-template("spvtools_core_tables") {
-  assert(defined(invoker.version), "Need version in $target_name generation.")
+action("spvtools_core_tables") {
+  script = "utils/ggt.py"
 
-  action("spvtools_core_tables_" + target_name) {
-    script = "utils/generate_grammar_tables.py"
+  core_tables_body_file = "${target_gen_dir}/core_tables_body.inc"
+  core_tables_header_file = "${target_gen_dir}/core_tables_header.inc"
 
-    version = invoker.version
+  # Extended instruction set grammar files
+  grammar_dir = "${spirv_headers}/include/spirv/unified1"
+  f0=rebase_path("${grammar_dir}/extinst.debuginfo.grammar.json", root_build_dir)
+  f1=rebase_path("${grammar_dir}/extinst.glsl.std.450.grammar.json", root_build_dir)
+  f2=rebase_path("${grammar_dir}/extinst.nonsemantic.clspvreflection.grammar.json", root_build_dir)
+  f3=rebase_path("${grammar_dir}/extinst.nonsemantic.shader.debuginfo.100.grammar.json", root_build_dir)
+  f4=rebase_path("${grammar_dir}/extinst.nonsemantic.vkspreflection.grammar.json", root_build_dir)
+  f5=rebase_path("${grammar_dir}/extinst.opencl.debuginfo.100.grammar.json", root_build_dir)
+  f6=rebase_path("${grammar_dir}/extinst.opencl.std.100.grammar.json", root_build_dir)
+  f7=rebase_path("${grammar_dir}/extinst.spv-amd-gcn-shader.grammar.json", root_build_dir)
+  f8=rebase_path("${grammar_dir}/extinst.spv-amd-shader-ballot.grammar.json", root_build_dir)
+  f9=rebase_path("${grammar_dir}/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json", root_build_dir)
+  f10=rebase_path("${grammar_dir}/extinst.spv-amd-shader-trinary-minmax.grammar.json", root_build_dir)
 
-    core_json_file =
-        "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json"
-    core_insts_file = "${target_gen_dir}/core.insts-$version.inc"
-    operand_kinds_file = "${target_gen_dir}/operand.kinds-$version.inc"
-    debuginfo_insts_file =
-        "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json"
-    cldebuginfo100_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json"
-
-    sources = [
-      cldebuginfo100_insts_file,
-      core_json_file,
-      debuginfo_insts_file,
-    ]
-    outputs = [
-      core_insts_file,
-      operand_kinds_file,
-    ]
-    args = [
-      "--spirv-core-grammar",
-      rebase_path(core_json_file, root_build_dir),
-      "--core-insts-output",
-      rebase_path(core_insts_file, root_build_dir),
-      "--extinst-debuginfo-grammar",
-      rebase_path(debuginfo_insts_file, root_build_dir),
-      "--extinst-cldebuginfo100-grammar",
-      rebase_path(cldebuginfo100_insts_file, root_build_dir),
-      "--operand-kinds-output",
-      rebase_path(operand_kinds_file, root_build_dir),
-      "--output-language",
-      "c++"
-    ]
-  }
-}
-
-template("spvtools_core_enums") {
-  assert(defined(invoker.version), "Need version in $target_name generation.")
-
-  action("spvtools_core_enums_" + target_name) {
-    script = "utils/generate_grammar_tables.py"
-
-    version = invoker.version
-
-    core_json_file =
-        "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json"
-    debuginfo_insts_file =
-        "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json"
-    cldebuginfo100_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json"
-
-    extension_enum_file = "${target_gen_dir}/extension_enum.inc"
-    extension_map_file = "${target_gen_dir}/enum_string_mapping.inc"
-
-    args = [
-      "--spirv-core-grammar",
-      rebase_path(core_json_file, root_build_dir),
-      "--extinst-debuginfo-grammar",
-      rebase_path(debuginfo_insts_file, root_build_dir),
-      "--extinst-cldebuginfo100-grammar",
-      rebase_path(cldebuginfo100_insts_file, root_build_dir),
-      "--extension-enum-output",
-      rebase_path(extension_enum_file, root_build_dir),
-      "--enum-string-mapping-output",
-      rebase_path(extension_map_file, root_build_dir),
-      "--output-language",
-      "c++"
-    ]
-    inputs = [
-      core_json_file,
-      debuginfo_insts_file,
-      cldebuginfo100_insts_file,
-    ]
-    outputs = [
-      extension_enum_file,
-      extension_map_file,
-    ]
-  }
-}
-
-template("spvtools_glsl_tables") {
-  assert(defined(invoker.version), "Need version in $target_name generation.")
-
-  action("spvtools_glsl_tables_" + target_name) {
-    script = "utils/generate_grammar_tables.py"
-
-    version = invoker.version
-
-    core_json_file =
-        "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json"
-    glsl_json_file = "${spirv_headers}/include/spirv/${version}/extinst.glsl.std.450.grammar.json"
-    debuginfo_insts_file =
-        "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json"
-    cldebuginfo100_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json"
-
-    glsl_insts_file = "${target_gen_dir}/glsl.std.450.insts.inc"
-
-    args = [
-      "--spirv-core-grammar",
-      rebase_path(core_json_file, root_build_dir),
-      "--extinst-debuginfo-grammar",
-      rebase_path(debuginfo_insts_file, root_build_dir),
-      "--extinst-cldebuginfo100-grammar",
-      rebase_path(cldebuginfo100_insts_file, root_build_dir),
-      "--extinst-glsl-grammar",
-      rebase_path(glsl_json_file, root_build_dir),
-      "--glsl-insts-output",
-      rebase_path(glsl_insts_file, root_build_dir),
-      "--output-language",
-      "c++"
-    ]
-    inputs = [
-      core_json_file,
-      glsl_json_file,
-      debuginfo_insts_file,
-      cldebuginfo100_insts_file,
-    ]
-    outputs = [ glsl_insts_file ]
-  }
-}
-
-template("spvtools_opencl_tables") {
-  assert(defined(invoker.version), "Need version in $target_name generation.")
-
-  action("spvtools_opencl_tables_" + target_name) {
-    script = "utils/generate_grammar_tables.py"
-
-    version = invoker.version
-
-    core_json_file =
-        "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json"
-    opencl_json_file = "${spirv_headers}/include/spirv/${version}/extinst.opencl.std.100.grammar.json"
-    debuginfo_insts_file =
-        "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json"
-    cldebuginfo100_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json"
-
-    opencl_insts_file = "${target_gen_dir}/opencl.std.insts.inc"
-
-    args = [
-      "--spirv-core-grammar",
-      rebase_path(core_json_file, root_build_dir),
-      "--extinst-debuginfo-grammar",
-      rebase_path(debuginfo_insts_file, root_build_dir),
-      "--extinst-cldebuginfo100-grammar",
-      rebase_path(cldebuginfo100_insts_file, root_build_dir),
-      "--extinst-opencl-grammar",
-      rebase_path(opencl_json_file, root_build_dir),
-      "--opencl-insts-output",
-      rebase_path(opencl_insts_file, root_build_dir),
-    ]
-    inputs = [
-      core_json_file,
-      opencl_json_file,
-      debuginfo_insts_file,
-      cldebuginfo100_insts_file,
-    ]
-    outputs = [ opencl_insts_file ]
-  }
+  sources = [
+    "utils/Table/__init__.py",
+    "utils/Table/Context.py",
+    "utils/Table/IndexRange.py",
+    "utils/Table/Operand.py",
+    "utils/Table/StringList.py",
+    "${grammar_dir}/extinst.debuginfo.grammar.json",
+    "${grammar_dir}/extinst.glsl.std.450.grammar.json",
+    "${grammar_dir}/extinst.nonsemantic.clspvreflection.grammar.json",
+    "${grammar_dir}/extinst.nonsemantic.shader.debuginfo.100.grammar.json",
+    "${grammar_dir}/extinst.nonsemantic.vkspreflection.grammar.json",
+    "${grammar_dir}/extinst.opencl.debuginfo.100.grammar.json",
+    "${grammar_dir}/extinst.opencl.std.100.grammar.json",
+    "${grammar_dir}/extinst.spv-amd-gcn-shader.grammar.json",
+    "${grammar_dir}/extinst.spv-amd-shader-ballot.grammar.json",
+    "${grammar_dir}/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json",
+    "${grammar_dir}/extinst.spv-amd-shader-trinary-minmax.grammar.json",
+    "${grammar_dir}/spirv.core.grammar.json",
+  ]
+  outputs = [
+    core_tables_body_file,
+    core_tables_header_file,
+  ]
+  args = [
+    "--spirv-core-grammar",
+    rebase_path("${grammar_dir}/spirv.core.grammar.json", root_build_dir),
+    "--extinst=,${f0}",
+    "--extinst=,${f1}",
+    "--extinst=,${f2}",
+    "--extinst=SHDEBUG100_,${f3}",
+    "--extinst=,${f4}",
+    "--extinst=CLDEBUG100_,${f5}",
+    "--extinst=,${f6}",
+    "--extinst=,${f7}",
+    "--extinst=,${f8}",
+    "--extinst=,${f9}",
+    "--extinst=,${f10}",
+    "--core-tables-body-output",
+    rebase_path(core_tables_body_file, root_build_dir),
+    "--core-tables-header-output",
+    rebase_path(core_tables_header_file, root_build_dir)
+  ]
 }
 
 template("spvtools_language_header") {
@@ -218,30 +117,6 @@
   }
 }
 
-template("spvtools_vendor_table") {
-  assert(defined(invoker.name), "Need name in $target_name generation.")
-
-  action("spvtools_vendor_tables_" + target_name) {
-    script = "utils/generate_grammar_tables.py"
-
-    name = invoker.name
-    extinst_vendor_grammar =
-        "${spirv_headers}/include/spirv/unified1/extinst.${name}.grammar.json"
-    extinst_file = "${target_gen_dir}/${name}.insts.inc"
-
-    args = [
-      "--extinst-vendor-grammar",
-      rebase_path(extinst_vendor_grammar, root_build_dir),
-      "--vendor-insts-output",
-      rebase_path(extinst_file, root_build_dir),
-      "--vendor-operand-kind-prefix",
-      invoker.operand_kind_prefix,
-    ]
-    inputs = [ extinst_vendor_grammar ]
-    outputs = [ extinst_file ]
-  }
-}
-
 action("spvtools_generators_inc") {
   script = "utils/generate_registry_tables.py"
 
@@ -283,18 +158,6 @@
   }
 }
 
-spvtools_core_tables("unified1") {
-  version = "unified1"
-}
-spvtools_core_enums("unified1") {
-  version = "unified1"
-}
-spvtools_glsl_tables("glsl1-0") {
-  version = "1.0"
-}
-spvtools_opencl_tables("opencl1-0") {
-  version = "1.0"
-}
 spvtools_language_header("debuginfo") {
   name = "DebugInfo"
   grammar_file =
@@ -304,55 +167,9 @@
   name = "OpenCLDebugInfo100"
   grammar_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json"
 }
-spvtools_language_header("vkdebuginfo100") {
-  name = "NonSemanticShaderDebugInfo100"
-  grammar_file = "${spirv_headers}/include/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json"
-}
-
-spvtools_vendor_tables = [
-  [
-    "spv-amd-shader-explicit-vertex-parameter",
-    "...nil...",
-  ],
-  [
-    "spv-amd-shader-trinary-minmax",
-    "...nil...",
-  ],
-  [
-    "spv-amd-gcn-shader",
-    "...nil...",
-  ],
-  [
-    "spv-amd-shader-ballot",
-    "...nil...",
-  ],
-  [
-    "debuginfo",
-    "...nil...",
-  ],
-  [
-    "opencl.debuginfo.100",
-    "CLDEBUG100_",
-  ],
-  [
-    "nonsemantic.clspvreflection",
-    "...nil...",
-  ],
-  [
-    "nonsemantic.vkspreflection",
-    "...nil...",
-  ],
-  [
-    "nonsemantic.shader.debuginfo.100",
-    "SHDEBUG100_",
-  ],
-]
-
-foreach(table_def, spvtools_vendor_tables) {
-  spvtools_vendor_table(table_def[0]) {
-    name = table_def[0]
-    operand_kind_prefix = table_def[1]
-  }
+spvtools_language_header("vkdebuginfo") {
+  name = "NonSemanticShaderDebugInfo"
+  grammar_file = "${spirv_headers}/include/spirv/unified1/extinst.nonsemantic.shader.debuginfo.grammar.json"
 }
 
 config("spvtools_public_config") {
@@ -419,24 +236,18 @@
   public_deps = [
     ":spvtools_language_header_cldebuginfo100",
     ":spvtools_language_header_debuginfo",
-    ":spvtools_language_header_vkdebuginfo100",
+    ":spvtools_language_header_vkdebuginfo",
   ]
 }
 
 static_library("spvtools") {
   deps = [
-    ":spvtools_core_tables_unified1",
+    ":spvtools_core_tables",
     ":spvtools_generators_inc",
-    ":spvtools_glsl_tables_glsl1-0",
     ":spvtools_language_header_cldebuginfo100",
     ":spvtools_language_header_debuginfo",
-    ":spvtools_language_header_vkdebuginfo100",
-    ":spvtools_opencl_tables_opencl1-0",
+    ":spvtools_language_header_vkdebuginfo",
   ]
-  foreach(table_def, spvtools_vendor_tables) {
-    _target_def_name = table_def[0]
-    deps += [ ":spvtools_vendor_tables_${_target_def_name}" ]
-  }
 
   sources = [
     "source/assembly_grammar.cpp",
@@ -450,8 +261,6 @@
     "source/disassemble.cpp",
     "source/disassemble.h",
     "source/enum_set.h",
-    "source/enum_string_mapping.cpp",
-    "source/enum_string_mapping.h",
     "source/ext_inst.cpp",
     "source/ext_inst.h",
     "source/extensions.cpp",
@@ -488,6 +297,8 @@
     "source/spirv_validator_options.h",
     "source/table.cpp",
     "source/table.h",
+    "source/table2.cpp",
+    "source/table2.h",
     "source/text.cpp",
     "source/text.h",
     "source/text_handler.cpp",
@@ -501,10 +312,13 @@
     "source/util/hex_float.h",
     "source/util/ilist.h",
     "source/util/ilist_node.h",
+    "source/util/index_range.h",
     "source/util/make_unique.h",
     "source/util/parse_number.cpp",
     "source/util/parse_number.h",
     "source/util/small_vector.h",
+    "source/util/span.h",
+    "source/util/status.h",
     "source/util/string_utils.cpp",
     "source/util/string_utils.h",
     "source/util/timer.cpp",
@@ -512,7 +326,6 @@
   ]
 
   public_deps = [
-    ":spvtools_core_enums_unified1",
     ":spvtools_headers",
     "${spirv_headers}:spv_headers",
   ]
@@ -551,15 +364,19 @@
     "source/val/validate_debug.cpp",
     "source/val/validate_decorations.cpp",
     "source/val/validate_derivatives.cpp",
+    "source/val/validate_dot_product.cpp",
     "source/val/validate_execution_limitations.cpp",
     "source/val/validate_extensions.cpp",
     "source/val/validate_function.cpp",
+    "source/val/validate_graph.cpp",
+    "source/val/validate_group.cpp",
     "source/val/validate_id.cpp",
     "source/val/validate_image.cpp",
     "source/val/validate_instruction.cpp",
     "source/val/validate_interfaces.cpp",
     "source/val/validate_layout.cpp",
     "source/val/validate_literals.cpp",
+    "source/val/validate_logical_pointers.cpp",
     "source/val/validate_logicals.cpp",
     "source/val/validate_memory.cpp",
     "source/val/validate_memory_semantics.cpp",
@@ -568,6 +385,7 @@
     "source/val/validate_misc.cpp",
     "source/val/validate_mode_setting.cpp",
     "source/val/validate_non_uniform.cpp",
+    "source/val/validate_pipe.cpp",
     "source/val/validate_primitives.cpp",
     "source/val/validate_ray_query.cpp",
     "source/val/validate_ray_tracing.cpp",
@@ -575,8 +393,10 @@
     "source/val/validate_scopes.cpp",
     "source/val/validate_scopes.h",
     "source/val/validate_small_type_uses.cpp",
+    "source/val/validate_tensor.cpp",
     "source/val/validate_tensor_layout.cpp",
     "source/val/validate_type.cpp",
+    "source/val/validate_invalid_type.cpp",
     "source/val/validation_state.cpp",
     "source/val/validation_state.h",
   ]
@@ -585,7 +405,7 @@
     ":spvtools",
     ":spvtools_language_header_cldebuginfo100",
     ":spvtools_language_header_debuginfo",
-    ":spvtools_language_header_vkdebuginfo100",
+    ":spvtools_language_header_vkdebuginfo",
   ]
   public_deps = [ ":spvtools_headers" ]
 
@@ -691,6 +511,8 @@
     "source/opt/freeze_spec_constant_value_pass.h",
     "source/opt/function.cpp",
     "source/opt/function.h",
+    "source/opt/graph.cpp",
+    "source/opt/graph.h",
     "source/opt/graphics_robust_access_pass.cpp",
     "source/opt/graphics_robust_access_pass.h",
     "source/opt/if_conversion.cpp",
@@ -717,6 +539,8 @@
     "source/opt/ir_loader.cpp",
     "source/opt/ir_loader.h",
     "source/opt/iterator.h",
+    "source/opt/legalize_multidim_array_pass.cpp",
+    "source/opt/legalize_multidim_array_pass.h",
     "source/opt/licm_pass.cpp",
     "source/opt/licm_pass.h",
     "source/opt/liveness.cpp",
@@ -785,10 +609,14 @@
     "source/opt/remove_duplicates_pass.h",
     "source/opt/remove_unused_interface_variables_pass.cpp",
     "source/opt/remove_unused_interface_variables_pass.h",
+    "source/opt/canonicalize_ids_pass.h",
+    "source/opt/canonicalize_ids_pass.cpp",
     "source/opt/replace_desc_array_access_using_var_index.cpp",
     "source/opt/replace_desc_array_access_using_var_index.h",
     "source/opt/replace_invalid_opc.cpp",
     "source/opt/replace_invalid_opc.h",
+    "source/opt/resolve_binding_conflicts_pass.cpp",
+    "source/opt/resolve_binding_conflicts_pass.h",
     "source/opt/scalar_analysis.cpp",
     "source/opt/scalar_analysis.h",
     "source/opt/scalar_analysis_nodes.h",
@@ -799,6 +627,8 @@
     "source/opt/set_spec_constant_default_value_pass.h",
     "source/opt/simplification_pass.cpp",
     "source/opt/simplification_pass.h",
+    "source/opt/split_combined_image_sampler_pass.cpp",
+    "source/opt/split_combined_image_sampler_pass.h",
     "source/opt/spread_volatile_semantics.cpp",
     "source/opt/spread_volatile_semantics.h",
     "source/opt/ssa_rewrite_pass.cpp",
@@ -839,12 +669,11 @@
   deps = [
     ":spvtools",
     ":spvtools_language_header_debuginfo",
-    ":spvtools_vendor_tables_spv-amd-shader-ballot",
   ]
   public_deps = [
     ":spvtools_headers",
     ":spvtools_language_header_cldebuginfo100",
-    ":spvtools_language_header_vkdebuginfo100",
+    ":spvtools_language_header_vkdebuginfo",
   ]
 
   if (build_with_chromium) {
@@ -855,7 +684,11 @@
 }
 
 static_library("spvtools_link") {
-  sources = [ "source/link/linker.cpp" ]
+  sources = [
+    "source/link/linker.cpp",
+    "source/link/fnvar.cpp",
+    "source/link/fnvar.h"
+  ]
   deps = [
     ":spvtools",
     ":spvtools_opt",
@@ -1400,6 +1233,7 @@
       "test/libspirv_macros_test.cpp",
       "test/name_mapper_test.cpp",
       "test/named_id_test.cpp",
+      "test/op_unknown_test.cpp",
       "test/opcode_make_test.cpp",
       "test/opcode_require_capabilities_test.cpp",
       "test/opcode_split_test.cpp",
@@ -1442,7 +1276,7 @@
       ":spvtools",
       ":spvtools_language_header_cldebuginfo100",
       ":spvtools_language_header_debuginfo",
-      ":spvtools_language_header_vkdebuginfo100",
+      ":spvtools_language_header_vkdebuginfo",
       ":spvtools_tools_io",
       ":spvtools_val",
       "//testing/gmock",
diff --git a/CHANGES b/CHANGES
index 007e901..80bf112 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,266 @@
 Revision history for SPIRV-Tools
 
+v2026.2 2026-04-24
+  - General
+    - Fix build for NonSemanticShaderDebugInfo in GN (#6647)
+    - Support SPV_KHR_abort and SPV_KHR_constant_data (#6625)
+    - Support versioned NonSemanticShaderDebugInfo imports (#6555)
+    - Fix C++20 compilation issue with large integer literals (#6557)
+    - Remove SVA (Shader Validation Assistant) (#6550)
+    - Improved Kokoro build configuration and storage usage (#6573, #6551)
+    - Various dependency rolls and CI updates
+  - Optimizer
+    - Fold OpCompositeExtract feeding from OpCopyLogical or OpLoad (#6614)
+    - Fix DebugValue placements and initialization (#6599, #6626)
+    - Support OpConstantCompositeReplicateEXT in constant manager (#6616)
+    - Aggressive DCE support for untyped pointers and OpUntypedVariableKHR (#6602, #6607)
+    - Optimize SPV_EXT_opacity_micromap and SPV_EXT_shader_invocation_reorder (#6571)
+    - Add LegalizeMultidimArrayPass and improve access chain reordering (#6535, #6545)
+    - Added various folding and reassociation rules (#6453, #6504, #6454, #6456)
+    - Support for optimizing SPV_NV_push_constant_bank (#6547)
+  - Validator
+    - Add validation for 4-bit integer types (#6644)
+    - Support and validate SPV_KHR_constant_data (OpConstantDataKHR, OpSpecConstantDataKHR) (#6646, #6643, #6642)
+    - Validate NonSemanticShaderDebugInfo 101 and versioned imports (#6635, #6555)
+    - Improve validation for Intel SPIR-V extensions and various KHR/EXT extensions (#6232, #6589, #6583, etc.)
+    - Extensive validation coverage for OpSpecConstantOp (#6596, #6585, #6582, #6579, #6578, #6576)
+    - Check output location uniqueness for GeometryStreams (#6630)
+    - Improved validation for variables, constants, and types (#6634, #6598, #6595, #6568)
+    - Added validation for several new opcodes and capabilities (OpGroupAsyncCopy, OpLifetimeStart/Stop, etc.) (#6519, #6514, #6515)
+  - Assembler/Disassembler
+    - Add --handle-unknown-opcodes flag to spirv-dis (#6604)
+    - Handle leading + in hex float literals in assembler (#6565)
+    - Validate bit width of float types with explicit encodings in assembler (#6562)
+
+v2026.1 2026-01-22
+  - General
+    - spirv-tools: Fix infinite recursion in SmallVector::operator== on MSVC (#6470)
+    - Add support for SPV_NV_push_constant_bank (#6507)
+    - Enable UseMultiToolTask for msbuild performance (#6472)
+    - Fix asan error in stack usage (#6434)
+    - Fix crash in array length validation (#6500)
+    - Fix infinite loop in logical pointer validation (#6450)
+    - Implement SPV_EXT_descriptor_heap (#6503)
+  - Optimizer
+    - Adding constant folding for NMin, NMax and NClamp (#6433)
+    - Also remap DebugScope instructions (#6501)
+    - Change ValueNumber to use spivOpcodeIsCommutativeBinaryOperator (#6431)
+    - Extending factoring folding rules to support subtraction (#6432)
+    - Extending negation folding rules (#6444)
+    - Folding Rules For Common Comparisons (#6437)
+    - Folding rules for negation and cancellation (#6452)
+    - Harden CFGCleanupPass against ID overflow (#6439)
+    - Harden CombineAccessChainsPass against ID overflow (#6440)
+    - Harden CopyPropagateArraysPass against ID overflow (#6441)
+    - Remove lingering debug-scopes. (#6416)
+    - add adce test for debug declare (#6462)
+    - fix adce when ns debug info present (#6217) (#6420)
+  - Validator
+    - Handle OpTypeVectorIdEXT in validate_interfaces.cpp (#6492)
+    - Validate SPV_EXT_long_vector (#6471)
+    - Validate floats/int Image Operands are 32-bit (#6477)
+    - Add Mesh Shading Read Output check (#6426)
+    - Add Mesh/Task to check for LocalSize (#6459)
+    - Add missing LOD non-zero MS (#6496)
+    - Add width to IsFloatScalarType (#6489)
+    - Better atomic storage class message (#6425)
+    - Better error message when types are the same (#6427)
+    - Check Mesh Topology builtin by entry point (#6457)
+    - Check for CoopMat scope (#6442)
+    - Combine Image Coordinate checks (#6494)
+    - Fix crash with bad OpUntypedArrayLengthKHR (#6493)
+    - Label VUID 10823 (#6488)
+    - Provide RuntimeDescriptorArray hint in message (#6448)
+    - Use new StorageClassToString util (#6460)
+    - Use validation_state.cpp helpers (#6478)
+    - Validate ImageQuerySizeLod is 32-bit (#6479)
+    - Validate Mesh Shader output are an array (#6449)
+    - add missing relational ops to InvalidTypePass (#6495)
+  - Diff
+    - diff: Match OpExtInst in debug and types/values sections (#6430)
+
+v2025.5 2025-11-18
+  - General
+  - Validator
+  - Optimizer
+    - spirv-opt: Add a folding rule for `OpBitReverse` and fix `OpBitCast` to support lower to higher bit conversions. (#6321)
+    - spirv-opt: Add basic support for graphs (#6351)
+    - spirv-opt: Adding folding rules for bitwise and (#6361)
+    - spirv-opt: Adding normal form to ValueNumber calculation (#6371)
+    - spirv-opt: Cache IsReadOnlyLoad in ValueNumberTable (#6385)
+    - spirv-opt: Clone decorations when unrolling loops (#6373)
+    - spirv-opt: Correct ADCE bug keeping dead DebugValue Value operands (#6368)
+    - spirv-opt: Fix constant folding for `OpBitcast` on signed integers. (#6378)
+    - spirv-opt: Fix id overflow in ConvertToSampledImage (#6339)
+    - spirv-opt: Handle ID overflow in AggressiveDCEPass (#6366)
+    - spirv-opt: Handle ID overflow in SplitCombinedImageSamplerPass (#6406)
+    - spirv-opt: Handle id overflow in CCP and constant folding (#6367)
+    - spirv-opt: Handle id overflow in MergeReturnPass (#6340)
+    - spirv-opt: Handle id overflow in amd_ext_to_khr (#6403)
+    - Add `SPV_KHR_maximal_reconvergence` to the allowlist (#6372)
+    - Adding folding rules for commutive bitwise operations (xor/or/and). (#6358)
+    - [OPT] Move definition of Pass::Status to Utils (#6402)
+  - Validator:
+    - spirv-val: Add LocalInvocationIndex checks (#6395)
+    - spirv-val: Add validation for SPV_KHR_fma (#6388)
+    - spirv-val: Break up the 3000 line ValidateExtInst function (#6364)
+    - spirv-val: Fix VUID churn for 10685 (#6389)
+    - spirv-val: Improve Explicit Layout message (#6331)
+    - spirv-val: Use spvOpcodeString consistently (#6393)
+    - Relax validation rule for DebugGlobalVariable variable (#6382)
+    - Validate SPV_EXT_shader_64bit_indexing (#6376)
+    - Validate logical pointer restrictions (#6240)
+    - [tools/val] fix u8str for real this time (#6341)
+    - Add VK_EXT_shader_uniform_buffer_unsized_array (#6386)
+  - Assembler
+    - Add as/dis support for Arm.MotionEngine.100 extended instruction set (#6284)
+    - spirv-as: fix command line for stdin as input (#6398)
+
+v2025.4 2025-09-22
+  - General
+    - Add support for SPV_INTEL_function_variants (#6211, #6195)
+    - Add support for SPV_ARM_graph (#6177)
+    - Add support for SPV_INTEL_inline_assembly (#6210)
+    - Add support for SPV_QCOM_cooperative_matrix_conversion (#6252)
+    - Add BFloat16 encoding support (#6222)
+    - Allow mimalloc in static builds (#6267)
+    - Allow processing of directories in spirv-val (#6292)
+  - Optimizer
+    - Handle ID overflow in many passes (#6310, #6325, #6302, #6309, #6306, #6305, #6304, #6299, #6301, #6312)
+    - Add support for OpTypeGraphARM and tensors to type manager (#6247, #6202)
+    - Add Geometry capability to the trim pass (#6278)
+    - Support CCP for 16bit integer min and max operations. (#6280, #6287)
+    - Fix ADCE DebugDeclare DebugValue treatment (#6179)
+    - Fix lerp and fmix folding with half types (#6293, #6297)
+    - Fix trimming pass with OpNop (#6314)
+    - Support OpSpecConstantCompositeReplicateEXT in FoldSpecConstantOpAndCompositePass (#6315)
+    - Fix OpUndef placement in ADCE (#6154)
+    - Add null checks for MakeSClampInst and GetValueForType (#6324)
+    - Fix crash if shader uses linkage decoration (#6191)
+  - Validator
+    - Update memory semantics rules to match the specification (#6096)
+    - Validate PhysicalStorageBuffer alignment (#6266)
+    - Validate DescriptorSet and Binding decorations for all relevant variables (#6242)
+    - Validate that OpDecorateId IDs are well-ordered (#6227)
+    - Add validation for SPV_QCOM_image_processing (#6268)
+    - Add validation for Patch Decoration (#6219)
+    - Add validation for PrimitiveID (#6209)
+    - Add Workgroup Size check for Compute Derivatives (#6294)
+    - Allow compare-exchange semantics equal=Release and unequal=Acquire (#6286)
+    - Accept graphs with no inputs and/or no body (#6291)
+    - Fix GetLargestScalarType with untyped PSB (#6327)
+    - Fix Mesh Builtin checking for signed int and multi-entrypoint shaders (#6238, #6313)
+    - Fix OpTensor{Read,Write}ARM for tensors with a spec constant rank (#6206)
+    - Accept NonReadable/NonWritable on tensor variables in UniformConstant (#6184)
+    - Fix location of DebugFunctionDefinition instruction (#6198)
+    - Print SPIR-V version info in error message (#6283)
+  - Assembler
+    - Fix docs: need '-' arg to specify stdin (#6194)
+  - Disassembler
+    - Fix missing color on result ID (#6272)
+  - Diff
+    - Add spirv-diff to list of install targets (#6258)
+
+v2025.3 2025-06-23
+  - General
+    - Add mimalloc to improve multithreaded performance (#6188)
+    - python: Use type annotations compatible with python 3.8 (#6119)
+    - fix clang-20 build issue (#6103)
+  - Optimizer
+    - Add --canonicalize-ids pass (#6174)
+    - Keep instructions used by the DebugBuildIdentifier (#6189)
+    - opt: Pass DebugDeclare scope to DebugValue (#6178)
+    - Add SPV_NV_linear_swept_spheres to allow list. (#6168)
+    - Add initial support for SPV_EXT_float8 (#6170)
+    - Keep DebugBuildIdentifier during dce (#6166)
+    - Add SPV_NV_cluster_acceleration_structure to allow lists (#6163)
+    - [OPT] prevent private_to_local_pass optimizing double pointer (#6161)
+    - [OPT] Use conservative default case for `GetPtr` (#6158)
+    - [OPT] Set the BB for the debug instruction when moved. (#6153)
+    - Add support for SPV_ARM_tensors (#6134)
+    - [opt] Move debug instruction when neccessary in copy prop arrays. (#6142)
+    - [OPT] Remove recursion from redundancy_elimination (#6141)
+    - opt: add SPV_NV_shader_invocation_reorder to allowlist (#6122)
+  - Validator
+    - spirv-val: tidy up validation of type constraints for IDs (#6185)
+    - Add validation support for MeshEXT based on the spec update PR https://github.com/KhronosGroup/Vulkan-Docs/pull/2475 (#6171)
+    - spirv-val: Label maintenance9 new VUID (#6176)
+    - spirv-val: add positive test for FP8 cooperative matrices (#6175)
+    - spirv-val: Give hints when user is forgetting feature bit (#6164)
+    - val: Fix CullPrimitiveEXT array of bool (#6155)
+    - [spirv-val] Add the validation checks for SPV_QCOM_tile_shading (#6130)
+    - spirv-val: allow Float16 in OpenCL environments (#6110)
+    - spirv-val: Update 1.4.312 VUID churn (#6082)
+  - Assembler
+    - Add minimal as/dis support for TOSA.001000.1 extended instruction set (#6183)
+  - Disassembler
+    - spirv-dis: Set a hard limit on last_instruction_comment_alignment_ (#6149)
+
+v2025.2 2025-04-22
+  - General
+    - Add SPV_KHR_bfloat16 support (#6057)
+  - Optimizer
+    - Fold bitwise operator and arithmetic with 0 (#6013)
+    - Support scalar replacement of large structs (#6019)
+    - Support optimization of OpCopyLogical (#6016)
+    - add pass to split combined image samplers (#6035)
+    - value numbering: preserve loads of image, sampler, sampled image (#6059)
+    - Delete decoration for OpPhi when unrolling (#6064)
+    - Add QuadControlKHR to trim pass and allow lists (#6068)
+    - In copy propagate arrays, debug instructions are not stores. (#6078)
+    - Minimal opt support for SPV_KHR_untyped_pointers (#6087)
+  - Validator
+    - Add validation for invalid layout decoration usage (#6012, #6020)
+    - Add Vulkan Aligned PowerOfTwo check (#6027)
+    - Validate PhysicalStorageBuffer Stage Interface (#6000)
+    - Update location/component conflict validation (#5993)
+    - add resolve-binding-conflicts pass (#6044)
+    - Check that layouts match runtime array requirement (#6048)
+    - Validation for relaxed control barrier with storage class semantics (#5984)
+    - Validate version requirement for Vulkan Memory Model (#6042)
+    - Add support for pointer types in vector when using extension SPV_INTEL_masked_gather_scatter (#6041)
+    - Restrict VUID 09557 to Vulkan environments (#6080)
+    - Add Vulkan 1.3 and 1.4 capability checks (#6063)
+  - Assembler
+    - Add OpUnknown pseudo-instruction (#6024)
+  - Diff
+    - Try to pair functions by their complete type. (#6021)
+
+v2025.1 2025-02-28
+  - General
+    - diff: Fix crash in OpString matching (#5988)
+    - Add SPV_AMDX_shader_enqueue version 2 support (#5838)
+    - add support for SPV_INTEL_subgroup_matrix_multiply_accumulate (#5928)
+    - update cmake_minimum_required to 3.22.1 (#5925)
+    - Add OpImageSampleFootprintNV to IsAllowedSampledImageOperand (#5914)
+    - assembler: ensure progress when seeking the version string (#5910)
+  - Optimizer
+    - opt: keep all OpSource instructions (#5901)
+    - [opt] Fix bug opt::InstructionBuilder::AddVariable (#6007)
+    - [OPT] Add SPV_KHR_ray_tracing to allow list (#5941)
+    - opt: keep all OpSource instructions (#5901)
+  - Validator
+    - spirv-val: Add AllowVulkan32BitBitwise option (#6001)
+    - Fix untyped pointer comparison validation (#6004)
+    - spirv-val: Update VUIDs for 308 header (#5990)
+    - spirv-val: fix env parsing for vk1.1spv1.4 (#5985)
+    - Add validation for SPV_NV_linear_swept_spheres. (#5975)
+    - Add validation SPV_NV_cluster_acceleration_structure. (#5974)
+    - Improve the instruction diagnostic for some access chain errors (#5978)
+    - Update physical storage buffer validation to match SPIR-V 1.6.5 (#5977)
+    - Validate SPV_NV_cooperative_vector (#5972)
+    - Fix layout checks with untyped pointers (#5970)
+    - spirv-val: Update mislabeled VUIDs (#5964)
+    - More explicit layout validation (#5958)
+    - spirv-val: Add VK_KHR_maintenance8 support (#5951)
+    - Add EXT_mesh_shader validation support (#5640)
+    - spirv-val: Remove OpenCL ivec3 req (#5940)
+    - spirv-val: Validate zero product workgroup size (#5407)
+    - Relax DebugLine validation (#5916)
+  - Linker
+    - linker: remove LinkOnceODR decorations when linking executables (#5979)
+    - fix: handle LinkOnceODR correctly (#5938)
+
 v2024.4 2024-12-04
   - General
     - Add FPEncoding operand type. (#5726)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e50fb56..b69ec9f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-cmake_minimum_required(VERSION 3.17.2)
+cmake_minimum_required(VERSION 3.22.1)
 
 project(spirv-tools)
 
@@ -112,13 +112,19 @@
     set(SPIRV_WARNINGS ${SPIRV_WARNINGS} -Werror)
   endif()
 elseif(MSVC)
-  set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS /wd4800 /wd4819)
+  set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS /wd4800 /wd4819 /wd4251 /W2 /WX)
 
   if(${SPIRV_WERROR})
     set(SPIRV_WARNINGS ${SPIRV_WARNINGS} /WX)
   endif()
 endif()
 
+if (PROJECT_IS_TOP_LEVEL)
+  # enable parallel builds for msbuild
+  list(APPEND CMAKE_VS_GLOBALS UseMultiToolTask=true)
+  list(APPEND CMAKE_VS_GLOBALS EnforceProcessCountAcrossBuilds=true)
+endif()
+
 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/)
 
 option(SPIRV_COLOR_TERMINAL "Enable color terminal output" ON)
@@ -240,6 +246,20 @@
 # Tests require Python3
 find_host_package(Python3 REQUIRED)
 
+# Check type annotations in Perl code. Assumes mypy.
+add_custom_target(spirv-tools-check-python-types
+	COMMAND mypy Table
+	WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/utils
+)
+set_target_properties(spirv-tools-check-python-types
+  PROPERTIES EXCLUDE_FROM_ALL ON)
+
+# Run Python unit tests
+add_custom_target(spirv-tools-check-python-tests
+	COMMAND Python3::Interpreter -m unittest discover -v -p "*test.py"
+	WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/utils
+)
+
 # Check for symbol exports on Linux.
 # At the moment, this check will fail on the OSX build machines for the Android NDK.
 # It appears they don't have objdump.
diff --git a/DEPS b/DEPS
index a119d1a..02212bc 100644
--- a/DEPS
+++ b/DEPS
@@ -3,18 +3,20 @@
 vars = {
   'github': 'https://github.com',
 
-  'abseil_revision': '7316f5616bad0a794b2a75901cc20b0099718085',
+  'abseil_revision': '351086314d46e73d430296c9eca1f6d6c0372cd1',
 
-  'effcee_revision': '2c97e5689ed8d7ab6ae5820f884f03a601ae124b',
+  'effcee_revision': 'ae38e040cbb7e83efa8bfbb4967e5b8c8c89b55a',
 
-  'googletest_revision': '35d0c365609296fa4730d62057c487e3cfa030ff',
+  'googletest_revision': 'd72f9c8aea6817cdf1ca0ac10887f328de7f3da2',
 
   # Use protobufs before they gained the dependency on abseil
   'protobuf_revision': 'v21.12',
 
-  're2_revision': '6dcd83d60f7944926bfd308cc13979fc53dd69ca',
+  're2_revision': '972a15cedd008d846f1a39b2e88ce48d7f166cbd',
 
-  'spirv_headers_revision': '3f17b2af6784bfa2c5aa5dbb8e0e74a607dd8b3b',
+  'spirv_headers_revision': 'ad9184e76a66b1001c29db9b0a3e87f646c64de0',
+
+  'mimalloc_revision': 'b1963961a5cdb1996c9ad2e356014089b7a94ea3',
 }
 
 deps = {
@@ -36,5 +38,8 @@
   'external/spirv-headers':
       Var('github') +  '/KhronosGroup/SPIRV-Headers.git@' +
           Var('spirv_headers_revision'),
+
+  'external/mimalloc':
+      Var('github') + '/microsoft/mimalloc.git@' + Var('mimalloc_revision'),
 }
 
diff --git a/MODULE.bazel b/MODULE.bazel
index 2e28e9e..1641063 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -19,7 +19,7 @@
 )
 
 bazel_dep(name = "rules_python",
-          version = "0.34.0")
+          version = "1.5.1")
 
 # https://rules-python.readthedocs.io/en/stable/toolchains.html#library-modules-with-dev-only-python-usage
 python = use_extension(
@@ -31,3 +31,5 @@
 python.toolchain(python_version = "3.12",
                  is_default = True,
                  ignore_root_user_error = True)
+
+bazel_dep(name = "rules_cc", version = "0.1.1")
diff --git a/README.md b/README.md
index d1ce2fb..fdc6f1d 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,7 @@
 
 <img alt="Linux" src="kokoro/img/linux.png" width="20px" height="20px" hspace="2px"/>[![Linux Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_linux_clang_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_release.html)
 <img alt="MacOS" src="kokoro/img/macos.png" width="20px" height="20px" hspace="2px"/>[![MacOS Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_macos_clang_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_release.html)
-<img alt="Windows" src="kokoro/img/windows.png" width="20px" height="20px" hspace="2px"/>[![Windows Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_windows_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2019_release.html)
+<img alt="Windows" src="kokoro/img/windows.png" width="20px" height="20px" hspace="2px"/>[![Windows Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_windows_vs2022_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2022_release.html)
 
 [More downloads](docs/downloads.md)
 
@@ -146,6 +146,7 @@
     decorations.
 * Normalization
   * Compact IDs
+  * Canonicalize IDs
   * CFG cleanup
   * Flatten decorations
   * Merge returns
@@ -300,6 +301,7 @@
     git clone https://github.com/google/effcee.git              spirv-tools/external/effcee
     git clone https://github.com/google/re2.git                 spirv-tools/external/re2
     git clone https://github.com/abseil/abseil-cpp.git          spirv-tools/external/abseil_cpp
+    git clone https://github.com/microsoft/mimalloc.git         spirv-tools/external/mimalloc
 
 #### Dependency on Effcee
 
@@ -312,6 +314,26 @@
   RE2 sources to appear in `external/re2`, and Abseil sources to appear in 
   `external/abseil_cpp`.
 
+#### Dependency on mimalloc
+
+SPIRV-Tools may be configured to use the [mimalloc][mimalloc] library to improve memory
+allocation performance.
+
+In the CMake build, usage of mimalloc is controlled by the `SPIRV_TOOLS_USE_MIMALLOC`
+option. This variable defaults on `ON` when building for Windows and `OFF` when building
+for other platforms. Enabling this option on non-Windows platforms is supported and is
+expected to work normally, but this has not been tested as thoroughly and extensively as
+the Windows version. In the future, the `SPIRV_TOOLS_USE_MIMALLOC` option may default to
+`ON` for non-Windows platforms as well.
+
+In order to avoid unexpectedly changing allocation behavior of applications that link
+SPIRV-Tools libraries statically, mimalloc is disabled by default on static libraries.
+The option 'SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD' can be used to force the usage of
+mimalloc on static libraries.
+
+*Note*: mimalloc is currently only supported when building with CMake. When using Bazel,
+mimalloc is not used.
+
 ### Source code organization
 
 * `example`: demo code of using SPIRV-Tools APIs
@@ -325,6 +347,7 @@
 * `external/abseil_cpp`: Location of [Abseil][abseil-cpp] sources, if Abseil is
    not already configured by an enclosing project.
   (The RE2 project already requires Abseil.)
+* `external/mimalloc`: Intended location for [mimalloc][mimalloc] sources, not provided
 * `include/`: API clients should add this directory to the include search path
 * `external/spirv-headers`: Intended location for
   [SPIR-V headers][spirv-headers], not provided
@@ -432,7 +455,7 @@
 - [Python 3](http://www.python.org/): for utility scripts and running the test
 suite.
 - [Bazel](https://bazel.build/) (optional): if building the source with Bazel,
-you need to install Bazel Version 7.0.2 on your machine. Other versions may
+you need to install Bazel Version 7.4.0 on your machine. Other versions may
 also work, but are not verified.
 - [Emscripten SDK](https://emscripten.org) (optional): if building the
   WebAssembly module.
@@ -440,20 +463,16 @@
 SPIRV-Tools is regularly tested with the following compilers:
 
 On Linux
-- GCC version 9.3
-- Clang version 10.0
+- GCC version 13
+- Clang version 18
 
 On MacOS
-- AppleClang 11.0
+- AppleClang 15.0
 
 On Windows
-- Visual Studio 2017
-- Visual Studio 2019
 - Visual Studio 2022
 
-Note: Visual Studio 2017 has incomplete c++17 support. We might stop
-testing it soon. Other compilers or later versions may work, but they are not
-tested.
+Note: Other compilers or later versions may work, but they are not tested.
 
 ### CMake options
 
@@ -804,6 +823,7 @@
 [effcee]: https://github.com/google/effcee
 [re2]: https://github.com/google/re2
 [abseil-cpp]: https://github.com/abseil/abseil-cpp
+[mimalloc]: https://github.com/microsoft/mimalloc
 [CMake]: https://cmake.org/
 [cpp-style-guide]: https://google.github.io/styleguide/cppguide.html
 [clang-sanitizers]: http://clang.llvm.org/docs/UsersManual.html#controlling-code-generation
diff --git a/build_defs.bzl b/build_defs.bzl
index 76bf3e7..c18768f 100644
--- a/build_defs.bzl
+++ b/build_defs.bzl
@@ -38,6 +38,8 @@
         "-Wno-self-assign",
         "-Wno-shadow",
         "-Wno-unused-parameter",
+        # Work around looseness in protobuf parse table generated code
+        "-Wno-implicit-int-conversion",
     ],
 })
 
@@ -47,6 +49,7 @@
         for constraint in incompatible_constraints
     }]))
 
+SPIRV_CORE_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_core_grammar_unified1"
 DEBUGINFO_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_debuginfo_grammar_unified1"
 CLDEBUGINFO100_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_opencl_debuginfo_100_grammar_unified1"
 SHDEBUGINFO100_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_nonsemantic_shader_debuginfo_100_grammar_unified1"
@@ -57,161 +60,84 @@
         merged.update(d)
     return merged
 
-def generate_core_tables(version):
-    if not version:
-        fail("Must specify version", "version")
+def ExtInst(name, target = "", prefix = ""):
+    """
+    Returns a dictionary specifying the info needed to
+    process an extended instruction set.
 
+    Args:
+        name: The extension name; forms part of the .json grammar file.
+        target: if non-empty, the name of the bazel target in spirv-headers
+           that names the JSON grammar file for the extended instrution set.
+           If empty, the target name is derived from 'name'.
+        prefix: The optional prefix for names of operand enums.
+
+    Returns a dictionary with keys 'name', 'target', 'prefix' and the
+    corresponding values.
+    """
+    return {"name": name, "target": target, "prefix": prefix}
+
+def _extinst_grammar_target(e):
+    """
+    Args: e, as returned from extinst
+    Returns the SPIRV-Headers target for the given extended instruction set spec.
+    """
+    target = e["target"]
+    name = e["name"]
+    if len(target) > 0:
+        return "@spirv_headers//:{}".format(target)
+    name_part = name.replace("-", "_").replace(".", "_")
+    return "@spirv_headers//:spirv_ext_inst_{}_grammar_unified1".format(name_part)
+
+def create_grammar_tables_target(name, extinsts):
+    """
+    Creates a ":gen_compressed_tables" target for SPIR-V instruction
+    set grammar tables.
+
+    Args:
+        name: unused. Required by convention.
+        extinsts: list of extended instruction specs.
+            Each spec is a dictionary, as returned from 'extinst'.
+    """
     grammars = dict(
-        core_grammar = "@spirv_headers//:spirv_core_grammar_{}".format(version),
-        debuginfo_grammar = DEBUGINFO_GRAMMAR_JSON_FILE,
-        cldebuginfo_grammar = CLDEBUGINFO100_GRAMMAR_JSON_FILE,
+        core_grammar = SPIRV_CORE_GRAMMAR_JSON_FILE,
     )
-
     outs = dict(
-        core_insts_output = "core.insts-{}.inc".format(version),
-        operand_kinds_output = "operand.kinds-{}.inc".format(version),
+        core_tables_header_output = "core_tables_header.inc",
+        core_tables_body_output = "core_tables_body.inc",
     )
+    extinst_args = []
+    for e in extinsts:
+        extinst_args.append("--extinst={},$(location {})".format(e["prefix"], _extinst_grammar_target(e)))
 
     cmd = (
-        "$(location :generate_grammar_tables)" +
+        "$(location :ggt)" +
         " --spirv-core-grammar=$(location {core_grammar})" +
-        " --extinst-debuginfo-grammar=$(location {debuginfo_grammar})" +
-        " --extinst-cldebuginfo100-grammar=$(location {cldebuginfo_grammar})" +
-        " --core-insts-output=$(location {core_insts_output})" +
-        " --operand-kinds-output=$(location {operand_kinds_output})" +
-        " --output-language=c++"
+        " --core-tables-body-output=$(location {core_tables_body_output})" +
+        " --core-tables-header-output=$(location {core_tables_header_output})" +
+        " " + " ".join(extinst_args)
     ).format(**_merge_dicts([grammars, outs]))
 
     native.genrule(
-        name = "gen_core_tables_" + version,
-        srcs = grammars.values(),
+        name = "gen_compressed_tables",
+        srcs = grammars.values() + [_extinst_grammar_target(e) for e in extinsts],
         outs = outs.values(),
         cmd = cmd,
         cmd_bat = cmd,
-        tools = [":generate_grammar_tables"],
-        visibility = ["//visibility:private"],
-    )
-
-def generate_enum_string_mapping(version):
-    if not version:
-        fail("Must specify version", "version")
-
-    grammars = dict(
-        core_grammar = "@spirv_headers//:spirv_core_grammar_{}".format(version),
-        debuginfo_grammar = DEBUGINFO_GRAMMAR_JSON_FILE,
-        cldebuginfo_grammar = CLDEBUGINFO100_GRAMMAR_JSON_FILE,
-    )
-
-    outs = dict(
-        extension_enum_ouput = "extension_enum.inc",
-        enum_string_mapping_output = "enum_string_mapping.inc",
-    )
-
-    cmd = (
-        "$(location :generate_grammar_tables)" +
-        " --spirv-core-grammar=$(location {core_grammar})" +
-        " --extinst-debuginfo-grammar=$(location {debuginfo_grammar})" +
-        " --extinst-cldebuginfo100-grammar=$(location {cldebuginfo_grammar})" +
-        " --extension-enum-output=$(location {extension_enum_ouput})" +
-        " --enum-string-mapping-output=$(location {enum_string_mapping_output})" +
-        " --output-language=c++"
-    ).format(**_merge_dicts([grammars, outs]))
-
-    native.genrule(
-        name = "gen_enum_string_mapping",
-        srcs = grammars.values(),
-        outs = outs.values(),
-        cmd = cmd,
-        cmd_bat = cmd,
-        tools = [":generate_grammar_tables"],
-        visibility = ["//visibility:private"],
-    )
-
-def generate_opencl_tables(version):
-    if not version:
-        fail("Must specify version", "version")
-
-    grammars = dict(
-        opencl_grammar = "@spirv_headers//:spirv_opencl_grammar_{}".format(version),
-    )
-
-    outs = dict(
-        opencl_insts_output = "opencl.std.insts.inc",
-    )
-
-    cmd = (
-        "$(location :generate_grammar_tables)" +
-        " --extinst-opencl-grammar=$(location {opencl_grammar})" +
-        " --opencl-insts-output=$(location {opencl_insts_output})"
-    ).format(**_merge_dicts([grammars, outs]))
-
-    native.genrule(
-        name = "gen_opencl_tables_" + version,
-        srcs = grammars.values(),
-        outs = outs.values(),
-        cmd = cmd,
-        cmd_bat = cmd,
-        tools = [":generate_grammar_tables"],
-        visibility = ["//visibility:private"],
-    )
-
-def generate_glsl_tables(version):
-    if not version:
-        fail("Must specify version", "version")
-
-    grammars = dict(
-        gsls_grammar = "@spirv_headers//:spirv_glsl_grammar_{}".format(version),
-    )
-    outs = dict(
-        gsls_insts_outs = "glsl.std.450.insts.inc",
-    )
-
-    cmd = (
-        "$(location :generate_grammar_tables)" +
-        " --extinst-glsl-grammar=$(location {gsls_grammar})" +
-        " --glsl-insts-output=$(location {gsls_insts_outs})" +
-        " --output-language=c++"
-    ).format(**_merge_dicts([grammars, outs]))
-
-    native.genrule(
-        name = "gen_glsl_tables_" + version,
-        srcs = grammars.values(),
-        outs = outs.values(),
-        cmd = cmd,
-        cmd_bat = cmd,
-        tools = [":generate_grammar_tables"],
-        visibility = ["//visibility:private"],
-    )
-
-def generate_vendor_tables(extension, operand_kind_prefix = ""):
-    if not extension:
-        fail("Must specify extension", "extension")
-
-    extension_rule = extension.replace("-", "_").replace(".", "_")
-    grammars = dict(
-        vendor_grammar = "@spirv_headers//:spirv_ext_inst_{}_grammar_unified1".format(extension_rule),
-    )
-    outs = dict(
-        vendor_insts_output = "{}.insts.inc".format(extension),
-    )
-    cmd = (
-        "$(location :generate_grammar_tables)" +
-        " --extinst-vendor-grammar=$(location {vendor_grammar})" +
-        " --vendor-insts-output=$(location {vendor_insts_output})" +
-        " --vendor-operand-kind-prefix={operand_kind_prefix}"
-    ).format(operand_kind_prefix = operand_kind_prefix, **_merge_dicts([grammars, outs]))
-
-    native.genrule(
-        name = "gen_vendor_tables_" + extension_rule,
-        srcs = grammars.values(),
-        outs = outs.values(),
-        cmd = cmd,
-        cmd_bat = cmd,
-        tools = [":generate_grammar_tables"],
+        tools = [":ggt"],
         visibility = ["//visibility:private"],
     )
 
 def generate_extinst_lang_headers(name, grammar = None):
+    """
+    Creates a :gen_extinst_lang_headers_* target for a C++ header
+    the enums in a SPIR-V extended instruction set.
+
+    Args:
+       name: the basename of the emitted header file.
+       grammar: the path to the JSON grammar file for the extended
+           instruction set.
+    """
     if not grammar:
         fail("Must specify grammar", "grammar")
     outs = dict(
diff --git a/docs/downloads.md b/docs/downloads.md
index 0454b9e..853c2be 100644
--- a/docs/downloads.md
+++ b/docs/downloads.md
@@ -20,14 +20,12 @@
 
 Download the latest builds of the [main](https://github.com/KhronosGroup/SPIRV-Tools/tree/main) branch.
 
-### Release build
-| Windows | Linux | MacOS |
-| --- | --- | --- |
-| [MSVC 2017](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_release.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_release.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_release.html) |
-| | [gcc](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_gcc_release.html) | |
+| Platform | Processor | Compiler | Release build | Debug build |
+| --- | --- | --- | --- | --- |
+| Windows | x86-64 | VisualStudio 2022 (MSVC v143) | Download: <a href="https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2022_release.html"> <img src="https://storage.googleapis.com/spirv-tools/badges/build_status_windows_vs2022_release.svg" alt="status of VS 2022 release build"></a> | Download: <a href="https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2022_debug.html"> <img src="https://storage.googleapis.com/spirv-tools/badges/build_status_windows_vs2022_debug.svg" alt="status of VS 2022 debug build"></a> |
+| Linux | x86-64 | GCC 9.4 | Download: <a href="https://storage.googleapis.com/spirv-tools/badges/build_link_linux_gcc_release.html"> <img src="https://storage.googleapis.com/spirv-tools/badges/build_status_linux_gcc_release.svg" alt="status of Linux GCC build"></a> | Download: <a href="https://storage.googleapis.com/spirv-tools/badges/build_link_linux_gcc_debug.html"> <img src="https://storage.googleapis.com/spirv-tools/badges/build_status_linux_gcc_debug.svg" alt="status of Linux GCC debug build"></a> |
+| macOS | x86-64 | Clang 15 | Download: <a href="https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_release.html"> <img src="https://storage.googleapis.com/spirv-tools/badges/build_status_macos_clang_release.svg" alt="status of macOS Clang build"></a> | Download: <a href="https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_debug.html"> <img src="https://storage.googleapis.com/spirv-tools/badges/build_status_macos_clang_debug.svg" alt="status of macOS Clang build"></a> |
 
-### Debug build
-| Windows | Linux | MacOS |
-| --- | --- | --- |
-| [MSVC 2017](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_debug.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_debug.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_debug.html) |
-| | [gcc](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_gcc_debug.html) | |
+Note: If you suspect something is wrong with the compiler versions mentioned,
+check the scripts and configurations in the [kokoro](../kokoro) source tree,
+or the results of the checks on the latest commits on the `main` branch.
diff --git a/docs/syntax.md b/docs/syntax.md
index c135d01..ff53d53 100644
--- a/docs/syntax.md
+++ b/docs/syntax.md
@@ -101,7 +101,7 @@
 
 The syntax for a floating point literal is the same as floating point
 constants in the C programming language, except:
-* An optional leading minus (`-`) is part of the literal.
+* An optional leading minus (`-`) or leading plus (`+`) is part of the literal.
 * An optional type specifier suffix is not allowed.
 Infinity and NaN values are expressed in hexadecimal float literals
 by using the maximum representable exponent for the bit width.
@@ -125,6 +125,9 @@
 Zero and normal values are printed in decimal form with enough digits
 to preserve all significand bits.
 
+Hex float values that underflow are rounded to zero.  If there is a leading
+minus sign, underflow goes to negative zero.
+
 ## Arbitrary Integers
 <a name="immediate"></a>
 
@@ -215,6 +218,50 @@
   that named ID being output.  This may be valid SPIR-V, contrary to the
   presumed intention of the writer.
 
+## OpUnknown
+<a name="op-unknown"></a>
+
+HLSL has a feature that allows users to specify an exact SPIR-V type using [the
+`SpirvType` template](https://github.com/microsoft/hlsl-specs/blob/main/proposals/0011-inline-spirv.md#types).
+This feature allows the user to specify a type opcode that the compiler does
+not know. In this case, it will be unable to generate assembly using the
+correct mnemonic.
+
+In order to represent unknown opcodes in assembly format, the `OpUnknown`
+pseudo-instruction may be used. The syntax is:
+
+```
+OpUnknown(<enumerant>, <WordCount>) <operand 1> ...
+```
+
+`enumerant` is the opcode enumerant, and `WordCount` is the number of words in
+the instruction. These will be assembled into a single word representing the
+opcode. Operands will be parsed according to the alternate parsing mode
+described in [Arbitrary Integers](#op-unknown). Named enumerated values cannot
+be handled by this mode and must be represented using the arbitrary integer
+syntax.
+
+It must be used at the beginning of a new instruction, and if there is a result
+ID it must explicitly be passed in as an operand. This is because the physical
+layout of a SPIR-V instruction may include a result type operand before the
+result ID operand, but it depends on the opcode and cannot be inferred for an
+unknown operand.
+
+For example, a 32-bit signed integer type could be represented like this:
+
+```
+OpUnknown(21, 4) %int_t 32 1
+```
+
+An OpStore instruction could be represented as:
+
+```
+OpUnknown(62, 3) %9 %12
+```
+
+The enumerant and word count must be decimal integers.
+
+
 ## Notes
 
 * Some enumerants cannot be used by name, because the target instruction
diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt
index 1ccab19..eb4e69e 100644
--- a/external/CMakeLists.txt
+++ b/external/CMakeLists.txt
@@ -26,6 +26,52 @@
     set(${var} ${val} PARENT_SCOPE)
 endfunction()
 
+if (DEFINED mimalloc_SOURCE_DIR)
+  # This allows flexible position of the mimalloc repo.
+  set(MIMALLOC_DIR ${mimalloc_SOURCE_DIR})
+else()
+  if (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/mimalloc)
+    set(MIMALLOC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/mimalloc)
+  endif()
+endif()
+
+# Used on Windows by default, but allow opt-in on other platforms
+if (WIN32)
+  set(SPIRV_TOOLS_USE_MIMALLOC_DEFAULT_VALUE ON)
+  set(SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD_DEFAULT_VALUE OFF)
+else()
+  set(SPIRV_TOOLS_USE_MIMALLOC_DEFAULT_VALUE OFF)
+  set(SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD_DEFAULT_VALUE OFF)
+endif()
+
+# To avoid unexpected side effects on users of the static library, mimalloc
+# must be explicitly enabled when building static libraries.
+include(CMakeDependentOption)
+cmake_dependent_option(SPIRV_TOOLS_USE_MIMALLOC
+  "Executables and shared libraries use mimalloc instead of the default allocator"
+  ${SPIRV_TOOLS_USE_MIMALLOC_DEFAULT_VALUE} "MIMALLOC_DIR" OFF)
+
+cmake_dependent_option(SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD
+  "Static libraries use mimalloc instead of the default allocator"
+  ${SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD_DEFAULT_VALUE} SPIRV_TOOLS_USE_MIMALLOC OFF)
+
+if (SPIRV_TOOLS_USE_MIMALLOC)
+  if (NOT WIN32)
+    push_variable(MI_OVERRIDE 0)
+  endif()
+  push_variable(MI_BUILD_TESTS 0)
+
+  add_subdirectory(${MIMALLOC_DIR} ${CMAKE_BINARY_DIR}/external/mimalloc EXCLUDE_FROM_ALL)
+  if (${CMAKE_CXX_COMPILER_ID} MATCHES Clang)
+    target_compile_options(mimalloc-static PRIVATE -Wno-int-conversion)
+  endif()
+
+  if (NOT WIN32)
+    pop_variable(MI_OVERRIDE)
+  endif()
+  pop_variable(MI_BUILD_TESTS)
+endif()
+
 if (DEFINED SPIRV-Headers_SOURCE_DIR)
   # This allows flexible position of the SPIRV-Headers repo.
   set(SPIRV_HEADER_DIR ${SPIRV-Headers_SOURCE_DIR})
diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h
index 100e61e..28fd916 100644
--- a/include/spirv-tools/libspirv.h
+++ b/include/spirv-tools/libspirv.h
@@ -80,6 +80,8 @@
   SPV_ERROR_INVALID_DATA = -14,  // Indicates data rules validation failure.
   SPV_ERROR_MISSING_EXTENSION = -15,
   SPV_ERROR_WRONG_VERSION = -16,  // Indicates wrong SPIR-V version
+  SPV_ERROR_FNVAR =
+      -17,  // Error related to SPV_INTEL_function_variants extension
   SPV_FORCE_32_BIT_ENUM(spv_result_t)
 } spv_result_t;
 
@@ -189,36 +191,24 @@
   SPV_OPERAND_TYPE_MEMORY_ACCESS,          // SPIR-V Sec 3.26
   SPV_OPERAND_TYPE_FRAGMENT_SHADING_RATE,  // SPIR-V Sec 3.FSR
 
-// NOTE: New concrete enum values should be added at the end.
+  // NOTE: New concrete enum values should be added at the end.
 
-// The "optional" and "variable"  operand types are only used internally by
-// the assembler and the binary parser.
-// There are two categories:
-//    Optional : expands to 0 or 1 operand, like ? in regular expressions.
-//    Variable : expands to 0, 1 or many operands or pairs of operands.
-//               This is similar to * in regular expressions.
+  // The "optional" and "variable"  operand types are only used internally by
+  // the assembler and the binary parser.
+  // There are two categories:
+  //    Optional : expands to 0 or 1 operand, like ? in regular expressions.
+  //    Variable : expands to 0, 1 or many operands or pairs of operands.
+  //               This is similar to * in regular expressions.
 
-// NOTE: These FIRST_* and LAST_* enum values are DEPRECATED.
-// The concept of "optional" and "variable" operand types are only intended
-// for use as an implementation detail of parsing SPIR-V, either in text or
-// binary form.  Instead of using enum ranges, use characteristic function
-// spvOperandIsConcrete.
-// The use of enum value ranges in a public API makes it difficult to insert
-// new values into a range without also breaking binary compatibility.
-//
-// Macros for defining bounds on optional and variable operand types.
-// Any variable operand type is also optional.
-// TODO(dneto): Remove SPV_OPERAND_TYPE_FIRST_* and SPV_OPERAND_TYPE_LAST_*
-#define FIRST_OPTIONAL(ENUM) ENUM, SPV_OPERAND_TYPE_FIRST_OPTIONAL_TYPE = ENUM
-#define FIRST_VARIABLE(ENUM) ENUM, SPV_OPERAND_TYPE_FIRST_VARIABLE_TYPE = ENUM
-#define LAST_VARIABLE(ENUM)                         \
-  ENUM, SPV_OPERAND_TYPE_LAST_VARIABLE_TYPE = ENUM, \
-        SPV_OPERAND_TYPE_LAST_OPTIONAL_TYPE = ENUM
+  // Use characteristic function spvOperandIsConcrete to classify the
+  // operand types; when it returns false, the operand is optional or variable.
+  //
+  // Any variable operand type is also optional.
 
   // An optional operand represents zero or one logical operands.
   // In an instruction definition, this may only appear at the end of the
   // operand types.
-  FIRST_OPTIONAL(SPV_OPERAND_TYPE_OPTIONAL_ID),
+  SPV_OPERAND_TYPE_OPTIONAL_ID,
   // An optional image operand type.
   SPV_OPERAND_TYPE_OPTIONAL_IMAGE,
   // An optional memory access type.
@@ -243,7 +233,7 @@
   // A variable operand represents zero or more logical operands.
   // In an instruction definition, this may only appear at the end of the
   // operand types.
-  FIRST_VARIABLE(SPV_OPERAND_TYPE_VARIABLE_ID),
+  SPV_OPERAND_TYPE_VARIABLE_ID,
   SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER,
   // A sequence of zero or more pairs of (typed literal integer, Id).
   // Expands to zero or more:
@@ -251,7 +241,7 @@
   // where the literal number must always be an integer of some sort.
   SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER_ID,
   // A sequence of zero or more pairs of (Id, Literal integer)
-  LAST_VARIABLE(SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER),
+  SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER,
 
   // The following are concrete enum types from the DebugInfo extended
   // instruction set.
@@ -320,6 +310,32 @@
   SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_REDUCE,
   // Enum type from SPV_NV_cooperative_matrix2
   SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS,
+  // Optional types from SPV_INTEL_subgroup_matrix_multiply_accumulate
+  SPV_OPERAND_TYPE_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS,
+  SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS,
+
+  SPV_OPERAND_TYPE_COOPERATIVE_VECTOR_MATRIX_LAYOUT,
+  SPV_OPERAND_TYPE_COMPONENT_TYPE,
+
+  // From nonesmantic.clspvreflection
+  SPV_OPERAND_TYPE_KERNEL_PROPERTY_FLAGS,
+
+  // From nonesmantic.shader.debuginfo.100
+  SPV_OPERAND_TYPE_SHDEBUG100_BUILD_IDENTIFIER_FLAGS,
+  SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING,
+  SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_COMPOSITE_TYPE,
+  SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_IMPORTED_ENTITY,
+  SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_INFO_FLAGS,
+  SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_OPERATION,
+  SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_TYPE_QUALIFIER,
+
+  // SPV_ARM_tensors
+  SPV_OPERAND_TYPE_TENSOR_OPERANDS,
+  SPV_OPERAND_TYPE_OPTIONAL_TENSOR_OPERANDS,
+
+  // SPV_INTEL_function_variants
+  SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY,
+  SPV_OPERAND_TYPE_VARIABLE_CAPABILITY,
 
   // This is a sentinel value, and does not represent an operand type.
   // It should come last.
@@ -347,6 +363,8 @@
   SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION,
   SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100,
   SPV_EXT_INST_TYPE_NONSEMANTIC_VKSPREFLECTION,
+  SPV_EXT_INST_TYPE_TOSA_001000_1,
+  SPV_EXT_INST_TYPE_ARM_MOTION_ENGINE_100,
 
   // Multiple distinct extended instruction set types could return this
   // value, if they are prefixed with NonSemantic. and are otherwise
@@ -368,6 +386,18 @@
   SPV_NUMBER_FLOATING,
 } spv_number_kind_t;
 
+// Represent the encoding of floating point values
+typedef enum spv_fp_encoding_t {
+  SPV_FP_ENCODING_UNKNOWN =
+      0,  // The encoding is not specified. Has to be deduced from bitwidth
+  SPV_FP_ENCODING_IEEE754_BINARY16,  // half float
+  SPV_FP_ENCODING_IEEE754_BINARY32,  // single float
+  SPV_FP_ENCODING_IEEE754_BINARY64,  // double float
+  SPV_FP_ENCODING_BFLOAT16,
+  SPV_FP_ENCODING_FLOAT8_E4M3,
+  SPV_FP_ENCODING_FLOAT8_E5M2,
+} spv_fp_encoding_t;
+
 typedef enum spv_text_to_binary_options_t {
   SPV_TEXT_TO_BINARY_OPTION_NONE = SPV_BIT(0),
   // Numeric IDs in the binary will have the same values as in the source.
@@ -396,6 +426,9 @@
   // Reorder blocks to match the structured control flow of SPIR-V to increase
   // readability.
   SPV_BINARY_TO_TEXT_OPTION_REORDER_BLOCKS = SPV_BIT(9),
+  // Handle unknown opcodes and unknown extended instruction numbers by emitting
+  // them as OpUnknown instructions with raw integer operands.
+  SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES = SPV_BIT(10),
   SPV_FORCE_32_BIT_ENUM(spv_binary_to_text_options_t)
 } spv_binary_to_text_options_t;
 
@@ -403,7 +436,7 @@
 
 // The default id bound is to the minimum value for the id limit
 // in the spir-v specification under the section "Universal Limits".
-const uint32_t kDefaultMaxIdBound = 0x3FFFFF;
+const static uint32_t kDefaultMaxIdBound = 0x3FFFFF;
 
 // Structures
 
@@ -423,6 +456,8 @@
   spv_number_kind_t number_kind;
   // The number of bits for a literal number type.
   uint32_t number_bit_width;
+  // The encoding used for floating point values
+  spv_fp_encoding_t fp_encoding;
 } spv_parsed_operand_t;
 
 // An instruction parsed from a binary SPIR-V module.
@@ -729,6 +764,16 @@
 SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetAllowLocalSizeId(
     spv_validator_options options, bool val);
 
+// Allow Offset (in addition to ConstOffset) for texture operations.
+// Was added for VK_KHR_maintenance8
+SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetAllowOffsetTextureOperand(
+    spv_validator_options options, bool val);
+
+// Allow base operands of some bit operations to be non-32-bit wide.
+// Was added for VK_KHR_maintenance9
+SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetAllowVulkan32BitBitwise(
+    spv_validator_options options, bool val);
+
 // Whether friendly names should be used in validation error messages.
 SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetFriendlyNames(
     spv_validator_options options, bool val);
diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp
index 6a64e93..1b3ed86 100644
--- a/include/spirv-tools/libspirv.hpp
+++ b/include/spirv-tools/libspirv.hpp
@@ -126,6 +126,18 @@
     spvValidatorOptionsSetAllowLocalSizeId(options_, val);
   }
 
+  // Allow Offset (in addition to ConstOffset) for texture
+  // operations. Was added for VK_KHR_maintenance8
+  void SetAllowOffsetTextureOperand(bool val) {
+    spvValidatorOptionsSetAllowOffsetTextureOperand(options_, val);
+  }
+
+  // Allow base operands of some bit operations to be non-32-bit wide.
+  // Was added for VK_KHR_maintenance9
+  void SetAllowVulkan32BitBitwise(bool val) {
+    spvValidatorOptionsSetAllowVulkan32BitBitwise(options_, val);
+  }
+
   // Records whether or not the validator should relax the rules on pointer
   // usage in logical addressing mode.
   //
diff --git a/include/spirv-tools/linker.hpp b/include/spirv-tools/linker.hpp
index 9037b94..99527ed 100644
--- a/include/spirv-tools/linker.hpp
+++ b/include/spirv-tools/linker.hpp
@@ -67,12 +67,36 @@
     allow_ptr_type_mismatch_ = allow_ptr_type_mismatch;
   }
 
+  std::string GetFnVarTargetsCsv() const { return fnvar_targets_csv_; }
+  void SetFnVarTargetsCsv(std::string fnvar_targets_csv) {
+    fnvar_targets_csv_ = fnvar_targets_csv;
+  }
+
+  std::string GetFnVarArchitecturesCsv() const {
+    return fnvar_architectures_csv_;
+  }
+  void SetFnVarArchitecturesCsv(std::string fnvar_architectures_csv) {
+    fnvar_architectures_csv_ = fnvar_architectures_csv;
+  }
+
+  bool GetHasFnVarCapabilities() const { return has_fnvar_capabilities_; }
+  void SetHasFnVarCapabilities(bool fnvar_capabilities) {
+    has_fnvar_capabilities_ = fnvar_capabilities;
+  }
+
+  std::vector<std::string> GetInFiles() const { return in_files_; }
+  void SetInFiles(std::vector<std::string> in_files) { in_files_ = in_files; }
+
  private:
   bool create_library_{false};
   bool verify_ids_{false};
   bool allow_partial_linkage_{false};
   bool use_highest_version_{false};
   bool allow_ptr_type_mismatch_{false};
+  std::string fnvar_targets_csv_{""};
+  std::string fnvar_architectures_csv_{""};
+  bool has_fnvar_capabilities_ = false;
+  std::vector<std::string> in_files_{{}};
 };
 
 // Links one or more SPIR-V modules into a new SPIR-V module. That is, combine
diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp
index 9427b01..fd4527b 100644
--- a/include/spirv-tools/optimizer.hpp
+++ b/include/spirv-tools/optimizer.hpp
@@ -240,7 +240,7 @@
 
  private:
   struct SPIRV_TOOLS_LOCAL Impl;  // Opaque struct for holding internal data.
-  std::unique_ptr<Impl> impl_;  // Unique pointer to internal data.
+  std::unique_ptr<Impl> impl_;    // Unique pointer to internal data.
 };
 
 // Creates a null pass.
@@ -645,6 +645,11 @@
 // Works best after LICM and local multi store elimination pass.
 Optimizer::PassToken CreateLoopUnswitchPass();
 
+// Creates a pass to legalize multidimensional arrays for Vulkan.
+// This pass will replace multidimensional arrays of resources with a single
+// dimensional array. Combine-access-chains should be run before this pass.
+Optimizer::PassToken CreateLegalizeMultidimArrayPass();
+
 // Create global value numbering pass.
 // This pass will look for instructions where the same value is computed on all
 // paths leading to the instruction.  Those instructions are deleted.
@@ -655,7 +660,7 @@
 // element if those elements are accessed individually.  The parameter is a
 // limit on the number of members in the composite variable that the pass will
 // consider replacing.
-Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit = 100);
+Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit = 0);
 
 // Create a private to local pass.
 // This pass looks for variables declared in the private storage class that are
@@ -968,6 +973,70 @@
 // Creates a pass to add/remove maximal reconvergence execution mode.
 // This pass either adds or removes maximal reconvergence from all entry points.
 Optimizer::PassToken CreateModifyMaximalReconvergencePass(bool add);
+
+// Creates a pass to split combined image+sampler variables and function
+// parameters into separate image and sampler parts. Binding numbers and
+// other decorations are copied.
+Optimizer::PassToken CreateSplitCombinedImageSamplerPass();
+
+// Creates a pass to remap bindings to avoid conflicts, assuming the module
+// is valid for Vulkan.  A conflict exits when an entry point uses two distinct
+// variables with the same descriptor set and binding.  Vulkan allows one kind
+// of conflict: when one varible is an image (or array of images), and the
+// other is a sampler (or an array of samplers).
+
+// Conflicts are eliminated by incrementing the binding number of the sampler
+// part, and then propagating that increment through variables with
+// higher-numbered bindings until no conflict remains. This handles the case
+// when multiple shaders may share the same resource variables; this can
+// introduce holes in binding slots.
+//
+// Here's an example where shaders Alpha, Beta, Gamma, Delta collectively use
+// resource variables %100, %101, %102, %103, %104 all with the same
+// DescriptorSet and with Bindings as in the following table:
+//
+// Before:
+//
+//   Binding:   0          1        2        3
+//   Alpha:    %100,%101
+//   Beta:     %100       %102
+//   Gamma:               %102     %103
+//   Delta:                        %103     %104
+//
+// The Alpha shader has a conflict where variables %100, %101 have the same
+// descriptor set and binding.  If %100 is a sampler resource variable, then
+// the conflict is resolved by incrementing the binding number on %100 from 0
+// to 1.  But this causes a new confict for shader Beta because it now uses
+// both %100 and %102 with binding number 1.  That conflict is resolved by
+// incrementing the binding number on its variable that originally appeared
+// second (i.e. %102), so %102 gets binding 2. This now produces a conflict
+// for Gamma between %102 and %103 using binding number 2. Since %103 originally
+// appeared second (in the view from Gamma), the algorithm bumps %103 to binding
+// number %103. Now Delta has a conflict between %103 and %104, resulting in
+// %104 getting the next binding number, 4.  The picture afterward is:
+//
+// After:
+//
+//   Binding:   0          1        2        3        4
+//   Alpha:    %101       %100
+//   Beta:                %100     %102
+//   Gamma:                        %102     %103
+//   Delta:                                 %103     %104
+//
+//
+// This pass assumes binding numbers are not applid via decoration groups
+// (OpDecorationGroup).
+Optimizer::PassToken CreateResolveBindingConflictsPass();
+
+// Create a pass to canonicalize IDs to improve compression of SPIR-V binary
+// files. The resulting modules have an increased ID range (IDs are not as
+// tightly packed around zero), but will compress better when multiple modules
+// are compressed together, since the compressor's dictionary can find better
+// cross module commonality. This pass should be run after most optimization
+// passes except for
+// --strip-debug because this pass will use OpName to canonicalize IDs. i.e. Run
+// --strip-debug after this pass.
+Optimizer::PassToken CreateCanonicalizeIdsPass();
 }  // namespace spvtools
 
 #endif  // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_
diff --git a/kokoro/check-format/build-docker.sh b/kokoro/check-format/build-docker.sh
new file mode 100755
index 0000000..b7af9ee
--- /dev/null
+++ b/kokoro/check-format/build-docker.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+# Copyright (c) 2025 Google LLC
+#
+# 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.
+
+# Fail on any error.
+set -e
+
+# This is required to run any git command in the docker since owner will
+# have changed between the clone environment, and the docker container.
+# Marking the root of the repo as safe for ownership changes.
+git config --global --add safe.directory "$PWD"
+
+echo $(date): Check formatting...
+./utils/check_code_format.sh ${1:-main}
+echo $(date): check completed.
diff --git a/kokoro/check-format/build.sh b/kokoro/check-format/build.sh
old mode 100644
new mode 100755
index 96603e4..6f073c6
--- a/kokoro/check-format/build.sh
+++ b/kokoro/check-format/build.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# Copyright (c) 2018 Google LLC.
+# Copyright (c) 2025 Google LLC
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -12,37 +12,16 @@
 # 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.
-#
-# Android Build Script.
 
 # Fail on any error.
 set -e
-# Display commands being run.
-set -x
 
-BUILD_ROOT=$PWD
-SRC=$PWD/github/SPIRV-Tools
+SCRIPT_DIR="$( cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
+SRC_ROOT="$( cd "${SCRIPT_DIR}/../.." >/dev/null 2>&1 && pwd )"
+TARGET_BRANCH="${KOKORO_GITHUB_PULL_REQUEST_TARGET_BRANCH-main}"
 
-# This is required to run any git command in the docker since owner will
-# have changed between the clone environment, and the docker container.
-# Marking the root of the repo as safe for ownership changes.
-git config --global --add safe.directory $SRC
-
-# Get clang-format-5.0.0.
-# Once kokoro upgrades the Ubuntu VMs, we can use 'apt-get install clang-format'
-curl -L http://releases.llvm.org/5.0.0/clang+llvm-5.0.0-linux-x86_64-ubuntu14.04.tar.xz -o clang-llvm.tar.xz
-tar xf clang-llvm.tar.xz
-export PATH=$PWD/clang+llvm-5.0.0-linux-x86_64-ubuntu14.04/bin:$PATH
-
-cd $SRC
-git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers
-git clone https://github.com/google/googletest          external/googletest
-cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd ..
-git clone --depth=1 https://github.com/google/effcee              external/effcee
-git clone --depth=1 https://github.com/google/re2                 external/re2
-# The --fail flag causes the command to fail on HTTP error response codes, like 404.
-curl -L --fail https://raw.githubusercontent.com/llvm/llvm-project/main/clang/tools/clang-format/clang-format-diff.py -o utils/clang-format-diff.py
-
-echo $(date): Check formatting...
-./utils/check_code_format.sh;
-echo $(date): check completed.
+docker run --rm -i \
+  --volume "${SRC_ROOT}:${SRC_ROOT}" \
+  --workdir "${SRC_ROOT}" \
+  "us-east4-docker.pkg.dev/shaderc-build/radial-docker/ubuntu-24.04-amd64/formatter" \
+  "${SCRIPT_DIR}/build-docker.sh" "${TARGET_BRANCH}"
diff --git a/kokoro/check-format/presubmit_check_format.cfg b/kokoro/check-format/continuous.cfg
similarity index 94%
rename from kokoro/check-format/presubmit_check_format.cfg
rename to kokoro/check-format/continuous.cfg
index 1993289..dec499f 100644
--- a/kokoro/check-format/presubmit_check_format.cfg
+++ b/kokoro/check-format/continuous.cfg
@@ -12,5 +12,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Presubmit build configuration.
 build_file: "SPIRV-Tools/kokoro/check-format/build.sh"
diff --git a/kokoro/check-format/presubmit_check_format.cfg b/kokoro/check-format/presubmit.cfg
similarity index 94%
copy from kokoro/check-format/presubmit_check_format.cfg
copy to kokoro/check-format/presubmit.cfg
index 1993289..dec499f 100644
--- a/kokoro/check-format/presubmit_check_format.cfg
+++ b/kokoro/check-format/presubmit.cfg
@@ -12,5 +12,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Presubmit build configuration.
 build_file: "SPIRV-Tools/kokoro/check-format/build.sh"
diff --git a/kokoro/check-format/presubmit_check_format.cfg b/kokoro/dxc-smoketest/build.sh
old mode 100644
new mode 100755
similarity index 68%
copy from kokoro/check-format/presubmit_check_format.cfg
copy to kokoro/dxc-smoketest/build.sh
index 1993289..0428f38
--- a/kokoro/check-format/presubmit_check_format.cfg
+++ b/kokoro/dxc-smoketest/build.sh
@@ -1,10 +1,11 @@
+#!/bin/bash
 # Copyright (c) 2018 Google LLC.
 #
 # 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
+#     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,
@@ -12,5 +13,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Presubmit build configuration.
-build_file: "SPIRV-Tools/kokoro/check-format/build.sh"
+# Fail on any error.
+set -e
+# Display commands being run.
+set -x
+
+SCRIPT_DIR=`dirname "$BASH_SOURCE"`
+source $SCRIPT_DIR/../scripts/linux/build.sh RELEASE gcc cmake-dxc-smoketest
diff --git a/kokoro/check-format/presubmit_check_format.cfg b/kokoro/dxc-smoketest/continuous.cfg
similarity index 85%
copy from kokoro/check-format/presubmit_check_format.cfg
copy to kokoro/dxc-smoketest/continuous.cfg
index 1993289..c52c216 100644
--- a/kokoro/check-format/presubmit_check_format.cfg
+++ b/kokoro/dxc-smoketest/continuous.cfg
@@ -12,5 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Presubmit build configuration.
-build_file: "SPIRV-Tools/kokoro/check-format/build.sh"
+# Continuous build configuration.
+build_file: "SPIRV-Tools/kokoro/dxc-smoketest/build.sh"
+
diff --git a/kokoro/check-format/presubmit_check_format.cfg b/kokoro/dxc-smoketest/presubmit.cfg
similarity index 90%
copy from kokoro/check-format/presubmit_check_format.cfg
copy to kokoro/dxc-smoketest/presubmit.cfg
index 1993289..6e0c861 100644
--- a/kokoro/check-format/presubmit_check_format.cfg
+++ b/kokoro/dxc-smoketest/presubmit.cfg
@@ -13,4 +13,5 @@
 # limitations under the License.
 
 # Presubmit build configuration.
-build_file: "SPIRV-Tools/kokoro/check-format/build.sh"
+build_file: "SPIRV-Tools/kokoro/dxc-smoketest/build.sh"
+
diff --git a/kokoro/macos-clang-release-bazel/build.sh b/kokoro/macos-clang-release-bazel/build.sh
index 4bb889a..592c50b 100644
--- a/kokoro/macos-clang-release-bazel/build.sh
+++ b/kokoro/macos-clang-release-bazel/build.sh
@@ -32,14 +32,16 @@
 cd $SRC
 /usr/bin/python3 utils/git-sync-deps --treeless
 
-# Get bazel 7.0.2
-gsutil cp gs://bazel/7.0.2/release/bazel-7.0.2-darwin-x86_64 .
-chmod +x bazel-7.0.2-darwin-x86_64
+# Get bazel 7.4.0
+BAZEL_VER=7.4.0
+gcloud config set auth/disable_credentials True
+gcloud storage cp gs://bazel/$BAZEL_VER/release/bazel-$BAZEL_VER-darwin-x86_64 .
+chmod +x bazel-$BAZEL_VER-darwin-x86_64
 
 echo $(date): Build everything...
-./bazel-7.0.2-darwin-x86_64 build --cxxopt=-std=c++17 :all
+./bazel-$BAZEL_VER-darwin-x86_64 build --cxxopt=-std=c++17 :all
 echo $(date): Build completed.
 
 echo $(date): Starting bazel test...
-./bazel-7.0.2-darwin-x86_64 test --cxxopt=-std=c++17 :all
+./bazel-$BAZEL_VER-darwin-x86_64 test --cxxopt=-std=c++17 :all
 echo $(date): Bazel test completed.
diff --git a/kokoro/scripts/linux/build-docker.sh b/kokoro/scripts/linux/build-docker.sh
index b2c034d..f6cd513 100755
--- a/kokoro/scripts/linux/build-docker.sh
+++ b/kokoro/scripts/linux/build-docker.sh
@@ -22,17 +22,17 @@
 
 # This is required to run any git command in the docker since owner will
 # have changed between the clone environment, and the docker container.
-# Marking the root of the repo as safe for ownership changes.
-git config --global --add safe.directory $ROOT_DIR
+# Mark all repositories as safe for ownership changes.
+git config --global --add safe.directory '*'
 
 . /bin/using.sh # Declare the bash `using` function for configuring toolchains.
 
 using python-3.12
 
 if [ $COMPILER = "clang" ]; then
-  using clang-10.0.0
+  using clang-18
 elif [ $COMPILER = "gcc" ]; then
-  using gcc-9
+  using gcc-13
 fi
 
 cd $ROOT_DIR
@@ -45,13 +45,13 @@
   mkdir "$dir"
 }
 
-if [ $TOOL != "cmake-smoketest" ]; then
+if [ $TOOL != "cmake-shaderc-smoketest" ] && [ $TOOL != "cmake-dxc-smoketest" ]; then
   # Get source for dependencies, as specified in the DEPS file
   /usr/bin/python3 utils/git-sync-deps --treeless
 fi
 
 if [ $TOOL = "cmake" ]; then
-  using cmake-3.17.2
+  using cmake-3.31.2
   using ninja-1.10.0
 
   # Possible configurations are:
@@ -115,8 +115,8 @@
   ninja install
   cd $KOKORO_ARTIFACTS_DIR
   tar czf install.tgz install
-elif [ $TOOL = "cmake-smoketest" ]; then
-  using cmake-3.17.2
+elif [ $TOOL = "cmake-shaderc-smoketest" ]; then
+  using cmake-3.31.2
   using ninja-1.10.0
 
   # Get shaderc.
@@ -156,9 +156,46 @@
   echo $(date): Starting ctest...
   ctest --output-on-failure -j4
   echo $(date): ctest completed.
+elif [ $TOOL = "cmake-dxc-smoketest" ]; then
+  using cmake-3.31.2
+  using ninja-1.10.0
+
+  # Get shaderc.
+  DXC_DIR=/tmp/dxc
+  clean_dir "$DXC_DIR"
+  cd $DXC_DIR
+  git clone https://github.com/microsoft/DirectXShaderCompiler.git .
+  cd $DXC_DIR/external
+
+  # Get DXC dependencies. Link the appropriate SPIRV-Tools.
+  git submodule update --init DirectX-Headers
+  rm -rf SPIRV-Tools
+  ln -s $ROOT_DIR SPIRV-Tools
+  git clone https://github.com/KhronosGroup/SPIRV-Headers.git SPIRV-Headers
+
+  cd $DXC_DIR
+  mkdir build
+  cd $DXC_DIR/build
+
+  # Invoke the build.
+  echo $(date): Configuring build...
+  cmake $DXC_DIR \
+  -C $DXC_DIR/cmake/caches/PredefinedParams.cmake \
+  -DCMAKE_BUILD_TYPE="Release" \
+  -G Ninja
+
+  echo $(date): Building ClangSPIRVTests...
+  ninja ClangSPIRVTests
+
+  echo $(date): Testing ClangSPIRVTests...
+  tools/clang/unittests/SPIRV/ClangSPIRVTests
+
+  echo $(date): Testing check-clang-codegenspirv...
+  ninja check-clang-codegenspirv
+
 elif [ $TOOL = "cmake-android-ndk" ]; then
-  using cmake-3.17.2
-  using ndk-r25c
+  using cmake-3.31.2
+  using ndk-r27c
   using ninja-1.10.0
 
   clean_dir "$ROOT_DIR/build"
@@ -178,7 +215,7 @@
   ninja
   echo $(date): Build completed.
 elif [ $TOOL = "android-ndk-build" ]; then
-  using ndk-r25c
+  using ndk-r27c
 
   clean_dir "$ROOT_DIR/build"
   cd "$ROOT_DIR/build"
@@ -193,7 +230,7 @@
 
   echo $(date): ndk-build completed.
 elif [ $TOOL = "bazel" ]; then
-  using bazel-7.0.2
+  using bazel-7.4.0
 
   echo $(date): Build everything...
   bazel build --cxxopt=-std=c++17 :all
diff --git a/kokoro/scripts/linux/build.sh b/kokoro/scripts/linux/build.sh
index 688ba79..cb6cf58 100644
--- a/kokoro/scripts/linux/build.sh
+++ b/kokoro/scripts/linux/build.sh
@@ -52,7 +52,7 @@
   --env KOKORO_ARTIFACTS_DIR="${KOKORO_ARTIFACTS_DIR}" \
   --env BUILD_SHA="${BUILD_SHA}" \
   --entrypoint "${SCRIPT_DIR}/build-docker.sh" \
-  "gcr.io/shaderc-build/radial-build:latest"
+  us-east4-docker.pkg.dev/shaderc-build/radial-docker/ubuntu-24.04-amd64/cpp-builder
 RESULT=$?
 
 # This is important. If the permissions are not fixed, kokoro will fail
diff --git a/kokoro/scripts/windows/build.bat b/kokoro/scripts/windows/build.bat
index fe15f2d..fbd5f97 100644
--- a/kokoro/scripts/windows/build.bat
+++ b/kokoro/scripts/windows/build.bat
@@ -21,21 +21,15 @@
 set BUILD_TYPE=%1
 set VS_VERSION=%2
 
-:: Force usage of python 3.6
-set PATH=C:\python36;"C:\Program Files\cmake-3.23.1-windows-x86_64\bin";%PATH%
+:: Force usage of python 3.12, cmake 3.31.2
+set PATH=C:\python312;c:\cmake-3.31.2\bin;%PATH%
 
 :: #########################################
 :: set up msvc build env
 :: #########################################
-if %VS_VERSION% == 2017 (
-  call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
-  echo "Using VS 2017..."
-
-  :: RE2 does not support VS2017, we we must disable tests.
-  set BUILD_TESTS=NO
-) else if %VS_VERSION% == 2019 (
-  call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
-  echo "Using VS 2019..."
+if %VS_VERSION% == 2022 (
+  call "C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvarsall.bat" x64
+  echo "Using VS 2022..."
 )
 
 cd %SRC%
@@ -63,6 +57,7 @@
   set CMAKE_FLAGS=-DSPIRV_SKIP_TESTS=ON %CMAKE_FLAGS%
 ) 
 
+cmake --version
 cmake %CMAKE_FLAGS% ..
 
 if %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL%
diff --git a/kokoro/shaderc-smoketest/build.sh b/kokoro/shaderc-smoketest/build.sh
index 60c816d..5756e21 100755
--- a/kokoro/shaderc-smoketest/build.sh
+++ b/kokoro/shaderc-smoketest/build.sh
@@ -19,4 +19,4 @@
 set -x
 
 SCRIPT_DIR=`dirname "$BASH_SOURCE"`
-source $SCRIPT_DIR/../scripts/linux/build.sh RELEASE gcc cmake-smoketest
+source $SCRIPT_DIR/../scripts/linux/build.sh RELEASE gcc cmake-shaderc-smoketest
diff --git a/kokoro/windows-msvc-2017-release/continuous.cfg b/kokoro/windows-msvc-2017-release/continuous.cfg
deleted file mode 100644
index a9ac6ec..0000000
--- a/kokoro/windows-msvc-2017-release/continuous.cfg
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright (c) 2018 Google LLC.
-#
-# 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.
-
-# Continuous build configuration.
-build_file: "SPIRV-Tools/kokoro/windows-msvc-2017-release/build.bat"
-
-action {
-  define_artifacts {
-    regex: "install.zip"
-  }
-}
diff --git a/kokoro/windows-msvc-2017-release/presubmit.cfg b/kokoro/windows-msvc-2017-release/presubmit.cfg
deleted file mode 100644
index 5efd429..0000000
--- a/kokoro/windows-msvc-2017-release/presubmit.cfg
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2018 Google LLC.
-#
-# 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.
-
-# Presubmit build configuration.
-build_file: "SPIRV-Tools/kokoro/windows-msvc-2017-release/build.bat"
diff --git a/kokoro/windows-msvc-2019-release/build.bat b/kokoro/windows-msvc-2019-release/build.bat
deleted file mode 100644
index 8212924..0000000
--- a/kokoro/windows-msvc-2019-release/build.bat
+++ /dev/null
@@ -1,24 +0,0 @@
-:: Copyright (c) 2023 Google LLC
-::
-:: 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.
-::
-:: Windows Build Script.
-
-@echo on
-
-:: Find out the directory of the common build script.
-set SCRIPT_DIR=%~dp0
-
-:: Call with correct parameter
-call %SCRIPT_DIR%\..\scripts\windows\build.bat RelWithDebInfo 2019
-
diff --git a/kokoro/windows-msvc-2019-release/presubmit.cfg b/kokoro/windows-msvc-2019-release/presubmit.cfg
deleted file mode 100644
index 4c578e0..0000000
--- a/kokoro/windows-msvc-2019-release/presubmit.cfg
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2023 Google LLC.
-#
-# 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.
-
-# Presubmit build configuration.
-build_file: "SPIRV-Tools/kokoro/windows-msvc-2019-release/build.bat"
diff --git a/kokoro/windows-msvc-2019-debug/build.bat b/kokoro/windows-vs2022-amd64-debug/build.bat
similarity index 92%
rename from kokoro/windows-msvc-2019-debug/build.bat
rename to kokoro/windows-vs2022-amd64-debug/build.bat
index 7ad94c1..cac551c 100644
--- a/kokoro/windows-msvc-2019-debug/build.bat
+++ b/kokoro/windows-vs2022-amd64-debug/build.bat
@@ -20,4 +20,4 @@
 set SCRIPT_DIR=%~dp0
 
 :: Call with correct parameter
-call %SCRIPT_DIR%\..\scripts\windows\build.bat Debug 2019
+call %SCRIPT_DIR%\..\scripts\windows\build.bat Debug 2022
diff --git a/kokoro/windows-msvc-2019-debug/continuous.cfg b/kokoro/windows-vs2022-amd64-debug/continuous.cfg
similarity index 86%
rename from kokoro/windows-msvc-2019-debug/continuous.cfg
rename to kokoro/windows-vs2022-amd64-debug/continuous.cfg
index e3a7863..6aed088 100644
--- a/kokoro/windows-msvc-2019-debug/continuous.cfg
+++ b/kokoro/windows-vs2022-amd64-debug/continuous.cfg
@@ -12,8 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Continuous build configuration.
-build_file: "SPIRV-Tools/kokoro/windows-msvc-2019-debug/build.bat"
+build_file: "SPIRV-Tools/kokoro/windows-vs2022-amd64-debug/build.bat"
 
 action {
   define_artifacts {
diff --git a/kokoro/windows-msvc-2019-debug/presubmit.cfg b/kokoro/windows-vs2022-amd64-debug/presubmit.cfg
similarity index 85%
rename from kokoro/windows-msvc-2019-debug/presubmit.cfg
rename to kokoro/windows-vs2022-amd64-debug/presubmit.cfg
index 0ed3594..69460b7 100644
--- a/kokoro/windows-msvc-2019-debug/presubmit.cfg
+++ b/kokoro/windows-vs2022-amd64-debug/presubmit.cfg
@@ -12,5 +12,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Presubmit build configuration.
-build_file: "SPIRV-Tools/kokoro/windows-msvc-2019-debug/build.bat"
+build_file: "SPIRV-Tools/kokoro/windows-vs2022-amd64-debug/build.bat"
diff --git a/kokoro/windows-msvc-2017-release/build.bat b/kokoro/windows-vs2022-amd64-release/build.bat
similarity index 95%
rename from kokoro/windows-msvc-2017-release/build.bat
rename to kokoro/windows-vs2022-amd64-release/build.bat
index 899fcbc..5294a90 100644
--- a/kokoro/windows-msvc-2017-release/build.bat
+++ b/kokoro/windows-vs2022-amd64-release/build.bat
@@ -1,4 +1,4 @@
-:: Copyright (c) 2018 Google LLC.
+:: Copyright (c) 2025 Google LLC
 ::
 :: Licensed under the Apache License, Version 2.0 (the "License");
 :: you may not use this file except in compliance with the License.
@@ -20,5 +20,5 @@
 set SCRIPT_DIR=%~dp0
 
 :: Call with correct parameter
-call %SCRIPT_DIR%\..\scripts\windows\build.bat RelWithDebInfo 2017
+call %SCRIPT_DIR%\..\scripts\windows\build.bat RelWithDebInfo 2022
 
diff --git a/kokoro/windows-msvc-2019-release/continuous.cfg b/kokoro/windows-vs2022-amd64-release/continuous.cfg
similarity index 81%
rename from kokoro/windows-msvc-2019-release/continuous.cfg
rename to kokoro/windows-vs2022-amd64-release/continuous.cfg
index 624ccbd..977957a 100644
--- a/kokoro/windows-msvc-2019-release/continuous.cfg
+++ b/kokoro/windows-vs2022-amd64-release/continuous.cfg
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 Google LLC.
+# Copyright (c) 2025 Google LLC.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -12,8 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Continuous build configuration.
-build_file: "SPIRV-Tools/kokoro/windows-msvc-2019-release/build.bat"
+build_file: "SPIRV-Tools/kokoro/windows-vs2022-amd64-release/build.bat"
 
 action {
   define_artifacts {
diff --git a/kokoro/check-format/presubmit_check_format.cfg b/kokoro/windows-vs2022-amd64-release/presubmit.cfg
similarity index 81%
copy from kokoro/check-format/presubmit_check_format.cfg
copy to kokoro/windows-vs2022-amd64-release/presubmit.cfg
index 1993289..0cf2b85 100644
--- a/kokoro/check-format/presubmit_check_format.cfg
+++ b/kokoro/windows-vs2022-amd64-release/presubmit.cfg
@@ -1,4 +1,4 @@
-# Copyright (c) 2018 Google LLC.
+# Copyright (c) 2025 Google LLC.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -12,5 +12,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Presubmit build configuration.
-build_file: "SPIRV-Tools/kokoro/check-format/build.sh"
+build_file: "SPIRV-Tools/kokoro/windows-vs2022-amd64-release/build.bat"
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index b20357b..8e918de 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -12,62 +12,65 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-set(GRAMMAR_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_grammar_tables.py")
-set(VIMSYNTAX_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_vim_syntax.py")
+set(VIMSYNTAX_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/vim/generate_syntax.py")
 set(XML_REGISTRY_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_registry_tables.py")
 set(LANG_HEADER_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_language_headers.py")
+set(GGT_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/ggt.py")
 
 # Pull in grammar files that have migrated to SPIRV-Headers
-set(DEBUGINFO_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.debuginfo.grammar.json")
-set(CLDEBUGINFO100_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json")
-set(VKDEBUGINFO100_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.nonsemantic.shader.debuginfo.100.grammar.json")
+set(GRAMMAR_DIR "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1")
+set(SPIRV_CORE_GRAMMAR_JSON_FILE "${GRAMMAR_DIR}/spirv.core.grammar.json")
+set(EI_debuginfo "${GRAMMAR_DIR}/extinst.debuginfo.grammar.json")
+set(EI_cldebuginfo "${GRAMMAR_DIR}/extinst.opencl.debuginfo.100.grammar.json")
+set(EI_glsl "${GRAMMAR_DIR}/extinst.glsl.std.450.grammar.json")
+set(EI_opencl "${GRAMMAR_DIR}/extinst.opencl.std.100.grammar.json")
+set(EI_amd_evp "${GRAMMAR_DIR}/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json")
+set(EI_amd_trimm "${GRAMMAR_DIR}/extinst.spv-amd-shader-trinary-minmax.grammar.json")
+set(EI_amd_gcn "${GRAMMAR_DIR}/extinst.spv-amd-gcn-shader.grammar.json")
+set(EI_amd_ballot "${GRAMMAR_DIR}/extinst.spv-amd-shader-ballot.grammar.json")
+set(EI_ns_debuginfo "${GRAMMAR_DIR}/extinst.nonsemantic.shader.debuginfo.grammar.json")
+set(EI_ns_clspvreflect "${GRAMMAR_DIR}/extinst.nonsemantic.clspvreflection.grammar.json")
+set(EI_ns_vkspreflect "${GRAMMAR_DIR}/extinst.nonsemantic.vkspreflection.grammar.json")
+set(EI_tosa_001000_1 "${GRAMMAR_DIR}/extinst.tosa.001000.1.grammar.json")
+set(EI_arm_motion_engine_100 "${GRAMMAR_DIR}/extinst.arm.motion-engine.100.grammar.json")
 
-# macro() definitions are used in the following because we need to append .inc
-# file paths into some global lists (*_CPP_DEPENDS). And those global lists are
-# later used by set_source_files_properties() calls.
-# function() definitions are not suitable because they create new scopes.
-macro(spvtools_core_tables CONFIG_VERSION)
-  set(GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/spirv.core.grammar.json")
-  set(GRAMMAR_INSTS_INC_FILE "${spirv-tools_BINARY_DIR}/core.insts-${CONFIG_VERSION}.inc")
-  set(GRAMMAR_KINDS_INC_FILE "${spirv-tools_BINARY_DIR}/operand.kinds-${CONFIG_VERSION}.inc")
-  add_custom_command(OUTPUT ${GRAMMAR_INSTS_INC_FILE} ${GRAMMAR_KINDS_INC_FILE}
-    COMMAND Python3::Interpreter ${GRAMMAR_PROCESSING_SCRIPT}
-      --spirv-core-grammar=${GRAMMAR_JSON_FILE}
-      --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE}
-      --extinst-cldebuginfo100-grammar=${CLDEBUGINFO100_GRAMMAR_JSON_FILE}
-      --core-insts-output=${GRAMMAR_INSTS_INC_FILE}
-      --operand-kinds-output=${GRAMMAR_KINDS_INC_FILE}
-      --output-language=c++
-    DEPENDS ${GRAMMAR_PROCESSING_SCRIPT}
-            ${GRAMMAR_JSON_FILE}
-            ${DEBUGINFO_GRAMMAR_JSON_FILE}
-            ${CLDEBUGINFO100_GRAMMAR_JSON_FILE}
-    COMMENT "Generate info tables for SPIR-V v${CONFIG_VERSION} core instructions and operands.")
-  list(APPEND OPCODE_CPP_DEPENDS ${GRAMMAR_INSTS_INC_FILE})
-  list(APPEND OPERAND_CPP_DEPENDS ${GRAMMAR_KINDS_INC_FILE})
-endmacro(spvtools_core_tables)
-
-macro(spvtools_enum_string_mapping CONFIG_VERSION)
-  set(GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/spirv.core.grammar.json")
-  set(GRAMMAR_EXTENSION_ENUM_INC_FILE "${spirv-tools_BINARY_DIR}/extension_enum.inc")
-  set(GRAMMAR_ENUM_STRING_MAPPING_INC_FILE "${spirv-tools_BINARY_DIR}/enum_string_mapping.inc")
-  add_custom_command(OUTPUT ${GRAMMAR_EXTENSION_ENUM_INC_FILE}
-     ${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE}
-    COMMAND Python3::Interpreter ${GRAMMAR_PROCESSING_SCRIPT}
-      --spirv-core-grammar=${GRAMMAR_JSON_FILE}
-      --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE}
-      --extinst-cldebuginfo100-grammar=${CLDEBUGINFO100_GRAMMAR_JSON_FILE}
-      --extension-enum-output=${GRAMMAR_EXTENSION_ENUM_INC_FILE}
-      --enum-string-mapping-output=${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE}
-      --output-language=c++
-    DEPENDS ${GRAMMAR_PROCESSING_SCRIPT}
-            ${GRAMMAR_JSON_FILE}
-            ${DEBUGINFO_GRAMMAR_JSON_FILE}
-            ${CLDEBUGINFO100_GRAMMAR_JSON_FILE}
-    COMMENT "Generate enum-string mapping for SPIR-V v${CONFIG_VERSION}.")
-  list(APPEND EXTENSION_H_DEPENDS ${GRAMMAR_EXTENSION_ENUM_INC_FILE})
-  list(APPEND ENUM_STRING_MAPPING_CPP_DEPENDS ${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE})
-endmacro(spvtools_enum_string_mapping)
+set(CORE_TABLES_BODY_INC_FILE ${spirv-tools_BINARY_DIR}/core_tables_body.inc)
+set(CORE_TABLES_HEADER_INC_FILE ${spirv-tools_BINARY_DIR}/core_tables_header.inc)
+add_custom_command(OUTPUT ${CORE_TABLES_BODY_INC_FILE} ${CORE_TABLES_HEADER_INC_FILE}
+    COMMAND Python3::Interpreter ${GGT_SCRIPT}
+      --core-tables-body-output=${CORE_TABLES_BODY_INC_FILE}
+      --core-tables-header-output=${CORE_TABLES_HEADER_INC_FILE}
+      --spirv-core-grammar=${SPIRV_CORE_GRAMMAR_JSON_FILE}
+      --extinst=,${EI_glsl}
+      --extinst=,${EI_opencl}
+      --extinst=CLDEBUG100_,${EI_cldebuginfo}
+      --extinst=SHDEBUG100_,${EI_ns_debuginfo}
+      --extinst=,${EI_amd_evp}
+      --extinst=,${EI_amd_trimm}
+      --extinst=,${EI_amd_gcn}
+      --extinst=,${EI_amd_ballot}
+      --extinst=,${EI_debuginfo}
+      --extinst=,${EI_ns_clspvreflect}
+      --extinst=,${EI_ns_vkspreflect}
+      --extinst=TOSA_,${EI_tosa_001000_1}
+      --extinst=,${EI_arm_motion_engine_100}
+    DEPENDS ${GGT_SCRIPT}
+            ${SPIRV_CORE_GRAMMAR_JSON_FILE}
+	    ${EI_glsl}
+	    ${EI_opencl}
+	    ${EI_cldebuginfo}
+	    ${EI_ns_debuginfo}
+	    ${EI_amd_evp}
+	    ${EI_amd_trimm}
+	    ${EI_amd_gcn}
+	    ${EI_amd_ballot}
+	    ${EI_debuginfo}
+	    ${EI_ns_clspvreflect}
+	    ${EI_ns_vkspreflect}
+	    ${EI_tosa_001000_1}
+	    ${EI_arm_motion_engine_100}
+    COMMENT "Generate grammar tables")
+add_custom_target(spirv-tools-tables DEPENDS ${CORE_TABLES_BODY_INC_FILE} ${CORE_TABLES_HEADER_INC_FILE})
 
 macro(spvtools_vimsyntax CONFIG_VERSION CLVERSION)
   set(GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/spirv.core.grammar.json")
@@ -77,60 +80,15 @@
   add_custom_command(OUTPUT ${VIMSYNTAX_FILE}
       COMMAND Python3::Interpreter ${VIMSYNTAX_PROCESSING_SCRIPT}
       --spirv-core-grammar=${GRAMMAR_JSON_FILE}
-      --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE}
+      --extinst-debuginfo-grammar=${EI_debuginfo}
       --extinst-glsl-grammar=${GLSL_GRAMMAR_JSON_FILE}
       --extinst-opencl-grammar=${OPENCL_GRAMMAR_JSON_FILE}
       >${VIMSYNTAX_FILE}
     DEPENDS ${VIMSYNTAX_PROCESSING_SCRIPT} ${GRAMMAR_JSON_FILE}
-            ${GLSL_GRAMMAR_JSON_FILE} ${OPENCL_GRAMMAR_JSON_FILE} ${DEBUGINFO_GRAMMAR_JSON_FILE}
+	    ${GLSL_GRAMMAR_JSON_FILE} ${OPENCL_GRAMMAR_JSON_FILE} ${EI_debuinfo}
     COMMENT "Generate spvasm.vim: Vim syntax file for SPIR-V assembly.")
 endmacro(spvtools_vimsyntax)
 
-macro(spvtools_glsl_tables CONFIG_VERSION)
-  set(CORE_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/spirv.core.grammar.json")
-  set(GLSL_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/extinst.glsl.std.450.grammar.json")
-  set(GRAMMAR_INC_FILE "${spirv-tools_BINARY_DIR}/glsl.std.450.insts.inc")
-  add_custom_command(OUTPUT ${GRAMMAR_INC_FILE}
-    COMMAND Python3::Interpreter ${GRAMMAR_PROCESSING_SCRIPT}
-      --extinst-glsl-grammar=${GLSL_GRAMMAR_JSON_FILE}
-      --glsl-insts-output=${GRAMMAR_INC_FILE}
-      --output-language=c++
-    DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${CORE_GRAMMAR_JSON_FILE} ${GLSL_GRAMMAR_JSON_FILE}
-    COMMENT "Generate info tables for GLSL extended instructions and operands v${CONFIG_VERSION}.")
-  list(APPEND EXTINST_CPP_DEPENDS ${GRAMMAR_INC_FILE})
-endmacro(spvtools_glsl_tables)
-
-macro(spvtools_opencl_tables CONFIG_VERSION)
-  set(CORE_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/spirv.core.grammar.json")
-  set(OPENCL_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/extinst.opencl.std.100.grammar.json")
-  set(GRAMMAR_INC_FILE "${spirv-tools_BINARY_DIR}/opencl.std.insts.inc")
-  add_custom_command(OUTPUT ${GRAMMAR_INC_FILE}
-    COMMAND Python3::Interpreter ${GRAMMAR_PROCESSING_SCRIPT}
-      --extinst-opencl-grammar=${OPENCL_GRAMMAR_JSON_FILE}
-      --opencl-insts-output=${GRAMMAR_INC_FILE}
-    DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${CORE_GRAMMAR_JSON_FILE} ${OPENCL_GRAMMAR_JSON_FILE}
-    COMMENT "Generate info tables for OpenCL extended instructions and operands v${CONFIG_VERSION}.")
-  list(APPEND EXTINST_CPP_DEPENDS ${GRAMMAR_INC_FILE})
-endmacro(spvtools_opencl_tables)
-
-macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME OPERAND_KIND_PREFIX)
-  set(INSTS_FILE "${spirv-tools_BINARY_DIR}/${VENDOR_TABLE}.insts.inc")
-  set(GRAMMAR_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.${VENDOR_TABLE}.grammar.json")
-  if(NOT EXISTS ${GRAMMAR_FILE})
-    set(GRAMMAR_FILE "${spirv-tools_SOURCE_DIR}/source/extinst.${VENDOR_TABLE}.grammar.json")
-  endif()
-  add_custom_command(OUTPUT ${INSTS_FILE}
-    COMMAND Python3::Interpreter ${GRAMMAR_PROCESSING_SCRIPT}
-      --extinst-vendor-grammar=${GRAMMAR_FILE}
-      --vendor-insts-output=${INSTS_FILE}
-      --vendor-operand-kind-prefix=${OPERAND_KIND_PREFIX}
-    DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_FILE}
-    COMMENT "Generate extended instruction tables for ${VENDOR_TABLE}.")
-  add_custom_target(spv-tools-${SHORT_NAME} DEPENDS ${INSTS_FILE})
-  set_property(TARGET spv-tools-${SHORT_NAME} PROPERTY FOLDER "SPIRV-Tools build")
-  list(APPEND EXTINST_CPP_DEPENDS spv-tools-${SHORT_NAME})
-endmacro(spvtools_vendor_tables)
-
 macro(spvtools_extinst_lang_headers NAME GRAMMAR_FILE)
   set(OUT_H ${spirv-tools_BINARY_DIR}/${NAME}.h)
   add_custom_command(OUTPUT ${OUT_H}
@@ -144,22 +102,8 @@
   list(APPEND EXTINST_CPP_DEPENDS spirv-tools-header-${NAME})
 endmacro(spvtools_extinst_lang_headers)
 
-spvtools_core_tables("unified1")
-spvtools_enum_string_mapping("unified1")
-spvtools_opencl_tables("unified1")
-spvtools_glsl_tables("unified1")
-spvtools_vendor_tables("spv-amd-shader-explicit-vertex-parameter" "spv-amd-sevp" "")
-spvtools_vendor_tables("spv-amd-shader-trinary-minmax" "spv-amd-stm" "")
-spvtools_vendor_tables("spv-amd-gcn-shader" "spv-amd-gs" "")
-spvtools_vendor_tables("spv-amd-shader-ballot" "spv-amd-sb" "")
-spvtools_vendor_tables("debuginfo" "debuginfo" "")
-spvtools_vendor_tables("opencl.debuginfo.100" "cldi100" "CLDEBUG100_")
-spvtools_vendor_tables("nonsemantic.shader.debuginfo.100" "shdi100" "SHDEBUG100_")
-spvtools_vendor_tables("nonsemantic.clspvreflection" "clspvreflection" "")
-spvtools_vendor_tables("nonsemantic.vkspreflection" "vkspreflection" "")
-spvtools_extinst_lang_headers("DebugInfo" ${DEBUGINFO_GRAMMAR_JSON_FILE})
-spvtools_extinst_lang_headers("OpenCLDebugInfo100" ${CLDEBUGINFO100_GRAMMAR_JSON_FILE})
-spvtools_extinst_lang_headers("NonSemanticShaderDebugInfo100" ${VKDEBUGINFO100_GRAMMAR_JSON_FILE})
+spvtools_extinst_lang_headers("DebugInfo" ${EI_debuginfo})
+spvtools_extinst_lang_headers("OpenCLDebugInfo100" ${EI_cldebuginfo})
 
 spvtools_vimsyntax("unified1" "1.0")
 add_custom_target(spirv-tools-vimsyntax DEPENDS ${VIMSYNTAX_FILE})
@@ -182,9 +126,8 @@
 # We need to wrap the .inc files with a custom target to avoid problems when
 # multiple targets depend on the same custom command.
 add_custom_target(core_tables
-  DEPENDS ${OPCODE_CPP_DEPENDS} ${OPERAND_CPP_DEPENDS})
-add_custom_target(enum_string_mapping
-  DEPENDS ${EXTENSION_H_DEPENDS} ${ENUM_STRING_MAPPING_CPP_DEPENDS})
+  DEPENDS ${OPCODE_CPP_DEPENDS}
+          spirv-tools-tables)
 add_custom_target(extinst_tables
   DEPENDS ${EXTINST_CPP_DEPENDS})
 
@@ -205,13 +148,12 @@
    DEPENDS ${SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR}
            ${SPIRV_TOOLS_CHANGES_FILE}
    COMMENT "Update build-version.inc in the SPIRV-Tools build directory (if necessary).")
-# Convenience target for standalone generation of the build-version.inc file.
-# This is not required for any dependence chain.
-add_custom_target(spirv-tools-build-version
-   DEPENDS ${SPIRV_TOOLS_BUILD_VERSION_INC})
-set_property(TARGET spirv-tools-build-version PROPERTY FOLDER "SPIRV-Tools build")
 
-list(APPEND PCH_DEPENDS ${ENUM_STRING_MAPPING_CPP_DEPENDS} ${OPCODE_CPP_DEPENDS} ${OPERAND_CPP_DEPENDS} ${EXTENSION_H_DEPENDS} ${EXTINST_CPP_DEPENDS} ${SPIRV_TOOLS_BUILD_VERSION_INC})
+list(APPEND PCH_DEPENDS
+	${CORE_TABLES_HEADER_INC_FILE}
+	${OPCODE_CPP_DEPENDS}
+	${EXTINST_CPP_DEPENDS}
+	${SPIRV_TOOLS_BUILD_VERSION_INC})
 set_source_files_properties(
   ${CMAKE_CURRENT_SOURCE_DIR}/pch_source.cpp
   PROPERTIES OBJECT_DEPENDS "${PCH_DEPENDS}")
@@ -242,7 +184,6 @@
   ${CMAKE_CURRENT_SOURCE_DIR}/diagnostic.h
   ${CMAKE_CURRENT_SOURCE_DIR}/disassemble.h
   ${CMAKE_CURRENT_SOURCE_DIR}/enum_set.h
-  ${CMAKE_CURRENT_SOURCE_DIR}/enum_string_mapping.h
   ${CMAKE_CURRENT_SOURCE_DIR}/ext_inst.h
   ${CMAKE_CURRENT_SOURCE_DIR}/extensions.h
   ${CMAKE_CURRENT_SOURCE_DIR}/instruction.h
@@ -264,6 +205,7 @@
   ${CMAKE_CURRENT_SOURCE_DIR}/spirv_target_env.h
   ${CMAKE_CURRENT_SOURCE_DIR}/spirv_validator_options.h
   ${CMAKE_CURRENT_SOURCE_DIR}/table.h
+  ${CMAKE_CURRENT_SOURCE_DIR}/table2.h
   ${CMAKE_CURRENT_SOURCE_DIR}/text.h
   ${CMAKE_CURRENT_SOURCE_DIR}/text_handler.h
   ${CMAKE_CURRENT_SOURCE_DIR}/to_string.h
@@ -276,7 +218,6 @@
   ${CMAKE_CURRENT_SOURCE_DIR}/binary.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/diagnostic.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/disassemble.cpp
-  ${CMAKE_CURRENT_SOURCE_DIR}/enum_string_mapping.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/ext_inst.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/extensions.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/libspirv.cpp
@@ -293,6 +234,7 @@
   ${CMAKE_CURRENT_SOURCE_DIR}/spirv_target_env.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/spirv_validator_options.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/table.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/table2.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/text.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/text_handler.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/to_string.cpp
@@ -312,9 +254,12 @@
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_debug.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_decorations.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_derivatives.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_dot_product.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_extensions.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_execution_limitations.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_function.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_graph.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_group.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_id.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_image.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_interfaces.cpp
@@ -322,12 +267,14 @@
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_layout.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_literals.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_logicals.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_logical_pointers.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_memory.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_memory_semantics.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_mesh_shading.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_misc.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_mode_setting.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_non_uniform.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_pipe.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_primitives.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_ray_query.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_ray_tracing.cpp
@@ -335,7 +282,9 @@
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_scopes.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_small_type_uses.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_tensor_layout.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_tensor.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_type.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_invalid_type.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/decoration.h
   ${CMAKE_CURRENT_SOURCE_DIR}/val/basic_block.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/construct.cpp
@@ -349,6 +298,12 @@
     ${CMAKE_CURRENT_SOURCE_DIR}/util/timer.cpp)
 endif()
 
+if (SPIRV_TOOLS_USE_MIMALLOC AND NOT SPIRV_TOOLS_BUILD_STATIC)
+  set(SPIRV_SOURCES
+      ${SPIRV_SOURCES}
+      ${CMAKE_CURRENT_SOURCE_DIR}/mimalloc.cpp)
+endif()
+
 # The software_version.cpp file includes build-version.inc.
 # Rebuild the software_version.cpp object file if it is older than
 # build-version.inc or whenever build-version.inc itself is out of
@@ -379,12 +334,15 @@
   )
   set_property(TARGET ${target} PROPERTY FOLDER "SPIRV-Tools libraries")
   spvtools_check_symbol_exports(${target})
-  add_dependencies(${target} spirv-tools-build-version core_tables enum_string_mapping extinst_tables)
+  add_dependencies(${target} core_tables extinst_tables)
 endfunction()
 
 # Always build ${SPIRV_TOOLS}-shared. This is expected distro packages, and
 # unlike the other SPIRV_TOOLS target, defaults to hidden symbol visibility.
 add_library(${SPIRV_TOOLS}-shared SHARED ${SPIRV_SOURCES})
+if (SPIRV_TOOLS_USE_MIMALLOC)
+  target_link_libraries(${SPIRV_TOOLS}-shared PRIVATE mimalloc-static)
+endif()
 spirv_tools_default_target_options(${SPIRV_TOOLS}-shared)
 set_target_properties(${SPIRV_TOOLS}-shared PROPERTIES CXX_VISIBILITY_PRESET hidden)
 target_compile_definitions(${SPIRV_TOOLS}-shared
@@ -394,6 +352,9 @@
 
 if(SPIRV_TOOLS_BUILD_STATIC)
   add_library(${SPIRV_TOOLS}-static STATIC ${SPIRV_SOURCES})
+  if (SPIRV_TOOLS_USE_MIMALLOC AND SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD)
+    target_link_libraries(${SPIRV_TOOLS}-shared PRIVATE mimalloc-static)
+  endif()
   spirv_tools_default_target_options(${SPIRV_TOOLS}-static)
   # The static target does not have the '-static' suffix.
   set_target_properties(${SPIRV_TOOLS}-static PROPERTIES OUTPUT_NAME "${SPIRV_TOOLS}")
@@ -409,6 +370,9 @@
   set(SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS}-static ${SPIRV_TOOLS}-shared)
 else()
   add_library(${SPIRV_TOOLS} ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_SOURCES})
+  if (SPIRV_TOOLS_USE_MIMALLOC)
+    target_link_libraries(${SPIRV_TOOLS} PRIVATE mimalloc-static)
+  endif()
   spirv_tools_default_target_options(${SPIRV_TOOLS})
   set(SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS} ${SPIRV_TOOLS}-shared)
 endif()
@@ -417,12 +381,15 @@
   find_library(LIBRT rt)
   if(LIBRT)
     foreach(target ${SPIRV_TOOLS_TARGETS})
-      target_link_libraries(${target} rt)
+      target_link_libraries(${target} PUBLIC rt)
     endforeach()
   endif()
 endif()
 
 if(ENABLE_SPIRV_TOOLS_INSTALL)
+  if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD))
+    list(APPEND SPIRV_TOOLS_TARGETS mimalloc-static)
+  endif()
   install(TARGETS ${SPIRV_TOOLS_TARGETS} EXPORT ${SPIRV_TOOLS}Targets)
   export(EXPORT ${SPIRV_TOOLS}Targets FILE ${SPIRV_TOOLS}Target.cmake)
 
@@ -438,8 +405,3 @@
     "endif()\n")
   install(FILES ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake DESTINATION ${PACKAGE_DIR})
 endif(ENABLE_SPIRV_TOOLS_INSTALL)
-
-if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")))
-  # Enable parallel builds across four cores for this lib
-  add_definitions(/MP4)
-endif()
diff --git a/source/assembly_grammar.cpp b/source/assembly_grammar.cpp
index 0092d01..4886ee6 100644
--- a/source/assembly_grammar.cpp
+++ b/source/assembly_grammar.cpp
@@ -23,6 +23,7 @@
 #include "source/operand.h"
 #include "source/spirv_target_env.h"
 #include "source/table.h"
+#include "source/table2.h"
 
 namespace spvtools {
 namespace {
@@ -35,15 +36,12 @@
 ///
 /// On success, the value is written to pValue.
 ///
-/// @param[in] operandTable operand lookup table
 /// @param[in] type of the operand
 /// @param[in] textValue word of text to be parsed
 /// @param[out] pValue where the resulting value is written
 ///
 /// @return result code
-spv_result_t spvTextParseMaskOperand(spv_target_env env,
-                                     const spv_operand_table operandTable,
-                                     const spv_operand_type_t type,
+spv_result_t spvTextParseMaskOperand(const spv_operand_type_t type,
                                      const char* textValue, uint32_t* pValue) {
   if (textValue == nullptr) return SPV_ERROR_INVALID_TEXT;
   size_t text_length = strlen(textValue);
@@ -62,9 +60,9 @@
   do {
     end = std::find(begin, text_end, separator);
 
-    spv_operand_desc entry = nullptr;
-    if (auto error = spvOperandTableNameLookup(env, operandTable, type, begin,
-                                               end - begin, &entry)) {
+    const spvtools::OperandDesc* entry = nullptr;
+    if (auto error =
+            spvtools::LookupOperand(type, begin, end - begin, &entry)) {
       return error;
     }
     value |= entry->value;
@@ -170,23 +168,20 @@
 
 }  // namespace
 
-bool AssemblyGrammar::isValid() const {
-  return operandTable_ && opcodeTable_ && extInstTable_;
-}
-
 CapabilitySet AssemblyGrammar::filterCapsAgainstTargetEnv(
     const spv::Capability* cap_array, uint32_t count) const {
   CapabilitySet cap_set;
   const auto version = spvVersionForTargetEnv(target_env_);
   for (uint32_t i = 0; i < count; ++i) {
-    spv_operand_desc entry = {};
-    if (SPV_SUCCESS == lookupOperand(SPV_OPERAND_TYPE_CAPABILITY,
-                                     static_cast<uint32_t>(cap_array[i]),
-                                     &entry)) {
+    const spvtools::OperandDesc* entry = nullptr;
+    if (SPV_SUCCESS ==
+        spvtools::LookupOperand(SPV_OPERAND_TYPE_CAPABILITY,
+                                static_cast<uint32_t>(cap_array[i]), &entry)) {
       // This token is visible in this environment if it's in an appropriate
       // core version, or it is enabled by a capability or an extension.
       if ((version >= entry->minVersion && version <= entry->lastVersion) ||
-          entry->numExtensions > 0u || entry->numCapabilities > 0u) {
+          entry->extensions_range.count() > 0u ||
+          entry->capabilities_range.count() > 0u) {
         cap_set.insert(cap_array[i]);
       }
     }
@@ -194,28 +189,13 @@
   return cap_set;
 }
 
-spv_result_t AssemblyGrammar::lookupOpcode(const char* name,
-                                           spv_opcode_desc* desc) const {
-  return spvOpcodeTableNameLookup(target_env_, opcodeTable_, name, desc);
-}
-
-spv_result_t AssemblyGrammar::lookupOpcode(spv::Op opcode,
-                                           spv_opcode_desc* desc) const {
-  return spvOpcodeTableValueLookup(target_env_, opcodeTable_, opcode, desc);
-}
-
-spv_result_t AssemblyGrammar::lookupOperand(spv_operand_type_t type,
-                                            const char* name, size_t name_len,
-                                            spv_operand_desc* desc) const {
-  return spvOperandTableNameLookup(target_env_, operandTable_, type, name,
-                                   name_len, desc);
-}
-
-spv_result_t AssemblyGrammar::lookupOperand(spv_operand_type_t type,
-                                            uint32_t operand,
-                                            spv_operand_desc* desc) const {
-  return spvOperandTableValueLookup(target_env_, operandTable_, type, operand,
-                                    desc);
+const char* AssemblyGrammar::lookupOperandName(spv_operand_type_t type,
+                                               uint32_t operand) const {
+  const spvtools::OperandDesc* desc = nullptr;
+  if (spvtools::LookupOperand(type, operand, &desc) != SPV_SUCCESS || !desc) {
+    return "Unknown";
+  }
+  return desc->name().data();
 }
 
 spv_result_t AssemblyGrammar::lookupSpecConstantOpcode(const char* name,
@@ -245,25 +225,13 @@
 spv_result_t AssemblyGrammar::parseMaskOperand(const spv_operand_type_t type,
                                                const char* textValue,
                                                uint32_t* pValue) const {
-  return spvTextParseMaskOperand(target_env_, operandTable_, type, textValue,
-                                 pValue);
-}
-spv_result_t AssemblyGrammar::lookupExtInst(spv_ext_inst_type_t type,
-                                            const char* textValue,
-                                            spv_ext_inst_desc* extInst) const {
-  return spvExtInstTableNameLookup(extInstTable_, type, textValue, extInst);
-}
-
-spv_result_t AssemblyGrammar::lookupExtInst(spv_ext_inst_type_t type,
-                                            uint32_t firstWord,
-                                            spv_ext_inst_desc* extInst) const {
-  return spvExtInstTableValueLookup(extInstTable_, type, firstWord, extInst);
+  return spvTextParseMaskOperand(type, textValue, pValue);
 }
 
 void AssemblyGrammar::pushOperandTypesForMask(
     const spv_operand_type_t type, const uint32_t mask,
     spv_operand_pattern_t* pattern) const {
-  spvPushOperandTypesForMask(target_env_, operandTable_, type, mask, pattern);
+  spvPushOperandTypesForMask(type, mask, pattern);
 }
 
 }  // namespace spvtools
diff --git a/source/assembly_grammar.h b/source/assembly_grammar.h
index 36fdd08..08e10e7 100644
--- a/source/assembly_grammar.h
+++ b/source/assembly_grammar.h
@@ -19,6 +19,7 @@
 #include "source/latest_version_spirv_header.h"
 #include "source/operand.h"
 #include "source/table.h"
+#include "source/util/span.h"
 #include "spirv-tools/libspirv.h"
 
 namespace spvtools {
@@ -28,54 +29,28 @@
 class AssemblyGrammar {
  public:
   explicit AssemblyGrammar(const spv_const_context context)
-      : target_env_(context->target_env),
-        operandTable_(context->operand_table),
-        opcodeTable_(context->opcode_table),
-        extInstTable_(context->ext_inst_table) {}
-
-  // Returns true if the internal tables have been initialized with valid data.
-  bool isValid() const;
+      : target_env_(context->target_env) {}
 
   // Returns the SPIR-V target environment.
   spv_target_env target_env() const { return target_env_; }
 
   // Removes capabilities not available in the current target environment and
   // returns the rest.
+  // TODO(crbug.com/266223071) Remove this.
   CapabilitySet filterCapsAgainstTargetEnv(const spv::Capability* cap_array,
                                            uint32_t count) const;
-
-  // Fills in the desc parameter with the information about the opcode
-  // of the given name. Returns SPV_SUCCESS if the opcode was found, and
-  // SPV_ERROR_INVALID_LOOKUP if the opcode does not exist.
-  spv_result_t lookupOpcode(const char* name, spv_opcode_desc* desc) const;
-
-  // Fills in the desc parameter with the information about the opcode
-  // of the valid. Returns SPV_SUCCESS if the opcode was found, and
-  // SPV_ERROR_INVALID_LOOKUP if the opcode does not exist.
-  spv_result_t lookupOpcode(spv::Op opcode, spv_opcode_desc* desc) const;
-
-  // Fills in the desc parameter with the information about the given
-  // operand. Returns SPV_SUCCESS if the operand was found, and
-  // SPV_ERROR_INVALID_LOOKUP otherwise.
-  spv_result_t lookupOperand(spv_operand_type_t type, const char* name,
-                             size_t name_len, spv_operand_desc* desc) const;
-
-  // Fills in the desc parameter with the information about the given
-  // operand. Returns SPV_SUCCESS if the operand was found, and
-  // SPV_ERROR_INVALID_LOOKUP otherwise.
-  spv_result_t lookupOperand(spv_operand_type_t type, uint32_t operand,
-                             spv_operand_desc* desc) const;
+  // Removes capabilities not available in the current target environment and
+  // returns the rest.
+  CapabilitySet filterCapsAgainstTargetEnv(
+      const spvtools::utils::Span<const spv::Capability>& caps) const {
+    return filterCapsAgainstTargetEnv(caps.begin(),
+                                      static_cast<uint32_t>(caps.size()));
+  }
 
   // Finds operand entry in the grammar table and returns its name.
   // Returns "Unknown" if not found.
   const char* lookupOperandName(spv_operand_type_t type,
-                                uint32_t operand) const {
-    spv_operand_desc desc = nullptr;
-    if (lookupOperand(type, operand, &desc) != SPV_SUCCESS || !desc) {
-      return "Unknown";
-    }
-    return desc->name;
-  }
+                                uint32_t operand) const;
 
   // Finds the opcode for the given OpSpecConstantOp opcode name. The name
   // should not have the "Op" prefix.  For example, "IAdd" corresponds to
@@ -101,18 +76,6 @@
   spv_result_t parseMaskOperand(const spv_operand_type_t type,
                                 const char* textValue, uint32_t* pValue) const;
 
-  // Writes the extended operand with the given type and text to the *extInst
-  // parameter.
-  // Returns SPV_SUCCESS if the value could be found.
-  spv_result_t lookupExtInst(spv_ext_inst_type_t type, const char* textValue,
-                             spv_ext_inst_desc* extInst) const;
-
-  // Writes the extended operand with the given type and first encoded word
-  // to the *extInst parameter.
-  // Returns SPV_SUCCESS if the value could be found.
-  spv_result_t lookupExtInst(spv_ext_inst_type_t type, uint32_t firstWord,
-                             spv_ext_inst_desc* extInst) const;
-
   // Inserts the operands expected after the given typed mask onto the end
   // of the given pattern.
   //
@@ -129,9 +92,6 @@
 
  private:
   const spv_target_env target_env_;
-  const spv_operand_table operandTable_;
-  const spv_opcode_table opcodeTable_;
-  const spv_ext_inst_table extInstTable_;
 };
 
 }  // namespace spvtools
diff --git a/source/binary.cpp b/source/binary.cpp
index ed57498..01ab65c 100644
--- a/source/binary.cpp
+++ b/source/binary.cpp
@@ -33,6 +33,7 @@
 #include "source/operand.h"
 #include "source/spirv_constant.h"
 #include "source/spirv_endian.h"
+#include "source/table2.h"
 #include "source/util/string_utils.h"
 
 spv_result_t spvBinaryHeaderGet(const spv_const_binary binary,
@@ -94,6 +95,13 @@
   spv_result_t parse(const uint32_t* words, size_t num_words,
                      spv_diagnostic* diagnostic);
 
+  // Sets whether to handle, rather than reject, unrecognized content:
+  // unknown opcodes, unknown extended instruction numbers in semantic sets,
+  // and known opcodes with unknown enum operands.  When set, unknown
+  // instructions are re-emitted as raw OpUnknown data instead of returning
+  // an error.
+  void SetHandleUnknownOpcodes(bool value) { handle_unknown_opcodes_ = value; }
+
  private:
   // All remaining methods work on the current module parse state.
 
@@ -184,11 +192,15 @@
   const spv_parsed_header_fn_t parsed_header_fn_;  // Parsed header callback
   const spv_parsed_instruction_fn_t
       parsed_instruction_fn_;  // Parsed instruction callback
+  // When true, unrecognized opcodes, ext inst numbers, and enum operands are
+  // passed to the callback as raw OpUnknown data instead of returning an error.
+  bool handle_unknown_opcodes_ = false;
 
   // Describes the format of a typed literal number.
   struct NumberType {
     spv_number_kind_t type;
     uint32_t bit_width;
+    spv_fp_encoding_t encoding;
   };
 
   // The state used to parse a single SPIR-V binary module.
@@ -218,6 +230,11 @@
     // Is the SPIR-V binary in a different endianness from the host native
     // endianness?
     bool requires_endian_conversion;
+    // Set by parseOperand when LookupOperand fails for an enum operand and
+    // handle_unknown_opcodes_ is set.  Signals parseInstruction to discard
+    // the partially-decoded instruction and re-emit it as raw OpUnknown data.
+    // Cleared by parseInstruction immediately before calling emitAsUnknown.
+    bool retry_instruction_as_unknown_ = false;
 
     // Maps a result ID to its type ID.  By convention:
     //  - a result ID that is a type definition maps to itself.
@@ -317,15 +334,50 @@
     return diagnostic() << "Invalid instruction word count: "
                         << inst_word_count;
   }
-  spv_opcode_desc opcode_desc;
-  if (grammar_.lookupOpcode(static_cast<spv::Op>(inst.opcode), &opcode_desc))
+  const spvtools::InstructionDesc* opcode_desc = nullptr;
+  const bool opcode_known =
+      spvtools::LookupOpcode(static_cast<spv::Op>(inst.opcode), &opcode_desc) ==
+      SPV_SUCCESS;
+  if (!opcode_known && !handle_unknown_opcodes_)
     return diagnostic() << "Invalid opcode: " << inst.opcode;
 
-  // Advance past the opcode word.  But remember the of the start
-  // of the instruction.
+  // Advance past the opcode word.  But remember the start of the instruction.
   const size_t inst_offset = _.word_index;
   _.word_index++;
 
+  // Emits the instruction at inst_offset as raw data with no decoded operands.
+  auto emitAsUnknown = [&]() -> spv_result_t {
+    if (inst_offset + inst_word_count > _.num_words) {
+      return diagnostic() << "Truncated binary: instruction at word "
+                          << inst_offset << " claims " << inst_word_count
+                          << " words but binary ends at " << _.num_words;
+    }
+    // Repopulate endian_converted_words from scratch.  The operand loop may
+    // have partially filled it before the unknown enum was detected.
+    _.endian_converted_words.clear();
+    _.endian_converted_words.push_back(first_word);
+    if (_.requires_endian_conversion) {
+      for (uint16_t i = 1; i < inst_word_count; i++) {
+        _.endian_converted_words.push_back(peekAt(inst_offset + i));
+      }
+    }
+    _.word_index = inst_offset + inst_word_count;
+    inst.words = _.requires_endian_conversion ? _.endian_converted_words.data()
+                                              : _.words + inst_offset;
+    inst.num_words = inst_word_count;
+    _.operands.clear();
+    inst.operands = _.operands.data();
+    inst.num_operands = 0;
+    if (parsed_instruction_fn_) {
+      if (auto error = parsed_instruction_fn_(user_data_, &inst)) return error;
+    }
+    return SPV_SUCCESS;
+  };
+
+  if (!opcode_known) {
+    return emitAsUnknown();
+  }
+
   // Maintains the ordered list of expected operand types.
   // For many instructions we only need the {numTypes, operandTypes}
   // entries in opcode_desc.  However, sometimes we need to modify
@@ -334,16 +386,15 @@
   // ExecutionMode), or for extended instructions that may have their
   // own operands depending on the selected extended instruction.
   _.expected_operands.clear();
-  for (auto i = 0; i < opcode_desc->numTypes; i++)
-    _.expected_operands.push_back(
-        opcode_desc->operandTypes[opcode_desc->numTypes - i - 1]);
+
+  spvPushOperandTypes(opcode_desc->operands(), &_.expected_operands);
 
   while (_.word_index < inst_offset + inst_word_count) {
     const uint16_t inst_word_index = uint16_t(_.word_index - inst_offset);
     if (_.expected_operands.empty()) {
-      return diagnostic() << "Invalid instruction Op" << opcode_desc->name
-                          << " starting at word " << inst_offset
-                          << ": expected no more operands after "
+      return diagnostic() << "Invalid instruction Op"
+                          << opcode_desc->name().data() << " starting at word "
+                          << inst_offset << ": expected no more operands after "
                           << inst_word_index
                           << " words, but stated word count is "
                           << inst_word_count << ".";
@@ -355,6 +406,10 @@
     if (auto error =
             parseOperand(inst_offset, &inst, type, &_.endian_converted_words,
                          &_.operands, &_.expected_operands)) {
+      if (_.retry_instruction_as_unknown_) {
+        _.retry_instruction_as_unknown_ = false;
+        return emitAsUnknown();
+      }
       return error;
     }
   }
@@ -362,15 +417,15 @@
   if (!_.expected_operands.empty() &&
       !spvOperandIsOptional(_.expected_operands.back())) {
     return diagnostic() << "End of input reached while decoding Op"
-                        << opcode_desc->name << " starting at word "
+                        << opcode_desc->name().data() << " starting at word "
                         << inst_offset << ": expected more operands after "
                         << inst_word_count << " words.";
   }
 
   if ((inst_offset + inst_word_count) != _.word_index) {
-    return diagnostic() << "Invalid word count: Op" << opcode_desc->name
-                        << " starting at word " << inst_offset
-                        << " says it has " << inst_word_count
+    return diagnostic() << "Invalid word count: Op"
+                        << opcode_desc->name().data() << " starting at word "
+                        << inst_offset << " says it has " << inst_word_count
                         << " words, but found " << _.word_index - inst_offset
                         << " words instead.";
   }
@@ -385,8 +440,6 @@
   assert(_.requires_endian_conversion ||
          (_.endian_converted_words.size() == 1));
 
-  recordNumberType(inst_offset, &inst);
-
   if (_.requires_endian_conversion) {
     // We must wait until here to set this pointer, because the vector might
     // have been be resized while we accumulated its elements.
@@ -398,6 +451,8 @@
   }
   inst.num_words = inst_word_count;
 
+  recordNumberType(inst_offset, &inst);
+
   // We must wait until here to set this pointer, because the vector might
   // have been be resized while we accumulated its elements.
   inst.operands = _.operands.data();
@@ -496,22 +551,33 @@
     case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: {
       assert(spvIsExtendedInstruction(opcode));
       assert(inst->ext_inst_type != SPV_EXT_INST_TYPE_NONE);
-      spv_ext_inst_desc ext_inst;
-      if (grammar_.lookupExtInst(inst->ext_inst_type, word, &ext_inst) ==
+
+      const spvtools::ExtInstDesc* desc = nullptr;
+      if (spvtools::LookupExtInst(inst->ext_inst_type, word, &desc) ==
           SPV_SUCCESS) {
-        // if we know about this ext inst, push the expected operands
-        spvPushOperandTypes(ext_inst->operandTypes, expected_operands);
-      } else {
-        // if we don't know this extended instruction and the set isn't
-        // non-semantic, we cannot process further
-        if (!spvExtInstIsNonSemantic(inst->ext_inst_type)) {
-          return diagnostic()
-                 << "Invalid extended instruction number: " << word;
-        } else {
-          // for non-semantic instruction sets, we know the form of all such
-          // extended instructions contains a series of IDs as parameters
+        // Push VARIABLE_ID so extra trailing operands from future NSDI
+        // versions are silently absorbed after the instruction-specific ones.
+        if (spvExtInstIsNonSemantic(inst->ext_inst_type)) {
           expected_operands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID);
         }
+
+        // if we know about this ext inst, push the expected operands
+        spvPushOperandTypes(desc->operands(), expected_operands);
+      } else {
+        // If we don't know this extended instruction and the set is semantic,
+        // fail unless handle_unknown_opcodes_ is set.  For non-semantic sets,
+        // always continue regardless of the flag. In both non-error cases the
+        // remaining operands are exposed as variable IDs. For non-semantic
+        // sets the disassembler emits the instruction via its normal operand
+        // loop; for semantic sets with handle_unknown_opcodes_ set, the
+        // disassembler independently detects the unknown number via
+        // LookupExtInst and emits the entire instruction as OpUnknown.
+        if (!spvExtInstIsNonSemantic(inst->ext_inst_type) &&
+            !handle_unknown_opcodes_) {
+          return diagnostic()
+                 << "Invalid extended instruction number: " << word;
+        }
+        expected_operands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID);
       }
     } break;
 
@@ -522,8 +588,8 @@
         return diagnostic()
                << "Invalid " << spvOperandTypeStr(type) << ": " << word;
       }
-      spv_opcode_desc opcode_entry = nullptr;
-      if (grammar_.lookupOpcode(spv::Op(word), &opcode_entry)) {
+      const spvtools::InstructionDesc* opcode_entry = nullptr;
+      if (spvtools::LookupOpcode(spv::Op(word), &opcode_entry)) {
         return diagnostic(SPV_ERROR_INTERNAL)
                << "OpSpecConstant opcode table out of sync";
       }
@@ -532,8 +598,9 @@
       // operants for the opcode.
       assert(opcode_entry->hasType);
       assert(opcode_entry->hasResult);
-      assert(opcode_entry->numTypes >= 2);
-      spvPushOperandTypes(opcode_entry->operandTypes + 2, expected_operands);
+      assert(opcode_entry->operands().size() >= 2);
+      spvPushOperandTypes(opcode_entry->operands().subspan(2),
+                          expected_operands);
     } break;
 
     case SPV_OPERAND_TYPE_LITERAL_INTEGER:
@@ -633,6 +700,7 @@
     } break;
 
     case SPV_OPERAND_TYPE_CAPABILITY:
+    case SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY:
     case SPV_OPERAND_TYPE_EXECUTION_MODEL:
     case SPV_OPERAND_TYPE_ADDRESSING_MODEL:
     case SPV_OPERAND_TYPE_MEMORY_MODEL:
@@ -686,20 +754,24 @@
         parsed_operand.type = SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT;
       if (type == SPV_OPERAND_TYPE_OPTIONAL_FPENCODING)
         parsed_operand.type = SPV_OPERAND_TYPE_FPENCODING;
+      if (type == SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY)
+        parsed_operand.type = SPV_OPERAND_TYPE_CAPABILITY;
 
-      spv_operand_desc entry;
-      if (grammar_.lookupOperand(type, word, &entry)) {
+      const spvtools::OperandDesc* entry = nullptr;
+      if (spvtools::LookupOperand(type, word, &entry)) {
+        if (handle_unknown_opcodes_) _.retry_instruction_as_unknown_ = true;
         return diagnostic()
                << "Invalid " << spvOperandTypeStr(parsed_operand.type)
                << " operand: " << word;
       }
       // Prepare to accept operands to this operand, if needed.
-      spvPushOperandTypes(entry->operandTypes, expected_operands);
+      spvPushOperandTypes(entry->operands(), expected_operands);
     } break;
 
     case SPV_OPERAND_TYPE_SOURCE_LANGUAGE: {
-      spv_operand_desc entry;
-      if (grammar_.lookupOperand(type, word, &entry)) {
+      const spvtools::OperandDesc* entry = nullptr;
+      if (spvtools::LookupOperand(type, word, &entry)) {
+        if (handle_unknown_opcodes_) _.retry_instruction_as_unknown_ = true;
         return diagnostic()
                << "Invalid " << spvOperandTypeStr(parsed_operand.type)
                << " operand: " << word
@@ -709,7 +781,7 @@
                   "SPIRV-Headers";
       }
       // Prepare to accept operands to this operand, if needed.
-      spvPushOperandTypes(entry->operandTypes, expected_operands);
+      spvPushOperandTypes(entry->operands(), expected_operands);
     } break;
 
     case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE:
@@ -718,6 +790,8 @@
     case SPV_OPERAND_TYPE_IMAGE:
     case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
     case SPV_OPERAND_TYPE_MEMORY_ACCESS:
+    case SPV_OPERAND_TYPE_TENSOR_OPERANDS:
+    case SPV_OPERAND_TYPE_OPTIONAL_TENSOR_OPERANDS:
     case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
     case SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS:
     case SPV_OPERAND_TYPE_SELECTION_CONTROL:
@@ -726,7 +800,9 @@
     case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS:
     case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS:
     case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_REDUCE:
-    case SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS: {
+    case SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS:
+    case SPV_OPERAND_TYPE_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS:
+    case SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS: {
       // This operand is a mask.
 
       // Map an optional operand type to its corresponding concrete type.
@@ -738,6 +814,11 @@
         parsed_operand.type = SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS;
       if (type == SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS)
         parsed_operand.type = SPV_OPERAND_TYPE_RAW_ACCESS_CHAIN_OPERANDS;
+      if (type == SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS)
+        parsed_operand.type =
+            SPV_OPERAND_TYPE_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS;
+      if (type == SPV_OPERAND_TYPE_OPTIONAL_TENSOR_OPERANDS)
+        parsed_operand.type = SPV_OPERAND_TYPE_TENSOR_OPERANDS;
 
       // Check validity of set mask bits. Also prepare for operands for those
       // masks if they have any.  To get operand order correct, scan from
@@ -748,23 +829,24 @@
       uint32_t remaining_word = word;
       for (uint32_t mask = (1u << 31); remaining_word; mask >>= 1) {
         if (remaining_word & mask) {
-          spv_operand_desc entry;
-          if (grammar_.lookupOperand(type, mask, &entry)) {
+          const spvtools::OperandDesc* entry = nullptr;
+          if (spvtools::LookupOperand(type, mask, &entry)) {
+            if (handle_unknown_opcodes_) _.retry_instruction_as_unknown_ = true;
             return diagnostic()
                    << "Invalid " << spvOperandTypeStr(parsed_operand.type)
                    << " operand: " << word << " has invalid mask component "
                    << mask;
           }
           remaining_word ^= mask;
-          spvPushOperandTypes(entry->operandTypes, expected_operands);
+          spvPushOperandTypes(entry->operands(), expected_operands);
         }
       }
       if (word == 0) {
         // An all-zeroes mask *might* also be valid.
-        spv_operand_desc entry;
-        if (SPV_SUCCESS == grammar_.lookupOperand(type, 0, &entry)) {
+        const spvtools::OperandDesc* entry = nullptr;
+        if (SPV_SUCCESS == spvtools::LookupOperand(type, 0, &entry)) {
           // Prepare for its operands, if any.
-          spvPushOperandTypes(entry->operandTypes, expected_operands);
+          spvPushOperandTypes(entry->operands(), expected_operands);
         }
       }
     } break;
@@ -822,6 +904,7 @@
 
   parsed_operand->number_kind = info.type;
   parsed_operand->number_bit_width = info.bit_width;
+  parsed_operand->fp_encoding = info.encoding;
   // Round up the word count.
   parsed_operand->num_words = static_cast<uint16_t>((info.bit_width + 31) / 32);
   return SPV_SUCCESS;
@@ -839,6 +922,17 @@
     } else if (spv::Op::OpTypeFloat == opcode) {
       info.type = SPV_NUMBER_FLOATING;
       info.bit_width = peekAt(inst_offset + 2);
+      if (inst->num_words >= 4) {
+        const spvtools::OperandDesc* desc = nullptr;
+        spv_result_t status = spvtools::LookupOperand(
+            SPV_OPERAND_TYPE_FPENCODING, peekAt(inst_offset + 3), &desc);
+        if (status == SPV_SUCCESS) {
+          info.encoding = spvFPEncodingFromOperandFPEncoding(
+              static_cast<spv::FPEncoding>(desc->value));
+        } else {
+          info.encoding = SPV_FP_ENCODING_UNKNOWN;
+        }
+      }
     }
     // The *result* Id of a type generating instruction is the type Id.
     _.type_id_to_number_type_info[inst->result_id] = info;
@@ -852,12 +946,25 @@
                             spv_parsed_header_fn_t parsed_header,
                             spv_parsed_instruction_fn_t parsed_instruction,
                             spv_diagnostic* diagnostic) {
+  return spvBinaryParseWithOptions(context, user_data, code, num_words,
+                                   parsed_header, parsed_instruction,
+                                   diagnostic, 0);
+}
+
+spv_result_t spvBinaryParseWithOptions(
+    const spv_const_context context, void* user_data, const uint32_t* code,
+    const size_t num_words, spv_parsed_header_fn_t parsed_header,
+    spv_parsed_instruction_fn_t parsed_instruction, spv_diagnostic* diagnostic,
+    uint32_t options) {
   spv_context_t hijack_context = *context;
   if (diagnostic) {
     *diagnostic = nullptr;
     spvtools::UseDiagnosticAsMessageConsumer(&hijack_context, diagnostic);
   }
   Parser parser(&hijack_context, user_data, parsed_header, parsed_instruction);
+  if (options & SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES) {
+    parser.SetHandleUnknownOpcodes(true);
+  }
   return parser.parse(code, num_words, diagnostic);
 }
 
diff --git a/source/binary.h b/source/binary.h
index eb3beac..7dbb9a8 100644
--- a/source/binary.h
+++ b/source/binary.h
@@ -35,6 +35,14 @@
 // replacement for C11's strnlen_s which might not exist in all environments.
 size_t spv_strnlen_s(const char* str, size_t strsz);
 
+// Like spvBinaryParse, but accepts a bitmask of spv_binary_to_text_options_t
+// flags to control parser behavior during disassembly.
+spv_result_t spvBinaryParseWithOptions(
+    const spv_const_context context, void* user_data, const uint32_t* code,
+    const size_t num_words, spv_parsed_header_fn_t parsed_header,
+    spv_parsed_instruction_fn_t parsed_instruction, spv_diagnostic* diagnostic,
+    uint32_t options);
+
 // Decode the string literal operand with index operand_index from instruction
 // inst.
 std::string spvDecodeLiteralStringOperand(const spv_parsed_instruction_t& inst,
diff --git a/source/cfa.h b/source/cfa.h
index 9ae3e39..dc8b9ba 100644
--- a/source/cfa.h
+++ b/source/cfa.h
@@ -15,6 +15,8 @@
 #ifndef SOURCE_CFA_H_
 #define SOURCE_CFA_H_
 
+#include <stddef.h>
+
 #include <algorithm>
 #include <cassert>
 #include <cstdint>
diff --git a/source/common_debug_info.h b/source/common_debug_info.h
index ffa5d34..66862d2 100644
--- a/source/common_debug_info.h
+++ b/source/common_debug_info.h
@@ -18,7 +18,7 @@
 #define SOURCE_COMMON_DEBUG_INFO_HEADER_H_
 
 // This enum defines the known common set of instructions that are the same
-// between OpenCL.DebugInfo.100 and NonSemantic.Shader.DebugInfo.100.
+// between OpenCL.DebugInfo.100 and NonSemantic.Shader.DebugInfo.
 // Note that NonSemantic.Shader.* instructions can still have slightly
 // different encoding, as it does not use literals anywhere and only constants.
 enum CommonDebugInfoInstructions {
diff --git a/source/diff/CMakeLists.txt b/source/diff/CMakeLists.txt
index 52f18f2..2bd6106 100644
--- a/source/diff/CMakeLists.txt
+++ b/source/diff/CMakeLists.txt
@@ -18,8 +18,16 @@
   diff.cpp
 )
 
+if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD))
+  list(APPEND SPIRV_TOOLS_DIFF_SOURCES ${spirv-tools_SOURCE_DIR}/source/mimalloc.cpp)
+endif()
+
 add_library(SPIRV-Tools-diff ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_DIFF_SOURCES})
 
+if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD))
+  target_link_libraries(SPIRV-Tools-diff PRIVATE mimalloc-static)
+endif()
+
 spvtools_default_compile_options(SPIRV-Tools-diff)
 target_include_directories(SPIRV-Tools-diff
   PUBLIC
@@ -39,7 +47,13 @@
 spvtools_check_symbol_exports(SPIRV-Tools-diff)
 
 if(ENABLE_SPIRV_TOOLS_INSTALL)
-  install(TARGETS SPIRV-Tools-diff EXPORT SPIRV-Tools-diffTargets)
+  set(SPIRV-Tools-diff-InstallTargets SPIRV-Tools-diff)
+
+  if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD))
+    list(APPEND SPIRV-Tools-diff-InstallTargets mimalloc-static)
+  endif()
+
+  install(TARGETS ${SPIRV-Tools-diff-InstallTargets} EXPORT SPIRV-Tools-diffTargets)
   export(EXPORT SPIRV-Tools-diffTargets FILE SPIRV-Tools-diffTargets.cmake)
 
   spvtools_config_package_dir(SPIRV-Tools-diff PACKAGE_DIR)
diff --git a/source/diff/diff.cpp b/source/diff/diff.cpp
index 6269af5..d548aea 100644
--- a/source/diff/diff.cpp
+++ b/source/diff/diff.cpp
@@ -67,7 +67,9 @@
   ids.resize(write_index);
 }
 
-// A mapping between src and dst ids.
+// A mapping from ids in one module to ids in the other.
+//
+// Differ contains two of these, for src->dst and dst->src.
 class IdMap {
  public:
   IdMap(size_t id_bound) { id_map_.resize(id_bound, 0); }
@@ -190,6 +192,7 @@
   IdMap dst_to_src_;
 };
 
+// Mappings from ids to instructions and metadata, for a single module's ids.
 struct IdInstructions {
   IdInstructions(const opt::Module* module)
       : inst_map_(module->IdBound(), nullptr),
@@ -198,6 +201,10 @@
         forward_pointer_map_(module->IdBound()) {
     // Map ids from all sections to instructions that define them.
     MapIdsToInstruction(module->ext_inst_imports());
+    MapIdsToInstruction(module->debugs1());
+    MapIdsToInstruction(module->debugs2());
+    MapIdsToInstruction(module->debugs3());
+    MapIdsToInstruction(module->ext_inst_debuginfo());
     MapIdsToInstruction(module->types_values());
     for (const opt::Function& function : *module) {
       function.ForEachInst(
@@ -321,6 +328,8 @@
   // Get various properties from an id.  These Helper functions are passed to
   // `GroupIds` and `GroupIdsAndMatch` below (as the `get_group` argument).
   uint32_t GroupIdsHelperGetTypeId(const IdInstructions& id_to, uint32_t id);
+  uint32_t GroupIdsHelperGetFunctionTypeId(const IdInstructions& id_to,
+                                           uint32_t id);
   spv::StorageClass GroupIdsHelperGetTypePointerStorageClass(
       const IdInstructions& id_to, uint32_t id);
   spv::Op GroupIdsHelperGetTypePointerTypeOp(const IdInstructions& id_to,
@@ -883,6 +892,17 @@
   return GetInst(id_to, id)->type_id();
 }
 
+// Return an `OpFunction` instruction's full `OpTypeFunction` type,
+// which includes parameter types.
+//
+// `GroupIdsHelperGetTypeId` applied to an `OpFunction` only gets the
+// function's return type, so this is a slightly more precise way to
+// match up functions by signature.
+uint32_t Differ::GroupIdsHelperGetFunctionTypeId(const IdInstructions& id_to,
+                                                 uint32_t id) {
+  return GetInst(id_to, id)->GetSingleWordOperand(3);
+}
+
 spv::StorageClass Differ::GroupIdsHelperGetTypePointerStorageClass(
     const IdInstructions& id_to, uint32_t id) {
   const opt::Instruction* inst = GetInst(id_to, id);
@@ -902,6 +922,24 @@
   return type_inst->opcode();
 }
 
+// Group unmatched ids in `ids` according to some characteristic,
+// determined by `get_group`.
+//
+// Using `get_group` to compute some sort of key for each id, set
+// `groups` to map each key to all the ids that have that key.
+//
+// For example, to group ids by name, pass `Differ::GetName` as
+// `get_group`. This will fill `groups` with a map from each name to
+// all the ids with that name.
+//
+// Under the assumption that we're trying to establish new pairings,
+// ids that are already paired are omitted from `groups`.
+//
+// The `is_src` parameter indicates whether `ids` are drawn from the
+// source module or the destination module.
+//
+// The template parameter `T` is the key type, like `std::string` or
+// `uint32_t`.
 template <typename T>
 void Differ::GroupIds(const IdGroup& ids, bool is_src,
                       std::map<T, IdGroup>* groups,
@@ -924,6 +962,10 @@
   }
 }
 
+// Group `src_ids` and `dst_ids` according to `get_group`, and then use
+// `match_group` to pair up ids in corresponding groups.
+//
+// Don't try to pair ids in groups whose key is `invalid_group_key`.
 template <typename T>
 void Differ::GroupIdsAndMatch(
     const IdGroup& src_ids, const IdGroup& dst_ids, T invalid_group_key,
@@ -1177,6 +1219,7 @@
     case spv::Op::OpMemberDecorate:
       return DoOperandsMatch(src_inst, dst_inst, 0, 3);
     case spv::Op::OpExtInst:
+      return DoOperandsMatch(src_inst, dst_inst, 0, 2);
     case spv::Op::OpDecorationGroup:
     case spv::Op::OpGroupDecorate:
     case spv::Op::OpGroupMemberDecorate:
@@ -2483,7 +2526,7 @@
 
         // If there are multiple functions with the same name, group them by
         // type, and match only if the types match (and are unique).
-        GroupIdsAndMatch<uint32_t>(src_group, dst_group, 0,
+        GroupIdsAndMatchByMappedId(src_group, dst_group,
                                    &Differ::GroupIdsHelperGetTypeId,
                                    [this](const IdGroup& src_group_by_type_id,
                                           const IdGroup& dst_group_by_type_id) {
@@ -2526,9 +2569,19 @@
                              dst_match_result, 0);
   }
 
-  // Best effort match functions with matching type.
-  GroupIdsAndMatch<uint32_t>(
-      src_func_ids, dst_func_ids, 0, &Differ::GroupIdsHelperGetTypeId,
+  // Best effort match functions with matching return and argument types.
+  GroupIdsAndMatchByMappedId(
+      src_func_ids, dst_func_ids, &Differ::GroupIdsHelperGetFunctionTypeId,
+      [this](const IdGroup& src_group_by_func_type_id,
+             const IdGroup& dst_group_by_func_type_id) {
+        BestEffortMatchFunctions(src_group_by_func_type_id,
+                                 dst_group_by_func_type_id, src_func_insts_,
+                                 dst_func_insts_);
+      });
+
+  // Best effort match functions with matching return types.
+  GroupIdsAndMatchByMappedId(
+      src_func_ids, dst_func_ids, &Differ::GroupIdsHelperGetTypeId,
       [this](const IdGroup& src_group_by_type_id,
              const IdGroup& dst_group_by_type_id) {
         BestEffortMatchFunctions(src_group_by_type_id, dst_group_by_type_id,
@@ -2560,6 +2613,9 @@
   // This section includes OpExtInst for DebugInfo extension
   MatchDebugAndAnnotationInstructions(src_->ext_inst_debuginfo(),
                                       dst_->ext_inst_debuginfo());
+  // OpExtInst can exist in other sections too, such as with non-semantic info.
+  MatchDebugAndAnnotationInstructions(src_->types_values(),
+                                      dst_->types_values());
 }
 
 void Differ::MatchAnnotations() {
@@ -2746,30 +2802,7 @@
   src_id_to_.inst_map_.resize(id_map_.SrcToDstMap().IdBound(), nullptr);
   dst_id_to_.inst_map_.resize(id_map_.DstToSrcMap().IdBound(), nullptr);
 
-  const spv_target_env target_env = SPV_ENV_UNIVERSAL_1_6;
-  spv_opcode_table opcode_table;
-  spv_operand_table operand_table;
-  spv_ext_inst_table ext_inst_table;
-  spv_result_t result;
-
-  result = spvOpcodeTableGet(&opcode_table, target_env);
-  if (result != SPV_SUCCESS) return result;
-
-  result = spvOperandTableGet(&operand_table, target_env);
-  if (result != SPV_SUCCESS) return result;
-
-  result = spvExtInstTableGet(&ext_inst_table, target_env);
-  if (result != SPV_SUCCESS) return result;
-
-  spv_context_t context{
-      target_env,
-      opcode_table,
-      operand_table,
-      ext_inst_table,
-  };
-
-  const AssemblyGrammar grammar(&context);
-  if (!grammar.isValid()) return SPV_ERROR_INVALID_TABLE;
+  spv_context_t context{SPV_ENV_UNIVERSAL_1_6, nullptr};
 
   uint32_t disassembly_options = SPV_BINARY_TO_TEXT_OPTION_PRINT;
   if (options_.indent) {
@@ -2777,7 +2810,7 @@
   }
 
   NameMapper name_mapper = GetTrivialNameMapper();
-  disassemble::InstructionDisassembler dis(grammar, out_, disassembly_options,
+  disassemble::InstructionDisassembler dis(out_, disassembly_options,
                                            name_mapper);
 
   if (!options_.no_header) {
diff --git a/source/disassemble.cpp b/source/disassemble.cpp
index 93791a0..b85958e 100644
--- a/source/disassemble.cpp
+++ b/source/disassemble.cpp
@@ -31,7 +31,6 @@
 #include <unordered_map>
 #include <utility>
 
-#include "source/assembly_grammar.h"
 #include "source/binary.h"
 #include "source/diagnostic.h"
 #include "source/ext_inst.h"
@@ -40,6 +39,7 @@
 #include "source/print.h"
 #include "source/spirv_constant.h"
 #include "source/spirv_endian.h"
+#include "source/table2.h"
 #include "source/util/hex_float.h"
 #include "source/util/make_unique.h"
 #include "spirv-tools/libspirv.h"
@@ -115,8 +115,7 @@
 // representation.
 class Disassembler {
  public:
-  Disassembler(const AssemblyGrammar& grammar, uint32_t options,
-               NameMapper name_mapper)
+  Disassembler(uint32_t options, NameMapper name_mapper)
       : print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)),
         nested_indent_(
             spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_NESTED_INDENT, options)),
@@ -124,7 +123,7 @@
             spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_REORDER_BLOCKS, options)),
         text_(),
         out_(print_ ? out_stream() : out_stream(text_)),
-        instruction_disassembler_(grammar, out_.get(), options, name_mapper),
+        instruction_disassembler_(out_.get(), options, name_mapper),
         header_(!spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER, options)),
         byte_offset_(0) {}
 
@@ -624,12 +623,10 @@
 }  // namespace
 
 namespace disassemble {
-InstructionDisassembler::InstructionDisassembler(const AssemblyGrammar& grammar,
-                                                 std::ostream& stream,
+InstructionDisassembler::InstructionDisassembler(std::ostream& stream,
                                                  uint32_t options,
                                                  NameMapper name_mapper)
-    : grammar_(grammar),
-      stream_(stream),
+    : stream_(stream),
       print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)),
       color_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COLOR, options)),
       indent_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_INDENT, options)
@@ -640,6 +637,8 @@
       comment_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COMMENT, options)),
       show_byte_offset_(
           spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET, options)),
+      handle_unknown_opcodes_(spvIsInBitfield(
+          SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES, options)),
       name_mapper_(std::move(name_mapper)),
       last_instruction_comment_alignment_(0) {}
 
@@ -691,18 +690,59 @@
   // first so its length can be readily available.
   std::ostringstream line;
 
+  if (handle_unknown_opcodes_) {
+    const InstructionDesc* opcode_desc = nullptr;
+    bool needs_raw_emit = LookupOpcode(opcode, &opcode_desc) != SPV_SUCCESS;
+
+    // Also check for an unknown extended instruction number in a semantic
+    // extended instruction set. The ext_inst_type is set by the parser even
+    // when the instruction number itself is unknown.
+    if (!needs_raw_emit && opcode == spv::Op::OpExtInst &&
+        inst.ext_inst_type != SPV_EXT_INST_TYPE_NONE &&
+        !spvExtInstIsNonSemantic(inst.ext_inst_type) && inst.num_words >= 5) {
+      const ExtInstDesc* ext_desc = nullptr;
+      needs_raw_emit = LookupExtInst(inst.ext_inst_type, inst.words[4],
+                                     &ext_desc) != SPV_SUCCESS;
+    }
+
+    // Also check for a known opcode whose operands were not decoded because
+    // one of its enum operands had an unknown value. The parser signals this
+    // by setting num_operands = 0 while leaving num_words intact.
+    // Legitimately zero-operand instructions (OpNop, OpReturn, etc.) have an
+    // empty grammar operand list, so this check does not trigger for them.
+    if (!needs_raw_emit && opcode_desc != nullptr &&
+        !opcode_desc->operands().empty() && inst.num_operands == 0) {
+      needs_raw_emit = true;
+    }
+
+    if (needs_raw_emit) {
+      line << std::string(indent_, ' ');
+      line << "OpUnknown(" << inst.opcode << ", " << inst.num_words << ")";
+      for (uint16_t i = 1; i < inst.num_words; i++) {
+        line << " " << inst.words[i];
+      }
+      // Warn that the ID bound in the reassembled module may be incorrect
+      // if this instruction defines a result ID, because the assembler does
+      // not track integers inside OpUnknown as ID assignments.
+      line << "  ; note: ID bound may be incorrect after reassembly";
+      stream_ << line.str() << "\n";
+      last_instruction_comment_alignment_ = 0;
+      return;
+    }
+  }
+
   if (nested_indent_ && opcode == spv::Op::OpLabel) {
     // Separate the blocks by an empty line to make them easier to separate
     stream_ << std::endl;
   }
 
   if (inst.result_id) {
-    SetBlue();
+    SetBlue(line);
     const std::string id_name = name_mapper_(inst.result_id);
     if (indent_)
       line << std::setw(std::max(0, indent_ - 3 - int(id_name.size())));
     line << "%" << id_name;
-    ResetColor();
+    ResetColor(line);
     line << " = ";
   } else {
     line << std::string(indent_, ' ');
@@ -771,7 +811,7 @@
         {line_length + 2, last_instruction_comment_alignment_, kCommentColumn});
     // Round up the alignment to a multiple of 4 for more niceness.
     align = (align + 3) & ~0x3u;
-    last_instruction_comment_alignment_ = align;
+    last_instruction_comment_alignment_ = std::min({align, 256u});
 
     stream_ << std::string(align - line_length, ' ') << "; " << comments.str();
   } else {
@@ -870,11 +910,10 @@
       stream << "%" << name_mapper_(word);
       break;
     case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: {
-      spv_ext_inst_desc ext_inst;
       SetRed(stream);
-      if (grammar_.lookupExtInst(inst.ext_inst_type, word, &ext_inst) ==
-          SPV_SUCCESS) {
-        stream << ext_inst->name;
+      const ExtInstDesc* desc = nullptr;
+      if (LookupExtInst(inst.ext_inst_type, word, &desc) == SPV_SUCCESS) {
+        stream << desc->name().data();
       } else {
         if (!spvExtInstIsNonSemantic(inst.ext_inst_type)) {
           assert(false && "should have caught this earlier");
@@ -885,11 +924,11 @@
       }
     } break;
     case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: {
-      spv_opcode_desc opcode_desc;
-      if (grammar_.lookupOpcode(spv::Op(word), &opcode_desc))
+      const spvtools::InstructionDesc* opcodeEntry = nullptr;
+      if (LookupOpcode(spv::Op(word), &opcodeEntry))
         assert(false && "should have caught this earlier");
       SetRed(stream);
-      stream << opcode_desc->name;
+      stream << opcodeEntry->name().data();
     } break;
     case SPV_OPERAND_TYPE_LITERAL_INTEGER:
     case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER:
@@ -911,6 +950,7 @@
       stream << '"';
     } break;
     case SPV_OPERAND_TYPE_CAPABILITY:
+    case SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY:
     case SPV_OPERAND_TYPE_SOURCE_LANGUAGE:
     case SPV_OPERAND_TYPE_EXECUTION_MODEL:
     case SPV_OPERAND_TYPE_ADDRESSING_MODEL:
@@ -948,10 +988,10 @@
     case SPV_OPERAND_TYPE_QUANTIZATION_MODES:
     case SPV_OPERAND_TYPE_FPENCODING:
     case SPV_OPERAND_TYPE_OVERFLOW_MODES: {
-      spv_operand_desc entry;
-      if (grammar_.lookupOperand(operand.type, word, &entry))
+      const spvtools::OperandDesc* entry = nullptr;
+      if (spvtools::LookupOperand(operand.type, word, &entry))
         assert(false && "should have caught this earlier");
-      stream << entry->name;
+      stream << entry->name().data();
     } break;
     case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE:
     case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
@@ -968,10 +1008,10 @@
       if (spvOperandIsConcreteMask(operand.type)) {
         EmitMaskOperand(stream, operand.type, word);
       } else if (spvOperandIsConcrete(operand.type)) {
-        spv_operand_desc entry;
-        if (grammar_.lookupOperand(operand.type, word, &entry))
+        const spvtools::OperandDesc* entry = nullptr;
+        if (spvtools::LookupOperand(operand.type, word, &entry))
           assert(false && "should have caught this earlier");
-        stream << entry->name;
+        stream << entry->name().data();
       } else {
         assert(false && "unhandled or invalid case");
       }
@@ -991,20 +1031,20 @@
   for (mask = 1; remaining_word; mask <<= 1) {
     if (remaining_word & mask) {
       remaining_word ^= mask;
-      spv_operand_desc entry;
-      if (grammar_.lookupOperand(type, mask, &entry))
+      const spvtools::OperandDesc* entry = nullptr;
+      if (spvtools::LookupOperand(type, mask, &entry))
         assert(false && "should have caught this earlier");
       if (num_emitted) stream << "|";
-      stream << entry->name;
+      stream << entry->name().data();
       num_emitted++;
     }
   }
   if (!num_emitted) {
     // An operand value of 0 was provided, so represent it by the name
     // of the 0 value. In many cases, that's "None".
-    spv_operand_desc entry;
-    if (SPV_SUCCESS == grammar_.lookupOperand(type, 0, &entry))
-      stream << entry->name;
+    const spvtools::OperandDesc* entry = nullptr;
+    if (SPV_SUCCESS == spvtools::LookupOperand(type, 0, &entry))
+      stream << entry->name().data();
   }
 }
 
@@ -1042,25 +1082,22 @@
                                        const size_t wordCount,
                                        const uint32_t options) {
   spv_context context = spvContextCreate(env);
-  const AssemblyGrammar grammar(context);
-  if (!grammar.isValid()) {
-    spvContextDestroy(context);
-    return "";
-  }
 
   // Generate friendly names for Ids if requested.
   std::unique_ptr<FriendlyNameMapper> friendly_mapper;
   NameMapper name_mapper = GetTrivialNameMapper();
   if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) {
-    friendly_mapper = MakeUnique<FriendlyNameMapper>(context, code, wordCount);
+    friendly_mapper =
+        MakeUnique<FriendlyNameMapper>(context, code, wordCount, options);
     name_mapper = friendly_mapper->GetNameMapper();
   }
 
   // Now disassemble!
-  Disassembler disassembler(grammar, options, name_mapper);
+  Disassembler disassembler(options, name_mapper);
   WrappedDisassembler wrapped(&disassembler, instCode, instWordCount);
-  spvBinaryParse(context, &wrapped, code, wordCount, DisassembleTargetHeader,
-                 DisassembleTargetInstruction, nullptr);
+  spvBinaryParseWithOptions(context, &wrapped, code, wordCount,
+                            DisassembleTargetHeader,
+                            DisassembleTargetInstruction, nullptr, options);
 
   spv_text text = nullptr;
   std::string output;
@@ -1086,24 +1123,21 @@
     spvtools::UseDiagnosticAsMessageConsumer(&hijack_context, pDiagnostic);
   }
 
-  const spvtools::AssemblyGrammar grammar(&hijack_context);
-  if (!grammar.isValid()) return SPV_ERROR_INVALID_TABLE;
-
   // Generate friendly names for Ids if requested.
   std::unique_ptr<spvtools::FriendlyNameMapper> friendly_mapper;
   spvtools::NameMapper name_mapper = spvtools::GetTrivialNameMapper();
   if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) {
     friendly_mapper = spvtools::MakeUnique<spvtools::FriendlyNameMapper>(
-        &hijack_context, code, wordCount);
+        &hijack_context, code, wordCount, options);
     name_mapper = friendly_mapper->GetNameMapper();
   }
 
   // Now disassemble!
-  spvtools::Disassembler disassembler(grammar, options, name_mapper);
-  if (auto error =
-          spvBinaryParse(&hijack_context, &disassembler, code, wordCount,
-                         spvtools::DisassembleHeader,
-                         spvtools::DisassembleInstruction, pDiagnostic)) {
+  spvtools::Disassembler disassembler(options, name_mapper);
+  if (auto error = spvBinaryParseWithOptions(
+          &hijack_context, &disassembler, code, wordCount,
+          spvtools::DisassembleHeader, spvtools::DisassembleInstruction,
+          pDiagnostic, options)) {
     return error;
   }
 
diff --git a/source/disassemble.h b/source/disassemble.h
index b6d13c6..7165a04 100644
--- a/source/disassemble.h
+++ b/source/disassemble.h
@@ -37,7 +37,6 @@
                                        const size_t word_count,
                                        const uint32_t options);
 
-class AssemblyGrammar;
 namespace disassemble {
 
 // Shared code with other tools (than the disassembler) that might need to
@@ -45,8 +44,8 @@
 // binary for an instruction to its assembly representation.
 class InstructionDisassembler {
  public:
-  InstructionDisassembler(const AssemblyGrammar& grammar, std::ostream& stream,
-                          uint32_t options, NameMapper name_mapper);
+  InstructionDisassembler(std::ostream& stream, uint32_t options,
+                          NameMapper name_mapper);
 
   // Emits the assembly header for the module.
   void EmitHeaderSpirv();
@@ -104,7 +103,6 @@
   // |id_comments_|.
   void GenerateCommentForDecoratedId(const spv_parsed_instruction_t& inst);
 
-  const spvtools::AssemblyGrammar& grammar_;
   std::ostream& stream_;
   const bool print_;  // Should we also print to the standard output stream?
   const bool color_;  // Should we print in colour?
@@ -112,6 +110,9 @@
   const bool nested_indent_;     // Whether indentation should indicate nesting
   const int comment_;            // Should we comment the source
   const bool show_byte_offset_;  // Should we print byte offset, in hex?
+  // Whether to emit unknown opcodes and unknown extended instructions as
+  // OpUnknown with raw integer operands rather than failing.
+  const bool handle_unknown_opcodes_;
   spvtools::NameMapper name_mapper_;
 
   // Some comments are generated as instructions (such as OpDecorate) are
diff --git a/source/enum_set.h b/source/enum_set.h
index a375138..340f5b9 100644
--- a/source/enum_set.h
+++ b/source/enum_set.h
@@ -12,11 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <stddef.h>
+
 #include <algorithm>
 #include <cassert>
 #include <cstdint>
 #include <functional>
 #include <initializer_list>
+#include <iterator>
 #include <limits>
 #include <type_traits>
 #include <vector>
diff --git a/source/enum_string_mapping.h b/source/enum_string_mapping.h
deleted file mode 100644
index b136584..0000000
--- a/source/enum_string_mapping.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) 2017 Google 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_ENUM_STRING_MAPPING_H_
-#define SOURCE_ENUM_STRING_MAPPING_H_
-
-#include <string>
-
-#include "source/extensions.h"
-#include "source/latest_version_spirv_header.h"
-
-namespace spvtools {
-
-// Finds Extension enum corresponding to |str|. Returns false if not found.
-bool GetExtensionFromString(const char* str, Extension* extension);
-
-// Returns text string corresponding to |extension|.
-const char* ExtensionToString(Extension extension);
-
-// Returns text string corresponding to |capability|.
-const char* CapabilityToString(spv::Capability capability);
-
-}  // namespace spvtools
-
-#endif  // SOURCE_ENUM_STRING_MAPPING_H_
diff --git a/source/ext_inst.cpp b/source/ext_inst.cpp
index f2ff63f..34daf7a 100644
--- a/source/ext_inst.cpp
+++ b/source/ext_inst.cpp
@@ -17,98 +17,7 @@
 #include <cstring>
 
 // DebugInfo extended instruction set.
-// See https://www.khronos.org/registry/spir-v/specs/1.0/DebugInfo.html
-// TODO(dneto): DebugInfo.h should probably move to SPIRV-Headers.
-#include "DebugInfo.h"
-
-#include "source/latest_version_glsl_std_450_header.h"
-#include "source/latest_version_opencl_std_header.h"
-#include "source/macro.h"
-#include "source/spirv_definition.h"
-
-#include "debuginfo.insts.inc"
-#include "glsl.std.450.insts.inc"
-#include "nonsemantic.clspvreflection.insts.inc"
-#include "nonsemantic.shader.debuginfo.100.insts.inc"
-#include "nonsemantic.vkspreflection.insts.inc"
-#include "opencl.debuginfo.100.insts.inc"
-#include "opencl.std.insts.inc"
-
-#include "spirv-tools/libspirv.h"
-#include "spv-amd-gcn-shader.insts.inc"
-#include "spv-amd-shader-ballot.insts.inc"
-#include "spv-amd-shader-explicit-vertex-parameter.insts.inc"
-#include "spv-amd-shader-trinary-minmax.insts.inc"
-
-static const spv_ext_inst_group_t kGroups_1_0[] = {
-    {SPV_EXT_INST_TYPE_GLSL_STD_450, ARRAY_SIZE(glsl_entries), glsl_entries},
-    {SPV_EXT_INST_TYPE_OPENCL_STD, ARRAY_SIZE(opencl_entries), opencl_entries},
-    {SPV_EXT_INST_TYPE_SPV_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER,
-     ARRAY_SIZE(spv_amd_shader_explicit_vertex_parameter_entries),
-     spv_amd_shader_explicit_vertex_parameter_entries},
-    {SPV_EXT_INST_TYPE_SPV_AMD_SHADER_TRINARY_MINMAX,
-     ARRAY_SIZE(spv_amd_shader_trinary_minmax_entries),
-     spv_amd_shader_trinary_minmax_entries},
-    {SPV_EXT_INST_TYPE_SPV_AMD_GCN_SHADER,
-     ARRAY_SIZE(spv_amd_gcn_shader_entries), spv_amd_gcn_shader_entries},
-    {SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT,
-     ARRAY_SIZE(spv_amd_shader_ballot_entries), spv_amd_shader_ballot_entries},
-    {SPV_EXT_INST_TYPE_DEBUGINFO, ARRAY_SIZE(debuginfo_entries),
-     debuginfo_entries},
-    {SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
-     ARRAY_SIZE(opencl_debuginfo_100_entries), opencl_debuginfo_100_entries},
-    {SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100,
-     ARRAY_SIZE(nonsemantic_shader_debuginfo_100_entries),
-     nonsemantic_shader_debuginfo_100_entries},
-    {SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION,
-     ARRAY_SIZE(nonsemantic_clspvreflection_entries),
-     nonsemantic_clspvreflection_entries},
-    {SPV_EXT_INST_TYPE_NONSEMANTIC_VKSPREFLECTION,
-     ARRAY_SIZE(nonsemantic_vkspreflection_entries),
-     nonsemantic_vkspreflection_entries},
-};
-
-static const spv_ext_inst_table_t kTable_1_0 = {ARRAY_SIZE(kGroups_1_0),
-                                                kGroups_1_0};
-
-spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable,
-                                spv_target_env env) {
-  if (!pExtInstTable) return SPV_ERROR_INVALID_POINTER;
-
-  switch (env) {
-    // The extended instruction sets are all version 1.0 so far.
-    case SPV_ENV_UNIVERSAL_1_0:
-    case SPV_ENV_VULKAN_1_0:
-    case SPV_ENV_UNIVERSAL_1_1:
-    case SPV_ENV_UNIVERSAL_1_2:
-    case SPV_ENV_OPENCL_1_2:
-    case SPV_ENV_OPENCL_EMBEDDED_1_2:
-    case SPV_ENV_OPENCL_2_0:
-    case SPV_ENV_OPENCL_EMBEDDED_2_0:
-    case SPV_ENV_OPENCL_2_1:
-    case SPV_ENV_OPENCL_EMBEDDED_2_1:
-    case SPV_ENV_OPENCL_2_2:
-    case SPV_ENV_OPENCL_EMBEDDED_2_2:
-    case SPV_ENV_OPENGL_4_0:
-    case SPV_ENV_OPENGL_4_1:
-    case SPV_ENV_OPENGL_4_2:
-    case SPV_ENV_OPENGL_4_3:
-    case SPV_ENV_OPENGL_4_5:
-    case SPV_ENV_UNIVERSAL_1_3:
-    case SPV_ENV_VULKAN_1_1:
-    case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
-    case SPV_ENV_UNIVERSAL_1_4:
-    case SPV_ENV_UNIVERSAL_1_5:
-    case SPV_ENV_VULKAN_1_2:
-    case SPV_ENV_UNIVERSAL_1_6:
-    case SPV_ENV_VULKAN_1_3:
-    case SPV_ENV_VULKAN_1_4:
-      *pExtInstTable = &kTable_1_0;
-      return SPV_SUCCESS;
-    default:
-      return SPV_ERROR_INVALID_TABLE;
-  }
-}
+// #include "DebugInfo.h"
 
 spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) {
   // The names are specified by the respective extension instruction
@@ -137,7 +46,9 @@
   if (!strcmp("OpenCL.DebugInfo.100", name)) {
     return SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100;
   }
-  if (!strcmp("NonSemantic.Shader.DebugInfo.100", name)) {
+  // Match any version of NonSemantic.Shader.DebugInfo.
+  // Later versions are supersets that share the same instruction numbering.
+  if (!strncmp("NonSemantic.Shader.DebugInfo.", name, 29)) {
     return SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100;
   }
   if (!strncmp("NonSemantic.ClspvReflection.", name, 28)) {
@@ -146,6 +57,12 @@
   if (!strncmp("NonSemantic.VkspReflection.", name, 27)) {
     return SPV_EXT_INST_TYPE_NONSEMANTIC_VKSPREFLECTION;
   }
+  if (!strcmp("TOSA.001000.1", name)) {
+    return SPV_EXT_INST_TYPE_TOSA_001000_1;
+  }
+  if (!strcmp("Arm.MotionEngine.100", name)) {
+    return SPV_EXT_INST_TYPE_ARM_MOTION_ENGINE_100;
+  }
   // ensure to add any known non-semantic extended instruction sets
   // above this point, and update spvExtInstIsNonSemantic()
   if (!strncmp("NonSemantic.", name, 12)) {
@@ -172,47 +89,3 @@
   }
   return false;
 }
-
-spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table,
-                                       const spv_ext_inst_type_t type,
-                                       const char* name,
-                                       spv_ext_inst_desc* pEntry) {
-  if (!table) return SPV_ERROR_INVALID_TABLE;
-  if (!pEntry) return SPV_ERROR_INVALID_POINTER;
-
-  for (uint32_t groupIndex = 0; groupIndex < table->count; groupIndex++) {
-    const auto& group = table->groups[groupIndex];
-    if (type != group.type) continue;
-    for (uint32_t index = 0; index < group.count; index++) {
-      const auto& entry = group.entries[index];
-      if (!strcmp(name, entry.name)) {
-        *pEntry = &entry;
-        return SPV_SUCCESS;
-      }
-    }
-  }
-
-  return SPV_ERROR_INVALID_LOOKUP;
-}
-
-spv_result_t spvExtInstTableValueLookup(const spv_ext_inst_table table,
-                                        const spv_ext_inst_type_t type,
-                                        const uint32_t value,
-                                        spv_ext_inst_desc* pEntry) {
-  if (!table) return SPV_ERROR_INVALID_TABLE;
-  if (!pEntry) return SPV_ERROR_INVALID_POINTER;
-
-  for (uint32_t groupIndex = 0; groupIndex < table->count; groupIndex++) {
-    const auto& group = table->groups[groupIndex];
-    if (type != group.type) continue;
-    for (uint32_t index = 0; index < group.count; index++) {
-      const auto& entry = group.entries[index];
-      if (value == entry.ext_inst) {
-        *pEntry = &entry;
-        return SPV_SUCCESS;
-      }
-    }
-  }
-
-  return SPV_ERROR_INVALID_LOOKUP;
-}
diff --git a/source/ext_inst.h b/source/ext_inst.h
index 4027f4c..3c3150b 100644
--- a/source/ext_inst.h
+++ b/source/ext_inst.h
@@ -27,20 +27,4 @@
 // Returns true if the extended instruction set is debug info
 bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type);
 
-// Finds the named extended instruction of the given type in the given extended
-// instruction table. On success, returns SPV_SUCCESS and writes a handle of
-// the instruction entry into *entry.
-spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table,
-                                       const spv_ext_inst_type_t type,
-                                       const char* name,
-                                       spv_ext_inst_desc* entry);
-
-// Finds the extended instruction of the given type in the given extended
-// instruction table by value. On success, returns SPV_SUCCESS and writes a
-// handle of the instruction entry into *entry.
-spv_result_t spvExtInstTableValueLookup(const spv_ext_inst_table table,
-                                        const spv_ext_inst_type_t type,
-                                        const uint32_t value,
-                                        spv_ext_inst_desc* pEntry);
-
 #endif  // SOURCE_EXT_INST_H_
diff --git a/source/extensions.cpp b/source/extensions.cpp
index ac987fc..ab4caa0 100644
--- a/source/extensions.cpp
+++ b/source/extensions.cpp
@@ -19,23 +19,29 @@
 #include <string>
 
 #include "source/binary.h"
-#include "source/enum_string_mapping.h"
+#include "source/table2.h"
 
 namespace spvtools {
 
 std::string GetExtensionString(const spv_parsed_instruction_t* inst) {
-  if (inst->opcode != static_cast<uint16_t>(spv::Op::OpExtension)) {
+  if ((inst->opcode != static_cast<uint16_t>(spv::Op::OpExtension)) &&
+      (inst->opcode !=
+       static_cast<uint16_t>(spv::Op::OpConditionalExtensionINTEL))) {
     return "ERROR_not_op_extension";
   }
 
-  assert(inst->num_operands == 1);
+  const bool is_conditional =
+      inst->opcode ==
+      static_cast<uint16_t>(spv::Op::OpConditionalExtensionINTEL);
+  assert(inst->num_operands == (is_conditional ? 2 : 1));
+  const uint16_t op_i = is_conditional ? 1 : 0;
 
-  const auto& operand = inst->operands[0];
+  const auto& operand = inst->operands[op_i];
   assert(operand.type == SPV_OPERAND_TYPE_LITERAL_STRING);
   assert(inst->num_words > operand.offset);
   (void)operand; /* No unused variables in release builds. */
 
-  return spvDecodeLiteralStringOperand(*inst, 0);
+  return spvDecodeLiteralStringOperand(*inst, op_i);
 }
 
 std::string ExtensionSetToString(const ExtensionSet& extensions) {
diff --git a/source/extensions.h b/source/extensions.h
index cda4924..ffca5f8 100644
--- a/source/extensions.h
+++ b/source/extensions.h
@@ -15,25 +15,19 @@
 #ifndef SOURCE_EXTENSIONS_H_
 #define SOURCE_EXTENSIONS_H_
 
-#include <cstdint>
 #include <string>
 
 #include "source/enum_set.h"
-#include "spirv-tools/libspirv.h"
+#include "source/table2.h"
 
 namespace spvtools {
 
-// The known SPIR-V extensions.
-enum Extension : uint32_t {
-#include "extension_enum.inc"
-};
-
 using ExtensionSet = EnumSet<Extension>;
 
-// Returns literal string operand of OpExtension instruction.
+// Returns the literal string operand of OpExtension instruction.
 std::string GetExtensionString(const spv_parsed_instruction_t* inst);
 
-// Returns text string listing |extensions| separated by whitespace.
+// Returns a text string listing |extensions| separated by whitespace.
 std::string ExtensionSetToString(const ExtensionSet& extensions);
 
 }  // namespace spvtools
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index 86ee657..a3e0395 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -431,15 +431,18 @@
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
         )
 
-  if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")))
-    # Enable parallel builds across four cores for this lib
-    add_definitions(/MP4)
-  endif()
-
   spvtools_pch(SPIRV_TOOLS_FUZZ_SOURCES pch_source_fuzz)
 
+  if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD))
+    list(APPEND SPIRV_TOOLS_DIFF_SOURCES ${spirv-tools_SOURCE_DIR}/source/mimalloc.cpp)
+  endif()
+
   add_library(SPIRV-Tools-fuzz ${SPIRV_TOOLS_FUZZ_SOURCES})
 
+  if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD))
+    target_link_libraries(SPIRV-Tools-fuzz PRIVATE mimalloc-static)
+  endif()
+
   spvtools_default_compile_options(SPIRV-Tools-fuzz)
 
   # Compilation of the auto-generated protobuf source file will yield warnings,
@@ -470,7 +473,13 @@
   spvtools_check_symbol_exports(SPIRV-Tools-fuzz)
 
   if(ENABLE_SPIRV_TOOLS_INSTALL)
-      install(TARGETS SPIRV-Tools-fuzz EXPORT SPIRV-Tools-fuzzTargets)
+      set(SPIRV-Tools-fuzz-InstallTargets SPIRV-Tools-fuzz)
+
+      if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD))
+        list(APPEND SPIRV-Tools-fuzz-InstallTargets mimalloc-static)
+      endif()
+
+      install(TARGETS ${SPIRV-Tools-fuzz-InstallTargets} EXPORT SPIRV-Tools-fuzzTargets)
       export(EXPORT SPIRV-Tools-fuzzTargets FILE SPIRV-Tools-fuzzTarget.cmake)
 
       spvtools_config_package_dir(SPIRV-Tools-fuzz PACKAGE_DIR)
diff --git a/source/fuzz/force_render_red.cpp b/source/fuzz/force_render_red.cpp
index 191fd71..2425ec6 100644
--- a/source/fuzz/force_render_red.cpp
+++ b/source/fuzz/force_render_red.cpp
@@ -36,8 +36,11 @@
   // Check that this is a fragment shader
   bool found_capability_shader = false;
   for (auto& capability : ir_context->capabilities()) {
-    assert(capability.opcode() == spv::Op::OpCapability);
-    if (spv::Capability(capability.GetSingleWordInOperand(0)) ==
+    assert(capability.opcode() == spv::Op::OpCapability ||
+           capability.opcode() == spv::Op::OpConditionalCapabilityINTEL);
+    const uint32_t i_cap =
+        capability.opcode() == spv::Op::OpConditionalCapabilityINTEL ? 1 : 0;
+    if (spv::Capability(capability.GetSingleWordInOperand(i_cap)) ==
         spv::Capability::Shader) {
       found_capability_shader = true;
       break;
diff --git a/source/fuzz/transformation_inline_function.cpp b/source/fuzz/transformation_inline_function.cpp
index 69e88fd..f4f373f 100644
--- a/source/fuzz/transformation_inline_function.cpp
+++ b/source/fuzz/transformation_inline_function.cpp
@@ -182,6 +182,8 @@
     }
 
     auto* cloned_block = block.Clone(ir_context);
+    // TODO: Handle the nullptr.
+    assert(cloned_block);
     cloned_block = caller_function->InsertBasicBlockBefore(
         std::unique_ptr<opt::BasicBlock>(cloned_block), successor_block);
     cloned_block->GetLabel()->SetResultId(result_id_map.at(cloned_block->id()));
diff --git a/source/fuzz/transformation_outline_function.cpp b/source/fuzz/transformation_outline_function.cpp
index 4ab68d0..0271166 100644
--- a/source/fuzz/transformation_outline_function.cpp
+++ b/source/fuzz/transformation_outline_function.cpp
@@ -819,6 +819,8 @@
     // Clone the block so that it can be added to the new function.
     auto cloned_block =
         std::unique_ptr<opt::BasicBlock>(block_it->Clone(ir_context));
+    // TODO: Handle a nullptr.
+    assert(cloned_block);
 
     // If this is the region's exit block, then the cloned block is the outlined
     // region's exit block.
diff --git a/source/link/CMakeLists.txt b/source/link/CMakeLists.txt
index a35b9a5..5fd89c3 100644
--- a/source/link/CMakeLists.txt
+++ b/source/link/CMakeLists.txt
@@ -12,6 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 add_library(SPIRV-Tools-link ${SPIRV_TOOLS_LIBRARY_TYPE}
+  fnvar.cpp
   linker.cpp
 )
 
diff --git a/source/link/fnvar.cpp b/source/link/fnvar.cpp
new file mode 100644
index 0000000..352e7ea
--- /dev/null
+++ b/source/link/fnvar.cpp
@@ -0,0 +1,1011 @@
+// Copyright 2025 The Khronos Group 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 "fnvar.h"
+
+#include <initializer_list>
+#include <memory>
+#include <sstream>
+
+#include "source/opt/instruction.h"
+
+namespace spvtools {
+
+using opt::Function;
+using opt::Instruction;
+using opt::analysis::Type;
+
+namespace {
+// Helper functions
+
+// Parses a CSV source string for the purpose of this extension.
+//
+// Required columns must be known in advance and supplied as the required_cols
+// argument -- this is used for error checking. Values are assumed to be
+// separated by CSV_SEP. The input source string is assumed to be the output of
+// io::ReadTextFile and no other validation, apart from the CSV parsing, is
+// performed.
+//
+// Returns true on success, false on error (with error message stored in
+// err_msg).
+bool ParseCsv(const std::string& source,
+              const std::vector<std::string>& required_cols,
+              std::stringstream& err_msg,
+              std::vector<std::vector<std::string>>& result) {
+  std::stringstream fn_variants_csv_stream(source);
+  std::string line;
+  std::vector<std::string> columns;
+  constexpr char CSV_SEP = ',';
+  bool first_line = true;
+
+  while (std::getline(fn_variants_csv_stream, line, '\n')) {
+    if (line.empty()) {
+      continue;
+    }
+
+    std::vector<std::string> vals;
+    std::string val;
+    std::stringstream line_stream(line);
+    auto* vec = first_line ? &columns : &vals;
+
+    while (std::getline(line_stream, val, CSV_SEP)) {
+      vec->push_back(val);
+    }
+
+    if (!line_stream && val.empty()) {
+      vec->push_back("");
+    }
+
+    if (!first_line) {
+      if (vals.size() != columns.size()) {
+        err_msg << "Number of values does not match the number of columns. "
+                   "Offending line:\n"
+                << line;
+        return false;
+      }
+      result.push_back(vals);
+    }
+
+    first_line = false;
+  }
+
+  // check if required columns match actual columns (ordering matters)
+
+  if (columns.size() != required_cols.size()) {
+    err_msg << "Invalid number of CSV columns: " << columns.size()
+            << ", expected " << required_cols.size() << ".";
+    return false;
+  }
+
+  for (size_t i = 0; i < columns.size(); ++i) {
+    if (columns[i] != required_cols[i]) {
+      err_msg << "Invalid name of column " << i + 1 << ". Expected '"
+              << required_cols[i] << "', got '" << columns[i] << "'.";
+      return false;
+    }
+  }
+
+  return true;
+}
+
+// Annotate ID with ConditionalINTEL decoration
+void DecorateConditional(IRContext* context, uint32_t id_to_decorate,
+                         uint32_t spec_const_id) {
+  auto decor_instr =
+      std::make_unique<Instruction>(context, spv::Op::OpDecorate);
+  decor_instr->AddOperand({SPV_OPERAND_TYPE_ID, {id_to_decorate}});
+  decor_instr->AddOperand({SPV_OPERAND_TYPE_DECORATION,
+                           {uint32_t(spv::Decoration::ConditionalINTEL)}});
+  decor_instr->AddOperand({SPV_OPERAND_TYPE_ID, {spec_const_id}});
+  context->module()->AddAnnotationInst(std::move(decor_instr));
+}
+
+// Finds entry point corresponding to a function
+//
+// Returns null if not found, otherwise returns pointer to the EP Instruction.
+Instruction* FindEntryPoint(const Instruction& fn_inst) {
+  auto* mod = fn_inst.context()->module();
+  for (auto& entry_point : mod->entry_points()) {
+    const int ep_i =
+        entry_point.opcode() == spv::Op::OpConditionalEntryPointINTEL ? 2 : 1;
+    if (entry_point.GetOperand(ep_i).AsId() == fn_inst.result_id()) {
+      return &entry_point;
+    }
+  }
+  return nullptr;
+}
+
+// If the function has an entry point, converts it to a conditional one
+void ConvertEPToConditional(Module* module, const Function& fn,
+                            uint32_t spec_const_id) {
+  for (const auto& ep_inst : module->entry_points()) {
+    if (ep_inst.opcode() == spv::Op::OpEntryPoint) {
+      auto* entry_point = FindEntryPoint(fn.DefInst());
+      if (entry_point != nullptr) {
+        std::vector<opt::Operand> old_operands;
+        for (auto operand : *entry_point) {
+          old_operands.push_back(operand);
+        }
+        entry_point->ToNop();
+        entry_point->SetOpcode(spv::Op::OpConditionalEntryPointINTEL);
+        entry_point->AddOperand({SPV_OPERAND_TYPE_ID, {spec_const_id}});
+        for (auto old_operand : old_operands) {
+          entry_point->AddOperand(old_operand);
+        }
+      }
+    }
+  }
+}
+
+// Finds ID of a bool type (returns 0 if not found)
+uint32_t FindIdOfBoolType(const Module* const mod) {
+  return mod->context()->get_type_mgr()->GetBoolTypeId();
+}
+
+// Combines IDs using OpSpecConstantOp with the operation defined by cmp_op.
+//
+// Returns the ID of the final result. If there are no IDs, returns 0. If there
+// is one ID, does not generate any instructions and returns the ID.
+uint32_t CombineIds(IRContext* const context, const std::vector<uint32_t>& ids,
+                    spv::Op cmp_op) {
+  if (ids.empty()) {
+    return 0;
+  } else if (ids.size() == 1) {
+    return ids[0];
+  } else {
+    uint32_t bool_id = FindIdOfBoolType(context->module());
+    assert(bool_id != 0);
+
+    uint32_t prev_spec_const_id = ids[0];
+
+    for (size_t i = 1; i < ids.size(); ++i) {
+      const uint32_t id = ids[i];
+      const uint32_t spec_const_op_id = context->TakeNextId();
+
+      auto inst = std::make_unique<Instruction>(
+          context, spv::Op::OpSpecConstantOp, bool_id, spec_const_op_id,
+          std::initializer_list<opt::Operand>{
+              {SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER, {(uint32_t)(cmp_op)}},
+              {SPV_OPERAND_TYPE_ID, {prev_spec_const_id}},
+              {SPV_OPERAND_TYPE_ID, {id}}});
+      context->module()->AddType(std::move(inst));
+
+      prev_spec_const_id = spec_const_op_id;
+    }
+
+    return prev_spec_const_id;
+  }
+}
+
+// Returns whether instruction can be shared between variant modules and
+// combined using spec constants (such as conditional capabilities).
+bool CanBeFnVarCombined(const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+
+  if ((opcode != spv::Op::OpExtInstImport) &&
+      (opcode != spv::Op::OpCapability) && (opcode != spv::Op::OpExtension) &&
+      !spvOpcodeGeneratesType(opcode)) {
+    return false;
+  }
+
+  if ((opcode == spv::Op::OpCapability) &&
+      ((inst->GetSingleWordOperand(0) ==
+        static_cast<uint32_t>(spv::Capability::FunctionVariantsINTEL)) ||
+       (inst->GetSingleWordOperand(0) ==
+        static_cast<uint32_t>(spv::Capability::SpecConditionalINTEL)))) {
+    // Always enabled
+    return false;
+  }
+
+  if ((opcode == spv::Op::OpExtension) &&
+      (inst->GetOperand(0).AsString() == FNVAR_EXT_NAME)) {
+    // Always enabled
+    return false;
+  }
+
+  return true;
+}
+
+// Calculates hash of an instruction.
+//
+// Applicable only to instructions that can be combined (ie. with
+// CanBeFnVarCombined being true) and from those, hash can be only computed for
+// selected instructions. Computing hash from other instruction is unsupported.
+size_t HashInst(const Instruction* inst) {
+  if (CanBeFnVarCombined(inst)) {
+    if (spvOpcodeGeneratesType(inst->opcode())) {
+      const Type* t =
+          inst->context()->get_type_mgr()->GetType(inst->result_id());
+      assert(t != nullptr);
+      return t->HashValue();
+    }
+
+    if (inst->opcode() == spv::Op::OpExtension) {
+      const auto name = inst->GetOperand(0).AsString();
+      return std::hash<std::string>()(name);
+    }
+
+    if (inst->opcode() == spv::Op::OpCapability) {
+      const auto cap = inst->GetSingleWordOperand(0);
+      return std::hash<uint32_t>()(cap);
+    }
+
+    if (inst->opcode() == spv::Op::OpExtInstImport) {
+      const auto name = inst->GetOperand(1).AsString();
+      return std::hash<std::string>()(name);
+    }
+  }
+
+  assert(false && "Unsupported instruction hash");
+  return std::hash<const Instruction*>()(inst);
+}
+
+std::string GetFnName(const Instruction& fn_inst) {
+  // Check entry point
+  const auto* ep_inst = FindEntryPoint(fn_inst);
+  if (ep_inst != nullptr) {
+    const int name_i =
+        ep_inst->opcode() == spv::Op::OpConditionalEntryPointINTEL ? 3 : 2;
+    return ep_inst->GetOperand(name_i).AsString();
+  }
+
+  // Check name of export linkage attribute decoration
+  const auto* decor_mgr = fn_inst.context()->get_decoration_mgr();
+  for (const auto* inst :
+       decor_mgr->GetDecorationsFor(fn_inst.result_id(), true)) {
+    const auto decoration = inst->GetOperand(1);
+    if ((decoration.type == SPV_OPERAND_TYPE_DECORATION) &&
+        (decoration.words.size() == 1) &&
+        (decoration.words[0] ==
+         static_cast<uint32_t>(spv::Decoration::LinkageAttributes))) {
+      const auto linkage = inst->GetOperand(3);
+      if ((linkage.type == SPV_OPERAND_TYPE_LINKAGE_TYPE) &&
+          (linkage.words.size() == 1) &&
+          (linkage.words[0] ==
+           static_cast<uint32_t>(spv::LinkageType::Export))) {
+        // decorates fn with LinkageAttribute and Export linkage type -> get the
+        // name
+        return inst->GetOperand(2).AsString();
+      }
+    }
+  }
+
+  return "";
+}
+
+uint32_t FindSpecConstByName(const Module* mod, std::string name) {
+  for (const auto* const_inst : mod->context()->GetConstants()) {
+    if (opt::IsSpecConstantInst(const_inst->opcode())) {
+      const auto id = const_inst->result_id();
+      for (const auto& name_inst : mod->debugs2()) {
+        if ((name_inst.opcode() == spv::Op::OpName) &&
+            (name_inst.GetOperand(0).AsId() == id) &&
+            (name_inst.GetOperand(1).AsString() == name)) {
+          return id;
+        }
+      }
+    }
+  }
+  return 0;
+}
+
+uint32_t CombineVariantDefs(const std::vector<VariantDef>& variant_defs,
+                            const std::vector<size_t> var_ids,
+                            IRContext* context,
+                            std::map<std::vector<size_t>, uint32_t>& cache) {
+  assert(var_ids.size() <= variant_defs.size());
+  uint32_t spec_const_comb_id = 0;
+  if (var_ids.size() != variant_defs.size()) {
+    // if not used by all variants
+    if (cache.find(var_ids) == cache.end()) {
+      // cache variant combinations
+      std::vector<uint32_t> spec_const_ids;
+      for (const auto& var_id : var_ids) {
+        const auto var_name = variant_defs[var_id].GetName();
+        const auto var_spec_id =
+            FindSpecConstByName(context->module(), var_name);
+        spec_const_ids.push_back(var_spec_id);
+      }
+      spec_const_comb_id =
+          CombineIds(context, spec_const_ids, spv::Op::OpLogicalOr);
+      assert(spec_const_comb_id != 0);
+      cache.insert({var_ids, spec_const_comb_id});
+    } else {
+      spec_const_comb_id = cache[var_ids];
+    }
+  }
+  return spec_const_comb_id;
+}
+
+bool strToInt(std::string s, uint32_t* x) {
+  for (const char& c : s) {
+    if (c < '0' || c > '9') {
+      return false;
+    }
+  }
+  if (!(std::stringstream(s) >> *x)) {
+    return false;
+  }
+  return true;
+}
+
+}  // anonymous namespace
+
+bool VariantDefs::ProcessFnVar(const LinkerOptions& options,
+                               const std::vector<Module*>& modules) {
+  assert(variant_defs_.empty());
+  assert(modules.size() == options.GetInFiles().size());
+
+  for (size_t i = 0; i < modules.size(); ++i) {
+    const auto* feat_mgr = modules[i]->context()->get_feature_mgr();
+    if ((feat_mgr->HasCapability(spv::Capability::FunctionVariantsINTEL)) ||
+        (feat_mgr->HasCapability(spv::Capability::SpecConditionalINTEL)) ||
+        (feat_mgr->HasExtension(kSPV_INTEL_function_variants))) {
+      // In principle, it can be done but it's complicated due to having to
+      // combine the existing conditionals with the new ones. For example,
+      // conditional capabilities would need to become "doubly-conditional".
+      err_ << "Creating multitarget modules from multitarget modules is not "
+              "supported. Offending file: "
+           << options.GetInFiles()[i];
+      return false;
+    }
+  }
+
+  std::vector<std::vector<std::string>> target_rows;
+  std::vector<std::vector<std::string>> architecture_rows;
+
+  if (!options.GetFnVarTargetsCsv().empty()) {
+    const std::vector<std::string> tgt_cols = {"module", "target", "features"};
+    if (!ParseCsv(options.GetFnVarTargetsCsv(), tgt_cols, err_, target_rows)) {
+      return false;
+    }
+  }
+
+  if (!options.GetFnVarArchitecturesCsv().empty()) {
+    const std::vector<std::string> arch_cols = {"module", "category", "family",
+                                                "op", "architecture"};
+    if (!ParseCsv(options.GetFnVarArchitecturesCsv(), arch_cols, err_,
+                  architecture_rows)) {
+      return false;
+    }
+  }
+
+  // check that all modules defined in the CSV exist
+
+  for (const auto& tgt_vals : target_rows) {
+    bool found = false;
+    for (const auto& in_file : options.GetInFiles()) {
+      if (tgt_vals[0] == in_file) {
+        found = true;
+      }
+    }
+    if (!found) {
+      err_ << "Module '" << tgt_vals[0]
+           << "' found in targets CSV not passed to the CLI.";
+      return false;
+    }
+  }
+
+  for (const auto& arch_vals : architecture_rows) {
+    bool found = false;
+    for (const auto& in_file : options.GetInFiles()) {
+      if (arch_vals[0] == in_file) {
+        found = true;
+      }
+    }
+    if (!found) {
+      err_ << "Module '" << arch_vals[0]
+           << "' found in architectures CSV not passed to the CLI.";
+      return false;
+    }
+  }
+
+  // create per-module variant defs
+
+  for (size_t i = 0; i < modules.size(); ++i) {
+    // first module passed to the CLI is considered the base module
+    bool is_base = i == 0;
+    const auto name = options.GetInFiles()[i];
+    auto variant_def = VariantDef(is_base, name, modules[i]);
+
+    for (const auto& arch_row : architecture_rows) {
+      const auto row_name = arch_row[0];
+      if (row_name == name) {
+        uint32_t category, family, op, architecture;
+
+        if (!strToInt(arch_row[1], &category)) {
+          err_ << "Error converting " << arch_row[1]
+               << " to architecture category.";
+          return false;
+        }
+        if (!strToInt(arch_row[2], &family)) {
+          err_ << "Error converting " << arch_row[2]
+               << " to architecture family.";
+          return false;
+        }
+        if (!strToInt(arch_row[3], &op)) {
+          err_ << "Error converting " << arch_row[3] << " to architecture op.";
+          return false;
+        }
+        if (!strToInt(arch_row[4], &architecture)) {
+          err_ << "Error converting " << arch_row[4] << " to architecture.";
+          return false;
+        }
+
+        variant_def.AddArchDef(category, family, op, architecture);
+      }
+    }
+
+    for (const auto& tgt_row : target_rows) {
+      const auto row_name = tgt_row[0];
+      if (row_name == name) {
+        uint32_t target;
+        std::vector<uint32_t> features;
+
+        if (!strToInt(tgt_row[1], &target)) {
+          err_ << "Error converting " << tgt_row[1] << " to target.";
+          return false;
+        }
+
+        // get features as FEAT_SEP-delimited integers
+
+        std::stringstream feat_stream(tgt_row[2]);
+        std::string feat;
+        while (std::getline(feat_stream, feat, FEAT_SEP)) {
+          uint32_t ufeat;
+          // if (!(std::stringstream(feat) >> ufeat)) {
+          if (!strToInt(feat, &ufeat)) {
+            err_ << "Error converting " << feat << " in " << tgt_row[2]
+                 << " to target feature.";
+            return false;
+          }
+          features.push_back(ufeat);
+        }
+
+        variant_def.AddTgtDef(target, features);
+      }
+    }
+
+    if (options.GetHasFnVarCapabilities()) {
+      variant_def.InferCapabilities();
+    }
+
+    variant_defs_.push_back(variant_def);
+  }
+
+  return true;
+}
+
+bool VariantDefs::ProcessVariantDefs() {
+  EnsureBoolType();
+  CollectVarInsts();
+  if (!GenerateFnVarConstants()) {
+    return false;
+  }
+  CollectBaseFnCalls();
+  return true;
+}
+
+void VariantDefs::GenerateHeader(IRContext* linked_context) {
+  linked_context->AddCapability(spv::Capability::SpecConditionalINTEL);
+  linked_context->AddCapability(spv::Capability::FunctionVariantsINTEL);
+  linked_context->AddExtension(std::string(FNVAR_EXT_NAME));
+
+  // Specifies used registry version
+  auto inst =
+      std::make_unique<Instruction>(linked_context, spv::Op::OpModuleProcessed);
+  std::stringstream line;
+  line << "SPV_INTEL_function_variants registry version "
+       << FNVAR_REGISTRY_VERSION;
+  inst->AddOperand(
+      {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(line.str())});
+  linked_context->AddDebug3Inst(std::move(inst));
+}
+
+void VariantDefs::CombineVariantInstructions(IRContext* linked_context) {
+  CombineBaseFnCalls(linked_context);
+  CombineInstructions(linked_context);
+}
+
+void VariantDefs::EnsureBoolType() {
+  for (auto& variant_def : variant_defs_) {
+    Module* module = variant_def.GetModule();
+    IRContext* context = module->context();
+
+    uint32_t bool_id = FindIdOfBoolType(module);
+    if (bool_id == 0) {
+      bool_id = context->TakeNextId();
+      auto variant_bool = std::make_unique<Instruction>(
+          context, spv::Op::OpTypeBool, 0, bool_id,
+          std::initializer_list<opt::Operand>{});
+      module->AddType(std::move(variant_bool));
+    }
+  }
+}
+
+void VariantDefs::CollectVarInsts() {
+  for (size_t i = 0; i < variant_defs_.size(); ++i) {
+    const auto variant_def = variant_defs_[i];
+    const auto* var_mod = variant_def.GetModule();
+
+    var_mod->ForEachInst([this, &i](const Instruction* inst) {
+      if (CanBeFnVarCombined(inst)) {
+        const size_t inst_hash = HashInst(inst);
+        if (fnvar_usage_.find(inst_hash) == fnvar_usage_.end()) {
+          fnvar_usage_.insert({inst_hash, {i}});
+        } else {
+          assert(fnvar_usage_[inst_hash].size() < variant_defs_.size());
+          fnvar_usage_[inst_hash].push_back(i);
+        }
+      }
+    });
+  }
+}
+
+bool VariantDefs::GenerateFnVarConstants() {
+  assert(variant_defs_.size() > 0);
+  assert(variant_defs_[0].IsBase());
+
+  if (variant_defs_.size() == 1) {
+    return true;
+  }
+
+  for (auto& variant_def : variant_defs_) {
+    Module* module = variant_def.GetModule();
+    IRContext* context = module->context();
+
+    uint32_t bool_id = FindIdOfBoolType(module);
+    if (bool_id == 0) {
+      // add a bool type if not present already
+      bool_id = context->TakeNextId();
+      auto variant_bool = std::make_unique<Instruction>(
+          context, spv::Op::OpTypeBool, 0, bool_id,
+          std::initializer_list<opt::Operand>{});
+      module->AddType(std::move(variant_bool));
+    }
+
+    // Spec constant architecture and target
+
+    std::vector<uint32_t> spec_const_arch_ids;
+    for (const auto& arch_def : variant_def.GetArchDefs()) {
+      const uint32_t spec_const_arch_id = context->TakeNextId();
+      spec_const_arch_ids.push_back(spec_const_arch_id);
+
+      auto inst = std::make_unique<Instruction>(
+          context, spv::Op::OpSpecConstantArchitectureINTEL, bool_id,
+          spec_const_arch_id,
+          std::initializer_list<opt::Operand>{
+              {SPV_OPERAND_TYPE_LITERAL_INTEGER, {arch_def.category}},
+              {SPV_OPERAND_TYPE_LITERAL_INTEGER, {arch_def.family}},
+              // Using spec op opcode here expects then next operand to be
+              // a type:
+              {SPV_OPERAND_TYPE_LITERAL_INTEGER, {arch_def.op}},
+              {SPV_OPERAND_TYPE_LITERAL_INTEGER, {arch_def.architecture}},
+          });
+      module->AddType(std::move(inst));
+    }
+
+    std::vector<uint32_t> spec_const_tgt_ids;
+    for (const auto& tgt_def : variant_def.GetTgtDefs()) {
+      const uint32_t spec_const_tgt_id = context->TakeNextId();
+      spec_const_tgt_ids.push_back(spec_const_tgt_id);
+
+      auto inst = std::make_unique<Instruction>(
+          context, spv::Op::OpSpecConstantTargetINTEL, bool_id,
+          spec_const_tgt_id,
+          std::initializer_list<opt::Operand>{
+              {SPV_OPERAND_TYPE_LITERAL_INTEGER, {tgt_def.target}},
+          });
+      for (const auto& feat : tgt_def.features) {
+        inst->AddOperand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {feat}});
+      }
+      module->AddType(std::move(inst));
+    }
+
+    std::vector<uint32_t> spec_const_ids;
+
+    // Spec constant capabilities
+
+    const auto variant_capabilities = variant_def.GetCapabilities();
+    if (!variant_capabilities.empty()) {
+      const uint32_t spec_const_cap_id = context->TakeNextId();
+      auto inst = std::make_unique<Instruction>(
+          context, spv::Op::OpSpecConstantCapabilitiesINTEL, bool_id,
+          spec_const_cap_id, std::initializer_list<opt::Operand>{});
+      for (const auto& cap : variant_capabilities) {
+        inst->AddOperand({SPV_OPERAND_TYPE_CAPABILITY, {uint32_t(cap)}});
+      }
+      module->AddType(std::move(inst));
+      spec_const_ids.push_back(spec_const_cap_id);
+    }
+
+    // Combine architectures such that, for the same module, those with the same
+    // category and family are combined with AND and different cat/fam are
+    // combined with OR.
+    // This lets you create combinations like "architecture between X and Y".
+
+    // map (category, family) -> IDs
+    std::map<std::pair<uint32_t, uint32_t>, std::vector<uint32_t>> arch_map_and;
+
+    for (size_t i = 0; i < spec_const_arch_ids.size(); ++i) {
+      const auto& arch_def = variant_def.GetArchDefs()[i];
+      const auto id = spec_const_arch_ids[i];
+      const auto key = std::make_pair(arch_def.category, arch_def.family);
+      if (arch_map_and.find(key) == arch_map_and.end()) {
+        arch_map_and[key] = {id};
+      } else {
+        arch_map_and[key].push_back(id);
+      }
+    }
+
+    std::vector<uint32_t> arch_ids_or;
+    for (const auto& it : arch_map_and) {
+      const auto id = CombineIds(context, it.second, spv::Op::OpLogicalAnd);
+      if (id > 0) {
+        arch_ids_or.push_back(id);
+      }
+    }
+
+    const uint32_t spec_const_arch_id =
+        CombineIds(context, arch_ids_or, spv::Op::OpLogicalOr);
+    if (spec_const_arch_id > 0) {
+      spec_const_ids.push_back(spec_const_arch_id);
+    }
+
+    const uint32_t spec_const_tgt_id =
+        CombineIds(context, spec_const_tgt_ids, spv::Op::OpLogicalOr);
+    if (spec_const_tgt_id > 0) {
+      spec_const_ids.push_back(spec_const_tgt_id);
+    }
+
+    uint32_t combined_spec_const_id =
+        CombineIds(context, spec_const_ids, spv::Op::OpLogicalAnd);
+    if (combined_spec_const_id == 0) {
+      // If the variant module has no constraints, use SpecConstantTrue
+      combined_spec_const_id = context->TakeNextId();
+      auto inst = std::make_unique<Instruction>(
+          context, spv::Op::OpSpecConstantTrue, bool_id, combined_spec_const_id,
+          std::initializer_list<opt::Operand>{});
+      context->module()->AddType(std::move(inst));
+    }
+    assert(combined_spec_const_id != 0);
+
+    // Add a name the combined boolean ID so we can look it up after the IDs are
+    // shifted
+    auto inst = std::make_unique<Instruction>(context, spv::Op::OpName);
+    inst->AddOperand({SPV_OPERAND_TYPE_ID, {combined_spec_const_id}});
+    std::vector<uint32_t> str_words;
+    utils::AppendToVector(variant_def.GetName(), &str_words);
+    inst->AddOperand({SPV_OPERAND_TYPE_LITERAL_STRING, {str_words}});
+    module->AddDebug2Inst(std::move(inst));
+
+    // Annotate all instructions in the types section (eg. constants) with
+    // ConditionalINTEL, unless they can be shared between variant_defs_ (eg.
+    // types). Spec constants are excluded because they might have been
+    // generated by this extension.
+    for (const auto& type_inst : module->types_values()) {
+      if (!CanBeFnVarCombined(&type_inst) &&
+          !spvOpcodeIsSpecConstant(type_inst.opcode())) {
+        DecorateConditional(context, type_inst.result_id(),
+                            combined_spec_const_id);
+      }
+    }
+  }
+
+  // Annotate functions with ConditionalINTEL
+
+  for (const auto& base_fn : *variant_defs_[0].GetModule()) {
+    // For each function of the base module, find matching variant functions in
+    // other modules
+
+    auto base_fn_name = GetFnName(base_fn.DefInst());
+    if (base_fn_name.empty()) {
+      err_ << "Could not find name of a function " << base_fn.result_id()
+           << " in a base module " << variant_defs_[0].GetName()
+           << ". To be usable by SPV_INTEL_function_variants, a function "
+              "must either have an entry point or an export "
+              "LinkAttribute decoration.";
+      return false;
+    }
+
+    bool base_fn_needs_conditional = false;
+    for (size_t i = 1; i < variant_defs_.size(); ++i) {
+      const auto& variant_def = variant_defs_[i];
+      auto* variant_module = variant_def.GetModule();
+      auto* variant_context = variant_module->context();
+
+      for (const auto& var_fn : *variant_module) {
+        auto var_fn_name = GetFnName(var_fn.DefInst());
+        if (var_fn_name.empty()) {
+          err_ << "Could not find name of a function " << var_fn.result_id()
+               << " in a base module " << variant_def.GetName()
+               << ". To be usable by SPV_INTEL_function_variants, a function "
+                  "must either have an entry point or an export "
+                  "LinkAttribute decoration.";
+          return false;
+        }
+
+        if (base_fn_name == var_fn_name) {
+          base_fn_needs_conditional = true;
+        }
+
+        // each function in a variant module gets a ConditionalINTEL decoration
+
+        uint32_t spec_const_id =
+            FindSpecConstByName(variant_module, variant_def.GetName());
+        assert(spec_const_id != 0);
+        DecorateConditional(variant_context, var_fn.result_id(), spec_const_id);
+        ConvertEPToConditional(variant_module, var_fn, spec_const_id);
+      }
+    }
+
+    if (base_fn_needs_conditional) {
+      // only a base function that has a variant in another module gets a
+      // ConditionalINTEL decoration, the others are common for all
+      // variant_defs_
+      auto* base_module = variant_defs_[0].GetModule();
+      auto* base_context = base_module->context();
+      uint32_t spec_const_id =
+          FindSpecConstByName(base_module, variant_defs_[0].GetName());
+      assert(spec_const_id != 0);
+      DecorateConditional(base_context, base_fn.result_id(), spec_const_id);
+      ConvertEPToConditional(base_module, base_fn, spec_const_id);
+    }
+  }
+
+  return true;
+}
+
+void VariantDefs::CollectBaseFnCalls() {
+  auto* base_mod = variant_defs_[0].GetModule();
+  assert(variant_defs_[0].IsBase());
+  const auto* base_def_use_mgr = base_mod->context()->get_def_use_mgr();
+
+  base_mod->ForEachInst([this, &base_def_use_mgr](const Instruction* inst) {
+    if (inst->opcode() == spv::Op::OpFunctionCall) {
+      // For each function call in base module, get the function name
+      const auto fn_id = inst->GetOperand(2).AsId();
+      const auto* called_fn_inst = base_def_use_mgr->GetDef(fn_id);
+      assert(called_fn_inst != nullptr);
+      const auto called_fn_name = GetFnName(*called_fn_inst);
+      assert(!called_fn_name.empty());
+
+      std::vector<std::pair<std::string, const opt::Function*>> called_fns;
+      for (size_t i = 1; i < variant_defs_.size(); ++i) {
+        // ... then see in which variant the called function was defined
+        const auto& variant_def = variant_defs_[i];
+        assert(!variant_def.IsBase());
+
+        for (const auto& fn : *variant_def.GetModule()) {
+          const auto fn_name = GetFnName(fn.DefInst());
+          if (fn_name == called_fn_name) {
+            called_fns.push_back(std::make_pair(variant_def.GetName(), &fn));
+          }
+        }
+      }
+
+      if (!called_fns.empty()) {
+        base_fn_calls_[inst->result_id()] = called_fns;
+      }
+    }
+  });
+}
+
+void VariantDefs::CombineBaseFnCalls(IRContext* linked_context) {
+  for (auto kv : base_fn_calls_) {
+    const uint32_t call_id = kv.first;
+    const auto called_fns = kv.second;
+
+    if (called_fns.empty()) {
+      return;
+    }
+
+    opt::BasicBlock* fn_call_bb = linked_context->get_instr_block(call_id);
+
+    Instruction* found_call_inst = nullptr;
+    auto bb_iter = fn_call_bb->begin();
+    while (bb_iter != fn_call_bb->end() && found_call_inst == nullptr) {
+      if (bb_iter->HasResultId() && bb_iter->result_id() == call_id) {
+        found_call_inst = &*bb_iter;
+      }
+      ++bb_iter;
+    }
+
+    if (found_call_inst == nullptr) {
+      return;
+    }
+
+    const auto base_spec_const_id = FindSpecConstByName(
+        variant_defs_[0].GetModule(), variant_defs_[0].GetName());
+    const auto base_type_op = found_call_inst->context()
+                                  ->get_def_use_mgr()
+                                  ->GetDef(found_call_inst->type_id())
+                                  ->opcode();
+    const auto base_call_id = found_call_inst->result_id();
+
+    // decorate the base call with ConditionalINTEL
+    DecorateConditional(linked_context, base_call_id, base_spec_const_id);
+
+    // Add OpFunctionCall for each variant
+    Instruction* last_inst = found_call_inst;
+    std::vector<std::pair<uint32_t, uint32_t>> var_call_ids;
+    for (const auto& kv2 : called_fns) {
+      const std::string var_name = kv2.first;
+      const opt::Function* fn = kv2.second;
+      const uint32_t spec_const_id =
+          FindSpecConstByName(linked_context->module(), var_name);
+      assert(spec_const_id != 0);
+      const uint32_t var_call_id = linked_context->TakeNextId();
+      var_call_ids.push_back(std::make_pair(spec_const_id, var_call_id));
+
+      auto* var_call_inst = found_call_inst->Clone(linked_context);
+      var_call_inst->SetResultId(var_call_id);
+      var_call_inst->SetOperand(2, {fn->result_id()});
+      var_call_inst->InsertAfter(last_inst);
+      linked_context->set_instr_block(var_call_inst, fn_call_bb);
+      last_inst = var_call_inst;
+
+      // decorate the variant call with ConditionalINTEL
+      DecorateConditional(linked_context, var_call_id, spec_const_id);
+    }
+
+    if (base_type_op != spv::Op::OpTypeVoid) {
+      // Add OpConditionalCopyObjectINTEL combining the function calls
+      const uint32_t result_id = linked_context->TakeNextId();
+      auto conditional_copy_inst = new Instruction(
+          linked_context, spv::Op::OpConditionalCopyObjectINTEL,
+          found_call_inst->type_id(), result_id,
+          {{SPV_OPERAND_TYPE_ID, {base_spec_const_id}},
+           {SPV_OPERAND_TYPE_ID, {found_call_inst->result_id()}}});
+
+      for (const auto& kv3 : var_call_ids) {
+        const auto spec_const_id = kv3.first;
+        const auto var_call_id = kv3.second;
+        conditional_copy_inst->AddOperand(
+            {SPV_OPERAND_TYPE_ID, {spec_const_id}});
+        conditional_copy_inst->AddOperand({SPV_OPERAND_TYPE_ID, {var_call_id}});
+      }
+      conditional_copy_inst->InsertAfter(last_inst);
+      linked_context->set_instr_block(conditional_copy_inst, fn_call_bb);
+      last_inst = conditional_copy_inst;
+
+      // In all remaining instructions within the basic block, replace all
+      // usages of the base call ID with the result of
+      // OpConditionalCopyObjectINTEL
+      do {
+        last_inst = last_inst->NextNode();
+        last_inst->ForEachInId([base_call_id, result_id](uint32_t* id) {
+          if (*id == base_call_id) {
+            *id = result_id;
+          }
+        });
+      } while (last_inst != nullptr && *last_inst != *fn_call_bb->tail());
+    }
+  }
+
+  // Combine spec consts for the base module (base module is activated if all
+  // variant defs are inactive AND the base module constraints are satisfied)
+
+  std::vector<uint32_t> var_spec_const_ids;
+  for (const auto& variant_def : variant_defs_) {
+    if (variant_def.IsBase()) {
+      continue;
+    }
+
+    const auto id =
+        FindSpecConstByName(linked_context->module(), variant_def.GetName());
+    assert(id != 0);
+    var_spec_const_ids.push_back(id);
+  }
+  const uint32_t base_or_id =
+      CombineIds(linked_context, var_spec_const_ids, spv::Op::OpLogicalOr);
+
+  if (base_or_id != 0) {
+    const uint32_t bool_id = FindIdOfBoolType(linked_context->module());
+    assert(bool_id != 0);
+
+    const uint32_t base_not_id = linked_context->TakeNextId();
+    auto spec_const_op_inst = std::make_unique<Instruction>(
+        linked_context, spv::Op::OpSpecConstantOp, bool_id, base_not_id,
+        std::initializer_list<opt::Operand>{
+            {SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER,
+             {(uint32_t)(spv::Op::OpLogicalNot)}},
+            {SPV_OPERAND_TYPE_ID, {base_or_id}}});
+    linked_context->module()->AddType(std::move(spec_const_op_inst));
+
+    // Update any ConditionalINTEL annotations, names and entry points
+    // referencing the old spec const ID to use the new one
+
+    const uint32_t old_base_spec_const_id = FindSpecConstByName(
+        linked_context->module(), variant_defs_[0].GetName());
+    assert(old_base_spec_const_id != 0);
+    const uint32_t base_spec_const_id =
+        CombineIds(linked_context, {old_base_spec_const_id, base_not_id},
+                   spv::Op::OpLogicalAnd);
+
+    for (auto& annot_inst : linked_context->module()->annotations()) {
+      if ((annot_inst.GetSingleWordOperand(1) ==
+           uint32_t(spv::Decoration::ConditionalINTEL)) &&
+          (annot_inst.GetOperand(2).AsId() == old_base_spec_const_id)) {
+        annot_inst.SetOperand(2, {base_spec_const_id});
+      }
+    }
+
+    for (auto& name_inst : linked_context->module()->debugs2()) {
+      if ((name_inst.opcode() == spv::Op::OpName) &&
+          (name_inst.GetOperand(0).AsId() == old_base_spec_const_id)) {
+        name_inst.SetOperand(0, {base_spec_const_id});
+      }
+    }
+
+    for (auto& ep_inst : linked_context->module()->entry_points()) {
+      if ((ep_inst.opcode() == spv::Op::OpConditionalEntryPointINTEL) &&
+          (ep_inst.GetOperand(0).AsId() == old_base_spec_const_id)) {
+        ep_inst.SetOperand(0, {base_spec_const_id});
+      }
+    }
+
+    linked_context->module()->ForEachInst(
+        [old_base_spec_const_id, base_spec_const_id](Instruction* inst) {
+          if (inst->opcode() == spv::Op::OpConditionalCopyObjectINTEL) {
+            inst->ForEachInId(
+                [old_base_spec_const_id, base_spec_const_id](uint32_t* id) {
+                  if (*id == old_base_spec_const_id) {
+                    *id = base_spec_const_id;
+                  }
+                });
+          }
+        });
+  }
+}
+
+void VariantDefs::CombineInstructions(IRContext* linked_context) {
+  // cache for existing variant ID combinations
+  std::map<std::vector<size_t>, uint32_t> spec_const_comb_ids;
+
+  linked_context->module()->ForEachInst(
+      [this, &linked_context, &spec_const_comb_ids](Instruction* inst) {
+        if (!CanBeFnVarCombined(inst)) {
+          return;
+        }
+
+        const size_t inst_hash = HashInst(inst);
+        if (fnvar_usage_.find(inst_hash) != fnvar_usage_.end()) {
+          const std::vector<size_t> var_ids = fnvar_usage_[inst_hash];
+          const uint32_t spec_const_comb_id = CombineVariantDefs(
+              variant_defs_, var_ids, linked_context, spec_const_comb_ids);
+          if (spec_const_comb_id != 0) {
+            if (inst->HasResultId()) {
+              DecorateConditional(linked_context, inst->result_id(),
+                                  spec_const_comb_id);
+            } else if (inst->opcode() == spv::Op::OpCapability) {
+              const uint32_t cap = inst->GetSingleWordOperand(0);
+              inst->SetOpcode(spv::Op::OpConditionalCapabilityINTEL);
+              inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {spec_const_comb_id}},
+                                   {SPV_OPERAND_TYPE_CAPABILITY, {cap}}});
+            } else if (inst->opcode() == spv::Op::OpExtension) {
+              const std::string ext_name = inst->GetOperand(0).AsString();
+              inst->SetOpcode(spv::Op::OpConditionalExtensionINTEL);
+              inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {spec_const_comb_id}},
+                                   {SPV_OPERAND_TYPE_LITERAL_STRING,
+                                    {utils::MakeVector(ext_name)}}});
+            } else {
+              assert(false && "Unsupported");
+            }
+          }
+        }
+      });
+}
+
+}  // namespace spvtools
diff --git a/source/link/fnvar.h b/source/link/fnvar.h
new file mode 100644
index 0000000..49db030
--- /dev/null
+++ b/source/link/fnvar.h
@@ -0,0 +1,244 @@
+// Copyright 2025 The Khronos Group 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.
+
+// Implementation of generating multitarget modules according to the
+// *SPV_INTEL_function_variants* extension
+//
+// Multitarget module is generated by linking separate modules: a base module
+// and variant modules containing device-specific variants of the functions in
+// the base module. The behavior is controlled by Comma-Separated Values (CSV)
+// files passed to the following flags:
+// --fnvar-targets: Required columns:
+//   module   - module file name
+//   target   - device target ISA value
+//   features - feature values for the target separated by '/' (FEAT_SEP)
+// --fnvar-architectures: Required columns:
+//   module       - module file name
+//   category     - device category value
+//   family       - device family value
+//   op           - opcode of the comparison instruction
+//   architecture - device architecture
+// The values (except module) are decimal strings with their meaning defined in
+// the 'targets registry' as described in the extension spec. The decimal
+// strings may only encode unsigned 32-bit integers (characters 0-9), possibly
+// with leading zeros.
+//
+// In addition, --fnvar-capabilities generates OpSpecConstantCapabilitiesINTEL
+// for each module with operands corresponding to the module's capabilities.
+//
+// Each line in the targets/architectures CSV file defines one
+// OpSpecConstant<Target/Architecture>INTEL instruction, the columns correspond
+// to the operands of these instructions. One module can have multiple lines, in
+// which case they are combined into a single boolean spec constant using
+// OpSpecConstantOp and OpLogicalOr (except when category and family in the
+// architectures CSV are the same, then the lines are combined with
+// OpLogicalAnd). For example, the following architectures CSV
+//
+//     module,category,family,op,architecture
+//     foo.spv,1,7,174,1
+//     foo.spv,1,7,178,3
+//     foo.spv,1,8,170,1
+//
+// is combined as follows:
+//
+//          %53 = OpSpecConstantArchitectureINTEL %bool 1 7 174 1
+//          %54 = OpSpecConstantArchitectureINTEL %bool 1 7 178 3
+//          %55 = OpSpecConstantArchitectureINTEL %bool 1 8 170 1
+//          %56 = OpSpecConstantOp %bool LogicalAnd %53 %54
+//     %foo_spv = OpSpecConstantOp %bool LogicalOr %55 %56
+//
+// The %foo_spv is annotated with OpName "foo.spv" (the module's name) which
+// serves as an identifier to find the constant later. We cannot use IDs for it
+// because the IDs get shifted during linking.
+//
+// The first module passed to `spirv-link` is considered the 'base' module. For
+// example, if base module defines functions 'foo' and 'bar' and the other
+// modules define only 'foo', only the 'foo' is treated as a function variant
+// guarded by spec constants. The 'bar' function will be untouched and therefore
+// present for all variants. The function variants are matched by name, and
+// therefore they must either have an entry point, or an Export linkage
+// attribute.
+
+#ifndef FNVAR_H
+#define FNVAR_H
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "source/opt/ir_context.h"
+#include "source/opt/module.h"
+#include "spirv-tools/linker.hpp"
+
+namespace spvtools {
+
+using opt::IRContext;
+using opt::Module;
+
+// Map of instruction hash -> which variants are using the instruction (denoted
+// by the index to the variants vector)
+using FnVarUsage = std::unordered_map<size_t, std::vector<size_t>>;
+
+// Map of base function call ID -> variant functions corresponding to the
+// called function (along with the variant name)
+using BaseFnCalls =
+    std::map<uint32_t,
+             std::vector<std::pair<std::string, const opt::Function*>>>;
+
+constexpr char FNVAR_EXT_NAME[] = "SPV_INTEL_function_variants";
+constexpr uint32_t FNVAR_REGISTRY_VERSION = 0;
+constexpr char FEAT_SEP = '/';
+
+struct FnVarArchDef {
+  uint32_t category;
+  uint32_t family;
+  uint32_t op;
+  uint32_t architecture;
+};
+
+struct FnVarTargetDef {
+  uint32_t target;
+  std::vector<uint32_t> features;
+};
+
+// Definition of a variant
+//
+// Stores architecture and target definitions inferred from lines in the CSV
+// files for a single module (as well as a pointer to the Module).
+class VariantDef {
+ public:
+  VariantDef(bool isbase, std::string nm, Module* mod)
+      : is_base(isbase), name(nm), module(mod) {}
+
+  bool IsBase() const { return this->is_base; }
+  std::string GetName() const { return this->name; }
+  Module* GetModule() const { return this->module; }
+
+  void AddArchDef(uint32_t category, uint32_t family, uint32_t op,
+                  uint32_t architecture) {
+    FnVarArchDef arch_def;
+    arch_def.category = category;
+    arch_def.family = family;
+    arch_def.op = op;
+    arch_def.architecture = architecture;
+    this->arch_defs.push_back(arch_def);
+  }
+  const std::vector<FnVarArchDef>& GetArchDefs() const {
+    return this->arch_defs;
+  }
+
+  void AddTgtDef(uint32_t target, std::vector<uint32_t> features) {
+    FnVarTargetDef tgt_def;
+    tgt_def.target = target;
+    tgt_def.features = features;
+    this->tgt_defs.push_back(tgt_def);
+  }
+  const std::vector<FnVarTargetDef>& GetTgtDefs() const {
+    return this->tgt_defs;
+  }
+
+  void InferCapabilities() {
+    for (const auto& cap_inst : module->capabilities()) {
+      capabilities.insert(spv::Capability(cap_inst.GetOperand(0).words[0]));
+    }
+  }
+  const std::set<spv::Capability>& GetCapabilities() const {
+    return this->capabilities;
+  }
+
+ private:
+  bool is_base;
+  std::string name;
+  Module* module;
+  std::vector<FnVarTargetDef> tgt_defs;
+  std::vector<FnVarArchDef> arch_defs;
+  std::set<spv::Capability> capabilities;
+};
+
+// Collection of VariantDef instances
+//
+// Apart from being a wrapper around a vector of VariantDef instances, it
+// defines the main API for generating SPV_INTEL_function_variants instructions
+// based on the CSV files.
+class VariantDefs {
+ public:
+  // Returns last error message.
+  std::string GetErr() { return err_.str(); }
+
+  // Processes CSV files passed to the CLI and populate _variants.
+  //
+  // Returns true on success, false on error.
+  bool ProcessFnVar(const LinkerOptions& options,
+                    const std::vector<Module*>& modules);
+
+  // Analyses each variant def module and generates those instructions that are
+  // module-specific, ie., not requiring knowledge from other modules.
+  //
+  // Returns true on success, false on error.
+  bool ProcessVariantDefs();
+
+  // Generates basic instructions required for this extension to work.
+  void GenerateHeader(IRContext* linked_context);
+
+  // Generates instructions from this extension that result from combining
+  // several variant def modules.
+  void CombineVariantInstructions(IRContext* linked_context);
+
+ private:
+  // Adds a boolean type to every module if there is none.
+  //
+  // These are necessary for spec constants.
+  void EnsureBoolType();
+
+  // Collects which combinable instructions are defined in which modules
+  void CollectVarInsts();
+
+  // Generates OpSpecConstant<Target/Architecture/Capabilities>INTEL and
+  // combines them as necessary. Also converts entry points to conditional ones
+  // and decorates module-specific instructions with ConditionalINTEL.
+  //
+  // Returns true on success, false on error.
+  bool GenerateFnVarConstants();
+
+  // Determines which functions in the base module are called by which function
+  // variants.
+  void CollectBaseFnCalls();
+
+  // Combines OpFunctionCall instructions collected with CollectBaseFnCalls()
+  // using conditional copy.
+  void CombineBaseFnCalls(IRContext* linked_context);
+
+  // Decorates instructions shared between modules with ConditionalINTEL or
+  // generates conditional capabilities and extensions, depending on which
+  // variants are used by each.
+  void CombineInstructions(IRContext* linked_context);
+
+  // Accumulates all errors encountered during processing.
+  std::stringstream err_;
+
+  // Collection of VariantDef instances
+  std::vector<VariantDef> variant_defs_;
+
+  // Used for combining OpFunctionCall instructions
+  BaseFnCalls base_fn_calls_;
+
+  // Used for determining which function variant uses which (applicable)
+  // instruction
+  FnVarUsage fnvar_usage_;
+};
+
+}  // namespace spvtools
+
+#endif  // FNVAR_H
diff --git a/source/link/linker.cpp b/source/link/linker.cpp
index e6aa72e..002cc41 100644
--- a/source/link/linker.cpp
+++ b/source/link/linker.cpp
@@ -15,9 +15,10 @@
 #include "spirv-tools/linker.hpp"
 
 #include <algorithm>
+#include <cstdint>
 #include <cstdio>
 #include <cstring>
-#include <iostream>
+#include <functional>
 #include <memory>
 #include <numeric>
 #include <string>
@@ -26,19 +27,18 @@
 #include <utility>
 #include <vector>
 
-#include "source/assembly_grammar.h"
+#include "fnvar.h"
 #include "source/diagnostic.h"
 #include "source/opt/build_module.h"
 #include "source/opt/compact_ids_pass.h"
 #include "source/opt/decoration_manager.h"
 #include "source/opt/ir_builder.h"
-#include "source/opt/ir_loader.h"
 #include "source/opt/pass_manager.h"
 #include "source/opt/remove_duplicates_pass.h"
 #include "source/opt/remove_unused_interface_variables_pass.h"
 #include "source/opt/type_manager.h"
 #include "source/spirv_constant.h"
-#include "source/spirv_target_env.h"
+#include "source/table2.h"
 #include "source/util/make_unique.h"
 #include "source/util/string_utils.h"
 #include "spirv-tools/libspirv.hpp"
@@ -103,7 +103,6 @@
 // |linked_context| should not be null.
 spv_result_t MergeModules(const MessageConsumer& consumer,
                           const std::vector<Module*>& in_modules,
-                          const AssemblyGrammar& grammar,
                           IRContext* linked_context);
 
 // Compute all pairs of import and export and return it in |linkings_to_do|.
@@ -246,7 +245,6 @@
 
 spv_result_t MergeModules(const MessageConsumer& consumer,
                           const std::vector<Module*>& input_modules,
-                          const AssemblyGrammar& grammar,
                           IRContext* linked_context) {
   spv_position_t position = {};
 
@@ -294,29 +292,33 @@
     const uint32_t module_addressing_model =
         memory_model_inst->GetSingleWordOperand(0u);
     if (module_addressing_model != linked_addressing_model) {
-      spv_operand_desc linked_desc = nullptr, module_desc = nullptr;
-      grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL,
-                            linked_addressing_model, &linked_desc);
-      grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL,
-                            module_addressing_model, &module_desc);
+      const spvtools::OperandDesc* linked_desc = nullptr;
+      const spvtools::OperandDesc* module_desc = nullptr;
+      spvtools::LookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL,
+                              linked_addressing_model, &linked_desc);
+      spvtools::LookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL,
+                              module_addressing_model, &module_desc);
       return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL)
-             << "Conflicting addressing models: " << linked_desc->name
+             << "Conflicting addressing models: " << linked_desc->name().data()
              << " (input modules 1 through " << i << ") vs "
-             << module_desc->name << " (input module " << (i + 1) << ").";
+             << module_desc->name().data() << " (input module " << (i + 1)
+             << ").";
     }
 
     const uint32_t module_memory_model =
         memory_model_inst->GetSingleWordOperand(1u);
     if (module_memory_model != linked_memory_model) {
-      spv_operand_desc linked_desc = nullptr, module_desc = nullptr;
-      grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, linked_memory_model,
-                            &linked_desc);
-      grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, module_memory_model,
-                            &module_desc);
+      const spvtools::OperandDesc* linked_desc = nullptr;
+      const spvtools::OperandDesc* module_desc = nullptr;
+      spvtools::LookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL,
+                              linked_memory_model, &linked_desc);
+      spvtools::LookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL,
+                              module_memory_model, &module_desc);
       return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL)
-             << "Conflicting memory models: " << linked_desc->name
+             << "Conflicting memory models: " << linked_desc->name().data()
              << " (input modules 1 through " << i << ") vs "
-             << module_desc->name << " (input module " << (i + 1) << ").";
+             << module_desc->name().data() << " (input module " << (i + 1)
+             << ").";
     }
   }
   linked_module->SetMemoryModel(std::unique_ptr<Instruction>(
@@ -326,18 +328,21 @@
   for (const auto& module : input_modules)
     for (const auto& inst : module->entry_points()) {
       const uint32_t model = inst.GetSingleWordInOperand(0);
-      const std::string name = inst.GetInOperand(2).AsString();
+      const std::string name =
+          inst.opcode() == spv::Op::OpConditionalEntryPointINTEL
+              ? inst.GetOperand(3).AsString()
+              : inst.GetOperand(2).AsString();
       const auto i = std::find_if(
           entry_points.begin(), entry_points.end(),
           [model, name](const std::pair<uint32_t, std::string>& v) {
             return v.first == model && v.second == name;
           });
       if (i != entry_points.end()) {
-        spv_operand_desc desc = nullptr;
-        grammar.lookupOperand(SPV_OPERAND_TYPE_EXECUTION_MODEL, model, &desc);
+        const spvtools::OperandDesc* desc = nullptr;
+        spvtools::LookupOperand(SPV_OPERAND_TYPE_EXECUTION_MODEL, model, &desc);
         return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL)
                << "The entry point \"" << name << "\", with execution model "
-               << desc->name << ", was already defined.";
+               << desc->name().data() << ", was already defined.";
       }
       linked_module->AddEntryPoint(
           std::unique_ptr<Instruction>(inst.Clone(linked_context)));
@@ -420,6 +425,7 @@
 
   std::vector<LinkageSymbolInfo> imports;
   std::unordered_map<std::string, std::vector<LinkageSymbolInfo>> exports;
+  std::unordered_map<std::string, LinkageSymbolInfo> linkonce;
 
   // Figure out the imports and exports
   for (const auto& decoration : linked_context.annotations()) {
@@ -478,10 +484,24 @@
              << " LinkageAttributes; " << id << " is neither of them.\n";
     }
 
-    if (spv::LinkageType(type) == spv::LinkageType::Import)
+    if (spv::LinkageType(type) == spv::LinkageType::Import) {
       imports.push_back(symbol_info);
-    else if (spv::LinkageType(type) == spv::LinkageType::Export)
+    } else if (spv::LinkageType(type) == spv::LinkageType::Export) {
       exports[symbol_info.name].push_back(symbol_info);
+    } else if (spv::LinkageType(type) == spv::LinkageType::LinkOnceODR) {
+      if (linkonce.find(symbol_info.name) == linkonce.end())
+        linkonce[symbol_info.name] = symbol_info;
+    }
+  }
+
+  for (const auto& possible_export : linkonce) {
+    if (exports.find(possible_export.first) == exports.end())
+      exports[possible_export.first].push_back(possible_export.second);
+    else
+      return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
+             << "Combination of Export and LinkOnceODR is not allowed, found "
+                "for \""
+             << possible_export.second.name << "\".";
   }
 
   // Find the import/export pairs
@@ -661,8 +681,10 @@
       if (inst->opcode() == spv::Op::OpDecorate &&
           spv::Decoration(inst->GetSingleWordOperand(1u)) ==
               spv::Decoration::LinkageAttributes &&
-          spv::LinkageType(inst->GetSingleWordOperand(3u)) ==
-              spv::LinkageType::Export) {
+          (spv::LinkageType(inst->GetSingleWordOperand(3u)) ==
+               spv::LinkageType::Export ||
+           spv::LinkageType(inst->GetSingleWordOperand(3u)) ==
+               spv::LinkageType::LinkOnceODR)) {
         linked_context->KillInst(&*inst);
       }
     }
@@ -709,8 +731,7 @@
   if (max_id_bound >= SPV_LIMIT_RESULT_ID_BOUND)
     DiagnosticStream({0u, 0u, 4u}, consumer, "", SPV_WARNING)
         << "The minimum limit of IDs, " << (SPV_LIMIT_RESULT_ID_BOUND - 1)
-        << ", was exceeded:"
-        << " " << max_id_bound << " is the current ID bound.\n"
+        << ", was exceeded: " << max_id_bound << " is the current ID bound.\n"
         << "The resulting module might not be supported by all "
            "implementations.";
 
@@ -721,8 +742,8 @@
   if (num_global_values >= SPV_LIMIT_GLOBAL_VARIABLES_MAX)
     DiagnosticStream(position, consumer, "", SPV_WARNING)
         << "The minimum limit of global values, "
-        << (SPV_LIMIT_GLOBAL_VARIABLES_MAX - 1) << ", was exceeded;"
-        << " " << num_global_values << " global values were found.\n"
+        << (SPV_LIMIT_GLOBAL_VARIABLES_MAX - 1) << ", was exceeded; "
+        << num_global_values << " global values were found.\n"
         << "The resulting module might not be supported by all "
            "implementations.";
 
@@ -834,6 +855,22 @@
     ir_contexts.push_back(std::move(ir_context));
   }
 
+  const bool make_multitarget = !options.GetFnVarArchitecturesCsv().empty() ||
+                                !options.GetFnVarTargetsCsv().empty();
+
+  VariantDefs variant_defs;
+
+  if (make_multitarget) {
+    if (!variant_defs.ProcessFnVar(options, modules)) {
+      return DiagnosticStream(position, consumer, "", SPV_ERROR_FNVAR)
+             << variant_defs.GetErr();
+    }
+    if (!variant_defs.ProcessVariantDefs()) {
+      return DiagnosticStream(position, consumer, "", SPV_ERROR_FNVAR)
+             << variant_defs.GetErr();
+    }
+  }
+
   // Phase 1: Shift the IDs used in each binary so that they occupy a disjoint
   //          range from the other binaries, and compute the new ID bound.
   uint32_t max_id_bound = 0u;
@@ -847,9 +884,12 @@
   IRContext linked_context(c_context->target_env, consumer);
   linked_context.module()->SetHeader(header);
 
+  if (make_multitarget) {
+    variant_defs.GenerateHeader(&linked_context);
+  }
+
   // Phase 3: Merge all the binaries into a single one.
-  AssemblyGrammar grammar(c_context);
-  res = MergeModules(consumer, modules, grammar, &linked_context);
+  res = MergeModules(consumer, modules, &linked_context);
   if (res != SPV_SUCCESS) return res;
 
   if (options.GetVerifyIds()) {
@@ -864,6 +904,10 @@
   opt::Pass::Status pass_res = manager.Run(&linked_context);
   if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA;
 
+  if (make_multitarget) {
+    variant_defs.CombineVariantInstructions(&linked_context);
+  }
+
   // Phase 5: Find the import/export pairs
   LinkageTable linkings_to_do;
   res = GetImportExportPairs(consumer, linked_context,
diff --git a/source/lint/CMakeLists.txt b/source/lint/CMakeLists.txt
index 4704beb..c2636ae 100644
--- a/source/lint/CMakeLists.txt
+++ b/source/lint/CMakeLists.txt
@@ -20,11 +20,6 @@
   lint_divergent_derivatives.cpp
 )
 
-if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")))
-  # Enable parallel builds across four cores for this lib.
-  add_definitions(/MP4)
-endif()
-
 add_library(SPIRV-Tools-lint ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_LINT_SOURCES})
 
 spvtools_default_compile_options(SPIRV-Tools-lint)
diff --git a/source/enum_string_mapping.cpp b/source/mimalloc.cpp
similarity index 65%
copy from source/enum_string_mapping.cpp
copy to source/mimalloc.cpp
index 32361a0..777cd6f 100644
--- a/source/enum_string_mapping.cpp
+++ b/source/mimalloc.cpp
@@ -1,29 +1,15 @@
-// Copyright (c) 2017 Google 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/enum_string_mapping.h"
-
-#include <algorithm>
-#include <cassert>
-#include <cstring>
-#include <string>
-#include <unordered_map>
-
-#include "source/extensions.h"
-
-namespace spvtools {
-
-#include "enum_string_mapping.inc"
-
-}  // namespace spvtools
+// Copyright (c) 2025 The Khronos Group 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 "mimalloc-new-delete.h"

diff --git a/source/name_mapper.cpp b/source/name_mapper.cpp
index 7e5f091..798c609 100644
--- a/source/name_mapper.cpp
+++ b/source/name_mapper.cpp
@@ -1,4 +1,6 @@
 // Copyright (c) 2016 Google Inc.
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -25,6 +27,7 @@
 #include "source/binary.h"
 #include "source/latest_version_spirv_header.h"
 #include "source/parsed_operand.h"
+#include "source/table2.h"
 #include "source/to_string.h"
 #include "spirv-tools/libspirv.h"
 
@@ -36,12 +39,12 @@
 
 FriendlyNameMapper::FriendlyNameMapper(const spv_const_context context,
                                        const uint32_t* code,
-                                       const size_t wordCount)
+                                       const size_t wordCount, uint32_t options)
     : grammar_(AssemblyGrammar(context)) {
   spv_diagnostic diag = nullptr;
   // We don't care if the parse fails.
-  spvBinaryParse(context, this, code, wordCount, nullptr,
-                 ParseInstructionForwarder, &diag);
+  spvBinaryParseWithOptions(context, this, code, wordCount, nullptr,
+                            ParseInstructionForwarder, &diag, options);
   spvDiagnosticDestroy(diag);
 }
 
@@ -209,7 +212,20 @@
     } break;
     case spv::Op::OpTypeFloat: {
       const auto bit_width = inst.words[2];
-      // TODO: Handle optional fpencoding enum once actually used.
+      if (inst.num_words > 3) {
+        if (spv::FPEncoding(inst.words[3]) == spv::FPEncoding::BFloat16KHR) {
+          SaveName(result_id, "bfloat16");
+          break;
+        }
+        if (spv::FPEncoding(inst.words[3]) == spv::FPEncoding::Float8E4M3EXT) {
+          SaveName(result_id, "fp8e4m3");
+          break;
+        }
+        if (spv::FPEncoding(inst.words[3]) == spv::FPEncoding::Float8E5M2EXT) {
+          SaveName(result_id, "fp8e5m2");
+          break;
+        }
+      }
       switch (bit_width) {
         case 16:
           SaveName(result_id, "half");
@@ -241,6 +257,10 @@
       SaveName(result_id,
                std::string("_runtimearr_") + NameForId(inst.words[2]));
       break;
+    case spv::Op::OpTypeNodePayloadArrayAMDX:
+      SaveName(result_id,
+               std::string("_payloadarr_") + NameForId(inst.words[2]));
+      break;
     case spv::Op::OpTypePointer:
       SaveName(result_id, std::string("_ptr_") +
                               NameForEnumOperand(SPV_OPERAND_TYPE_STORAGE_CLASS,
@@ -316,9 +336,9 @@
 
 std::string FriendlyNameMapper::NameForEnumOperand(spv_operand_type_t type,
                                                    uint32_t word) {
-  spv_operand_desc desc = nullptr;
-  if (SPV_SUCCESS == grammar_.lookupOperand(type, word, &desc)) {
-    return desc->name;
+  const spvtools::OperandDesc* desc = nullptr;
+  if (SPV_SUCCESS == spvtools::LookupOperand(type, word, &desc)) {
+    return desc->name().data();
   } else {
     // Invalid input.  Just give something.
     return std::string("StorageClass") + to_string(word);
diff --git a/source/name_mapper.h b/source/name_mapper.h
index 6902141..106a012 100644
--- a/source/name_mapper.h
+++ b/source/name_mapper.h
@@ -63,9 +63,12 @@
  public:
   // Construct a friendly name mapper, and determine friendly names for each
   // defined Id in the specified module.  The module is specified by the code
-  // wordCount, and should be parseable in the specified context.
+  // wordCount, and should be parseable in the specified context.  The options
+  // bitmask is passed to the binary parser; pass
+  // SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES to tolerate unknown
+  // opcodes in the module.
   FriendlyNameMapper(const spv_const_context context, const uint32_t* code,
-                     const size_t wordCount);
+                     const size_t wordCount, uint32_t options = 0);
 
   // Returns a NameMapper which maps ids to the friendly names parsed from the
   // module provided to the constructor.
diff --git a/source/opcode.cpp b/source/opcode.cpp
index 2b25fc3..43fcd5e 100644
--- a/source/opcode.cpp
+++ b/source/opcode.cpp
@@ -1,6 +1,6 @@
 // Copyright (c) 2015-2022 The Khronos Group Inc.
-// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
-// reserved.
+// Modifications Copyright (C) 2020-2024 Advanced Micro Devices, Inc. All
+// rights reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -27,18 +27,10 @@
 #include "source/spirv_constant.h"
 #include "source/spirv_endian.h"
 #include "source/spirv_target_env.h"
+#include "source/table2.h"
 #include "spirv-tools/libspirv.h"
 
 namespace {
-struct OpcodeDescPtrLen {
-  const spv_opcode_desc_t* ptr;
-  uint32_t len;
-};
-
-#include "core.insts-unified1.inc"
-
-static const spv_opcode_table_t kOpcodeTable = {ARRAY_SIZE(kOpcodeTableEntries),
-                                                kOpcodeTableEntries};
 
 // Represents a vendor tool entry in the SPIR-V XML Registry.
 struct VendorTool {
@@ -78,115 +70,6 @@
   }
 }
 
-spv_result_t spvOpcodeTableGet(spv_opcode_table* pInstTable, spv_target_env) {
-  if (!pInstTable) return SPV_ERROR_INVALID_POINTER;
-
-  // Descriptions of each opcode.  Each entry describes the format of the
-  // instruction that follows a particular opcode.
-
-  *pInstTable = &kOpcodeTable;
-  return SPV_SUCCESS;
-}
-
-spv_result_t spvOpcodeTableNameLookup(spv_target_env env,
-                                      const spv_opcode_table table,
-                                      const char* name,
-                                      spv_opcode_desc* pEntry) {
-  if (!name || !pEntry) return SPV_ERROR_INVALID_POINTER;
-  if (!table) return SPV_ERROR_INVALID_TABLE;
-
-  // TODO: This lookup of the Opcode table is suboptimal! Binary sort would be
-  // preferable but the table requires sorting on the Opcode name, but it's
-  // static const initialized and matches the order of the spec.
-  const size_t nameLength = strlen(name);
-  const auto version = spvVersionForTargetEnv(env);
-  for (uint64_t opcodeIndex = 0; opcodeIndex < table->count; ++opcodeIndex) {
-    const spv_opcode_desc_t& entry = table->entries[opcodeIndex];
-    // We consider the current opcode as available as long as
-    // 1. The target environment satisfies the minimal requirement of the
-    //    opcode; or
-    // 2. There is at least one extension enabling this opcode.
-    //
-    // Note that the second rule assumes the extension enabling this instruction
-    // 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) {
-      // Exact match case.
-      if (nameLength == strlen(entry.name) &&
-          !strncmp(name, entry.name, nameLength)) {
-        *pEntry = &entry;
-        return SPV_SUCCESS;
-      }
-      // Lack of binary search really hurts here. There isn't an easy filter to
-      // apply before checking aliases since we need to handle promotion from
-      // vendor to KHR/EXT and KHR/EXT to core. It would require a sure-fire way
-      // of dropping suffices. Fortunately, most lookup are based on token
-      // value.
-      //
-      // If this was a binary search we could iterate between the lower and
-      // upper bounds.
-      if (entry.numAliases > 0) {
-        for (uint32_t aliasIndex = 0; aliasIndex < entry.numAliases;
-             aliasIndex++) {
-          // Skip Op prefix. Should this be encoded in the table instead?
-          const auto alias = entry.aliases[aliasIndex] + 2;
-          const size_t aliasLength = strlen(alias);
-          if (nameLength == aliasLength && !strncmp(name, alias, nameLength)) {
-            *pEntry = &entry;
-            return SPV_SUCCESS;
-          }
-        }
-      }
-    }
-  }
-
-  return SPV_ERROR_INVALID_LOOKUP;
-}
-
-spv_result_t spvOpcodeTableValueLookup(spv_target_env env,
-                                       const spv_opcode_table table,
-                                       const spv::Op opcode,
-                                       spv_opcode_desc* pEntry) {
-  if (!table) return SPV_ERROR_INVALID_TABLE;
-  if (!pEntry) return SPV_ERROR_INVALID_POINTER;
-
-  const auto beg = table->entries;
-  const auto end = table->entries + table->count;
-
-  spv_opcode_desc_t needle = {"", opcode, 0,     nullptr, 0,       {},  0,
-                              {}, false,  false, 0,       nullptr, ~0u, ~0u};
-
-  auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) {
-    return lhs.opcode < rhs.opcode;
-  };
-
-  // We need to loop here because there can exist multiple symbols for the same
-  // opcode value, and they can be introduced in different target environments,
-  // which means they can have different minimal version requirements.
-  // Assumes the underlying table is already sorted ascendingly according to
-  // opcode value.
-  const auto version = spvVersionForTargetEnv(env);
-  for (auto it = std::lower_bound(beg, end, needle, comp);
-       it != end && it->opcode == opcode; ++it) {
-    // We considers the current opcode as available as long as
-    // 1. The target environment satisfies the minimal requirement of the
-    //    opcode; or
-    // 2. There is at least one extension enabling this opcode.
-    //
-    // Note that the second rule assumes the extension enabling this instruction
-    // is indeed requested in the SPIR-V code; checking that should be
-    // validator's work.
-    if ((version >= it->minVersion && version <= it->lastVersion) ||
-        it->numExtensions > 0u || it->numCapabilities > 0u) {
-      *pEntry = it;
-      return SPV_SUCCESS;
-    }
-  }
-
-  return SPV_ERROR_INVALID_LOOKUP;
-}
-
 void spvInstructionCopy(const uint32_t* words, const spv::Op opcode,
                         const uint16_t wordCount, const spv_endianness_t endian,
                         spv_instruction_t* pInst) {
@@ -205,25 +88,13 @@
 }
 
 const char* spvOpcodeString(const uint32_t opcode) {
-  const auto beg = kOpcodeTableEntries;
-  const auto end = kOpcodeTableEntries + ARRAY_SIZE(kOpcodeTableEntries);
-  spv_opcode_desc_t needle = {"",    static_cast<spv::Op>(opcode),
-                              0,     nullptr,
-                              0,     {},
-                              0,     {},
-                              false, false,
-                              0,     nullptr,
-                              ~0u,   ~0u};
-  auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) {
-    return lhs.opcode < rhs.opcode;
-  };
-  auto it = std::lower_bound(beg, end, needle, comp);
-  if (it != end && it->opcode == spv::Op(opcode)) {
-    return it->name;
+  const spvtools::InstructionDesc* desc = nullptr;
+  if (SPV_SUCCESS !=
+      spvtools::LookupOpcode(static_cast<spv::Op>(opcode), &desc)) {
+    assert(0 && "Unreachable!");
+    return "unknown";
   }
-
-  assert(0 && "Unreachable!");
-  return "unknown";
+  return desc->name().data();
 }
 
 const char* spvOpcodeString(const spv::Op opcode) {
@@ -249,6 +120,10 @@
     case spv::Op::OpSpecConstantComposite:
     case spv::Op::OpSpecConstantCompositeReplicateEXT:
     case spv::Op::OpSpecConstantOp:
+    case spv::Op::OpSpecConstantArchitectureINTEL:
+    case spv::Op::OpSpecConstantTargetINTEL:
+    case spv::Op::OpSpecConstantCapabilitiesINTEL:
+    case spv::Op::OpSpecConstantDataKHR:
       return true;
     default:
       return false;
@@ -265,12 +140,23 @@
     case spv::Op::OpConstantSampler:
     case spv::Op::OpConstantNull:
     case spv::Op::OpConstantFunctionPointerINTEL:
+    case spv::Op::OpConstantStringAMDX:
     case spv::Op::OpSpecConstantTrue:
     case spv::Op::OpSpecConstantFalse:
     case spv::Op::OpSpecConstant:
     case spv::Op::OpSpecConstantComposite:
     case spv::Op::OpSpecConstantCompositeReplicateEXT:
     case spv::Op::OpSpecConstantOp:
+    case spv::Op::OpSpecConstantStringAMDX:
+    case spv::Op::OpGraphConstantARM:
+    case spv::Op::OpAsmTargetINTEL:
+    case spv::Op::OpAsmINTEL:
+    case spv::Op::OpSpecConstantArchitectureINTEL:
+    case spv::Op::OpSpecConstantTargetINTEL:
+    case spv::Op::OpSpecConstantCapabilitiesINTEL:
+    case spv::Op::OpConstantSizeOfEXT:
+    case spv::Op::OpConstantDataKHR:
+    case spv::Op::OpSpecConstantDataKHR:
       return true;
     default:
       return false;
@@ -281,17 +167,6 @@
   return opcode == spv::Op::OpUndef || spvOpcodeIsConstant(opcode);
 }
 
-bool spvOpcodeIsScalarSpecConstant(const spv::Op opcode) {
-  switch (opcode) {
-    case spv::Op::OpSpecConstantTrue:
-    case spv::Op::OpSpecConstantFalse:
-    case spv::Op::OpSpecConstant:
-      return true;
-    default:
-      return false;
-  }
-}
-
 int32_t spvOpcodeIsComposite(const spv::Op opcode) {
   switch (opcode) {
     case spv::Op::OpTypeVector:
@@ -301,6 +176,7 @@
     case spv::Op::OpTypeRuntimeArray:
     case spv::Op::OpTypeCooperativeMatrixNV:
     case spv::Op::OpTypeCooperativeMatrixKHR:
+    case spv::Op::OpTypeVectorIdEXT:
       return true;
     default:
       return false;
@@ -315,9 +191,12 @@
     case spv::Op::OpInBoundsAccessChain:
     case spv::Op::OpUntypedAccessChainKHR:
     case spv::Op::OpUntypedInBoundsAccessChainKHR:
+    case spv::Op::OpBufferPointerEXT:
     case spv::Op::OpFunctionParameter:
     case spv::Op::OpImageTexelPointer:
+    case spv::Op::OpUntypedImageTexelPointerEXT:
     case spv::Op::OpCopyObject:
+    case spv::Op::OpAllocateNodePayloadsAMDX:
     case spv::Op::OpSelect:
     case spv::Op::OpPhi:
     case spv::Op::OpFunctionCall:
@@ -340,10 +219,13 @@
     case spv::Op::OpInBoundsAccessChain:
     case spv::Op::OpUntypedAccessChainKHR:
     case spv::Op::OpUntypedInBoundsAccessChainKHR:
+    case spv::Op::OpBufferPointerEXT:
     case spv::Op::OpFunctionParameter:
     case spv::Op::OpImageTexelPointer:
+    case spv::Op::OpUntypedImageTexelPointerEXT:
     case spv::Op::OpCopyObject:
     case spv::Op::OpRawAccessChainNV:
+    case spv::Op::OpAllocateNodePayloadsAMDX:
       return true;
     default:
       return false;
@@ -377,13 +259,20 @@
     case spv::Op::OpTypeAccelerationStructureNV:
     case spv::Op::OpTypeCooperativeMatrixNV:
     case spv::Op::OpTypeCooperativeMatrixKHR:
+    case spv::Op::OpTypeVectorIdEXT:
     // case spv::Op::OpTypeAccelerationStructureKHR: covered by
     // spv::Op::OpTypeAccelerationStructureNV
     case spv::Op::OpTypeRayQueryKHR:
     case spv::Op::OpTypeHitObjectNV:
+    case spv::Op::OpTypeHitObjectEXT:
     case spv::Op::OpTypeUntypedPointerKHR:
+    case spv::Op::OpTypeNodePayloadArrayAMDX:
     case spv::Op::OpTypeTensorLayoutNV:
     case spv::Op::OpTypeTensorViewNV:
+    case spv::Op::OpTypeTensorARM:
+    case spv::Op::OpTypeTaskSequenceINTEL:
+    case spv::Op::OpTypeGraphARM:
+    case spv::Op::OpTypeBufferEXT:
       return true;
     default:
       // In particular, OpTypeForwardPointer does not generate a type,
@@ -399,6 +288,7 @@
     case spv::Op::OpDecorate:
     case spv::Op::OpDecorateId:
     case spv::Op::OpMemberDecorate:
+    case spv::Op::OpMemberDecorateIdEXT:
     case spv::Op::OpGroupDecorate:
     case spv::Op::OpGroupMemberDecorate:
     case spv::Op::OpDecorateStringGOOGLE:
@@ -421,6 +311,7 @@
     case spv::Op::OpImageSampleProjExplicitLod:
     case spv::Op::OpImageSampleProjDrefImplicitLod:
     case spv::Op::OpImageSampleProjDrefExplicitLod:
+    case spv::Op::OpImageSampleFootprintNV:
     case spv::Op::OpImageFetch:
     case spv::Op::OpImageGather:
     case spv::Op::OpImageDrefGather:
@@ -501,6 +392,7 @@
     case spv::Op::OpTerminateRayKHR:
     case spv::Op::OpIgnoreIntersectionKHR:
     case spv::Op::OpEmitMeshTasksEXT:
+    case spv::Op::OpAbortKHR:
       return true;
     default:
       return false;
@@ -747,6 +639,7 @@
     case spv::Op::OpImageSparseSampleExplicitLod:
     case spv::Op::OpImageSparseSampleDrefImplicitLod:
     case spv::Op::OpImageSparseSampleDrefExplicitLod:
+    case spv::Op::OpImageSampleFootprintNV:
       return true;
     default:
       return false;
diff --git a/source/opcode.h b/source/opcode.h
index 08fc56d..3469c3b 100644
--- a/source/opcode.h
+++ b/source/opcode.h
@@ -35,19 +35,6 @@
 void spvOpcodeSplit(const uint32_t word, uint16_t* word_count,
                     uint16_t* opcode);
 
-// Finds the named opcode in the given opcode table. On success, returns
-// SPV_SUCCESS and writes a handle of the table entry into *entry.
-spv_result_t spvOpcodeTableNameLookup(spv_target_env,
-                                      const spv_opcode_table table,
-                                      const char* name, spv_opcode_desc* entry);
-
-// Finds the opcode by enumerant in the given opcode table. On success, returns
-// SPV_SUCCESS and writes a handle of the table entry into *entry.
-spv_result_t spvOpcodeTableValueLookup(spv_target_env,
-                                       const spv_opcode_table table,
-                                       const spv::Op opcode,
-                                       spv_opcode_desc* entry);
-
 // Copies an instruction's word and fixes the endianness to host native. The
 // source instruction's stream/opcode/endianness is in the words/opcode/endian
 // parameter. The word_count parameter specifies the number of words to copy.
@@ -71,9 +58,6 @@
 // Returns true if the given opcode is a constant or undef.
 bool spvOpcodeIsConstantOrUndef(const spv::Op opcode);
 
-// Returns true if the given opcode is a scalar specialization constant.
-bool spvOpcodeIsScalarSpecConstant(const spv::Op opcode);
-
 // Determines if the given opcode is a composite type. Returns zero if false,
 // non-zero otherwise.
 int32_t spvOpcodeIsComposite(const spv::Op opcode);
diff --git a/source/operand.cpp b/source/operand.cpp
index 5485646..3bb800f 100644
--- a/source/operand.cpp
+++ b/source/operand.cpp
@@ -26,110 +26,9 @@
 #include "source/macro.h"
 #include "source/opcode.h"
 #include "source/spirv_constant.h"
-
-// For now, assume unified1 contains up to SPIR-V 1.3 and no later
-// SPIR-V version.
-// TODO(dneto): Make one set of tables, but with version tags on a
-// per-item basis. https://github.com/KhronosGroup/SPIRV-Tools/issues/1195
-
-#include "operand.kinds-unified1.inc"
+#include "source/table2.h"
 #include "spirv-tools/libspirv.h"
 
-static const spv_operand_table_t kOperandTable = {
-    ARRAY_SIZE(pygen_variable_OperandInfoTable),
-    pygen_variable_OperandInfoTable};
-
-spv_result_t spvOperandTableGet(spv_operand_table* pOperandTable,
-                                spv_target_env) {
-  if (!pOperandTable) return SPV_ERROR_INVALID_POINTER;
-
-  *pOperandTable = &kOperandTable;
-  return SPV_SUCCESS;
-}
-
-spv_result_t spvOperandTableNameLookup(spv_target_env,
-                                       const spv_operand_table table,
-                                       const spv_operand_type_t type,
-                                       const char* name,
-                                       const size_t nameLength,
-                                       spv_operand_desc* pEntry) {
-  if (!table) return SPV_ERROR_INVALID_TABLE;
-  if (!name || !pEntry) return SPV_ERROR_INVALID_POINTER;
-
-  for (uint64_t typeIndex = 0; typeIndex < table->count; ++typeIndex) {
-    const auto& group = table->types[typeIndex];
-    if (type != group.type) continue;
-    for (uint64_t index = 0; index < group.count; ++index) {
-      const auto& entry = group.entries[index];
-      // We consider the current operand as available as long as
-      // it is in the grammar.  It might not be *valid* to use,
-      // but that should be checked by the validator, not by parsing.
-      //
-      // Exact match case
-      if (nameLength == strlen(entry.name) &&
-          !strncmp(entry.name, name, nameLength)) {
-        *pEntry = &entry;
-        return SPV_SUCCESS;
-      }
-
-      // Check the aliases. Ideally we would have a version of the table sorted
-      // by name and then we could iterate between the lower and upper bounds to
-      // restrict the amount comparisons. Fortunately, name-based lookups are
-      // mostly restricted to the assembler.
-      if (entry.numAliases > 0) {
-        for (uint32_t aliasIndex = 0; aliasIndex < entry.numAliases;
-             aliasIndex++) {
-          const auto alias = entry.aliases[aliasIndex];
-          const size_t aliasLength = strlen(alias);
-          if (nameLength == aliasLength && !strncmp(name, alias, nameLength)) {
-            *pEntry = &entry;
-            return SPV_SUCCESS;
-          }
-        }
-      }
-    }
-  }
-
-  return SPV_ERROR_INVALID_LOOKUP;
-}
-
-spv_result_t spvOperandTableValueLookup(spv_target_env,
-                                        const spv_operand_table table,
-                                        const spv_operand_type_t type,
-                                        const uint32_t value,
-                                        spv_operand_desc* pEntry) {
-  if (!table) return SPV_ERROR_INVALID_TABLE;
-  if (!pEntry) return SPV_ERROR_INVALID_POINTER;
-
-  spv_operand_desc_t needle = {"", value,   0,  nullptr, 0,  nullptr,
-                               0,  nullptr, {}, ~0u,     ~0u};
-
-  auto comp = [](const spv_operand_desc_t& lhs, const spv_operand_desc_t& rhs) {
-    return lhs.value < rhs.value;
-  };
-
-  for (uint64_t typeIndex = 0; typeIndex < table->count; ++typeIndex) {
-    const auto& group = table->types[typeIndex];
-    if (type != group.type) continue;
-
-    const auto beg = group.entries;
-    const auto end = group.entries + group.count;
-
-    // Assumes the underlying table is already sorted ascendingly according to
-    // opcode value.
-    auto it = std::lower_bound(beg, end, needle, comp);
-    if (it != end && it->value == value) {
-      // The current operand is considered available as long as
-      // it is in the grammar.  It might not be *valid* to use,
-      // but that should be checked by the validator, not by parsing.
-      *pEntry = it;
-      return SPV_SUCCESS;
-    }
-  }
-
-  return SPV_ERROR_INVALID_LOOKUP;
-}
-
 const char* spvOperandTypeStr(spv_operand_type_t type) {
   switch (type) {
     case SPV_OPERAND_TYPE_ID:
@@ -212,6 +111,7 @@
     case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO:
       return "kernel profiling info";
     case SPV_OPERAND_TYPE_CAPABILITY:
+    case SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY:
       return "capability";
     case SPV_OPERAND_TYPE_RAY_FLAGS:
       return "ray flags";
@@ -237,6 +137,12 @@
       return "cooperative matrix reduce";
     case SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS:
       return "tensor addressing operands";
+    case SPV_OPERAND_TYPE_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS:
+    case SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS:
+      return "matrix multiply accumulate operands";
+    case SPV_OPERAND_TYPE_TENSOR_OPERANDS:
+    case SPV_OPERAND_TYPE_OPTIONAL_TENSOR_OPERANDS:
+      return "tensor operands";
     case SPV_OPERAND_TYPE_INITIALIZATION_MODE_QUALIFIER:
       return "initialization mode qualifier";
     case SPV_OPERAND_TYPE_HOST_ACCESS_QUALIFIER:
@@ -297,6 +203,28 @@
       return "quantization mode";
     case SPV_OPERAND_TYPE_OVERFLOW_MODES:
       return "overflow mode";
+    case SPV_OPERAND_TYPE_COOPERATIVE_VECTOR_MATRIX_LAYOUT:
+      return "cooperative vector matrix layout";
+    case SPV_OPERAND_TYPE_COMPONENT_TYPE:
+      return "component type";
+
+    case SPV_OPERAND_TYPE_KERNEL_PROPERTY_FLAGS:
+      return "kernel property flags";
+    case SPV_OPERAND_TYPE_SHDEBUG100_BUILD_IDENTIFIER_FLAGS:
+      return "NonSemantic.Shader.DebugInfo.100 debug build identifier flags";
+    case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
+      return "NonSemantic.Shader.DebugInfo.100 debug base type attribute "
+             "encoding";
+    case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_COMPOSITE_TYPE:
+      return "NonSemantic.Shader.DebugInfo.100 debug composite type";
+    case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_IMPORTED_ENTITY:
+      return "NonSemantic.Shader.DebugInfo.100 debug imported entity";
+    case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_INFO_FLAGS:
+      return "NonSemantic.Shader.DebugInfo.100 debug info flags";
+    case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_OPERATION:
+      return "NonSemantic.Shader.DebugInfo.100 debug operation";
+    case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_TYPE_QUALIFIER:
+      return "NonSemantic.Shader.DebugInfo.100 debug type qualifier";
 
     case SPV_OPERAND_TYPE_NONE:
       return "NONE";
@@ -308,6 +236,7 @@
 
 void spvPushOperandTypes(const spv_operand_type_t* types,
                          spv_operand_pattern_t* pattern) {
+  // Push them on in backward order.
   const spv_operand_type_t* endTypes;
   for (endTypes = types; *endTypes != SPV_OPERAND_TYPE_NONE; ++endTypes) {
   }
@@ -317,9 +246,22 @@
   }
 }
 
-void spvPushOperandTypesForMask(spv_target_env env,
-                                const spv_operand_table operandTable,
-                                const spv_operand_type_t type,
+void spvPushOperandTypes(
+    const spvtools::utils::Span<const spv_operand_type_t>& types,
+    spv_operand_pattern_t* pattern) {
+  // Push them on in backward order.
+  auto n = types.size();
+  for (auto i = 0u; i < n; i++) {
+    auto type = types[n - 1 - i];
+    // Check against the NONE type, in case the tables have them.
+    // This might be cleaned up.
+    if (type != SPV_OPERAND_TYPE_NONE) {
+      pattern->push_back(type);
+    }
+  }
+}
+
+void spvPushOperandTypesForMask(const spv_operand_type_t type,
                                 const uint32_t mask,
                                 spv_operand_pattern_t* pattern) {
   // Scan from highest bits to lowest bits because we will append in LIFO
@@ -327,10 +269,9 @@
   for (uint32_t candidate_bit = (1u << 31u); candidate_bit;
        candidate_bit >>= 1) {
     if (candidate_bit & mask) {
-      spv_operand_desc entry = nullptr;
-      if (SPV_SUCCESS == spvOperandTableValueLookup(env, operandTable, type,
-                                                    candidate_bit, &entry)) {
-        spvPushOperandTypes(entry->operandTypes, pattern);
+      const spvtools::OperandDesc* entry = nullptr;
+      if (SPV_SUCCESS == spvtools::LookupOperand(type, candidate_bit, &entry)) {
+        spvPushOperandTypes(entry->operands(), pattern);
       }
     }
   }
@@ -396,6 +337,16 @@
     case SPV_OPERAND_TYPE_NAMED_MAXIMUM_NUMBER_OF_REGISTERS:
     case SPV_OPERAND_TYPE_FPENCODING:
     case SPV_OPERAND_TYPE_TENSOR_CLAMP_MODE:
+    case SPV_OPERAND_TYPE_COOPERATIVE_VECTOR_MATRIX_LAYOUT:
+    case SPV_OPERAND_TYPE_COMPONENT_TYPE:
+    case SPV_OPERAND_TYPE_KERNEL_PROPERTY_FLAGS:
+    case SPV_OPERAND_TYPE_SHDEBUG100_BUILD_IDENTIFIER_FLAGS:
+    case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
+    case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_COMPOSITE_TYPE:
+    case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_IMPORTED_ENTITY:
+    case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_INFO_FLAGS:
+    case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_OPERATION:
+    case SPV_OPERAND_TYPE_SHDEBUG100_DEBUG_TYPE_QUALIFIER:
       return true;
     default:
       break;
@@ -415,9 +366,11 @@
     case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS:
     case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
     case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS:
+    case SPV_OPERAND_TYPE_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS:
     case SPV_OPERAND_TYPE_RAW_ACCESS_CHAIN_OPERANDS:
     case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_REDUCE:
     case SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS:
+    case SPV_OPERAND_TYPE_TENSOR_OPERANDS:
       return true;
     default:
       break;
@@ -437,9 +390,12 @@
     case SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER:
     case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT:
     case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS:
+    case SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS:
     case SPV_OPERAND_TYPE_OPTIONAL_CIV:
     case SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS:
     case SPV_OPERAND_TYPE_OPTIONAL_FPENCODING:
+    case SPV_OPERAND_TYPE_OPTIONAL_TENSOR_OPERANDS:
+    case SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY:
       return true;
     default:
       break;
@@ -454,6 +410,7 @@
     case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER:
     case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER_ID:
     case SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER:
+    case SPV_OPERAND_TYPE_VARIABLE_CAPABILITY:
       return true;
     default:
       break;
@@ -485,6 +442,10 @@
       pattern->push_back(SPV_OPERAND_TYPE_LITERAL_INTEGER);
       pattern->push_back(SPV_OPERAND_TYPE_OPTIONAL_ID);
       return true;
+    case SPV_OPERAND_TYPE_VARIABLE_CAPABILITY:
+      pattern->push_back(type);
+      pattern->push_back(SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY);
+      return true;
     default:
       break;
   }
@@ -562,11 +523,15 @@
     case spv::Op::OpSelectionMerge:
     case spv::Op::OpDecorate:
     case spv::Op::OpMemberDecorate:
+    case spv::Op::OpMemberDecorateIdEXT:
     case spv::Op::OpDecorateId:
     case spv::Op::OpDecorateStringGOOGLE:
     case spv::Op::OpMemberDecorateStringGOOGLE:
     case spv::Op::OpBranch:
     case spv::Op::OpLoopMerge:
+    case spv::Op::OpConditionalEntryPointINTEL:
+    case spv::Op::OpConditionalCapabilityINTEL:
+    case spv::Op::OpConditionalExtensionINTEL:
       out = [](unsigned) { return true; };
       break;
     case spv::Op::OpGroupDecorate:
@@ -581,6 +546,11 @@
       out = [](unsigned index) { return index == 2; };
       break;
 
+    case spv::Op::OpConstantFunctionPointerINTEL:
+      // The Function parameter.
+      out = [](unsigned index) { return index == 2; };
+      break;
+
     case spv::Op::OpPhi:
       out = [](unsigned index) { return index > 1; };
       break;
@@ -617,6 +587,9 @@
       // approximate, due to variable operands
       out = [](unsigned index) { return index > 6; };
       break;
+    case spv::Op::OpGraphEntryPointARM:
+      out = [](unsigned index) { return index == 0; };
+      break;
     default:
       out = [](unsigned) { return false; };
       break;
@@ -665,3 +638,17 @@
   }
   return out;
 }
+
+spv_fp_encoding_t spvFPEncodingFromOperandFPEncoding(spv::FPEncoding encoding) {
+  switch (encoding) {
+    case spv::FPEncoding::BFloat16KHR:
+      return SPV_FP_ENCODING_BFLOAT16;
+    case spv::FPEncoding::Float8E4M3EXT:
+      return SPV_FP_ENCODING_FLOAT8_E4M3;
+    case spv::FPEncoding::Float8E5M2EXT:
+      return SPV_FP_ENCODING_FLOAT8_E5M2;
+    case spv::FPEncoding::Max:
+      break;
+  }
+  return SPV_FP_ENCODING_UNKNOWN;
+}
diff --git a/source/operand.h b/source/operand.h
index 3d42a05..9fc9b74 100644
--- a/source/operand.h
+++ b/source/operand.h
@@ -19,6 +19,7 @@
 #include <vector>
 
 #include "source/table.h"
+#include "source/util/span.h"
 #include "spirv-tools/libspirv.h"
 
 // A sequence of operand types.
@@ -35,25 +36,6 @@
 // performance.
 using spv_operand_pattern_t = std::vector<spv_operand_type_t>;
 
-// Finds the named operand in the table. The type parameter specifies the
-// operand's group. A handle of the operand table entry for this operand will
-// be written into *entry.
-spv_result_t spvOperandTableNameLookup(spv_target_env,
-                                       const spv_operand_table table,
-                                       const spv_operand_type_t type,
-                                       const char* name,
-                                       const size_t name_length,
-                                       spv_operand_desc* entry);
-
-// Finds the operand with value in the table. The type parameter specifies the
-// operand's group. A handle of the operand table entry for this operand will
-// be written into *entry.
-spv_result_t spvOperandTableValueLookup(spv_target_env,
-                                        const spv_operand_table table,
-                                        const spv_operand_type_t type,
-                                        const uint32_t value,
-                                        spv_operand_desc* entry);
-
 // Gets the name string of the non-variable operand type.
 const char* spvOperandTypeStr(spv_operand_type_t type);
 
@@ -68,10 +50,10 @@
 bool spvOperandIsVariable(spv_operand_type_t type);
 
 // Append a list of operand types to the end of the pattern vector.
-// The types parameter specifies the source array of types, ending with
-// SPV_OPERAND_TYPE_NONE.
-void spvPushOperandTypes(const spv_operand_type_t* types,
-                         spv_operand_pattern_t* pattern);
+// The types parameter specifies the source span of types.
+void spvPushOperandTypes(
+    const spvtools::utils::Span<const spv_operand_type_t>& types,
+    spv_operand_pattern_t* pattern);
 
 // Appends the operands expected after the given typed mask onto the
 // end of the given pattern.
@@ -81,9 +63,7 @@
 // appear after operands for a more significant bit.
 //
 // If a set bit is unknown, then we assume it has no operands.
-void spvPushOperandTypesForMask(spv_target_env,
-                                const spv_operand_table operand_table,
-                                const spv_operand_type_t mask_type,
+void spvPushOperandTypesForMask(const spv_operand_type_t mask_type,
                                 const uint32_t mask,
                                 spv_operand_pattern_t* pattern);
 
@@ -142,4 +122,7 @@
 std::function<bool(unsigned)> spvDbgInfoExtOperandCanBeForwardDeclaredFunction(
     spv::Op opcode, spv_ext_inst_type_t ext_type, uint32_t key);
 
+// Converts an spv::FPEncoding to spv_fp_encoding_t
+spv_fp_encoding_t spvFPEncodingFromOperandFPEncoding(spv::FPEncoding encoding);
+
 #endif  // SOURCE_OPERAND_H_
diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt
index 4a12e6e..a0ca5b8 100644
--- a/source/opt/CMakeLists.txt
+++ b/source/opt/CMakeLists.txt
@@ -59,6 +59,7 @@
   fold_spec_constant_op_and_composite_pass.h
   freeze_spec_constant_value_pass.h
   function.h
+  graph.h
   graphics_robust_access_pass.h
   if_conversion.h
   inline_exhaustive_pass.h
@@ -74,6 +75,7 @@
   ir_context.h
   ir_loader.h
   licm_pass.h
+  legalize_multidim_array_pass.h
   liveness.h
   local_access_chain_convert_pass.h
   local_redundancy_elimination.h
@@ -104,16 +106,19 @@
   reflect.h
   register_pressure.h
   relax_float_ops_pass.h
+  canonicalize_ids_pass.h
   remove_dontinline_pass.h
   remove_duplicates_pass.h
   remove_unused_interface_variables_pass.h
   replace_desc_array_access_using_var_index.h
   replace_invalid_opc.h
+  resolve_binding_conflicts_pass.h
   scalar_analysis.h
   scalar_analysis_nodes.h
   scalar_replacement_pass.h
   set_spec_constant_default_value_pass.h
   simplification_pass.h
+  split_combined_image_sampler_pass.h
   spread_volatile_semantics.h
   ssa_rewrite_pass.h
   strength_reduction_pass.h
@@ -178,6 +183,7 @@
   fold_spec_constant_op_and_composite_pass.cpp
   freeze_spec_constant_value_pass.cpp
   function.cpp
+  graph.cpp
   graphics_robust_access_pass.cpp
   if_conversion.cpp
   inline_exhaustive_pass.cpp
@@ -192,6 +198,7 @@
   ir_context.cpp
   ir_loader.cpp
   licm_pass.cpp
+  legalize_multidim_array_pass.cpp
   liveness.cpp
   local_access_chain_convert_pass.cpp
   local_redundancy_elimination.cpp
@@ -220,16 +227,19 @@
   redundancy_elimination.cpp
   register_pressure.cpp
   relax_float_ops_pass.cpp
+  canonicalize_ids_pass.cpp
   remove_dontinline_pass.cpp
   remove_duplicates_pass.cpp
   remove_unused_interface_variables_pass.cpp
   replace_desc_array_access_using_var_index.cpp
   replace_invalid_opc.cpp
+  resolve_binding_conflicts_pass.cpp
   scalar_analysis.cpp
   scalar_analysis_simplification.cpp
   scalar_replacement_pass.cpp
   set_spec_constant_default_value_pass.cpp
   simplification_pass.cpp
+  split_combined_image_sampler_pass.cpp
   spread_volatile_semantics.cpp
   ssa_rewrite_pass.cpp
   strength_reduction_pass.cpp
@@ -249,15 +259,18 @@
   wrap_opkill.cpp
 )
 
-if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")))
-  # Enable parallel builds across four cores for this lib
-  add_definitions(/MP4)
-endif()
-
 spvtools_pch(SPIRV_TOOLS_OPT_SOURCES pch_source_opt)
 
+if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD))
+  list(APPEND SPIRV_TOOLS_OPT_SOURCES ${spirv-tools_SOURCE_DIR}/source/mimalloc.cpp)
+endif()
+
 add_library(SPIRV-Tools-opt ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_OPT_SOURCES})
 
+if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD))
+  target_link_libraries(SPIRV-Tools-opt PRIVATE mimalloc-static)
+endif()
+
 spvtools_default_compile_options(SPIRV-Tools-opt)
 target_include_directories(SPIRV-Tools-opt
   PUBLIC
@@ -274,7 +287,13 @@
 spvtools_check_symbol_exports(SPIRV-Tools-opt)
 
 if(ENABLE_SPIRV_TOOLS_INSTALL)
-  install(TARGETS SPIRV-Tools-opt EXPORT SPIRV-Tools-optTargets)
+  set(SPIRV-Tools-opt-InstallTargets SPIRV-Tools-opt)
+
+  if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD))
+    list(APPEND SPIRV-Tools-opt-InstallTargets mimalloc-static)
+  endif()
+
+  install(TARGETS ${SPIRV-Tools-opt-InstallTargets} EXPORT SPIRV-Tools-optTargets)
   export(EXPORT SPIRV-Tools-optTargets FILE SPIRV-Tools-optTargets.cmake)
 
   spvtools_config_package_dir(SPIRV-Tools-opt PACKAGE_DIR)
diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp
index d78d63c..8ca2518 100644
--- a/source/opt/aggressive_dead_code_elim_pass.cpp
+++ b/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -44,6 +44,8 @@
 constexpr uint32_t kExtInstOpInIdx = 1;
 constexpr uint32_t kInterpolantInIdx = 2;
 constexpr uint32_t kCooperativeMatrixLoadSourceAddrInIdx = 0;
+constexpr uint32_t kDebugDeclareVariableInIdx = 3;
+constexpr uint32_t kDebugValueValueInIdx = 3;
 
 // Sorting functor to present annotation instructions in an easy-to-process
 // order. The functor orders by opcode first and falls back on unique id
@@ -126,6 +128,7 @@
     switch (user->opcode()) {
       case spv::Op::OpAccessChain:
       case spv::Op::OpInBoundsAccessChain:
+      case spv::Op::OpUntypedAccessChainKHR:
       case spv::Op::OpCopyObject:
         this->AddStores(func, user->result_id());
         break;
@@ -158,14 +161,16 @@
     if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
       return false;
   }
-  // Only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise
-  // around unknown extended instruction sets even if they are non-semantic
+  // Only allow NonSemantic.Shader.DebugInfo (any version) and
+  // NonSemantic.DebugPrintf; we cannot safely optimise around unknown extended
+  // instruction sets even if they are non-semantic.
   for (auto& inst : context()->module()->ext_inst_imports()) {
     assert(inst.opcode() == spv::Op::OpExtInstImport &&
            "Expecting an import of an extension's instruction set.");
     const std::string extension_name = inst.GetInOperand(0).AsString();
     if (spvtools::utils::starts_with(extension_name, "NonSemantic.") &&
-        (extension_name != "NonSemantic.Shader.DebugInfo.100") &&
+        !spvtools::utils::starts_with(extension_name,
+                                      "NonSemantic.Shader.DebugInfo.") &&
         (extension_name != "NonSemantic.DebugPrintf")) {
       return false;
     }
@@ -270,17 +275,93 @@
   });
 }
 
-bool AggressiveDCEPass::AggressiveDCE(Function* func) {
-  if (func->IsDeclaration()) return false;
+Pass::Status AggressiveDCEPass::AggressiveDCE(Function* func) {
+  if (func->IsDeclaration()) return Pass::Status::SuccessWithoutChange;
   std::list<BasicBlock*> structured_order;
   cfg()->ComputeStructuredOrder(func, &*func->begin(), &structured_order);
   live_local_vars_.clear();
   InitializeWorkList(func, structured_order);
   ProcessWorkList(func);
+  if (ProcessDebugInformation(structured_order) == Pass::Status::Failure)
+    return Pass::Status::Failure;
+  ProcessWorkList(func);
   return KillDeadInstructions(func, structured_order);
 }
 
-bool AggressiveDCEPass::KillDeadInstructions(
+Pass::Status AggressiveDCEPass::ProcessDebugInformation(
+    std::list<BasicBlock*>& structured_order) {
+  for (auto bi = structured_order.begin(); bi != structured_order.end(); bi++) {
+    bool succeeded = (*bi)->WhileEachInst([this](Instruction* inst) {
+      if (!inst->IsNonSemanticInstruction()) return true;
+
+      if (inst->GetShaderDebugOpcode() ==
+          NonSemanticShaderDebugInfoDebugDeclare) {
+        if (IsLive(inst)) return true;
+
+        uint32_t var_id =
+            inst->GetSingleWordInOperand(kDebugDeclareVariableInIdx);
+        auto var_def = get_def_use_mgr()->GetDef(var_id);
+
+        if (IsLive(var_def)) {
+          AddToWorklist(inst);
+          return true;
+        }
+
+        // DebugDeclare Variable is not live. Find the value that was being
+        // stored to this variable. If it's live then create a new DebugValue
+        // with this value. Otherwise let it die in peace.
+        get_def_use_mgr()->ForEachUser(var_id, [this,
+                                                var_id](Instruction* user) {
+          if (user->opcode() == spv::Op::OpStore) {
+            uint32_t stored_value_id = 0;
+            const uint32_t kStoreValueInIdx = 1;
+            stored_value_id = user->GetSingleWordInOperand(kStoreValueInIdx);
+            if (!IsLive(get_def_use_mgr()->GetDef(stored_value_id))) {
+              return true;
+            }
+
+            // value being stored is still live
+            Instruction* next_inst = user->NextNode();
+            bool added =
+                context()->get_debug_info_mgr()->AddDebugValueForVariable(
+                    user, var_id, stored_value_id, user);
+            if (added && next_inst) {
+              auto new_debug_value = next_inst->PreviousNode();
+              AddToWorklist(new_debug_value);
+            }
+          }
+          return true;
+        });
+      } else if (inst->GetShaderDebugOpcode() ==
+                 NonSemanticShaderDebugInfoDebugValue) {
+        uint32_t var_operand_idx = kDebugValueValueInIdx;
+        uint32_t id = inst->GetSingleWordInOperand(var_operand_idx);
+        auto def = get_def_use_mgr()->GetDef(id);
+
+        if (IsLive(def)) {
+          AddToWorklist(inst);
+          return true;
+        }
+
+        // Value operand of DebugValue is not live
+        // Set Value to Undef of appropriate type
+        uint32_t type_id = def->type_id();
+        uint32_t undef_id = Type2Undef(type_id);
+        if (undef_id == 0) return false;
+
+        inst->SetInOperand(var_operand_idx, {undef_id});
+        context()->get_def_use_mgr()->AnalyzeInstUse(inst);
+        AddToWorklist(inst);
+      }
+      return true;
+    });
+
+    if (!succeeded) return Pass::Status::Failure;
+  }
+  return Pass::Status::SuccessWithoutChange;
+}
+
+Pass::Status AggressiveDCEPass::KillDeadInstructions(
     const Function* func, std::list<BasicBlock*>& structured_order) {
   bool modified = false;
   for (auto bi = structured_order.begin(); bi != structured_order.end();) {
@@ -315,6 +396,9 @@
           // Find an undef for the return value and make sure it gets kept by
           // the pass.
           auto undef_id = Type2Undef(func->type_id());
+          if (undef_id == 0) {
+            return Pass::Status::Failure;
+          }
           auto undef = get_def_use_mgr()->GetDef(undef_id);
           live_insts_.Set(undef->unique_id());
           merge_terminator->SetOpcode(spv::Op::OpReturnValue);
@@ -333,7 +417,8 @@
       ++bi;
     }
   }
-  return modified;
+  return modified ? Pass::Status::SuccessWithChange
+                  : Pass::Status::SuccessWithoutChange;
 }
 
 void AggressiveDCEPass::ProcessWorkList(Function* func) {
@@ -593,7 +678,7 @@
   }
 }
 
-void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
+Pass::Status AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
   // Keep all execution modes.
   for (auto& exec : get_module()->execution_modes()) {
     AddToWorklist(&exec);
@@ -668,18 +753,27 @@
   }
   if (debug_global_seen) {
     auto dbg_none = context()->get_debug_info_mgr()->GetDebugInfoNone();
+    if (dbg_none == nullptr) {
+      return Pass::Status::Failure;
+    }
     AddToWorklist(dbg_none);
   }
 
-  // Add top level DebugInfo to worklist
+  // Add DebugInfo which should never be eliminated to worklist
   for (auto& dbg : get_module()->ext_inst_debuginfo()) {
-    auto op = dbg.GetShader100DebugOpcode();
-    if (op == NonSemanticShaderDebugInfo100DebugCompilationUnit ||
-        op == NonSemanticShaderDebugInfo100DebugEntryPoint ||
-        op == NonSemanticShaderDebugInfo100DebugSourceContinued) {
+    auto op = dbg.GetShaderDebugOpcode();
+    if (op == NonSemanticShaderDebugInfoDebugCompilationUnit ||
+        op == NonSemanticShaderDebugInfoDebugEntryPoint ||
+        op == NonSemanticShaderDebugInfoDebugSource ||
+        op == NonSemanticShaderDebugInfoDebugSourceContinued ||
+        op == NonSemanticShaderDebugInfoDebugLocalVariable ||
+        op == NonSemanticShaderDebugInfoDebugExpression ||
+        op == NonSemanticShaderDebugInfoDebugOperation ||
+        op == NonSemanticShaderDebugInfoDebugBuildIdentifier) {
       AddToWorklist(&dbg);
     }
   }
+  return Pass::Status::SuccessWithoutChange;
 }
 
 Pass::Status AggressiveDCEPass::ProcessImpl() {
@@ -707,7 +801,9 @@
   // Eliminate Dead functions.
   bool modified = EliminateDeadFunctions();
 
-  InitializeModuleScopeLiveInstructions();
+  if (InitializeModuleScopeLiveInstructions() == Pass::Status::Failure) {
+    return Pass::Status::Failure;
+  }
 
   // Run |AggressiveDCE| on the remaining functions.  The order does not matter,
   // since |AggressiveDCE| is intra-procedural.  This can mean that function
@@ -715,7 +811,13 @@
   // function will still be in the module after this pass.  We expect this to be
   // rare.
   for (Function& fp : *context()->module()) {
-    modified |= AggressiveDCE(&fp);
+    Pass::Status function_status = AggressiveDCE(&fp);
+    if (function_status == Pass::Status::Failure) {
+      return Pass::Status::Failure;
+    }
+    if (function_status == Pass::Status::SuccessWithChange) {
+      modified = true;
+    }
   }
 
   // If the decoration manager is kept live then the context will try to keep it
@@ -743,7 +845,9 @@
 
   // Cleanup all CFG including all unreachable blocks.
   for (Function& fp : *context()->module()) {
-    modified |= CFGCleanup(&fp);
+    auto status = CFGCleanup(&fp);
+    if (status == Status::Failure) return Status::Failure;
+    if (status == Status::SuccessWithChange) modified = true;
   }
 
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
@@ -913,6 +1017,19 @@
       context()->AnalyzeUses(&dbg);
       continue;
     }
+    // Save debug build identifier even if no other instructions refer to it.
+    if (dbg.GetShaderDebugOpcode() ==
+        NonSemanticShaderDebugInfoDebugBuildIdentifier) {
+      // The debug build identifier refers to other instructions that
+      // can potentially be removed, they also need to be kept alive.
+      dbg.ForEachInId([this](const uint32_t* id) {
+        Instruction* ref_inst = get_def_use_mgr()->GetDef(*id);
+        if (ref_inst) {
+          live_insts_.Set(ref_inst->unique_id());
+        }
+      });
+      continue;
+    }
     to_kill_.push_back(&dbg);
     modified = true;
   }
@@ -970,7 +1087,6 @@
 void AggressiveDCEPass::InitExtensions() {
   extensions_allowlist_.clear();
 
-  // clang-format off
   extensions_allowlist_.insert({
       "SPV_AMD_shader_explicit_vertex_parameter",
       "SPV_AMD_shader_trinary_minmax",
@@ -1008,6 +1124,8 @@
       "SPV_NV_shader_subgroup_partitioned",
       "SPV_EXT_demote_to_helper_invocation",
       "SPV_EXT_descriptor_indexing",
+      "SPV_EXT_descriptor_heap",
+      "SPV_KHR_untyped_pointers",
       "SPV_NV_fragment_shader_barycentric",
       "SPV_NV_compute_shader_derivatives",
       "SPV_NV_shader_image_footprint",
@@ -1022,13 +1140,13 @@
       "SPV_KHR_physical_storage_buffer",
       "SPV_KHR_terminate_invocation",
       "SPV_KHR_shader_clock",
-      "SPV_KHR_vulkan_memory_model",
       "SPV_KHR_subgroup_uniform_control_flow",
       "SPV_KHR_integer_dot_product",
       "SPV_EXT_shader_image_int64",
       "SPV_KHR_non_semantic_info",
       "SPV_KHR_uniform_group_instructions",
       "SPV_KHR_fragment_shader_barycentric",
+      "SPV_KHR_vulkan_memory_model",
       "SPV_NV_bindless_texture",
       "SPV_EXT_shader_atomic_float_add",
       "SPV_EXT_fragment_shader_interlock",
@@ -1036,9 +1154,19 @@
       "SPV_NV_cooperative_matrix",
       "SPV_KHR_cooperative_matrix",
       "SPV_KHR_ray_tracing_position_fetch",
-      "SPV_KHR_fragment_shading_rate"
+      "SPV_KHR_fragment_shading_rate",
+      "SPV_KHR_quad_control",
+      "SPV_NV_shader_invocation_reorder",
+      "SPV_NV_cluster_acceleration_structure",
+      "SPV_NV_linear_swept_spheres",
+      "SPV_KHR_maximal_reconvergence",
+      "SPV_NV_push_constant_bank",
+      "SPV_EXT_opacity_micromap",
+      "SPV_EXT_shader_invocation_reorder",
+      "SPV_EXT_shader_atomic_float16_add",
+      "SPV_KHR_abort",
+      "SPV_KHR_constant_data",
   });
-  // clang-format on
 }
 
 Instruction* AggressiveDCEPass::GetHeaderBranch(BasicBlock* blk) {
diff --git a/source/opt/aggressive_dead_code_elim_pass.h b/source/opt/aggressive_dead_code_elim_pass.h
index fbe08ad..12a5ff7 100644
--- a/source/opt/aggressive_dead_code_elim_pass.h
+++ b/source/opt/aggressive_dead_code_elim_pass.h
@@ -85,8 +85,10 @@
   }
 
   // Adds entry points, execution modes and workgroup size decorations to the
-  // worklist for processing with the first function.
-  void InitializeModuleScopeLiveInstructions();
+  // worklist for processing with the first function. Returns
+  // Pass::Status::Failure if it could not create a required debug instruction.
+  // Returns Pass::Status::SuccessWithoutChange otherwise.
+  Pass::Status InitializeModuleScopeLiveInstructions();
 
   // Add |inst| to worklist_ and live_insts_.
   void AddToWorklist(Instruction* inst) {
@@ -136,7 +138,7 @@
   // existing control structures will remain. This can leave not-insignificant
   // sequences of ultimately useless code.
   // TODO(): Remove useless control constructs.
-  bool AggressiveDCE(Function* func);
+  Pass::Status AggressiveDCE(Function* func);
 
   Pass::Status ProcessImpl();
 
@@ -150,9 +152,21 @@
   // will be empty at the end.
   void ProcessWorkList(Function* func);
 
+  // Process each DebugDeclare and DebugValue in |func| that has not been
+  // marked as live in the work list. DebugDeclare's are marked live now, and
+  // DebugValue Value operands are set to OpUndef.  The work list will be empty
+  // at the end.
+  // Returns Pass::Status::Failure if it could not create an OpUndef.
+  // Otherwise, returns Pass::Status::SuccessWithChange if it made changes,
+  Pass::Status ProcessDebugInformation(
+      std::list<BasicBlock*>& structured_order);
+
   // Kills any instructions in |func| that have not been marked as live.
-  bool KillDeadInstructions(const Function* func,
-                            std::list<BasicBlock*>& structured_order);
+  // Returns Pass::Status::Failure if it could not create an OpUndef.
+  // Otherwise, returns Pass::Status::SuccessWithChange if it made changes,
+  // and Pass::Status::SuccessWithoutChange otherwise.
+  Pass::Status KillDeadInstructions(const Function* func,
+                                    std::list<BasicBlock*>& structured_order);
 
   // Adds the instructions that define the operands of |inst| to the work list.
   void AddOperandsToWorkList(const Instruction* inst);
diff --git a/source/opt/amd_ext_to_khr.cpp b/source/opt/amd_ext_to_khr.cpp
index a314567..7ad2f98 100644
--- a/source/opt/amd_ext_to_khr.cpp
+++ b/source/opt/amd_ext_to_khr.cpp
@@ -19,7 +19,6 @@
 
 #include "ir_builder.h"
 #include "source/opt/ir_context.h"
-#include "spv-amd-shader-ballot.insts.inc"
 #include "type_manager.h"
 
 namespace spvtools {
@@ -77,6 +76,7 @@
 
   Instruction* temp = ir_builder.AddNaryExtendedInstruction(
       inst->type_id(), glsl405_ext_inst_id, opcode, {op1, op2});
+  if (temp == nullptr) return false;
 
   Instruction::OperandList new_operands;
   new_operands.push_back({SPV_OPERAND_TYPE_ID, {glsl405_ext_inst_id}});
@@ -115,9 +115,12 @@
   Instruction* min = ir_builder.AddNaryExtendedInstruction(
       inst->type_id(), glsl405_ext_inst_id, static_cast<uint32_t>(min_opcode),
       {op2, op3});
+  if (min == nullptr) return false;
+
   Instruction* max = ir_builder.AddNaryExtendedInstruction(
       inst->type_id(), glsl405_ext_inst_id, static_cast<uint32_t>(max_opcode),
       {op2, op3});
+  if (max == nullptr) return false;
 
   Instruction::OperandList new_operands;
   new_operands.push_back({SPV_OPERAND_TYPE_ID, {glsl405_ext_inst_id}});
@@ -227,55 +230,81 @@
   // Get the subgroup invocation id.
   uint32_t var_id = ctx->GetBuiltinInputVarId(
       uint32_t(spv::BuiltIn::SubgroupLocalInvocationId));
-  assert(var_id != 0 && "Could not get SubgroupLocalInvocationId variable.");
+  if (var_id == 0) return false;
   Instruction* var_inst = ctx->get_def_use_mgr()->GetDef(var_id);
+  if (var_inst == nullptr) return false;
   Instruction* var_ptr_type =
       ctx->get_def_use_mgr()->GetDef(var_inst->type_id());
+  if (var_ptr_type == nullptr) return false;
   uint32_t uint_type_id = var_ptr_type->GetSingleWordInOperand(1);
+  if (uint_type_id == 0) return false;
 
   Instruction* id = ir_builder.AddLoad(uint_type_id, var_id);
+  if (id == nullptr) return false;
 
   uint32_t quad_mask = ir_builder.GetUintConstantId(3);
+  if (quad_mask == 0) return false;
 
   // This gives the offset in the group of 4 of this invocation.
   Instruction* quad_idx = ir_builder.AddBinaryOp(
       uint_type_id, spv::Op::OpBitwiseAnd, id->result_id(), quad_mask);
+  if (quad_idx == nullptr) return false;
 
   // Get the invocation id of the first invocation in the group of 4.
   Instruction* quad_ldr =
       ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpBitwiseXor,
                              id->result_id(), quad_idx->result_id());
+  if (quad_ldr == nullptr) return false;
 
   // Get the offset of the target invocation from the offset vector.
   Instruction* my_offset =
       ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpVectorExtractDynamic,
                              offset_id, quad_idx->result_id());
+  if (my_offset == nullptr) return false;
 
   // Determine the index of the invocation to read from.
   Instruction* target_inv =
       ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpIAdd,
                              quad_ldr->result_id(), my_offset->result_id());
+  if (target_inv == nullptr) return false;
 
   // Do the group operations
   uint32_t uint_max_id = ir_builder.GetUintConstantId(0xFFFFFFFF);
+  if (uint_max_id == 0) return false;
   uint32_t subgroup_scope =
       ir_builder.GetUintConstantId(uint32_t(spv::Scope::Subgroup));
+  if (subgroup_scope == 0) return false;
+  const auto* vec_type = type_mgr->GetUIntVectorType(4);
+  if (vec_type == nullptr) return false;
   const auto* ballot_value_const = const_mgr->GetConstant(
-      type_mgr->GetUIntVectorType(4),
-      {uint_max_id, uint_max_id, uint_max_id, uint_max_id});
+      vec_type, {uint_max_id, uint_max_id, uint_max_id, uint_max_id});
+  if (ballot_value_const == nullptr) return false;
   Instruction* ballot_value =
       const_mgr->GetDefiningInstruction(ballot_value_const);
+  if (ballot_value == nullptr) return false;
+  uint32_t bool_type_id = type_mgr->GetBoolTypeId();
+  if (bool_type_id == 0) return false;
   Instruction* is_active = ir_builder.AddNaryOp(
-      type_mgr->GetBoolTypeId(), spv::Op::OpGroupNonUniformBallotBitExtract,
+      bool_type_id, spv::Op::OpGroupNonUniformBallotBitExtract,
       {subgroup_scope, ballot_value->result_id(), target_inv->result_id()});
+  if (is_active == nullptr) return false;
   Instruction* shuffle =
       ir_builder.AddNaryOp(inst->type_id(), spv::Op::OpGroupNonUniformShuffle,
                            {subgroup_scope, data_id, target_inv->result_id()});
+  if (shuffle == nullptr) return false;
 
   // Create the null constant to use in the select.
-  const auto* null = const_mgr->GetConstant(type_mgr->GetType(inst->type_id()),
-                                            std::vector<uint32_t>());
+  const auto* result_type = type_mgr->GetType(inst->type_id());
+  if (result_type == nullptr) return false;
+  const auto* null =
+      const_mgr->GetConstant(result_type, std::vector<uint32_t>());
+  if (null == nullptr) {
+    return false;
+  }
   Instruction* null_inst = const_mgr->GetDefiningInstruction(null);
+  if (null_inst == nullptr) {
+    return false;
+  }
 
   // Build the select.
   inst->SetOpcode(spv::Op::OpSelect);
@@ -340,59 +369,85 @@
   uint32_t data_id = inst->GetSingleWordInOperand(2);
 
   Instruction* mask_inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(3));
+  if (mask_inst == nullptr) return false;
   assert(mask_inst->opcode() == spv::Op::OpConstantComposite &&
          "The mask is suppose to be a vector constant.");
   assert(mask_inst->NumInOperands() == 3 &&
          "The mask is suppose to have 3 components.");
 
   uint32_t uint_x = mask_inst->GetSingleWordInOperand(0);
+  if (uint_x == 0) return false;
   uint32_t uint_y = mask_inst->GetSingleWordInOperand(1);
+  if (uint_y == 0) return false;
   uint32_t uint_z = mask_inst->GetSingleWordInOperand(2);
+  if (uint_z == 0) return false;
 
   // Get the subgroup invocation id.
   uint32_t var_id = ctx->GetBuiltinInputVarId(
       uint32_t(spv::BuiltIn::SubgroupLocalInvocationId));
+  if (var_id == 0) return false;
   ctx->AddExtension("SPV_KHR_shader_ballot");
-  assert(var_id != 0 && "Could not get SubgroupLocalInvocationId variable.");
   Instruction* var_inst = ctx->get_def_use_mgr()->GetDef(var_id);
+  if (var_inst == nullptr) return false;
   Instruction* var_ptr_type =
       ctx->get_def_use_mgr()->GetDef(var_inst->type_id());
+  if (var_ptr_type == nullptr) return false;
   uint32_t uint_type_id = var_ptr_type->GetSingleWordInOperand(1);
+  if (uint_type_id == 0) return false;
 
   Instruction* id = ir_builder.AddLoad(uint_type_id, var_id);
+  if (id == nullptr) return false;
 
   // Do the bitwise operations.
   uint32_t mask_extended = ir_builder.GetUintConstantId(0xFFFFFFE0);
+  if (mask_extended == 0) return false;
   Instruction* and_mask = ir_builder.AddBinaryOp(
       uint_type_id, spv::Op::OpBitwiseOr, uint_x, mask_extended);
+  if (and_mask == nullptr) return false;
   Instruction* and_result =
       ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpBitwiseAnd,
                              id->result_id(), and_mask->result_id());
+  if (and_result == nullptr) return false;
   Instruction* or_result = ir_builder.AddBinaryOp(
       uint_type_id, spv::Op::OpBitwiseOr, and_result->result_id(), uint_y);
+  if (or_result == nullptr) return false;
   Instruction* target_inv = ir_builder.AddBinaryOp(
       uint_type_id, spv::Op::OpBitwiseXor, or_result->result_id(), uint_z);
+  if (target_inv == nullptr) return false;
 
   // Do the group operations
   uint32_t uint_max_id = ir_builder.GetUintConstantId(0xFFFFFFFF);
+  if (uint_max_id == 0) return false;
   uint32_t subgroup_scope =
       ir_builder.GetUintConstantId(uint32_t(spv::Scope::Subgroup));
+  if (subgroup_scope == 0) return false;
+  const auto* vec_type = type_mgr->GetUIntVectorType(4);
+  if (vec_type == nullptr) return false;
   const auto* ballot_value_const = const_mgr->GetConstant(
-      type_mgr->GetUIntVectorType(4),
-      {uint_max_id, uint_max_id, uint_max_id, uint_max_id});
+      vec_type, {uint_max_id, uint_max_id, uint_max_id, uint_max_id});
+  if (ballot_value_const == nullptr) return false;
   Instruction* ballot_value =
       const_mgr->GetDefiningInstruction(ballot_value_const);
+  if (ballot_value == nullptr) return false;
+  uint32_t bool_type_id = type_mgr->GetBoolTypeId();
+  if (bool_type_id == 0) return false;
   Instruction* is_active = ir_builder.AddNaryOp(
-      type_mgr->GetBoolTypeId(), spv::Op::OpGroupNonUniformBallotBitExtract,
+      bool_type_id, spv::Op::OpGroupNonUniformBallotBitExtract,
       {subgroup_scope, ballot_value->result_id(), target_inv->result_id()});
+  if (is_active == nullptr) return false;
   Instruction* shuffle =
       ir_builder.AddNaryOp(inst->type_id(), spv::Op::OpGroupNonUniformShuffle,
                            {subgroup_scope, data_id, target_inv->result_id()});
+  if (shuffle == nullptr) return false;
 
   // Create the null constant to use in the select.
-  const auto* null = const_mgr->GetConstant(type_mgr->GetType(inst->type_id()),
-                                            std::vector<uint32_t>());
+  const auto* result_type = type_mgr->GetType(inst->type_id());
+  if (result_type == nullptr) return false;
+  const auto* null =
+      const_mgr->GetConstant(result_type, std::vector<uint32_t>());
+  if (null == nullptr) return false;
   Instruction* null_inst = const_mgr->GetDefiningInstruction(null);
+  if (null_inst == nullptr) return false;
 
   // Build the select.
   inst->SetOpcode(spv::Op::OpSelect);
@@ -426,23 +481,28 @@
                             const std::vector<const analysis::Constant*>&) {
   uint32_t var_id = ctx->GetBuiltinInputVarId(
       uint32_t(spv::BuiltIn::SubgroupLocalInvocationId));
-  ctx->AddCapability(spv::Capability::SubgroupBallotKHR);
-  ctx->AddExtension("SPV_KHR_shader_ballot");
-  assert(var_id != 0 && "Could not get SubgroupLocalInvocationId variable.");
+  if (var_id == 0) return false;
   Instruction* var_inst = ctx->get_def_use_mgr()->GetDef(var_id);
+  if (var_inst == nullptr) return false;
   Instruction* var_ptr_type =
       ctx->get_def_use_mgr()->GetDef(var_inst->type_id());
+  if (var_ptr_type == nullptr) return false;
+  ctx->AddCapability(spv::Capability::SubgroupBallotKHR);
+  ctx->AddExtension("SPV_KHR_shader_ballot");
 
   InstructionBuilder ir_builder(
       ctx, inst,
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
   Instruction* t =
       ir_builder.AddLoad(var_ptr_type->GetSingleWordInOperand(1), var_id);
+  if (t == nullptr) return false;
   analysis::Bool bool_type;
   uint32_t bool_type_id = ctx->get_type_mgr()->GetTypeInstruction(&bool_type);
+  if (bool_type_id == 0) return false;
   Instruction* cmp =
       ir_builder.AddBinaryOp(bool_type_id, spv::Op::OpIEqual, t->result_id(),
                              inst->GetSingleWordInOperand(4));
+  if (cmp == nullptr) return false;
 
   // Build a select.
   inst->SetOpcode(spv::Op::OpSelect);
@@ -485,7 +545,8 @@
 
   uint32_t var_id =
       context->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::SubgroupLtMask));
-  assert(var_id != 0 && "Could not get SubgroupLtMask variable.");
+  if (var_id == 0) return false;
+
   context->AddCapability(spv::Capability::GroupNonUniformBallot);
   Instruction* var_inst = def_use_mgr->GetDef(var_id);
   Instruction* var_ptr_type = def_use_mgr->GetDef(var_inst->type_id());
@@ -498,6 +559,7 @@
   analysis::Vector temp_type(GetUIntType(context), 2);
   const analysis::Type* shuffle_type =
       context->get_type_mgr()->GetRegisteredType(&temp_type);
+  if (shuffle_type == nullptr) return false;
   uint32_t shuffle_type_id = type_mgr->GetTypeInstruction(shuffle_type);
 
   uint32_t mask_id = inst->GetSingleWordInOperand(2);
@@ -511,13 +573,17 @@
       context, inst,
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
   Instruction* load = ir_builder.AddLoad(var_type->result_id(), var_id);
+  if (load == nullptr) return false;
   Instruction* shuffle = ir_builder.AddVectorShuffle(
       shuffle_type_id, load->result_id(), load->result_id(), {0, 1});
+  if (shuffle == nullptr) return false;
   Instruction* bitcast = ir_builder.AddUnaryOp(
       mask_inst->type_id(), spv::Op::OpBitcast, shuffle->result_id());
+  if (bitcast == nullptr) return false;
   Instruction* t =
       ir_builder.AddBinaryOp(mask_inst->type_id(), spv::Op::OpBitwiseAnd,
                              bitcast->result_id(), mask_id);
+  if (t == nullptr) return false;
 
   inst->SetOpcode(spv::Op::OpBitCount);
   inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {t->result_id()}}});
@@ -572,9 +638,13 @@
   analysis::ConstantManager* const_mgr = ctx->get_constant_mgr();
 
   uint32_t float_type_id = type_mgr->GetFloatTypeId();
+  if (float_type_id == 0) return false;
   const analysis::Type* v2_float_type = type_mgr->GetFloatVectorType(2);
+  if (v2_float_type == nullptr) return false;
   uint32_t v2_float_type_id = type_mgr->GetId(v2_float_type);
+  if (v2_float_type_id == 0) return false;
   uint32_t bool_id = type_mgr->GetBoolTypeId();
+  if (bool_id == 0) return false;
 
   InstructionBuilder ir_builder(
       ctx, inst,
@@ -588,20 +658,29 @@
     glsl405_ext_inst_id =
         ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
   }
+  if (glsl405_ext_inst_id == 0) return false;
 
   // Get the constants that will be used.
   uint32_t f0_const_id = const_mgr->GetFloatConstId(0.0);
+  if (f0_const_id == 0) return false;
   uint32_t f2_const_id = const_mgr->GetFloatConstId(2.0);
+  if (f2_const_id == 0) return false;
   uint32_t f0_5_const_id = const_mgr->GetFloatConstId(0.5);
+  if (f0_5_const_id == 0) return false;
   const analysis::Constant* vec_const =
       const_mgr->GetConstant(v2_float_type, {f0_5_const_id, f0_5_const_id});
-  uint32_t vec_const_id =
-      const_mgr->GetDefiningInstruction(vec_const)->result_id();
+  if (vec_const == nullptr) return false;
+  Instruction* vec_const_inst = const_mgr->GetDefiningInstruction(vec_const);
+  if (vec_const_inst == nullptr) return false;
+  uint32_t vec_const_id = vec_const_inst->result_id();
 
   // Extract the input values.
   Instruction* x = ir_builder.AddCompositeExtract(float_type_id, input_id, {0});
+  if (x == nullptr) return false;
   Instruction* y = ir_builder.AddCompositeExtract(float_type_id, input_id, {1});
+  if (y == nullptr) return false;
   Instruction* z = ir_builder.AddCompositeExtract(float_type_id, input_id, {2});
+  if (z == nullptr) return false;
 
   // Negate the input values.
   Instruction* nx =
@@ -610,72 +689,97 @@
       ir_builder.AddUnaryOp(float_type_id, spv::Op::OpFNegate, y->result_id());
   Instruction* nz =
       ir_builder.AddUnaryOp(float_type_id, spv::Op::OpFNegate, z->result_id());
+  if (nx == nullptr) return false;
+  if (ny == nullptr) return false;
+  if (nz == nullptr) return false;
 
   // Get the abolsute values of the inputs.
   Instruction* ax = ir_builder.AddNaryExtendedInstruction(
       float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {x->result_id()});
+  if (ax == nullptr) return false;
   Instruction* ay = ir_builder.AddNaryExtendedInstruction(
       float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {y->result_id()});
+  if (ay == nullptr) return false;
   Instruction* az = ir_builder.AddNaryExtendedInstruction(
       float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {z->result_id()});
+  if (az == nullptr) return false;
 
   // Find which values are negative.  Used in later computations.
   Instruction* is_z_neg = ir_builder.AddBinaryOp(
       bool_id, spv::Op::OpFOrdLessThan, z->result_id(), f0_const_id);
+  if (is_z_neg == nullptr) return false;
   Instruction* is_y_neg = ir_builder.AddBinaryOp(
       bool_id, spv::Op::OpFOrdLessThan, y->result_id(), f0_const_id);
+  if (is_y_neg == nullptr) return false;
   Instruction* is_x_neg = ir_builder.AddBinaryOp(
       bool_id, spv::Op::OpFOrdLessThan, x->result_id(), f0_const_id);
+  if (is_x_neg == nullptr) return false;
 
   // Compute cubema
   Instruction* amax_x_y = ir_builder.AddNaryExtendedInstruction(
       float_type_id, glsl405_ext_inst_id, GLSLstd450FMax,
       {ax->result_id(), ay->result_id()});
+  if (amax_x_y == nullptr) return false;
   Instruction* amax = ir_builder.AddNaryExtendedInstruction(
       float_type_id, glsl405_ext_inst_id, GLSLstd450FMax,
       {az->result_id(), amax_x_y->result_id()});
+  if (amax == nullptr) return false;
   Instruction* cubema = ir_builder.AddBinaryOp(float_type_id, spv::Op::OpFMul,
                                                f2_const_id, amax->result_id());
+  if (cubema == nullptr) return false;
 
   // Do the comparisons needed for computing cubesc and cubetc.
   Instruction* is_z_max =
       ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual,
                              az->result_id(), amax_x_y->result_id());
+  if (is_z_max == nullptr) return false;
   Instruction* not_is_z_max = ir_builder.AddUnaryOp(
       bool_id, spv::Op::OpLogicalNot, is_z_max->result_id());
+  if (not_is_z_max == nullptr) return false;
   Instruction* y_gr_x =
       ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual,
                              ay->result_id(), ax->result_id());
+  if (y_gr_x == nullptr) return false;
   Instruction* is_y_max =
       ir_builder.AddBinaryOp(bool_id, spv::Op::OpLogicalAnd,
                              not_is_z_max->result_id(), y_gr_x->result_id());
+  if (is_y_max == nullptr) return false;
 
   // Select the correct value for cubesc.
   Instruction* cubesc_case_1 = ir_builder.AddSelect(
       float_type_id, is_z_neg->result_id(), nx->result_id(), x->result_id());
+  if (cubesc_case_1 == nullptr) return false;
   Instruction* cubesc_case_2 = ir_builder.AddSelect(
       float_type_id, is_x_neg->result_id(), z->result_id(), nz->result_id());
+  if (cubesc_case_2 == nullptr) return false;
   Instruction* sel =
       ir_builder.AddSelect(float_type_id, is_y_max->result_id(), x->result_id(),
                            cubesc_case_2->result_id());
+  if (sel == nullptr) return false;
   Instruction* cubesc =
       ir_builder.AddSelect(float_type_id, is_z_max->result_id(),
                            cubesc_case_1->result_id(), sel->result_id());
+  if (cubesc == nullptr) return false;
 
   // Select the correct value for cubetc.
   Instruction* cubetc_case_1 = ir_builder.AddSelect(
       float_type_id, is_y_neg->result_id(), nz->result_id(), z->result_id());
+  if (cubetc_case_1 == nullptr) return false;
   Instruction* cubetc =
       ir_builder.AddSelect(float_type_id, is_y_max->result_id(),
                            cubetc_case_1->result_id(), ny->result_id());
+  if (cubetc == nullptr) return false;
 
   // Do the division
   Instruction* cube = ir_builder.AddCompositeConstruct(
       v2_float_type_id, {cubesc->result_id(), cubetc->result_id()});
+  if (cube == nullptr) return false;
   Instruction* denom = ir_builder.AddCompositeConstruct(
       v2_float_type_id, {cubema->result_id(), cubema->result_id()});
+  if (denom == nullptr) return false;
   Instruction* div = ir_builder.AddBinaryOp(
       v2_float_type_id, spv::Op::OpFDiv, cube->result_id(), denom->result_id());
+  if (div == nullptr) return false;
 
   // Get the final result by adding 0.5 to |div|.
   inst->SetOpcode(spv::Op::OpFAdd);
@@ -723,7 +827,9 @@
   analysis::ConstantManager* const_mgr = ctx->get_constant_mgr();
 
   uint32_t float_type_id = type_mgr->GetFloatTypeId();
+  if (float_type_id == 0) return false;
   uint32_t bool_id = type_mgr->GetBoolTypeId();
+  if (bool_id == 0) return false;
 
   InstructionBuilder ir_builder(
       ctx, inst,
@@ -740,56 +846,79 @@
 
   // Get the constants that will be used.
   uint32_t f0_const_id = const_mgr->GetFloatConstId(0.0);
+  if (f0_const_id == 0) return false;
   uint32_t f1_const_id = const_mgr->GetFloatConstId(1.0);
+  if (f1_const_id == 0) return false;
   uint32_t f2_const_id = const_mgr->GetFloatConstId(2.0);
+  if (f2_const_id == 0) return false;
   uint32_t f3_const_id = const_mgr->GetFloatConstId(3.0);
+  if (f3_const_id == 0) return false;
   uint32_t f4_const_id = const_mgr->GetFloatConstId(4.0);
+  if (f4_const_id == 0) return false;
   uint32_t f5_const_id = const_mgr->GetFloatConstId(5.0);
+  if (f5_const_id == 0) return false;
 
   // Extract the input values.
   Instruction* x = ir_builder.AddCompositeExtract(float_type_id, input_id, {0});
+  if (x == nullptr) return false;
   Instruction* y = ir_builder.AddCompositeExtract(float_type_id, input_id, {1});
+  if (y == nullptr) return false;
+  // TODO(1-841): Handle id overflow.
   Instruction* z = ir_builder.AddCompositeExtract(float_type_id, input_id, {2});
+  if (z == nullptr) return false;
 
   // Get the absolute values of the inputs.
   Instruction* ax = ir_builder.AddNaryExtendedInstruction(
       float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {x->result_id()});
+  if (ax == nullptr) return false;
   Instruction* ay = ir_builder.AddNaryExtendedInstruction(
       float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {y->result_id()});
+  if (ay == nullptr) return false;
   Instruction* az = ir_builder.AddNaryExtendedInstruction(
       float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {z->result_id()});
+  if (az == nullptr) return false;
 
   // Find which values are negative.  Used in later computations.
   Instruction* is_z_neg = ir_builder.AddBinaryOp(
       bool_id, spv::Op::OpFOrdLessThan, z->result_id(), f0_const_id);
+  if (is_z_neg == nullptr) return false;
   Instruction* is_y_neg = ir_builder.AddBinaryOp(
       bool_id, spv::Op::OpFOrdLessThan, y->result_id(), f0_const_id);
+  if (is_y_neg == nullptr) return false;
   Instruction* is_x_neg = ir_builder.AddBinaryOp(
       bool_id, spv::Op::OpFOrdLessThan, x->result_id(), f0_const_id);
+  if (is_x_neg == nullptr) return false;
 
   // Find the max value.
   Instruction* amax_x_y = ir_builder.AddNaryExtendedInstruction(
       float_type_id, glsl405_ext_inst_id, GLSLstd450FMax,
       {ax->result_id(), ay->result_id()});
+  if (amax_x_y == nullptr) return false;
   Instruction* is_z_max =
       ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual,
                              az->result_id(), amax_x_y->result_id());
+  if (is_z_max == nullptr) return false;
   Instruction* y_gr_x =
       ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual,
                              ay->result_id(), ax->result_id());
+  if (y_gr_x == nullptr) return false;
 
   // Get the value for each case.
   Instruction* case_z = ir_builder.AddSelect(
       float_type_id, is_z_neg->result_id(), f5_const_id, f4_const_id);
+  if (case_z == nullptr) return false;
   Instruction* case_y = ir_builder.AddSelect(
       float_type_id, is_y_neg->result_id(), f3_const_id, f2_const_id);
+  if (case_y == nullptr) return false;
   Instruction* case_x = ir_builder.AddSelect(
       float_type_id, is_x_neg->result_id(), f1_const_id, f0_const_id);
+  if (case_x == nullptr) return false;
 
   // Select the correct case.
   Instruction* sel =
       ir_builder.AddSelect(float_type_id, y_gr_x->result_id(),
                            case_y->result_id(), case_x->result_id());
+  if (sel == nullptr) return false;
 
   // Get the final result by adding 0.5 to |div|.
   inst->SetOpcode(spv::Op::OpSelect);
@@ -929,11 +1058,18 @@
       std::unique_ptr<AmdExtFoldingRules>(new AmdExtFoldingRules(context())),
       MakeUnique<AmdExtConstFoldingRules>(context()));
   for (Function& func : *get_module()) {
-    func.ForEachInst([&changed, &folder](Instruction* inst) {
-      if (folder.FoldInstruction(inst)) {
-        changed = true;
-      }
-    });
+    bool failed =
+        !func.WhileEachInst([&changed, &folder, this](Instruction* inst) {
+          if (folder.FoldInstruction(inst)) {
+            changed = true;
+            return true;
+          } else if (context()->id_overflow()) {
+            return false;
+          }
+          return true;
+        });
+
+    if (failed) return Status::Failure;
   }
 
   // Now that instruction that require the extensions have been removed, we can
diff --git a/source/opt/basic_block.cpp b/source/opt/basic_block.cpp
index a9fc8e2..84cf488 100644
--- a/source/opt/basic_block.cpp
+++ b/source/opt/basic_block.cpp
@@ -29,11 +29,19 @@
 }  // namespace
 
 BasicBlock* BasicBlock::Clone(IRContext* context) const {
-  BasicBlock* clone = new BasicBlock(
-      std::unique_ptr<Instruction>(GetLabelInst()->Clone(context)));
+  Instruction* label_clone = GetLabelInst()->Clone(context);
+  if (!label_clone) {
+    return nullptr;
+  }
+  BasicBlock* clone = new BasicBlock(std::unique_ptr<Instruction>(label_clone));
   for (const auto& inst : insts_) {
     // Use the incoming context
-    clone->AddInstruction(std::unique_ptr<Instruction>(inst.Clone(context)));
+    Instruction* inst_clone = inst.Clone(context);
+    if (!inst_clone) {
+      delete clone;
+      return nullptr;
+    }
+    clone->AddInstruction(std::unique_ptr<Instruction>(inst_clone));
   }
 
   if (context->AreAnalysesValid(
diff --git a/source/opt/canonicalize_ids_pass.cpp b/source/opt/canonicalize_ids_pass.cpp
new file mode 100644
index 0000000..8b07c38
--- /dev/null
+++ b/source/opt/canonicalize_ids_pass.cpp
@@ -0,0 +1,532 @@
+// Copyright (c) 2025 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/canonicalize_ids_pass.h"
+
+#include <algorithm>
+#include <limits>
+
+namespace spvtools {
+namespace opt {
+
+Pass::Status CanonicalizeIdsPass::Process() {
+  // Initialize the new ID map.
+  new_id_.resize(GetBound(), unused_);
+
+  // Scan the IDs and set to unmapped.
+  ScanIds();
+
+  // Create new IDs for types and consts.
+  CanonicalizeTypeAndConst();
+
+  // Create new IDs for names.
+  CanonicalizeNames();
+
+  // Create new IDs for functions.
+  CanonicalizeFunctions();
+
+  // Create new IDs for everything else.
+  CanonicalizeRemainders();
+
+  // Apply the new IDs to the module.
+  auto const modified = ApplyMap();
+
+  // Update bound in the header.
+  if (modified) {
+    UpdateBound();
+  }
+
+  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
+}
+
+void CanonicalizeIdsPass::ScanIds() {
+  get_module()->ForEachInst(
+      [this](Instruction* inst) {
+        // Look for types and constants.
+        if (spvOpcodeGeneratesType(inst->opcode()) ||
+            spvOpcodeIsConstant(inst->opcode())) {
+          type_and_const_ids_.push_back(inst->result_id());
+          SetNewId(inst->result_id(), unmapped_);
+        }
+        // Look for names.
+        else if (inst->opcode() == spv::Op::OpName) {
+          // store name string in map so that we can compute the hash later
+          auto const name = inst->GetOperand(1).AsString();
+          auto const target = inst->GetSingleWordInOperand(0);
+          name_ids_[name] = target;
+          SetNewId(target, unmapped_);
+        }
+        // Look for function IDs.
+        else if (inst->opcode() == spv::Op::OpFunction) {
+          auto const res_id = inst->result_id();
+          function_ids_.push_back(res_id);
+          SetNewId(res_id, unmapped_);
+        }
+        // Look for remaining result IDs.
+        else if (inst->HasResultId()) {
+          auto const res_id = inst->result_id();
+          SetNewId(res_id, unmapped_);
+        }
+      },
+      true);
+}
+
+void CanonicalizeIdsPass::CanonicalizeTypeAndConst() {
+  // Remap type IDs.
+  static constexpr std::uint32_t soft_type_id_limit = 3011;  // small prime.
+  static constexpr std::uint32_t first_mapped_id = 8;  // offset into ID space
+  for (auto const id : type_and_const_ids_) {
+    if (!IsOldIdUnmapped(id)) {
+      continue;
+    }
+
+    // Compute the hash value.
+    auto const hash_value = HashTypeAndConst(id);
+    if (hash_value != unmapped_) {
+      SetNewId(id, hash_value % soft_type_id_limit + first_mapped_id);
+    }
+  }
+}
+
+// Hash types to canonical values.  This can return ID collisions (it's a bit
+// inevitable): it's up to the caller to handle that gracefully.
+spv::Id CanonicalizeIdsPass::HashTypeAndConst(spv::Id const id) const {
+  spv::Id value = 0;
+
+  auto const inst = get_def_use_mgr()->GetDef(id);
+  auto const op_code = inst->opcode();
+  switch (op_code) {
+    case spv::Op::OpTypeVoid:
+      value = 0;
+      break;
+    case spv::Op::OpTypeBool:
+      value = 1;
+      break;
+    case spv::Op::OpTypeInt: {
+      auto const signedness = inst->GetSingleWordOperand(2);
+      value = 3 + signedness;
+      break;
+    }
+    case spv::Op::OpTypeFloat:
+      value = 5;
+      break;
+    case spv::Op::OpTypeVector: {
+      auto const component_type = inst->GetSingleWordOperand(1);
+      auto const component_count = inst->GetSingleWordOperand(2);
+      value = 6 + HashTypeAndConst(component_type) * (component_count - 1);
+      break;
+    }
+    case spv::Op::OpTypeMatrix: {
+      auto const column_type = inst->GetSingleWordOperand(1);
+      auto const column_count = inst->GetSingleWordOperand(2);
+      value = 30 + HashTypeAndConst(column_type) * (column_count - 1);
+      break;
+    }
+    case spv::Op::OpTypeImage: {
+      // TODO: Why isn't the format used to compute the hash value?
+      auto const sampled_type = inst->GetSingleWordOperand(1);
+      auto const dim = inst->GetSingleWordOperand(2);
+      auto const depth = inst->GetSingleWordOperand(3);
+      auto const arrayed = inst->GetSingleWordOperand(4);
+      auto const ms = inst->GetSingleWordOperand(5);
+      auto const sampled = inst->GetSingleWordOperand(6);
+      value = 120 + HashTypeAndConst(sampled_type) + dim + depth * 8 * 16 +
+              arrayed * 4 * 16 + ms * 2 * 16 + sampled * 1 * 16;
+      break;
+    }
+    case spv::Op::OpTypeSampler:
+      value = 500;
+      break;
+    case spv::Op::OpTypeSampledImage:
+      value = 502;
+      break;
+    case spv::Op::OpTypeArray: {
+      auto const element_type = inst->GetSingleWordOperand(1);
+      auto const length = inst->GetSingleWordOperand(2);
+      value = 501 + HashTypeAndConst(element_type) * length;
+      break;
+    }
+    case spv::Op::OpTypeRuntimeArray: {
+      auto const element_type = inst->GetSingleWordOperand(1);
+      value = 5000 + HashTypeAndConst(element_type);
+      break;
+    }
+    case spv::Op::OpTypeStruct:
+      value = 10000;
+      for (uint32_t w = 1; w < inst->NumOperandWords(); ++w) {
+        value += (w + 1) * HashTypeAndConst(inst->GetSingleWordOperand(w));
+      }
+      break;
+    case spv::Op::OpTypeOpaque: {
+      // TODO: Name is a literal that may have more than one word.
+      auto const name = inst->GetSingleWordOperand(1);
+      value = 6000 + name;
+      break;
+    }
+    case spv::Op::OpTypePointer: {
+      auto const type = inst->GetSingleWordOperand(2);
+      value = 100000 + HashTypeAndConst(type);
+      break;
+    }
+    case spv::Op::OpTypeFunction:
+      value = 200000;
+      for (uint32_t w = 1; w < inst->NumOperandWords(); ++w) {
+        value += (w + 1) * HashTypeAndConst(inst->GetSingleWordOperand(w));
+      }
+      break;
+    case spv::Op::OpTypeEvent:
+      value = 300000;
+      break;
+    case spv::Op::OpTypeDeviceEvent:
+      value = 300001;
+      break;
+    case spv::Op::OpTypeReserveId:
+      value = 300002;
+      break;
+    case spv::Op::OpTypeQueue:
+      value = 300003;
+      break;
+    case spv::Op::OpTypePipe:
+      value = 300004;
+      break;
+    case spv::Op::OpTypePipeStorage:
+      value = 300005;
+      break;
+    case spv::Op::OpTypeNamedBarrier:
+      value = 300006;
+      break;
+    case spv::Op::OpConstantTrue:
+      value = 300007;
+      break;
+    case spv::Op::OpConstantFalse:
+      value = 300008;
+      break;
+    case spv::Op::OpTypeRayQueryKHR:
+      value = 300009;
+      break;
+    case spv::Op::OpTypeAccelerationStructureKHR:
+      value = 300010;
+      break;
+    // Don't map the following types.
+    // TODO: These types were not remapped in the glslang version of the
+    // remapper. Support should be added as necessary.
+    case spv::Op::OpTypeCooperativeMatrixNV:
+    case spv::Op::OpTypeCooperativeMatrixKHR:
+    case spv::Op::OpTypeVectorIdEXT:
+    case spv::Op::OpTypeHitObjectNV:
+    case spv::Op::OpTypeUntypedPointerKHR:
+    case spv::Op::OpTypeNodePayloadArrayAMDX:
+    case spv::Op::OpTypeTensorLayoutNV:
+    case spv::Op::OpTypeTensorViewNV:
+    case spv::Op::OpTypeTensorARM:
+    case spv::Op::OpTypeTaskSequenceINTEL:
+      value = unmapped_;
+      break;
+    case spv::Op::OpConstant: {
+      auto const result_type = inst->GetSingleWordOperand(0);
+      value = 400011 + HashTypeAndConst(result_type);
+      auto const literal = inst->GetOperand(2);
+      for (uint32_t w = 0; w < literal.words.size(); ++w) {
+        value += (w + 3) * literal.words[w];
+      }
+      break;
+    }
+    case spv::Op::OpConstantComposite: {
+      auto const result_type = inst->GetSingleWordOperand(0);
+      value = 300011 + HashTypeAndConst(result_type);
+      for (uint32_t w = 2; w < inst->NumOperandWords(); ++w) {
+        value += (w + 1) * HashTypeAndConst(inst->GetSingleWordOperand(w));
+      }
+      break;
+    }
+    case spv::Op::OpConstantNull: {
+      auto const result_type = inst->GetSingleWordOperand(0);
+      value = 500009 + HashTypeAndConst(result_type);
+      break;
+    }
+    case spv::Op::OpConstantSampler: {
+      auto const result_type = inst->GetSingleWordOperand(0);
+      value = 600011 + HashTypeAndConst(result_type);
+      for (uint32_t w = 2; w < inst->NumOperandWords(); ++w) {
+        value += (w + 1) * inst->GetSingleWordOperand(w);
+      }
+      break;
+    }
+    // Don't map the following constants.
+    // TODO: These constants were not remapped in the glslang version of the
+    // remapper. Support should be added as necessary.
+    case spv::Op::OpConstantCompositeReplicateEXT:
+    case spv::Op::OpConstantFunctionPointerINTEL:
+    case spv::Op::OpConstantStringAMDX:
+    case spv::Op::OpSpecConstantTrue:
+    case spv::Op::OpSpecConstantFalse:
+    case spv::Op::OpSpecConstant:
+    case spv::Op::OpSpecConstantComposite:
+    case spv::Op::OpSpecConstantCompositeReplicateEXT:
+    case spv::Op::OpSpecConstantOp:
+    case spv::Op::OpSpecConstantStringAMDX:
+    case spv::Op::OpSpecConstantDataKHR:
+      value = unmapped_;
+      break;
+    // TODO: Add additional types/constants as needed. See
+    // spvOpcodeGeneratesType and spvOpcodeIsConstant.
+    default:
+      context()->consumer()(SPV_MSG_WARNING, "", {0, 0, 0},
+                            "unhandled opcode will not be canonicalized");
+      break;
+  }
+
+  return value;
+}
+
+void CanonicalizeIdsPass::CanonicalizeNames() {
+  static constexpr std::uint32_t soft_type_id_limit = 3011;  // Small prime.
+  static constexpr std::uint32_t first_mapped_id =
+      3019;  // Offset into ID space.
+
+  for (auto const& [name, target] : name_ids_) {
+    if (!IsOldIdUnmapped(target)) {
+      continue;
+    }
+
+    spv::Id hash_value = 1911;
+    for (const char c : name) {
+      hash_value = hash_value * 1009 + c;
+    }
+
+    if (IsOldIdUnmapped(target)) {
+      SetNewId(target, hash_value % soft_type_id_limit + first_mapped_id);
+    }
+  }
+}
+
+void CanonicalizeIdsPass::CanonicalizeFunctions() {
+  static constexpr std::uint32_t soft_type_id_limit = 19071;  // Small prime.
+  static constexpr std::uint32_t first_mapped_id =
+      6203;  // Offset into ID space.
+  // Window size for context-sensitive canonicalization values
+  // Empirical best size from a single data set.  TODO: Would be a good tunable.
+  // We essentially perform a little convolution around each instruction,
+  // to capture the flavor of nearby code, to hopefully match to similar
+  // code in other modules.
+  static const int32_t window_size = 2;
+
+  for (auto const func_id : function_ids_) {
+    // Store the instructions and opcode hash values in vectors so that the
+    // window of instructions can be easily accessed and avoid having to
+    // recompute the hash value repeatedly in overlapping windows.
+    std::vector<Instruction*> insts;
+    std::vector<uint32_t> opcode_hashvals;
+    auto const func = context()->GetFunction(func_id);
+    func->WhileEachInst([&](Instruction* inst) {
+      insts.emplace_back(inst);
+      opcode_hashvals.emplace_back(HashOpCode(inst));
+      return true;
+    });
+
+    // For every instruction in the function, compute the hash value using the
+    // instruction and a small window of surrounding instructions.
+    assert(insts.size() < (size_t)std::numeric_limits<int32_t>::max());
+    for (int32_t i = 0; i < (int32_t)insts.size(); ++i) {
+      auto const inst = insts[i];
+      if (!inst->HasResultId()) {
+        continue;
+      }
+
+      auto const old_id = inst->result_id();
+      if (!IsOldIdUnmapped(old_id)) {
+        continue;
+      }
+
+      int32_t const lower_bound = std::max(0, i - window_size);
+      int32_t const upper_bound =
+          std::min((int32_t)insts.size() - 1, i + window_size);
+      spv::Id hash_value = func_id * 17;  // Small prime.
+      // Include the hash value of the preceding instructions in the hash but
+      // don't include instructions before the OpFunction.
+      for (int32_t j = i - 1; j >= lower_bound; --j) {
+        auto const local_inst = insts[j];
+        if (local_inst->opcode() == spv::Op::OpFunction) {
+          break;
+        }
+
+        hash_value = hash_value * 30103 +
+                     opcode_hashvals[j];  // 30103 is a semi-arbitrary prime.
+      }
+
+      // Include the hash value of the subsequent instructions in the hash but
+      // don't include instructions past OpFunctionEnd.
+      for (int32_t j = i; j <= upper_bound; ++j) {
+        auto const local_inst = insts[j];
+        if (local_inst->opcode() == spv::Op::OpFunctionEnd) {
+          break;
+        }
+
+        hash_value = hash_value * 30103 +
+                     opcode_hashvals[j];  // 30103 is a semiarbitrary prime.
+      }
+
+      SetNewId(old_id, hash_value % soft_type_id_limit + first_mapped_id);
+    }
+  }
+}
+
+spv::Id CanonicalizeIdsPass::HashOpCode(Instruction const* const inst) const {
+  auto const op_code = inst->opcode();
+  std::uint32_t offset = 0;
+  if (op_code == spv::Op::OpExtInst) {
+    // offset is literal instruction
+    offset = inst->GetSingleWordOperand(3);
+  }
+
+  return (std::uint32_t)op_code * 19 + offset;  // 19 is a small prime.
+}
+
+// Assign remaining IDs sequentially from remaining holes in the new ID space.
+void CanonicalizeIdsPass::CanonicalizeRemainders() {
+  spv::Id next_id = 1;
+  for (uint32_t old_id = 0; old_id < new_id_.size(); ++old_id) {
+    if (IsOldIdUnmapped(old_id)) {
+      next_id = SetNewId(old_id, next_id);
+    }
+  }
+}
+
+bool CanonicalizeIdsPass::ApplyMap() {
+  bool modified = false;
+  context()->module()->ForEachInst(
+      [this, &modified](Instruction* inst) {
+        for (auto operand = inst->begin(); operand != inst->end(); ++operand) {
+          const auto type = operand->type;
+          if (spvIsIdType(type)) {
+            uint32_t& id = operand->words[0];
+            uint32_t const new_id = GetNewId(id);
+            if (new_id == unused_) {
+              continue;
+            }
+
+            assert(new_id != unmapped_ && "new_id should not be unmapped_");
+
+            if (id != new_id) {
+              modified = true;
+              id = new_id;
+              if (type == SPV_OPERAND_TYPE_RESULT_ID) {
+                inst->SetResultId(new_id);
+              } else if (type == SPV_OPERAND_TYPE_TYPE_ID) {
+                inst->SetResultType(new_id);
+              }
+            }
+          }
+        }
+        const auto& debug_scope = inst->GetDebugScope();
+        if (debug_scope.GetLexicalScope() != kNoDebugScope) {
+          uint32_t old_scope = debug_scope.GetLexicalScope();
+          uint32_t new_scope = GetNewId(old_scope);
+          uint32_t old_inlined_at = debug_scope.GetInlinedAt();
+          uint32_t new_inlined_at = old_inlined_at != kNoInlinedAt
+                                        ? GetNewId(old_inlined_at)
+                                        : old_inlined_at;
+          if ((new_scope != unused_ && new_scope != old_scope) ||
+              (new_inlined_at != unused_ && new_inlined_at != old_inlined_at)) {
+            DebugScope new_debug_scope(new_scope, new_inlined_at);
+            inst->SetDebugScope(new_debug_scope);
+            modified = true;
+          }
+        }
+      },
+      true);
+
+  return modified;
+}
+
+spv::Id CanonicalizeIdsPass::GetBound() const {
+  return context()->module()->id_bound();
+}
+
+void CanonicalizeIdsPass::UpdateBound() {
+  context()->module()->SetIdBound(context()->module()->ComputeIdBound());
+
+  context()->ResetFeatureManager();
+}
+
+// Set a new ID. If the new ID is alreadly claimed, the next consecutive ID
+// will be claimed, mapped, and returned to the caller.
+spv::Id CanonicalizeIdsPass::SetNewId(spv::Id const old_id, spv::Id new_id) {
+  assert(old_id < GetBound() && "don't remap an ID that is out of bounds");
+
+  if (old_id >= new_id_.size()) {
+    new_id_.resize(old_id + 1, unused_);
+  }
+
+  if (new_id != unmapped_ && new_id != unused_) {
+    assert(!IsOldIdUnused(old_id) && "don't remap unused IDs");
+    assert(IsOldIdUnmapped(old_id) && "don't remap already mapped IDs");
+
+    new_id = ClaimNewId(new_id);
+  }
+
+  new_id_[old_id] = new_id;
+
+  return new_id;
+}
+
+// Helper function for SetNewID. Claim a new ID. If the new ID is already
+// claimed, the next consecutive ID will be claimed and returned to the caller.
+spv::Id CanonicalizeIdsPass::ClaimNewId(spv::Id new_id) {
+  // Return the ID if it's not taken.
+  auto iter = claimed_new_ids_.find(new_id);
+  if (iter != claimed_new_ids_.end()) {
+    // Otherwise, search for the next unused ID using our current iterator.
+    // Technically, it's a linear search across the set starting at the
+    // iterator, but it's not as bad as it would appear in practice assuming the
+    // hash values are well distributed.
+    iter = std::adjacent_find(iter, claimed_new_ids_.end(), [](int a, int b) {
+      return a + 1 != b;  // Stop at the first non-consecutive pair.
+    });
+    if (iter != claimed_new_ids_.end()) {
+      new_id =
+          *iter + 1;  // We need the next ID after where the search stopped.
+    } else {
+      new_id = *(--iter) + 1;  // We reached the end so we use the next ID.
+    }
+  }
+
+  assert(!IsNewIdClaimed(new_id) &&
+         "don't remap to an ID that is already claimed");
+  iter = claimed_new_ids_.insert(iter, new_id);
+  assert(*iter == new_id);
+
+  return new_id;
+}
+
+std::string CanonicalizeIdsPass::IdAsString(spv::Id const id) const {
+  if (id == unused_) {
+    return "unused";
+  } else if (id == unmapped_) {
+    return "unmapped";
+  } else {
+    return std::to_string(id);
+  }
+}
+
+void CanonicalizeIdsPass::PrintNewIds() const {
+  for (spv::Id id = 0; id < new_id_.size(); ++id) {
+    auto const message =
+        "new id[" + IdAsString(id) + "]: " + IdAsString(new_id_[id]);
+    context()->consumer()(SPV_MSG_INFO, "", {0, 0, 0}, message.c_str());
+  }
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/canonicalize_ids_pass.h b/source/opt/canonicalize_ids_pass.h
new file mode 100644
index 0000000..6c38d34
--- /dev/null
+++ b/source/opt/canonicalize_ids_pass.h
@@ -0,0 +1,115 @@
+// Copyright (c) 2025 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 <algorithm>
+#include <map>
+#include <set>
+#include <vector>
+
+#include "source/opt/pass.h"
+
+namespace spvtools {
+namespace opt {
+
+// The canonicalize IDs pass is an optimization to improve compression of SPIR-V
+// binary files via entropy reduction. It transforms SPIR-V to SPIR-V, remapping
+// IDs. The resulting modules have an increased ID range (IDs are not as tightly
+// packed around zero), but will compress better when multiple modules are
+// compressed together, since the compressor's dictionary can find better cross
+// module commonality. Remapping is accomplished via canonicalization. Thus,
+// modules can be compressed one at a time with no loss of quality relative to
+// operating on many modules at once.
+
+// This pass should be run after most optimization passes except for
+// --strip-debug because this pass will use OpName to canonicalize IDs. i.e. Run
+// --strip-debug after this pass.
+
+// This is a port of remap utility in glslang. There are great deal of magic
+// numbers that are present throughout this code. The general goal is to replace
+// the IDs with a hash value such that the distribution of IDs is deterministic
+// and minimizes collisions. The magic numbers in the glslang version were
+// chosen semi-arbitrarily and have been preserved in this port in order to
+// maintain backward compatibility.
+
+class CanonicalizeIdsPass : public Pass {
+ public:
+  CanonicalizeIdsPass() = default;
+  virtual ~CanonicalizeIdsPass() = default;
+
+  Pass::Status Process() override;
+
+  const char* name() const override { return "canonicalize-ids"; }
+
+ private:
+  // Special values for IDs.
+  static constexpr spv::Id unmapped_{spv::Id(-10000)};
+  static constexpr spv::Id unused_{spv::Id(-10001)};
+
+  // Scans the module for IDs and sets them to unmapped_.
+  void ScanIds();
+
+  // Functions to compute new IDs.
+  void CanonicalizeTypeAndConst();
+  spv::Id HashTypeAndConst(
+      spv::Id const id) const;  // Helper for CanonicalizeTypeAndConst.
+  void CanonicalizeNames();
+  void CanonicalizeFunctions();
+  spv::Id HashOpCode(Instruction const* const inst)
+      const;  // Helper for CanonicalizeFunctions.
+  void CanonicalizeRemainders();
+
+  // Applies the new IDs.
+  bool ApplyMap();
+
+  // Methods to manage the bound field in header.
+  spv::Id GetBound() const;  // All IDs must satisfy 0 < ID < bound.
+  void UpdateBound();
+
+  // Methods to map from old IDs to new IDs.
+  spv::Id GetNewId(spv::Id const old_id) const { return new_id_[old_id]; }
+  spv::Id SetNewId(spv::Id const old_id, spv::Id new_id);
+
+  // Methods to manage claimed IDs.
+  spv::Id ClaimNewId(spv::Id new_id);
+  bool IsNewIdClaimed(spv::Id const new_id) const {
+    return claimed_new_ids_.find(new_id) != claimed_new_ids_.end();
+  }
+
+  // Queries for old IDs.
+  bool IsOldIdUnmapped(spv::Id const old_id) const {
+    return GetNewId(old_id) == unmapped_;
+  }
+  bool IsOldIdUnused(spv::Id const old_id) const {
+    return GetNewId(old_id) == unused_;
+  }
+
+  // Container to map old IDs to new IDs. e.g. new_id_[old_id] = new_id
+  std::vector<spv::Id> new_id_;
+
+  // IDs from the new ID space that have been claimed (faster than searching
+  // through new_id_).
+  std::set<spv::Id> claimed_new_ids_;
+
+  // Helper functions for printing IDs (useful for debugging).
+  std::string IdAsString(spv::Id const id) const;
+  void PrintNewIds() const;
+
+  // Containers to track IDs we want to canonicalize.
+  std::vector<spv::Id> type_and_const_ids_;
+  std::map<std::string, spv::Id> name_ids_;
+  std::vector<spv::Id> function_ids_;
+};
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/ccp_pass.cpp b/source/opt/ccp_pass.cpp
index 46bfc90..c53ee19 100644
--- a/source/opt/ccp_pass.cpp
+++ b/source/opt/ccp_pass.cpp
@@ -165,6 +165,9 @@
       context()->get_instruction_folder().FoldInstructionToConstant(instr,
                                                                     map_func);
 
+  if (folded_inst && context()->id_overflow()) {
+    return SSAPropagator::kFailed;
+  }
   if (folded_inst != nullptr) {
     // We do not want to change the body of the function by adding new
     // instructions.  When folding we can only generate new constants.
@@ -360,6 +363,13 @@
     }
   }
 
+  // Mark the extended instruction imports as `kVarying`. We know they
+  // will not be constants, and will be used by `OpExtInst` instructions.
+  // This allows those instructions to be fully processed.
+  for (const auto& inst : get_module()->ext_inst_imports()) {
+    values_[inst.result_id()] = kVaryingSSAId;
+  }
+
   original_id_bound_ = context()->module()->IdBound();
 }
 
@@ -369,6 +379,7 @@
   // Process all entry point functions.
   ProcessFunction pfn = [this](Function* fp) { return PropagateConstants(fp); };
   bool modified = context()->ProcessReachableCallTree(pfn);
+  if (context()->id_overflow()) return Pass::Status::Failure;
   return modified ? Pass::Status::SuccessWithChange
                   : Pass::Status::SuccessWithoutChange;
 }
diff --git a/source/opt/cfg.cpp b/source/opt/cfg.cpp
index 4c4bb25..5f4a7d1 100644
--- a/source/opt/cfg.cpp
+++ b/source/opt/cfg.cpp
@@ -254,12 +254,13 @@
   }
 
   // Adjust the OpPhi instructions as needed.
-  bb->ForEachPhiInst([latch_block, bb, new_header, context](Instruction* phi) {
+  bool ok = bb->WhileEachPhiInst([latch_block, bb, new_header,
+                                  context](Instruction* phi) -> bool {
     std::vector<uint32_t> preheader_phi_ops;
     std::vector<Operand> header_phi_ops;
 
-    // Identify where the original inputs to original OpPhi belong: header or
-    // preheader.
+    // Identify where the original inputs to original OpPhi belong: header
+    // or preheader.
     for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) {
       uint32_t def_id = phi->GetSingleWordInOperand(i);
       uint32_t branch_id = phi->GetSingleWordInOperand(i + 1);
@@ -272,21 +273,24 @@
       }
     }
 
-    // Create a phi instruction if and only if the preheader_phi_ops has more
-    // than one pair.
+    // Create a phi instruction if and only if the preheader_phi_ops has
+    // more than one pair.
     if (preheader_phi_ops.size() > 2) {
       InstructionBuilder builder(
           context, &*bb->begin(),
           IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
 
       Instruction* new_phi = builder.AddPhi(phi->type_id(), preheader_phi_ops);
+      if (!new_phi) {
+        return false;
+      }
 
       // Add the OpPhi to the header bb.
       header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {new_phi->result_id()}});
       header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {bb->id()}});
     } else {
-      // An OpPhi with a single entry is just a copy.  In this case use the same
-      // instruction in the new header.
+      // An OpPhi with a single entry is just a copy.  In this case use the
+      // same instruction in the new header.
       header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {preheader_phi_ops[0]}});
       header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {bb->id()}});
     }
@@ -297,8 +301,13 @@
     new_header->begin()->InsertBefore(std::move(phi_owner));
     context->set_instr_block(phi, new_header);
     context->AnalyzeUses(phi);
+    return true;
   });
 
+  if (!ok) {
+    return nullptr;
+  }
+
   // Add a branch to the new header.
   InstructionBuilder branch_builder(
       context, bb,
diff --git a/source/opt/cfg_cleanup_pass.cpp b/source/opt/cfg_cleanup_pass.cpp
index 26fed89..6cd0479 100644
--- a/source/opt/cfg_cleanup_pass.cpp
+++ b/source/opt/cfg_cleanup_pass.cpp
@@ -25,8 +25,17 @@
 
 Pass::Status CFGCleanupPass::Process() {
   // Process all entry point functions.
-  ProcessFunction pfn = [this](Function* fp) { return CFGCleanup(fp); };
+  bool failure = false;
+  ProcessFunction pfn = [this, &failure](Function* fp) {
+    auto status = CFGCleanup(fp);
+    if (status == Status::Failure) {
+      failure = true;
+      return false;
+    }
+    return status == Status::SuccessWithChange;
+  };
   bool modified = context()->ProcessReachableCallTree(pfn);
+  if (failure) return Pass::Status::Failure;
   return modified ? Pass::Status::SuccessWithChange
                   : Pass::Status::SuccessWithoutChange;
 }
diff --git a/source/opt/combine_access_chains.cpp b/source/opt/combine_access_chains.cpp
index 99ec796..ec90d97 100644
--- a/source/opt/combine_access_chains.cpp
+++ b/source/opt/combine_access_chains.cpp
@@ -27,36 +27,48 @@
   bool modified = false;
 
   for (auto& function : *get_module()) {
-    modified |= ProcessFunction(function);
+    auto status = ProcessFunction(function);
+    if (status == Status::Failure) return Status::Failure;
+    if (status == Status::SuccessWithChange) modified = true;
   }
 
   return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
 }
 
-bool CombineAccessChains::ProcessFunction(Function& function) {
+Pass::Status CombineAccessChains::ProcessFunction(Function& function) {
   if (function.IsDeclaration()) {
-    return false;
+    return Status::SuccessWithoutChange;
   }
 
   bool modified = false;
+  bool failure = false;
 
   cfg()->ForEachBlockInReversePostOrder(
-      function.entry().get(), [&modified, this](BasicBlock* block) {
-        block->ForEachInst([&modified, this](Instruction* inst) {
+      function.entry().get(), [&modified, &failure, this](BasicBlock* block) {
+        if (failure) return;
+        block->ForEachInst([&modified, &failure, this](Instruction* inst) {
+          if (failure) return;
           switch (inst->opcode()) {
             case spv::Op::OpAccessChain:
             case spv::Op::OpInBoundsAccessChain:
             case spv::Op::OpPtrAccessChain:
-            case spv::Op::OpInBoundsPtrAccessChain:
-              modified |= CombineAccessChain(inst);
+            case spv::Op::OpInBoundsPtrAccessChain: {
+              auto status = CombineAccessChain(inst);
+              if (status == Status::Failure) {
+                failure = true;
+              } else if (status == Status::SuccessWithChange) {
+                modified = true;
+              }
               break;
+            }
             default:
               break;
           }
         });
       });
 
-  return modified;
+  if (failure) return Status::Failure;
+  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
 uint32_t CombineAccessChains::GetConstantValue(
@@ -121,9 +133,9 @@
   return type;
 }
 
-bool CombineAccessChains::CombineIndices(Instruction* ptr_input,
-                                         Instruction* inst,
-                                         std::vector<Operand>* new_operands) {
+Pass::Status CombineAccessChains::CombineIndices(
+    Instruction* ptr_input, Instruction* inst,
+    std::vector<Operand>* new_operands) {
   analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
   analysis::ConstantManager* constant_mgr = context()->get_constant_mgr();
 
@@ -150,8 +162,10 @@
                          GetConstantValue(element_constant);
     const analysis::Constant* new_value_constant =
         constant_mgr->GetConstant(last_index_constant->type(), {new_value});
+    if (!new_value_constant) return Status::Failure;
     Instruction* new_value_inst =
         constant_mgr->GetDefiningInstruction(new_value_constant);
+    if (!new_value_inst) return Status::Failure;
     new_value_id = new_value_inst->result_id();
   } else if (!type->AsStruct() || combining_element_operands) {
     // Generate an addition of the two indices.
@@ -161,16 +175,17 @@
     Instruction* addition = builder.AddIAdd(last_index_inst->type_id(),
                                             last_index_inst->result_id(),
                                             element_inst->result_id());
+    if (!addition) return Status::Failure;
     new_value_id = addition->result_id();
   } else {
     // Indexing into structs must be constant, so bail out here.
-    return false;
+    return Status::SuccessWithoutChange;
   }
   new_operands->push_back({SPV_OPERAND_TYPE_ID, {new_value_id}});
-  return true;
+  return Status::SuccessWithChange;
 }
 
-bool CombineAccessChains::CreateNewInputOperands(
+Pass::Status CombineAccessChains::CreateNewInputOperands(
     Instruction* ptr_input, Instruction* inst,
     std::vector<Operand>* new_operands) {
   // Start by copying all the input operands of the feeder access chain.
@@ -182,7 +197,8 @@
   if (IsPtrAccessChain(inst->opcode())) {
     // The last index of the feeder should be combined with the element operand
     // of |inst|.
-    if (!CombineIndices(ptr_input, inst, new_operands)) return false;
+    auto status = CombineIndices(ptr_input, inst, new_operands);
+    if (status != Status::SuccessWithChange) return status;
   } else {
     // The indices aren't being combined so now add the last index operand of
     // |ptr_input|.
@@ -196,10 +212,10 @@
     new_operands->push_back(inst->GetInOperand(i));
   }
 
-  return true;
+  return Status::SuccessWithChange;
 }
 
-bool CombineAccessChains::CombineAccessChain(Instruction* inst) {
+Pass::Status CombineAccessChains::CombineAccessChain(Instruction* inst) {
   assert((inst->opcode() == spv::Op::OpPtrAccessChain ||
           inst->opcode() == spv::Op::OpAccessChain ||
           inst->opcode() == spv::Op::OpInBoundsAccessChain ||
@@ -212,10 +228,11 @@
       ptr_input->opcode() != spv::Op::OpInBoundsAccessChain &&
       ptr_input->opcode() != spv::Op::OpPtrAccessChain &&
       ptr_input->opcode() != spv::Op::OpInBoundsPtrAccessChain) {
-    return false;
+    return Status::SuccessWithoutChange;
   }
 
-  if (Has64BitIndices(inst) || Has64BitIndices(ptr_input)) return false;
+  if (Has64BitIndices(inst) || Has64BitIndices(ptr_input))
+    return Status::SuccessWithoutChange;
 
   // Handles the following cases:
   // 1. |ptr_input| is an index-less access chain. Replace the pointer
@@ -237,7 +254,7 @@
   // size/alignment of the type and converting the stride into an element
   // index.
   uint32_t array_stride = GetArrayStride(ptr_input);
-  if (array_stride != 0) return false;
+  if (array_stride != 0) return Status::SuccessWithoutChange;
 
   if (ptr_input->NumInOperands() == 1) {
     // The input is effectively a no-op.
@@ -249,14 +266,15 @@
     inst->SetOpcode(spv::Op::OpCopyObject);
   } else {
     std::vector<Operand> new_operands;
-    if (!CreateNewInputOperands(ptr_input, inst, &new_operands)) return false;
+    auto status = CreateNewInputOperands(ptr_input, inst, &new_operands);
+    if (status != Status::SuccessWithChange) return status;
 
     // Update the instruction.
     inst->SetOpcode(UpdateOpcode(inst->opcode(), ptr_input->opcode()));
     inst->SetInOperands(std::move(new_operands));
     context()->AnalyzeUses(inst);
   }
-  return true;
+  return Status::SuccessWithChange;
 }
 
 spv::Op CombineAccessChains::UpdateOpcode(spv::Op base_opcode,
diff --git a/source/opt/combine_access_chains.h b/source/opt/combine_access_chains.h
index 32ee50d..1872720 100644
--- a/source/opt/combine_access_chains.h
+++ b/source/opt/combine_access_chains.h
@@ -40,12 +40,12 @@
  private:
   // Combine access chains in |function|. Blocks are processed in reverse
   // post-order. Returns true if the function is modified.
-  bool ProcessFunction(Function& function);
+  Status ProcessFunction(Function& function);
 
   // Combines an access chain (normal, in bounds or pointer) |inst| if its base
   // pointer is another access chain. Returns true if the access chain was
   // modified.
-  bool CombineAccessChain(Instruction* inst);
+  Status CombineAccessChain(Instruction* inst);
 
   // Returns the value of |constant_inst| as a uint32_t.
   uint32_t GetConstantValue(const analysis::Constant* constant_inst);
@@ -59,13 +59,13 @@
 
   // Populates |new_operands| with the operands for the combined access chain.
   // Returns false if the access chains cannot be combined.
-  bool CreateNewInputOperands(Instruction* ptr_input, Instruction* inst,
-                              std::vector<Operand>* new_operands);
+  Status CreateNewInputOperands(Instruction* ptr_input, Instruction* inst,
+                                std::vector<Operand>* new_operands);
 
   // Combines the last index of |ptr_input| with the element operand of |inst|.
   // Adds the combined operand to |new_operands|.
-  bool CombineIndices(Instruction* ptr_input, Instruction* inst,
-                      std::vector<Operand>* new_operands);
+  Status CombineIndices(Instruction* ptr_input, Instruction* inst,
+                        std::vector<Operand>* new_operands);
 
   // Returns the opcode to use for the combined access chain.
   spv::Op UpdateOpcode(spv::Op base_opcode, spv::Op input_opcode);
diff --git a/source/opt/const_folding_rules.cpp b/source/opt/const_folding_rules.cpp
index a5d4cbe..b6dd681 100644
--- a/source/opt/const_folding_rules.cpp
+++ b/source/opt/const_folding_rules.cpp
@@ -14,6 +14,8 @@
 
 #include "source/opt/const_folding_rules.h"
 
+#include <optional>
+
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
@@ -829,7 +831,9 @@
     // Build the constant object and return it.
     std::vector<uint32_t> ids;
     for (const analysis::Constant* member : results_components) {
-      ids.push_back(const_mgr->GetDefiningInstruction(member)->result_id());
+      Instruction* def = const_mgr->GetDefiningInstruction(member);
+      if (!def) return nullptr;
+      ids.push_back(def->result_id());
     }
     return const_mgr->GetConstant(vector_type, ids);
   } else {
@@ -986,6 +990,32 @@
 ConstantFoldingRule FoldFAdd() { return FoldFPBinaryOp(FOLD_FPARITH_OP(+)); }
 ConstantFoldingRule FoldFMul() { return FoldFPBinaryOp(FOLD_FPARITH_OP(*)); }
 
+// x - x = 0
+ConstantFoldingRule FoldRedundantSub() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>&)
+             -> const analysis::Constant* {
+    assert(inst->opcode() == spv::Op::OpFSub ||
+           inst->opcode() == spv::Op::OpISub);
+
+    if (inst->GetSingleWordInOperand(0) == inst->GetSingleWordInOperand(1)) {
+      bool use_float = inst->opcode() == spv::Op::OpFSub;
+      if (use_float && !inst->IsFloatingPointFoldingAllowed()) {
+        return nullptr;
+      }
+      analysis::TypeManager* type_mgr = context->get_type_mgr();
+      const analysis::Type* type = type_mgr->GetType(inst->type_id());
+      if (type->IsCooperativeMatrix()) {
+        return nullptr;
+      }
+      analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+      uint32_t null_id = const_mgr->GetNullConstId(type);
+      return const_mgr->FindDeclaredConstant(null_id);
+    }
+    return nullptr;
+  };
+}
+
 // Returns the constant that results from evaluating |numerator| / 0.0.  Returns
 // |nullptr| if the result could not be evaluated.
 const analysis::Constant* FoldFPScalarDivideByZero(
@@ -1045,6 +1075,107 @@
 // Returns the constant folding rule to fold |OpFDiv| with two constants.
 ConstantFoldingRule FoldFDiv() { return FoldFPBinaryOp(FoldScalarFPDivide); }
 
+// Get a singular uniform value, which is repeated when the |type| is a vector.
+const analysis::Constant* GetConstantUniformValue(
+    analysis::ConstantManager* const_mgr, const analysis::Type* type,
+    std::optional<double> f = {}, std::optional<uint64_t> i = {}) {
+  const analysis::Constant* uniform = nullptr;
+  bool is_vector = false;
+  const analysis::Type* base_type = type;
+
+  if (base_type->AsVector()) {
+    is_vector = true;
+    base_type = base_type->AsVector()->element_type();
+  }
+
+  if (f && base_type->AsFloat()) {
+    if (base_type->AsFloat()->width() == 32) {
+      uniform = const_mgr->GetConstant(
+          base_type, utils::FloatProxy<float>((float)f.value()).GetWords());
+    } else if (base_type->AsFloat()->width() == 64) {
+      uniform = const_mgr->GetConstant(
+          base_type, utils::FloatProxy<double>(f.value()).GetWords());
+    }
+  } else if (i && base_type->AsInteger()) {
+    uniform =
+        const_mgr->GenerateIntegerConstant(base_type->AsInteger(), i.value());
+  }
+
+  if (!uniform) {
+    return nullptr;
+  }
+
+  if (is_vector) {
+    Instruction* uniform_inst = const_mgr->GetDefiningInstruction(uniform);
+    if (!uniform_inst) return nullptr;
+
+    uint32_t uniform_id = uniform_inst->result_id();
+    uniform =
+        const_mgr->GetConstant(type, std::vector<uint32_t>(4, uniform_id));
+  }
+
+  return uniform;
+}
+
+//  x /  x =  1
+// -x /  x = -1
+//  x / -x = -1
+ConstantFoldingRule FoldRedundantDiv() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants)
+             -> const analysis::Constant* {
+    assert(inst->opcode() == spv::Op::OpFDiv ||
+           inst->opcode() == spv::Op::OpSDiv ||
+           inst->opcode() == spv::Op::OpUDiv);
+
+    if (constants[0] || constants[1]) {
+      return nullptr;
+    }
+
+    analysis::TypeManager* type_mgr = context->get_type_mgr();
+    const analysis::Type* type = type_mgr->GetType(inst->type_id());
+
+    if (type->IsCooperativeMatrix()) {
+      return nullptr;
+    }
+
+    bool use_float = inst->opcode() == spv::Op::OpFDiv;
+    if (use_float && !inst->IsFloatingPointFoldingAllowed()) {
+      return nullptr;
+    }
+
+    analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+
+    if (inst->GetSingleWordInOperand(0) == inst->GetSingleWordInOperand(1)) {
+      return GetConstantUniformValue(const_mgr, type, 1.0, 1);
+    }
+
+    if (inst->opcode() == spv::Op::OpUDiv) {
+      return nullptr;
+    }
+
+    analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+
+    Instruction* lhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0));
+    if ((lhs->opcode() == spv::Op::OpSNegate ||
+         lhs->opcode() == spv::Op::OpFNegate) &&
+        lhs->GetSingleWordInOperand(0) == inst->GetSingleWordInOperand(1) &&
+        (!use_float || lhs->IsFloatingPointFoldingAllowed())) {
+      return GetConstantUniformValue(const_mgr, type, -1.0, UINT64_MAX);
+    }
+
+    Instruction* rhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(1));
+    if ((rhs->opcode() == spv::Op::OpSNegate ||
+         rhs->opcode() == spv::Op::OpFNegate) &&
+        rhs->GetSingleWordInOperand(0) == inst->GetSingleWordInOperand(0) &&
+        (!use_float || rhs->IsFloatingPointFoldingAllowed())) {
+      return GetConstantUniformValue(const_mgr, type, -1.0, UINT64_MAX);
+    }
+
+    return nullptr;
+  };
+}
+
 bool CompareFloatingPoint(bool op_result, bool op_unordered,
                           bool need_ordered) {
   if (need_ordered) {
@@ -1124,6 +1255,26 @@
   return FoldFPBinaryOp(FOLD_FPCMP_OP(>=, false));
 }
 
+ConstantFoldingRule FoldInvariantSelect() {
+  return [](IRContext*, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants)
+             -> const analysis::Constant* {
+    assert(inst->opcode() == spv::Op::OpSelect);
+    (void)inst;
+
+    if (!constants[1] || !constants[2]) {
+      return nullptr;
+    }
+    if (constants[1] == constants[2]) {
+      return constants[1];
+    }
+    if (constants[1]->IsZero() && constants[2]->IsZero()) {
+      return constants[1];
+    }
+    return nullptr;
+  };
+}
+
 // Folds an OpDot where all of the inputs are constants to a
 // constant.  A new constant is created if necessary.
 ConstantFoldingRule FoldOpDotWithConstants() {
@@ -1395,13 +1546,18 @@
     if (base_type->AsFloat()->width() == 32) {
       one = const_mgr->GetConstant(base_type,
                                    utils::FloatProxy<float>(1.0f).GetWords());
-    } else {
+    } else if (base_type->AsFloat()->width() == 64) {
       one = const_mgr->GetConstant(base_type,
                                    utils::FloatProxy<double>(1.0).GetWords());
+    } else {
+      // We won't support folding half types.
+      return nullptr;
     }
 
     if (is_vector) {
-      uint32_t one_id = const_mgr->GetDefiningInstruction(one)->result_id();
+      Instruction* one_inst = const_mgr->GetDefiningInstruction(one);
+      if (one_inst == nullptr) return nullptr;
+      uint32_t one_id = one_inst->result_id();
       one =
           const_mgr->GetConstant(result_type, std::vector<uint32_t>(4, one_id));
     }
@@ -1428,19 +1584,46 @@
   };
 }
 
+template <typename FloatType>
+static bool NegZeroAwareLessThan(FloatType a, FloatType b) {
+  if (a == 0.0 && b == 0.0) {
+    bool sba = std::signbit(a);
+    bool sbb = std::signbit(b);
+    if (sba && !sbb) {
+      return true;
+    }
+  }
+  return a < b;
+}
+
 const analysis::Constant* FoldMin(const analysis::Type* result_type,
                                   const analysis::Constant* a,
                                   const analysis::Constant* b,
                                   analysis::ConstantManager*) {
   if (const analysis::Integer* int_type = result_type->AsInteger()) {
-    if (int_type->width() == 32) {
+    if (int_type->width() <= 32) {
+      assert(
+          (a->AsIntConstant() != nullptr || a->AsNullConstant() != nullptr) &&
+          "Must be an integer or null constant.");
+      assert(
+          (b->AsIntConstant() != nullptr || b->AsNullConstant() != nullptr) &&
+          "Must be an integer or null constant.");
+
       if (int_type->IsSigned()) {
-        int32_t va = a->GetS32();
-        int32_t vb = b->GetS32();
+        int32_t va = (a->AsIntConstant() != nullptr)
+                         ? a->AsIntConstant()->GetS32BitValue()
+                         : 0;
+        int32_t vb = (b->AsIntConstant() != nullptr)
+                         ? b->AsIntConstant()->GetS32BitValue()
+                         : 0;
         return (va < vb ? a : b);
       } else {
-        uint32_t va = a->GetU32();
-        uint32_t vb = b->GetU32();
+        uint32_t va = (a->AsIntConstant() != nullptr)
+                          ? a->AsIntConstant()->GetU32BitValue()
+                          : 0;
+        uint32_t vb = (b->AsIntConstant() != nullptr)
+                          ? b->AsIntConstant()->GetU32BitValue()
+                          : 0;
         return (va < vb ? a : b);
       }
     } else if (int_type->width() == 64) {
@@ -1458,11 +1641,11 @@
     if (float_type->width() == 32) {
       float va = a->GetFloat();
       float vb = b->GetFloat();
-      return (va < vb ? a : b);
+      return NegZeroAwareLessThan(va, vb) ? a : b;
     } else if (float_type->width() == 64) {
       double va = a->GetDouble();
       double vb = b->GetDouble();
-      return (va < vb ? a : b);
+      return NegZeroAwareLessThan(va, vb) ? a : b;
     }
   }
   return nullptr;
@@ -1473,14 +1656,29 @@
                                   const analysis::Constant* b,
                                   analysis::ConstantManager*) {
   if (const analysis::Integer* int_type = result_type->AsInteger()) {
-    if (int_type->width() == 32) {
+    if (int_type->width() <= 32) {
+      assert(
+          (a->AsIntConstant() != nullptr || a->AsNullConstant() != nullptr) &&
+          "Must be an integer or null constant.");
+      assert(
+          (b->AsIntConstant() != nullptr || b->AsNullConstant() != nullptr) &&
+          "Must be an integer or null constant.");
+
       if (int_type->IsSigned()) {
-        int32_t va = a->GetS32();
-        int32_t vb = b->GetS32();
+        int32_t va = (a->AsIntConstant() != nullptr)
+                         ? a->AsIntConstant()->GetS32BitValue()
+                         : 0;
+        int32_t vb = (b->AsIntConstant() != nullptr)
+                         ? b->AsIntConstant()->GetS32BitValue()
+                         : 0;
         return (va > vb ? a : b);
       } else {
-        uint32_t va = a->GetU32();
-        uint32_t vb = b->GetU32();
+        uint32_t va = (a->AsIntConstant() != nullptr)
+                          ? a->AsIntConstant()->GetU32BitValue()
+                          : 0;
+        uint32_t vb = (b->AsIntConstant() != nullptr)
+                          ? b->AsIntConstant()->GetU32BitValue()
+                          : 0;
         return (va > vb ? a : b);
       }
     } else if (int_type->width() == 64) {
@@ -1498,11 +1696,71 @@
     if (float_type->width() == 32) {
       float va = a->GetFloat();
       float vb = b->GetFloat();
-      return (va > vb ? a : b);
+      return NegZeroAwareLessThan(vb, va) ? a : b;
     } else if (float_type->width() == 64) {
       double va = a->GetDouble();
       double vb = b->GetDouble();
-      return (va > vb ? a : b);
+      return NegZeroAwareLessThan(vb, va) ? a : b;
+    }
+  }
+  return nullptr;
+}
+
+const analysis::Constant* FoldNMin(const analysis::Type* result_type,
+                                   const analysis::Constant* a,
+                                   const analysis::Constant* b,
+                                   analysis::ConstantManager*) {
+  if (const analysis::Float* float_type = result_type->AsFloat()) {
+    if (float_type->width() == 32) {
+      float va = a->GetFloat();
+      float vb = b->GetFloat();
+      if (std::isnan(va)) {
+        return b;
+      }
+      if (std::isnan(vb)) {
+        return a;
+      }
+      return NegZeroAwareLessThan(va, vb) ? a : b;
+    } else if (float_type->width() == 64) {
+      double va = a->GetDouble();
+      double vb = b->GetDouble();
+      if (std::isnan(va)) {
+        return b;
+      }
+      if (std::isnan(vb)) {
+        return a;
+      }
+      return NegZeroAwareLessThan(va, vb) ? a : b;
+    }
+  }
+  return nullptr;
+}
+
+const analysis::Constant* FoldNMax(const analysis::Type* result_type,
+                                   const analysis::Constant* a,
+                                   const analysis::Constant* b,
+                                   analysis::ConstantManager*) {
+  if (const analysis::Float* float_type = result_type->AsFloat()) {
+    if (float_type->width() == 32) {
+      float va = a->GetFloat();
+      float vb = b->GetFloat();
+      if (std::isnan(va)) {
+        return b;
+      }
+      if (std::isnan(vb)) {
+        return a;
+      }
+      return NegZeroAwareLessThan(vb, va) ? a : b;
+    } else if (float_type->width() == 64) {
+      double va = a->GetDouble();
+      double vb = b->GetDouble();
+      if (std::isnan(va)) {
+        return b;
+      }
+      if (std::isnan(vb)) {
+        return a;
+      }
+      return NegZeroAwareLessThan(vb, va) ? a : b;
     }
   }
   return nullptr;
@@ -1590,6 +1848,88 @@
   return nullptr;
 }
 
+// Fold an clamp instruction when all three operands are constant.
+const analysis::Constant* FoldNClamp1(
+    IRContext* context, Instruction* inst,
+    const std::vector<const analysis::Constant*>& constants) {
+  assert(inst->opcode() == spv::Op::OpExtInst &&
+         "Expecting an extended instruction.");
+  assert(inst->GetSingleWordInOperand(0) ==
+             context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() &&
+         "Expecting a GLSLstd450 extended instruction.");
+
+  // Make sure all Clamp operands are constants.
+  for (uint32_t i = 1; i < 4; i++) {
+    if (constants[i] == nullptr) {
+      return nullptr;
+    }
+  }
+
+  const analysis::Constant* temp = FoldFPBinaryOp(
+      FoldNMax, inst->type_id(), {constants[1], constants[2]}, context);
+  if (temp == nullptr) {
+    return nullptr;
+  }
+  return FoldFPBinaryOp(FoldNMin, inst->type_id(), {temp, constants[3]},
+                        context);
+}
+
+// Fold a clamp instruction when |x <= min_val|.
+const analysis::Constant* FoldNClamp2(
+    IRContext* context, Instruction* inst,
+    const std::vector<const analysis::Constant*>& constants) {
+  assert(inst->opcode() == spv::Op::OpExtInst &&
+         "Expecting an extended instruction.");
+  assert(inst->GetSingleWordInOperand(0) ==
+             context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() &&
+         "Expecting a GLSLstd450 extended instruction.");
+
+  const analysis::Constant* x = constants[1];
+  const analysis::Constant* min_val = constants[2];
+
+  if (x == nullptr || min_val == nullptr) {
+    return nullptr;
+  }
+
+  const analysis::Constant* temp =
+      FoldFPBinaryOp(FoldNMax, inst->type_id(), {x, min_val}, context);
+  if (temp == min_val) {
+    // We can assume that |min_val| is less than |max_val|.  Therefore, if the
+    // result of the max operation is |min_val|, we know the result of the min
+    // operation, even if |max_val| is not a constant.
+    return min_val;
+  }
+  return nullptr;
+}
+
+// Fold a clamp instruction when |x >= max_val|.
+const analysis::Constant* FoldNClamp3(
+    IRContext* context, Instruction* inst,
+    const std::vector<const analysis::Constant*>& constants) {
+  assert(inst->opcode() == spv::Op::OpExtInst &&
+         "Expecting an extended instruction.");
+  assert(inst->GetSingleWordInOperand(0) ==
+             context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() &&
+         "Expecting a GLSLstd450 extended instruction.");
+
+  const analysis::Constant* x = constants[1];
+  const analysis::Constant* max_val = constants[3];
+
+  if (x == nullptr || max_val == nullptr) {
+    return nullptr;
+  }
+
+  const analysis::Constant* temp =
+      FoldFPBinaryOp(FoldNMin, inst->type_id(), {x, max_val}, context);
+  if (temp == max_val) {
+    // We can assume that |min_val| is less than |max_val|.  Therefore, if the
+    // result of the max operation is |min_val|, we know the result of the min
+    // operation, even if |max_val| is not a constant.
+    return max_val;
+  }
+  return nullptr;
+}
+
 UnaryScalarFoldingRule FoldFTranscendentalUnary(double (*fp)(double)) {
   return
       [fp](const analysis::Type* result_type, const analysis::Constant* a,
@@ -1734,9 +2074,16 @@
 
   rules_[spv::Op::OpDot].push_back(FoldOpDotWithConstants());
   rules_[spv::Op::OpFAdd].push_back(FoldFAdd());
+
   rules_[spv::Op::OpFDiv].push_back(FoldFDiv());
+  rules_[spv::Op::OpFDiv].push_back(FoldRedundantDiv());
+
   rules_[spv::Op::OpFMul].push_back(FoldFMul());
+
   rules_[spv::Op::OpFSub].push_back(FoldFSub());
+  rules_[spv::Op::OpFSub].push_back(FoldRedundantSub());
+
+  rules_[spv::Op::OpSelect].push_back(FoldInvariantSelect());
 
   rules_[spv::Op::OpFOrdEqual].push_back(FoldFOrdEqual());
 
@@ -1792,21 +2139,29 @@
   rules_[spv::Op::OpIAdd].push_back(
       FoldBinaryOp(FoldBinaryIntegerOperation<Unsigned>(
           [](uint64_t a, uint64_t b) { return a + b; })));
+
   rules_[spv::Op::OpISub].push_back(
       FoldBinaryOp(FoldBinaryIntegerOperation<Unsigned>(
           [](uint64_t a, uint64_t b) { return a - b; })));
+  rules_[spv::Op::OpISub].push_back(FoldRedundantSub());
+
   rules_[spv::Op::OpIMul].push_back(
       FoldBinaryOp(FoldBinaryIntegerOperation<Unsigned>(
           [](uint64_t a, uint64_t b) { return a * b; })));
+
   rules_[spv::Op::OpUDiv].push_back(
       FoldBinaryOp(FoldBinaryIntegerOperation<Unsigned>(
           [](uint64_t a, uint64_t b) { return (b != 0 ? a / b : 0); })));
+  rules_[spv::Op::OpUDiv].push_back(FoldRedundantDiv());
+
   rules_[spv::Op::OpSDiv].push_back(FoldBinaryOp(
       FoldBinaryIntegerOperation<Signed>([](uint64_t a, uint64_t b) {
         return (b != 0 ? static_cast<uint64_t>(static_cast<int64_t>(a) /
                                                static_cast<int64_t>(b))
                        : 0);
       })));
+  rules_[spv::Op::OpSDiv].push_back(FoldRedundantDiv());
+
   rules_[spv::Op::OpUMod].push_back(
       FoldBinaryOp(FoldBinaryIntegerOperation<Unsigned>(
           [](uint64_t a, uint64_t b) { return (b != 0 ? a % b : 0); })));
@@ -1841,12 +2196,16 @@
         FoldFPBinaryOp(FoldMin));
     ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FMin}].push_back(
         FoldFPBinaryOp(FoldMin));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450NMin}].push_back(
+        FoldFPBinaryOp(FoldNMin));
     ext_rules_[{ext_inst_glslstd450_id, GLSLstd450SMax}].push_back(
         FoldFPBinaryOp(FoldMax));
     ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UMax}].push_back(
         FoldFPBinaryOp(FoldMax));
     ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FMax}].push_back(
         FoldFPBinaryOp(FoldMax));
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450NMax}].push_back(
+        FoldFPBinaryOp(FoldNMax));
     ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UClamp}].push_back(
         FoldClamp1);
     ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UClamp}].push_back(
@@ -1865,6 +2224,12 @@
         FoldClamp2);
     ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FClamp}].push_back(
         FoldClamp3);
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450NClamp}].push_back(
+        FoldNClamp1);
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450NClamp}].push_back(
+        FoldNClamp2);
+    ext_rules_[{ext_inst_glslstd450_id, GLSLstd450NClamp}].push_back(
+        FoldNClamp3);
     ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Sin}].push_back(
         FoldFPUnaryOp(FoldFTranscendentalUnary(std::sin)));
     ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Cos}].push_back(
diff --git a/source/opt/constants.cpp b/source/opt/constants.cpp
index 7dc02de..f443e2a 100644
--- a/source/opt/constants.cpp
+++ b/source/opt/constants.cpp
@@ -302,6 +302,8 @@
                                 inst->GetInOperand(i).words.end());
   }
 
+  const Type* type = GetType(inst);
+
   switch (inst->opcode()) {
     // OpConstant{True|False} have the value embedded in the opcode. So they
     // are not handled by the for-loop above. Here we add the value explicitly.
@@ -316,11 +318,20 @@
     case spv::Op::OpConstantComposite:
     case spv::Op::OpSpecConstantComposite:
       break;
+    // Replicated composite constant instructions have a single operand for the
+    // value. We need to replicate it as many times as there are components.
+    case spv::Op::OpConstantCompositeReplicateEXT:
+    case spv::Op::OpSpecConstantCompositeReplicateEXT: {
+      uint32_t value = literal_words_or_ids[0];
+      literal_words_or_ids.assign(
+          static_cast<size_t>(type->NumberOfComponents()), value);
+      break;
+    }
     default:
       return nullptr;
   }
 
-  return GetConstant(GetType(inst), literal_words_or_ids);
+  return GetConstant(type, literal_words_or_ids);
 }
 
 std::unique_ptr<Instruction> ConstantManager::CreateInstruction(
@@ -461,7 +472,9 @@
 
 uint32_t ConstantManager::GetFloatConstId(float val) {
   const Constant* c = GetFloatConst(val);
-  return GetDefiningInstruction(c)->result_id();
+  Instruction* inst = GetDefiningInstruction(c);
+  if (inst == nullptr) return 0;
+  return inst->result_id();
 }
 
 const Constant* ConstantManager::GetFloatConst(float val) {
@@ -473,7 +486,9 @@
 
 uint32_t ConstantManager::GetDoubleConstId(double val) {
   const Constant* c = GetDoubleConst(val);
-  return GetDefiningInstruction(c)->result_id();
+  Instruction* inst = GetDefiningInstruction(c);
+  if (inst == nullptr) return 0;
+  return inst->result_id();
 }
 
 const Constant* ConstantManager::GetDoubleConst(double val) {
diff --git a/source/opt/convert_to_half_pass.cpp b/source/opt/convert_to_half_pass.cpp
index e243bed..a3527ff 100644
--- a/source/opt/convert_to_half_pass.cpp
+++ b/source/opt/convert_to_half_pass.cpp
@@ -22,6 +22,7 @@
 namespace opt {
 namespace {
 // Indices of operands in SPIR-V instructions
+constexpr int kImageSampleCoordinateIdInIdx = 1;
 constexpr int kImageSampleDrefIdInIdx = 2;
 }  // namespace
 
@@ -75,6 +76,9 @@
 analysis::Type* ConvertToHalfPass::FloatVectorType(uint32_t v_len,
                                                    uint32_t width) {
   analysis::Type* reg_float_ty = FloatScalarType(width);
+  if (reg_float_ty == nullptr) {
+    return nullptr;
+  }
   analysis::Vector vec_ty(reg_float_ty, v_len);
   return context()->get_type_mgr()->GetRegisteredType(&vec_ty);
 }
@@ -85,6 +89,9 @@
   Instruction* vty_inst = get_def_use_mgr()->GetDef(vty_id);
   uint32_t v_len = vty_inst->GetSingleWordInOperand(1);
   analysis::Type* reg_vec_ty = FloatVectorType(v_len, width);
+  if (reg_vec_ty == nullptr) {
+    return nullptr;
+  }
   analysis::Matrix mat_ty(reg_vec_ty, v_cnt);
   return context()->get_type_mgr()->GetRegisteredType(&mat_ty);
 }
@@ -99,6 +106,9 @@
     reg_equiv_ty = FloatVectorType(ty_inst->GetSingleWordInOperand(1), width);
   else  // spv::Op::OpTypeFloat
     reg_equiv_ty = FloatScalarType(width);
+  if (reg_equiv_ty == nullptr) {
+    return 0;
+  }
   return context()->get_type_mgr()->GetTypeInstruction(reg_equiv_ty);
 }
 
@@ -107,6 +117,10 @@
   Instruction* val_inst = get_def_use_mgr()->GetDef(*val_idp);
   uint32_t ty_id = val_inst->type_id();
   uint32_t nty_id = EquivFloatTypeId(ty_id, width);
+  if (nty_id == 0) {
+    status_ = Status::Failure;
+    return;
+  }
   if (nty_id == ty_id) return;
   Instruction* cvt_inst;
   InstructionBuilder builder(
@@ -116,6 +130,10 @@
     cvt_inst = builder.AddNullaryOp(nty_id, spv::Op::OpUndef);
   else
     cvt_inst = builder.AddUnaryOp(nty_id, spv::Op::OpFConvert, *val_idp);
+  if (cvt_inst == nullptr) {
+    status_ = Status::Failure;
+    return;
+  }
   *val_idp = cvt_inst->result_id();
 }
 
@@ -137,22 +155,43 @@
   uint32_t orig_width = (cty_inst->GetSingleWordInOperand(0) == 16) ? 32 : 16;
   uint32_t orig_mat_id = inst->GetSingleWordInOperand(0);
   uint32_t orig_vty_id = EquivFloatTypeId(vty_id, orig_width);
+  if (orig_vty_id == 0) {
+    status_ = Status::Failure;
+    return false;
+  }
   std::vector<Operand> opnds = {};
   for (uint32_t vidx = 0; vidx < v_cnt; ++vidx) {
     Instruction* ext_inst = builder.AddIdLiteralOp(
         orig_vty_id, spv::Op::OpCompositeExtract, orig_mat_id, vidx);
+    if (ext_inst == nullptr) {
+      status_ = Status::Failure;
+      return false;
+    }
     Instruction* cvt_inst =
         builder.AddUnaryOp(vty_id, spv::Op::OpFConvert, ext_inst->result_id());
+    if (cvt_inst == nullptr) {
+      status_ = Status::Failure;
+      return false;
+    }
     opnds.push_back({SPV_OPERAND_TYPE_ID, {cvt_inst->result_id()}});
   }
   uint32_t mat_id = TakeNextId();
+  if (mat_id == 0) {
+    status_ = Status::Failure;
+    return false;
+  }
   std::unique_ptr<Instruction> mat_inst(new Instruction(
       context(), spv::Op::OpCompositeConstruct, mty_id, mat_id, opnds));
   (void)builder.AddInstruction(std::move(mat_inst));
   context()->ReplaceAllUsesWith(inst->result_id(), mat_id);
   // Turn original instruction into copy so it is valid.
+  uint32_t new_type_id = EquivFloatTypeId(mty_id, orig_width);
+  if (new_type_id == 0) {
+    status_ = Status::Failure;
+    return false;
+  }
   inst->SetOpcode(spv::Op::OpCopyObject);
-  inst->SetResultType(EquivFloatTypeId(mty_id, orig_width));
+  inst->SetResultType(new_type_id);
   get_def_use_mgr()->AnalyzeInstUse(inst);
   return true;
 }
@@ -187,13 +226,24 @@
   // Convert all float32 based operands to float16 equivalent and change
   // instruction type to float16 equivalent.
   inst->ForEachInId([&inst, &modified, this](uint32_t* idp) {
+    if (status_ == Status::Failure) {
+      return;
+    }
     Instruction* op_inst = get_def_use_mgr()->GetDef(*idp);
     if (!IsFloat(op_inst, 32)) return;
     GenConvert(idp, 16, inst);
     modified = true;
   });
+  if (status_ == Status::Failure) {
+    return false;
+  }
   if (IsFloat(inst, 32)) {
-    inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
+    uint32_t new_type_id = EquivFloatTypeId(inst->type_id(), 16);
+    if (new_type_id == 0) {
+      status_ = Status::Failure;
+      return false;
+    }
+    inst->SetResultType(new_type_id);
     converted_ids_.insert(inst->result_id());
     modified = true;
   }
@@ -211,6 +261,9 @@
   bool modified = false;
   inst->ForEachInId([&ocnt, &prev_idp, &from_width, &to_width, &modified,
                      this](uint32_t* idp) {
+    if (status_ == Status::Failure) {
+      return;
+    }
     if (ocnt % 2 == 0) {
       prev_idp = idp;
     } else {
@@ -230,8 +283,16 @@
     }
     ++ocnt;
   });
+  if (status_ == Status::Failure) {
+    return false;
+  }
   if (to_width == 16u) {
-    inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16u));
+    uint32_t new_type_id = EquivFloatTypeId(inst->type_id(), 16u);
+    if (new_type_id == 0) {
+      status_ = Status::Failure;
+      return false;
+    }
+    inst->SetResultType(new_type_id);
     converted_ids_.insert(inst->result_id());
     modified = true;
   }
@@ -242,7 +303,12 @@
 bool ConvertToHalfPass::ProcessConvert(Instruction* inst) {
   // If float32 and relaxed, change to float16 convert
   if (IsFloat(inst, 32) && IsRelaxed(inst->result_id())) {
-    inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
+    uint32_t new_type_id = EquivFloatTypeId(inst->type_id(), 16);
+    if (new_type_id == 0) {
+      status_ = Status::Failure;
+      return false;
+    }
+    inst->SetResultType(new_type_id);
     get_def_use_mgr()->AnalyzeInstUse(inst);
     converted_ids_.insert(inst->result_id());
   }
@@ -255,21 +321,37 @@
   Instruction* val_inst = get_def_use_mgr()->GetDef(val_id);
   if (inst->type_id() == val_inst->type_id())
     inst->SetOpcode(spv::Op::OpCopyObject);
-  return true;  // modified
+  return true;
 }
 
 bool ConvertToHalfPass::ProcessImageRef(Instruction* inst) {
   bool modified = false;
-  // If image reference, only need to convert dref args back to float32
+  // If image reference, some operands aren't allowed to be non-32 bit floats
   if (dref_image_ops_.count(inst->opcode()) != 0) {
     uint32_t dref_id = inst->GetSingleWordInOperand(kImageSampleDrefIdInIdx);
     if (converted_ids_.count(dref_id) > 0) {
       GenConvert(&dref_id, 32, inst);
+      if (status_ == Status::Failure) {
+        return false;
+      }
       inst->SetInOperand(kImageSampleDrefIdInIdx, {dref_id});
       get_def_use_mgr()->AnalyzeInstUse(inst);
       modified = true;
     }
   }
+  if (coordinate_image_ops_.count(inst->opcode()) != 0) {
+    uint32_t coordinate_id =
+        inst->GetSingleWordInOperand(kImageSampleCoordinateIdInIdx);
+    if (converted_ids_.count(coordinate_id) > 0) {
+      GenConvert(&coordinate_id, 32, inst);
+      if (status_ == Status::Failure) {
+        return false;
+      }
+      inst->SetInOperand(kImageSampleCoordinateIdInIdx, {coordinate_id});
+      get_def_use_mgr()->AnalyzeInstUse(inst);
+      modified = true;
+    }
+  }
   return modified;
 }
 
@@ -279,11 +361,17 @@
   if (inst->opcode() == spv::Op::OpPhi) return ProcessPhi(inst, 16u, 32u);
   bool modified = false;
   inst->ForEachInId([&inst, &modified, this](uint32_t* idp) {
+    if (status_ == Status::Failure) {
+      return;
+    }
     if (converted_ids_.count(*idp) == 0) return;
     uint32_t old_id = *idp;
     GenConvert(idp, 32, inst);
     if (*idp != old_id) modified = true;
   });
+  if (status_ == Status::Failure) {
+    return false;
+  }
   if (modified) get_def_use_mgr()->AnalyzeInstUse(inst);
   return modified;
 }
@@ -370,19 +458,38 @@
       });
   // Replace invalid converts of matrix into equivalent vector extracts,
   // converts and finally a composite construct
+  bool ok = true;
   cfg()->ForEachBlockInReversePostOrder(
-      func->entry().get(), [&modified, this](BasicBlock* bb) {
-        for (auto ii = bb->begin(); ii != bb->end(); ++ii)
-          modified |= MatConvertCleanup(&*ii);
+      func->entry().get(), [&modified, &ok, this](BasicBlock* bb) {
+        if (!ok) {
+          return;
+        }
+        for (auto ii = bb->begin(); ii != bb->end(); ++ii) {
+          bool Mmodified = MatConvertCleanup(&*ii);
+          if (status_ == Status::Failure) {
+            ok = false;
+            break;
+          }
+          modified |= Mmodified;
+        }
       });
+
+  if (!ok) {
+    return false;
+  }
   return modified;
 }
 
 Pass::Status ConvertToHalfPass::ProcessImpl() {
+  status_ = Status::SuccessWithoutChange;
   Pass::ProcessFunction pfn = [this](Function* fp) {
     return ProcessFunction(fp);
   };
   bool modified = context()->ProcessReachableCallTree(pfn);
+  if (status_ == Status::Failure) {
+    return status_;
+  }
+
   // If modified, make sure module has Float16 capability
   if (modified) context()->AddCapability(spv::Capability::Float16);
   // Remove all RelaxedPrecision decorations from instructions and globals
@@ -498,6 +605,30 @@
       spv::Op::OpImageSparseSampleProjDrefExplicitLod,
       spv::Op::OpImageSparseDrefGather,
   };
+  coordinate_image_ops_ = {
+      spv::Op::OpImageSampleImplicitLod,
+      spv::Op::OpImageSampleExplicitLod,
+      spv::Op::OpImageSampleDrefImplicitLod,
+      spv::Op::OpImageSampleDrefExplicitLod,
+      spv::Op::OpImageSampleProjImplicitLod,
+      spv::Op::OpImageSampleProjExplicitLod,
+      spv::Op::OpImageSampleProjDrefImplicitLod,
+      spv::Op::OpImageSampleProjDrefExplicitLod,
+      spv::Op::OpImageFetch,
+      spv::Op::OpImageGather,
+      spv::Op::OpImageDrefGather,
+      spv::Op::OpImageRead,
+      spv::Op::OpImageWrite,
+      spv::Op::OpImageQueryLod,
+      spv::Op::OpImageSparseSampleImplicitLod,
+      spv::Op::OpImageSparseSampleExplicitLod,
+      spv::Op::OpImageSparseSampleDrefImplicitLod,
+      spv::Op::OpImageSparseSampleDrefExplicitLod,
+      spv::Op::OpImageSparseFetch,
+      spv::Op::OpImageSparseGather,
+      spv::Op::OpImageSparseDrefGather,
+      spv::Op::OpImageSparseRead,
+  };
   closure_ops_ = {
       spv::Op::OpVectorExtractDynamic,
       spv::Op::OpVectorInsertDynamic,
@@ -514,4 +645,4 @@
 }
 
 }  // namespace opt
-}  // namespace spvtools
+}  // namespace spvtools
\ No newline at end of file
diff --git a/source/opt/convert_to_half_pass.h b/source/opt/convert_to_half_pass.h
index 8e10c4f..c04f27a 100644
--- a/source/opt/convert_to_half_pass.h
+++ b/source/opt/convert_to_half_pass.h
@@ -130,6 +130,9 @@
     }
   };
 
+  // The status of the pass.
+  Pass::Status status_;
+
   // Set of core operations to be processed
   std::unordered_set<spv::Op, hasher> target_ops_core_;
 
@@ -142,6 +145,9 @@
   // Set of only dref sample operations
   std::unordered_set<spv::Op, hasher> dref_image_ops_;
 
+  // Set of only sample operations that have a Coordinate operand
+  std::unordered_set<spv::Op, hasher> coordinate_image_ops_;
+
   // Set of operations that can be marked as relaxed
   std::unordered_set<spv::Op, hasher> closure_ops_;
 
diff --git a/source/opt/convert_to_sampled_image_pass.cpp b/source/opt/convert_to_sampled_image_pass.cpp
index d2da4d1..e720392 100644
--- a/source/opt/convert_to_sampled_image_pass.cpp
+++ b/source/opt/convert_to_sampled_image_pass.cpp
@@ -246,9 +246,10 @@
   InstructionBuilder builder(
       context(), sampled_image->NextNode(),
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-  return builder.AddUnaryOp(
+  Instruction* result = builder.AddUnaryOp(
       GetImageTypeOfSampledImage(context()->get_type_mgr(), sampled_image),
       spv::Op::OpImage, sampled_image->result_id());
+  return result;
 }
 
 uint32_t ConvertToSampledImagePass::GetSampledImageTypeForImage(
@@ -270,6 +271,9 @@
   if (uses_of_load.empty()) return nullptr;
 
   auto* extracted_image = CreateImageExtraction(sampled_image_load);
+  if (extracted_image == nullptr) {
+    return nullptr;
+  }
   for (auto* user : uses_of_load) {
     user->SetInOperand(0, {extracted_image->result_id()});
     context()->get_def_use_mgr()->AnalyzeInstUse(user);
@@ -306,8 +310,12 @@
       def_use_mgr->AnalyzeInstUse(image_load);
       context()->KillInst(sampled_image_inst);
     } else {
-      if (!image_extraction)
+      if (!image_extraction) {
         image_extraction = CreateImageExtraction(image_load);
+        if (image_extraction == nullptr) {
+          return;
+        }
+      }
       sampled_image_inst->SetInOperand(0, {image_extraction->result_id()});
       def_use_mgr->AnalyzeInstUse(sampled_image_inst);
     }
@@ -333,6 +341,9 @@
   // reference.
   uint32_t type_id = context()->get_type_mgr()->FindPointerToType(
       sampled_image_type_id, storage_class);
+  if (type_id == 0) {
+    return false;
+  }
   MoveInstructionNextToType(image_variable, type_id);
   return true;
 }
diff --git a/source/opt/copy_prop_arrays.cpp b/source/opt/copy_prop_arrays.cpp
index 0a42074..547a5e4 100644
--- a/source/opt/copy_prop_arrays.cpp
+++ b/source/opt/copy_prop_arrays.cpp
@@ -95,16 +95,25 @@
     std::unique_ptr<MemoryObject> source_object =
         FindSourceObjectIfPossible(&*var_inst, store_inst);
 
-    if (source_object != nullptr) {
-      if (!IsPointerToArrayType(var_inst->type_id()) &&
-          source_object->GetStorageClass() != spv::StorageClass::Input) {
-        continue;
-      }
+    if (source_object == nullptr) {
+      continue;
+    }
 
-      if (CanUpdateUses(&*var_inst, source_object->GetPointerTypeId(this))) {
-        modified = true;
+    if (!IsPointerToArrayType(var_inst->type_id()) &&
+        source_object->GetStorageClass() != spv::StorageClass::Input) {
+      continue;
+    }
 
-        PropagateObject(&*var_inst, source_object.get(), store_inst);
+    uint32_t pointer_type_id = source_object->GetPointerTypeId(this);
+    if (pointer_type_id == 0) {
+      return Status::Failure;
+    }
+
+    if (CanUpdateUses(&*var_inst, pointer_type_id)) {
+      modified = true;
+
+      if (!PropagateObject(&*var_inst, source_object.get(), store_inst)) {
+        return Status::Failure;
       }
     }
   }
@@ -168,15 +177,16 @@
   return store_inst;
 }
 
-void CopyPropagateArrays::PropagateObject(Instruction* var_inst,
+bool CopyPropagateArrays::PropagateObject(Instruction* var_inst,
                                           MemoryObject* source,
                                           Instruction* insertion_point) {
   assert(var_inst->opcode() == spv::Op::OpVariable &&
          "This function propagates variables.");
 
   Instruction* new_access_chain = BuildNewAccessChain(insertion_point, source);
+  if (!new_access_chain) return false;
   context()->KillNamesAndDecorates(var_inst);
-  UpdateUses(var_inst, new_access_chain);
+  return UpdateUses(var_inst, new_access_chain);
 }
 
 Instruction* CopyPropagateArrays::BuildNewAccessChain(
@@ -190,7 +200,7 @@
     return source->GetVariable();
   }
 
-  source->BuildConstants();
+  if (!source->BuildConstants()) return nullptr;
   std::vector<uint32_t> access_ids(source->AccessChain().size());
   std::transform(
       source->AccessChain().cbegin(), source->AccessChain().cend(),
@@ -219,6 +229,8 @@
       return true;
     } else if (IsInterpolationInstruction(use)) {
       return true;
+    } else if (use->IsCommonDebugInstr()) {
+      return true;
     }
     // Some other instruction.  Be conservative.
     return false;
@@ -252,11 +264,14 @@
         } else if (use->IsDecoration() || use->opcode() == spv::Op::OpName) {
           return true;
         } else if (use->opcode() == spv::Op::OpStore) {
-          // If we are storing to part of the object it is not an candidate.
+          // If we are storing to part of the object it is not a candidate.
           return ptr_inst->opcode() == spv::Op::OpVariable &&
                  store_inst->GetSingleWordInOperand(kStorePointerInOperand) ==
                      ptr_inst->result_id();
         } else if (IsDebugDeclareOrValue(use)) {
+          // The store does not have to dominate debug instructions. We do not
+          // want debugging info to stop the transformation. It will be fixed
+          // up later.
           return true;
         }
         // Some other instruction.  Be conservative.
@@ -276,6 +291,7 @@
     case spv::Op::OpCompositeConstruct:
       return BuildMemoryObjectFromCompositeConstruct(result_inst);
     case spv::Op::OpCopyObject:
+    case spv::Op::OpCopyLogical:
       return GetSourceObjectIfAny(result_inst->GetSingleWordInOperand(0));
     case spv::Op::OpCompositeInsert:
       return BuildMemoryObjectFromInsert(result_inst);
@@ -634,7 +650,7 @@
   });
 }
 
-void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst,
+bool CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst,
                                      Instruction* new_ptr_inst) {
   analysis::TypeManager* type_mgr = context()->get_type_mgr();
   analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
@@ -651,6 +667,22 @@
     uint32_t index = pair.second;
 
     if (use->IsCommonDebugInstr()) {
+      // It is possible that the debug instructions are not dominated by
+      // `new_ptr_inst`. If not, move the debug instruction to just after
+      // `new_ptr_inst`.
+      BasicBlock* store_block = context()->get_instr_block(new_ptr_inst);
+      if (store_block) {
+        Function* function = store_block->GetParent();
+        DominatorAnalysis* dominator_analysis =
+            context()->GetDominatorAnalysis(function);
+        if (!dominator_analysis->Dominates(new_ptr_inst, use)) {
+          assert(dominator_analysis->Dominates(use, new_ptr_inst));
+          use->InsertAfter(new_ptr_inst);
+          context()->set_instr_block(use,
+                                     context()->get_instr_block(new_ptr_inst));
+        }
+      }
+
       switch (use->GetCommonDebugOpcode()) {
         case CommonDebugInfoDebugDeclare: {
           if (new_ptr_inst->opcode() == spv::Op::OpVariable ||
@@ -675,6 +707,7 @@
                 def_use_mgr->GetDef(use->GetSingleWordOperand(index + 1));
             auto* deref_expr_instr =
                 context()->get_debug_info_mgr()->DerefDebugExpression(dbg_expr);
+            if (!deref_expr_instr) return false;
             use->SetOperand(index + 1, {deref_expr_instr->result_id()});
 
             context()->AnalyzeUses(deref_expr_instr);
@@ -759,6 +792,8 @@
         uint32_t new_pointer_type_id =
             type_mgr->FindPointerToType(new_pointee_type_id, storage_class);
 
+        if (new_pointer_type_id == 0) return false;
+
         if (new_pointer_type_id != use->type_id()) {
           use->SetResultType(new_pointer_type_id);
           context()->AnalyzeUses(use);
@@ -805,8 +840,7 @@
           uint32_t pointee_type_id =
               pointer_type->GetSingleWordInOperand(kTypePointerPointeeInIdx);
           uint32_t copy = GenerateCopy(original_ptr_inst, pointee_type_id, use);
-          assert(copy != 0 &&
-                 "Should not be updating uses unless we know it can be done.");
+          if (copy == 0) return false;
 
           context()->ForgetUses(use);
           use->SetInOperand(index, {copy});
@@ -828,6 +862,7 @@
         break;
     }
   }
+  return true;
 }
 
 uint32_t CopyPropagateArrays::GetMemberTypeId(
@@ -892,9 +927,7 @@
                                                 iterator begin, iterator end)
     : variable_inst_(var_inst) {
   std::transform(begin, end, std::back_inserter(access_chain_),
-                 [](uint32_t id) {
-                   return AccessChainEntry{true, {id}};
-                 });
+                 [](uint32_t id) { return AccessChainEntry{true, {id}}; });
 }
 
 std::vector<uint32_t> CopyPropagateArrays::MemoryObject::GetAccessIds() const {
@@ -933,7 +966,7 @@
   return true;
 }
 
-void CopyPropagateArrays::MemoryObject::BuildConstants() {
+bool CopyPropagateArrays::MemoryObject::BuildConstants() {
   for (auto& entry : access_chain_) {
     if (entry.is_result_id) {
       continue;
@@ -946,10 +979,13 @@
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
     const analysis::Constant* index_const =
         const_mgr->GetConstant(uint32_type, {entry.immediate});
-    entry.result_id =
-        const_mgr->GetDefiningInstruction(index_const)->result_id();
+    if (!index_const) return false;
+    Instruction* constant_inst = const_mgr->GetDefiningInstruction(index_const);
+    if (!constant_inst) return false;
+    entry.result_id = constant_inst->result_id();
     entry.is_result_id = true;
   }
+  return true;
 }
 
 }  // namespace opt
diff --git a/source/opt/copy_prop_arrays.h b/source/opt/copy_prop_arrays.h
index bf4bfb5..cb04a14 100644
--- a/source/opt/copy_prop_arrays.h
+++ b/source/opt/copy_prop_arrays.h
@@ -118,7 +118,8 @@
 
     // Converts all immediate values in the AccessChain their OpConstant
     // equivalent.
-    void BuildConstants();
+    // Returns false if the constants could not be created.
+    bool BuildConstants();
 
     // Returns the type id of the pointer type that can be used to point to this
     // memory object.
@@ -175,7 +176,8 @@
   // Replaces all loads of |var_inst| with a load from |source| instead.
   // |insertion_pos| is a position where it is possible to construct the
   // address of |source| and also dominates all of the loads of |var_inst|.
-  void PropagateObject(Instruction* var_inst, MemoryObject* source,
+  // Returns false if the propagation failed.
+  bool PropagateObject(Instruction* var_inst, MemoryObject* source,
                        Instruction* insertion_pos);
 
   // Returns true if all of the references to |ptr_inst| can be rewritten and
@@ -241,7 +243,7 @@
   // types of other instructions as needed.  This function should not be called
   // if |CanUpdateUses(original_ptr_inst, new_pointer_inst->type_id())| returns
   // false.
-  void UpdateUses(Instruction* original_ptr_inst,
+  bool UpdateUses(Instruction* original_ptr_inst,
                   Instruction* new_pointer_inst);
 
   // Return true if |UpdateUses| is able to change all of the uses of
diff --git a/source/opt/debug_info_manager.cpp b/source/opt/debug_info_manager.cpp
index 24094b3..e7fa1d7 100644
--- a/source/opt/debug_info_manager.cpp
+++ b/source/opt/debug_info_manager.cpp
@@ -19,7 +19,7 @@
 
 #include "source/opt/ir_context.h"
 
-// Constants for OpenCL.DebugInfo.100 & NonSemantic.Shader.DebugInfo.100
+// Constants for OpenCL.DebugInfo.100 & NonSemantic.Shader.DebugInfo
 // extension instructions.
 
 namespace spvtools {
@@ -86,8 +86,7 @@
   uint32_t setId =
       context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo();
   if (setId == 0) {
-    setId =
-        context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo();
+    setId = context()->get_feature_mgr()->GetExtInstImportId_ShaderDebugInfo();
   }
   return setId;
 }
@@ -118,14 +117,14 @@
         fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() &&
         "Register DebugFunction for a function that already has DebugFunction");
     fn_id_to_dbg_fn_[fn_id] = inst;
-  } else if (inst->GetShader100DebugOpcode() ==
-             NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
+  } else if (inst->GetShaderDebugOpcode() ==
+             NonSemanticShaderDebugInfoDebugFunctionDefinition) {
     auto fn_id = inst->GetSingleWordOperand(
         kDebugFunctionDefinitionOperandOpFunctionIndex);
     auto fn_inst = GetDbgInst(inst->GetSingleWordOperand(
         kDebugFunctionDefinitionOperandDebugFunctionIndex));
-    assert(fn_inst && fn_inst->GetShader100DebugOpcode() ==
-                          NonSemanticShaderDebugInfo100DebugFunction);
+    assert(fn_inst && fn_inst->GetShaderDebugOpcode() ==
+                          NonSemanticShaderDebugInfoDebugFunction);
     assert(fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() &&
            "Register DebugFunctionDefinition for a function that already has "
            "DebugFunctionDefinition");
@@ -176,10 +175,10 @@
   spv_operand_type_t line_number_type =
       spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER;
 
-  // In NonSemantic.Shader.DebugInfo.100, all constants are IDs of OpConstant,
+  // In NonSemantic.Shader.DebugInfo, all constants are IDs of OpConstant,
   // not literals.
   if (setId ==
-      context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo())
+      context()->get_feature_mgr()->GetExtInstImportId_ShaderDebugInfo())
     line_number_type = spv_operand_type_t::SPV_OPERAND_TYPE_ID;
 
   uint32_t line_number = 0;
@@ -214,8 +213,8 @@
   } else {
     if (line->opcode() == spv::Op::OpLine) {
       line_number = line->GetSingleWordOperand(kOpLineOperandLineIndex);
-    } else if (line->GetShader100DebugOpcode() ==
-               NonSemanticShaderDebugInfo100DebugLine) {
+    } else if (line->GetShaderDebugOpcode() ==
+               NonSemanticShaderDebugInfoDebugLine) {
       line_number = line->GetSingleWordOperand(kLineOperandIndexDebugLine);
     } else {
       assert(false &&
@@ -331,6 +330,7 @@
   if (deref_operation_ != nullptr) return deref_operation_;
 
   uint32_t result_id = context()->TakeNextId();
+  if (result_id == 0) return nullptr;
   std::unique_ptr<Instruction> deref_operation;
 
   if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) {
@@ -346,18 +346,17 @@
         }));
   } else {
     uint32_t deref_id = context()->get_constant_mgr()->GetUIntConstId(
-        NonSemanticShaderDebugInfo100Deref);
+        NonSemanticShaderDebugInfoDeref);
 
-    deref_operation = std::unique_ptr<Instruction>(
-        new Instruction(context(), spv::Op::OpExtInst,
-                        context()->get_type_mgr()->GetVoidTypeId(), result_id,
-                        {
-                            {SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}},
-                            {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
-                             {static_cast<uint32_t>(
-                                 NonSemanticShaderDebugInfo100DebugOperation)}},
-                            {SPV_OPERAND_TYPE_ID, {deref_id}},
-                        }));
+    deref_operation = std::unique_ptr<Instruction>(new Instruction(
+        context(), spv::Op::OpExtInst,
+        context()->get_type_mgr()->GetVoidTypeId(), result_id,
+        {
+            {SPV_OPERAND_TYPE_ID, {GetDbgSetImportId()}},
+            {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+             {static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugOperation)}},
+            {SPV_OPERAND_TYPE_ID, {deref_id}},
+        }));
   }
 
   // Add to the front of |ext_inst_debuginfo_|.
@@ -374,10 +373,13 @@
 Instruction* DebugInfoManager::DerefDebugExpression(Instruction* dbg_expr) {
   assert(dbg_expr->GetCommonDebugOpcode() == CommonDebugInfoDebugExpression);
   std::unique_ptr<Instruction> deref_expr(dbg_expr->Clone(context()));
-  deref_expr->SetResultId(context()->TakeNextId());
-  deref_expr->InsertOperand(
-      kDebugExpressOperandOperationIndex,
-      {SPV_OPERAND_TYPE_ID, {GetDebugOperationWithDeref()->result_id()}});
+  uint32_t result_id = context()->TakeNextId();
+  if (result_id == 0) return nullptr;
+  deref_expr->SetResultId(result_id);
+  Instruction* deref_op = GetDebugOperationWithDeref();
+  if (!deref_op) return nullptr;
+  deref_expr->InsertOperand(kDebugExpressOperandOperationIndex,
+                            {SPV_OPERAND_TYPE_ID, {deref_op->result_id()}});
   auto* deref_expr_instr =
       context()->ext_inst_debuginfo_end()->InsertBefore(std::move(deref_expr));
   AnalyzeDebugInst(deref_expr_instr);
@@ -390,6 +392,7 @@
   if (debug_info_none_inst_ != nullptr) return debug_info_none_inst_;
 
   uint32_t result_id = context()->TakeNextId();
+  if (result_id == 0) return nullptr;
   std::unique_ptr<Instruction> dbg_info_none_inst(new Instruction(
       context(), spv::Op::OpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
       result_id,
@@ -558,11 +561,11 @@
   return false;
 }
 
-bool DebugInfoManager::AddDebugValueForVariable(Instruction* scope_and_line,
+bool DebugInfoManager::AddDebugValueForVariable(Instruction* line,
                                                 uint32_t variable_id,
                                                 uint32_t value_id,
                                                 Instruction* insert_pos) {
-  assert(scope_and_line != nullptr);
+  assert(line != nullptr);
 
   auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
   if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return false;
@@ -577,14 +580,15 @@
       insert_before = insert_before->NextNode();
     }
     modified |= AddDebugValueForDecl(dbg_decl_or_val, value_id, insert_before,
-                                     scope_and_line) != nullptr;
+                                     line) != nullptr;
   }
   return modified;
 }
 
-Instruction* DebugInfoManager::AddDebugValueForDecl(
-    Instruction* dbg_decl, uint32_t value_id, Instruction* insert_before,
-    Instruction* scope_and_line) {
+Instruction* DebugInfoManager::AddDebugValueForDecl(Instruction* dbg_decl,
+                                                    uint32_t value_id,
+                                                    Instruction* insert_before,
+                                                    Instruction* line) {
   if (dbg_decl == nullptr || !IsDebugDeclare(dbg_decl)) return nullptr;
 
   std::unique_ptr<Instruction> dbg_val(dbg_decl->Clone(context()));
@@ -593,7 +597,7 @@
   dbg_val->SetOperand(kDebugDeclareOperandVariableIndex, {value_id});
   dbg_val->SetOperand(kDebugValueOperandExpressionIndex,
                       {GetEmptyDebugExpression()->result_id()});
-  dbg_val->UpdateDebugInfoFrom(scope_and_line);
+  dbg_val->UpdateDebugInfoFrom(dbg_decl, line);
 
   auto* added_dbg_val = insert_before->InsertBefore(std::move(dbg_val));
   AnalyzeDebugInst(added_dbg_val);
@@ -608,8 +612,8 @@
 }
 
 uint32_t DebugInfoManager::GetVulkanDebugOperation(Instruction* inst) {
-  assert(inst->GetShader100DebugOpcode() ==
-             NonSemanticShaderDebugInfo100DebugOperation &&
+  assert(inst->GetShaderDebugOpcode() ==
+             NonSemanticShaderDebugInfoDebugOperation &&
          "inst must be Vulkan DebugOperation");
   return context()
       ->get_constant_mgr()
@@ -640,7 +644,7 @@
     }
   } else {
     uint32_t operation_const = GetVulkanDebugOperation(operation);
-    if (operation_const != NonSemanticShaderDebugInfo100Deref) {
+    if (operation_const != NonSemanticShaderDebugInfoDeref) {
       return 0;
     }
   }
@@ -717,8 +721,8 @@
   RegisterDbgInst(inst);
 
   if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction ||
-      inst->GetShader100DebugOpcode() ==
-          NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
+      inst->GetShaderDebugOpcode() ==
+          NonSemanticShaderDebugInfoDebugFunctionDefinition) {
     RegisterDbgFunction(inst);
   }
 
@@ -730,10 +734,10 @@
   }
 
   if (deref_operation_ == nullptr &&
-      inst->GetShader100DebugOpcode() ==
-          NonSemanticShaderDebugInfo100DebugOperation) {
+      inst->GetShaderDebugOpcode() ==
+          NonSemanticShaderDebugInfoDebugOperation) {
     uint32_t operation_const = GetVulkanDebugOperation(inst);
-    if (operation_const == NonSemanticShaderDebugInfo100Deref) {
+    if (operation_const == NonSemanticShaderDebugInfoDeref) {
       deref_operation_ = inst;
     }
   }
@@ -870,8 +874,8 @@
         instr->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex);
     fn_id_to_dbg_fn_.erase(fn_id);
   }
-  if (instr->GetShader100DebugOpcode() ==
-      NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
+  if (instr->GetShaderDebugOpcode() ==
+      NonSemanticShaderDebugInfoDebugFunctionDefinition) {
     auto fn_id = instr->GetSingleWordOperand(
         kDebugFunctionDefinitionOperandOpFunctionIndex);
     fn_id_to_dbg_fn_.erase(fn_id);
@@ -903,10 +907,10 @@
         deref_operation_ = &*dbg_instr_itr;
         break;
       } else if (instr != &*dbg_instr_itr &&
-                 dbg_instr_itr->GetShader100DebugOpcode() ==
-                     NonSemanticShaderDebugInfo100DebugOperation) {
+                 dbg_instr_itr->GetShaderDebugOpcode() ==
+                     NonSemanticShaderDebugInfoDebugOperation) {
         uint32_t operation_const = GetVulkanDebugOperation(&*dbg_instr_itr);
-        if (operation_const == NonSemanticShaderDebugInfo100Deref) {
+        if (operation_const == NonSemanticShaderDebugInfoDeref) {
           deref_operation_ = &*dbg_instr_itr;
           break;
         }
diff --git a/source/opt/debug_info_manager.h b/source/opt/debug_info_manager.h
index abb7b9a..a23e9ab 100644
--- a/source/opt/debug_info_manager.h
+++ b/source/opt/debug_info_manager.h
@@ -69,7 +69,7 @@
 };
 
 // A class for analyzing, managing, and creating OpenCL.DebugInfo.100 and
-// NonSemantic.Shader.DebugInfo.100 extension instructions.
+// NonSemantic.Shader.DebugInfo extension instructions.
 class DebugInfoManager {
  public:
   // Constructs a debug information manager from the given |context|.
@@ -143,22 +143,21 @@
   bool KillDebugDeclares(uint32_t variable_id);
 
   // Generates a DebugValue instruction with value |value_id| for every local
-  // variable that is in the scope of |scope_and_line| and whose memory is
-  // |variable_id| and inserts it after the instruction |insert_pos|.
+  // variable that is in the scope of |line| and whose memory is |variable_id|
+  // and inserts it after the instruction |insert_pos|.
   // Returns whether a DebugValue is added or not.
-  bool AddDebugValueForVariable(Instruction* scope_and_line,
-                                uint32_t variable_id, uint32_t value_id,
-                                Instruction* insert_pos);
+  bool AddDebugValueForVariable(Instruction* line, uint32_t variable_id,
+                                uint32_t value_id, Instruction* insert_pos);
 
   // Creates a DebugValue for DebugDeclare |dbg_decl| and inserts it before
-  // |insert_before|. The new DebugValue has the same line and scope as
-  // |scope_and_line|, or no scope and line information if |scope_and_line|
-  // is nullptr. The new DebugValue has the same operands as DebugDeclare
-  // but it uses |value_id| for the value. Returns the created DebugValue,
+  // |insert_before|. The new DebugValue has the same line as |line} and the
+  // same scope as |dbg_decl|. The new DebugValue has the same operands as
+  // DebugDeclare but it uses |value_id| for the value. Returns the created
+  // DebugValue,
   // or nullptr if fails to create one.
   Instruction* AddDebugValueForDecl(Instruction* dbg_decl, uint32_t value_id,
                                     Instruction* insert_before,
-                                    Instruction* scope_and_line);
+                                    Instruction* line);
 
   // Erases |instr| from data structures of this class.
   void ClearDebugInfo(Instruction* instr);
diff --git a/source/opt/decoration_manager.cpp b/source/opt/decoration_manager.cpp
index 3e95dbc..bee7d94 100644
--- a/source/opt/decoration_manager.cpp
+++ b/source/opt/decoration_manager.cpp
@@ -543,7 +543,8 @@
         const uint32_t num_operands = inst->NumOperands();
         for (uint32_t i = 1; i < num_operands; i += 2) {
           Operand op = inst->GetOperand(i);
-          if (op.words[0] == from) {  // add new pair of operands: (to, literal)
+          if (!op.words.empty() &&
+              op.words[0] == from) {  // add new pair of operands: (to, literal)
             inst->AddOperand(
                 Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {to}));
             op = inst->GetOperand(i + 1);
diff --git a/source/opt/desc_sroa.cpp b/source/opt/desc_sroa.cpp
index 124a3d3..b2f1268 100644
--- a/source/opt/desc_sroa.cpp
+++ b/source/opt/desc_sroa.cpp
@@ -58,7 +58,7 @@
   std::vector<Instruction*> access_chain_work_list;
   std::vector<Instruction*> load_work_list;
   std::vector<Instruction*> entry_point_work_list;
-  bool failed = !get_def_use_mgr()->WhileEachUser(
+  bool ok = get_def_use_mgr()->WhileEachUser(
       var->result_id(), [this, &access_chain_work_list, &load_work_list,
                          &entry_point_work_list](Instruction* use) {
         if (use->opcode() == spv::Op::OpName) {
@@ -88,7 +88,7 @@
         return true;
       });
 
-  if (failed) {
+  if (!ok) {
     return false;
   }
 
@@ -128,6 +128,9 @@
 
   uint32_t idx = const_index->GetU32();
   uint32_t replacement_var = GetReplacementVariable(var, idx);
+  if (replacement_var == 0) {
+    return false;
+  }
 
   if (use->NumInOperands() == 2) {
     // We are not indexing into the replacement variable.  We can replaces the
@@ -186,8 +189,11 @@
   uint32_t num_replacement_vars =
       descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var);
   for (uint32_t i = 0; i < num_replacement_vars; i++) {
-    new_operands.push_back(
-        {SPV_OPERAND_TYPE_ID, {GetReplacementVariable(var, i)}});
+    uint32_t replacement_var_id = GetReplacementVariable(var, i);
+    if (replacement_var_id == 0) {
+      return false;
+    }
+    new_operands.push_back({SPV_OPERAND_TYPE_ID, {replacement_var_id}});
   }
 
   use->ReplaceOperands(new_operands);
@@ -310,7 +316,10 @@
       element_type_id, storage_class);
 
   // Create the variable.
-  uint32_t id = TakeNextId();
+  uint32_t id = context()->TakeNextId();
+  if (id == 0) {
+    return 0;
+  }
   std::unique_ptr<Instruction> variable(
       new Instruction(context(), spv::Op::OpVariable, ptr_element_type_id, id,
                       std::initializer_list<Operand>{
@@ -444,10 +453,16 @@
 
   uint32_t replacement_var =
       GetReplacementVariable(var, extract->GetSingleWordInOperand(1));
+  if (replacement_var == 0) {
+    return false;
+  }
 
   // The result type of the OpLoad is the same as the result type of the
   // OpCompositeExtract.
-  uint32_t load_id = TakeNextId();
+  uint32_t load_id = context()->TakeNextId();
+  if (load_id == 0) {
+    return false;
+  }
   std::unique_ptr<Instruction> load(
       new Instruction(context(), spv::Op::OpLoad, extract->type_id(), load_id,
                       std::initializer_list<Operand>{
diff --git a/source/opt/eliminate_dead_members_pass.cpp b/source/opt/eliminate_dead_members_pass.cpp
index e440296..33c0503 100644
--- a/source/opt/eliminate_dead_members_pass.cpp
+++ b/source/opt/eliminate_dead_members_pass.cpp
@@ -207,6 +207,7 @@
       case spv::Op::OpTypeMatrix:
       case spv::Op::OpTypeCooperativeMatrixNV:
       case spv::Op::OpTypeCooperativeMatrixKHR:
+      case spv::Op::OpTypeVectorIdEXT:
         type_id = type_inst->GetSingleWordInOperand(0);
         break;
       default:
@@ -255,6 +256,7 @@
       case spv::Op::OpTypeMatrix:
       case spv::Op::OpTypeCooperativeMatrixNV:
       case spv::Op::OpTypeCooperativeMatrixKHR:
+      case spv::Op::OpTypeVectorIdEXT:
         type_id = type_inst->GetSingleWordInOperand(0);
         break;
       default:
@@ -516,6 +518,7 @@
       case spv::Op::OpTypeMatrix:
       case spv::Op::OpTypeCooperativeMatrixNV:
       case spv::Op::OpTypeCooperativeMatrixKHR:
+      case spv::Op::OpTypeVectorIdEXT:
         new_operands.emplace_back(inst->GetInOperand(i));
         type_id = type_inst->GetSingleWordInOperand(0);
         break;
@@ -591,6 +594,7 @@
       case spv::Op::OpTypeMatrix:
       case spv::Op::OpTypeCooperativeMatrixNV:
       case spv::Op::OpTypeCooperativeMatrixKHR:
+      case spv::Op::OpTypeVectorIdEXT:
         type_id = type_inst->GetSingleWordInOperand(0);
         break;
       default:
@@ -654,6 +658,7 @@
       case spv::Op::OpTypeMatrix:
       case spv::Op::OpTypeCooperativeMatrixNV:
       case spv::Op::OpTypeCooperativeMatrixKHR:
+      case spv::Op::OpTypeVectorIdEXT:
         type_id = type_inst->GetSingleWordInOperand(0);
         break;
       default:
diff --git a/source/opt/feature_manager.cpp b/source/opt/feature_manager.cpp
index 5188370..c5c9c4c 100644
--- a/source/opt/feature_manager.cpp
+++ b/source/opt/feature_manager.cpp
@@ -16,7 +16,7 @@
 
 #include <string>
 
-#include "source/enum_string_mapping.h"
+#include "source/table2.h"
 
 namespace spvtools {
 namespace opt {
@@ -34,10 +34,13 @@
 }
 
 void FeatureManager::AddExtension(Instruction* ext) {
-  assert(ext->opcode() == spv::Op::OpExtension &&
+  assert((ext->opcode() == spv::Op::OpExtension ||
+          ext->opcode() == spv::Op::OpConditionalExtensionINTEL) &&
          "Expecting an extension instruction.");
 
-  const std::string name = ext->GetInOperand(0u).AsString();
+  const uint32_t name_i =
+      ext->opcode() == spv::Op::OpConditionalExtensionINTEL ? 1u : 0u;
+  const std::string name = ext->GetInOperand(name_i).AsString();
   Extension extension;
   if (GetExtensionFromString(name.c_str(), &extension)) {
     extensions_.insert(extension);
@@ -54,11 +57,12 @@
 
   capabilities_.insert(cap);
 
-  spv_operand_desc desc = {};
-  if (SPV_SUCCESS == grammar_.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY,
-                                            uint32_t(cap), &desc)) {
+  const spvtools::OperandDesc* desc = nullptr;
+  if (SPV_SUCCESS == spvtools::LookupOperand(SPV_OPERAND_TYPE_CAPABILITY,
+                                             uint32_t(cap), &desc)) {
     for (auto capability :
-         CapabilitySet(desc->numCapabilities, desc->capabilities)) {
+         CapabilitySet(static_cast<uint32_t>(desc->capabilities().size()),
+                       desc->capabilities().data())) {
       AddCapability(capability);
     }
   }
@@ -71,7 +75,10 @@
 
 void FeatureManager::AddCapabilities(Module* module) {
   for (Instruction& inst : module->capabilities()) {
-    AddCapability(static_cast<spv::Capability>(inst.GetSingleWordInOperand(0)));
+    const uint32_t i_cap =
+        inst.opcode() == spv::Op::OpConditionalCapabilityINTEL ? 1 : 0;
+    AddCapability(
+        static_cast<spv::Capability>(inst.GetSingleWordInOperand(i_cap)));
   }
 }
 
@@ -79,8 +86,14 @@
   extinst_importid_GLSLstd450_ = module->GetExtInstImportId("GLSL.std.450");
   extinst_importid_OpenCL100DebugInfo_ =
       module->GetExtInstImportId("OpenCL.DebugInfo.100");
-  extinst_importid_Shader100DebugInfo_ =
-      module->GetExtInstImportId("NonSemantic.Shader.DebugInfo.100");
+  // Match any version of NonSemantic.Shader.DebugInfo.
+  for (auto& ei : module->ext_inst_imports()) {
+    const std::string name = ei.GetInOperand(0).AsString();
+    if (name.compare(0, 29, "NonSemantic.Shader.DebugInfo.") == 0) {
+      extinst_importid_ShaderDebugInfo_ = ei.result_id();
+      break;
+    }
+  }
 }
 
 bool operator==(const FeatureManager& a, const FeatureManager& b) {
@@ -108,8 +121,8 @@
     return false;
   }
 
-  if (a.extinst_importid_Shader100DebugInfo_ !=
-      b.extinst_importid_Shader100DebugInfo_) {
+  if (a.extinst_importid_ShaderDebugInfo_ !=
+      b.extinst_importid_ShaderDebugInfo_) {
     return false;
   }
 
diff --git a/source/opt/feature_manager.h b/source/opt/feature_manager.h
index d150a2f..a0caac7 100644
--- a/source/opt/feature_manager.h
+++ b/source/opt/feature_manager.h
@@ -47,8 +47,8 @@
     return extinst_importid_OpenCL100DebugInfo_;
   }
 
-  uint32_t GetExtInstImportId_Shader100DebugInfo() const {
-    return extinst_importid_Shader100DebugInfo_;
+  uint32_t GetExtInstImportId_ShaderDebugInfo() const {
+    return extinst_importid_ShaderDebugInfo_;
   }
 
   friend bool operator==(const FeatureManager& a, const FeatureManager& b);
@@ -100,9 +100,9 @@
   // for performance.
   uint32_t extinst_importid_OpenCL100DebugInfo_ = 0;
 
-  // Common NonSemanticShader100DebugInfo external instruction import ids,
+  // Common NonSemanticShaderDebugInfo external instruction import ids,
   // cached for performance.
-  uint32_t extinst_importid_Shader100DebugInfo_ = 0;
+  uint32_t extinst_importid_ShaderDebugInfo_ = 0;
 
   friend class IRContext;
 };
diff --git a/source/opt/fix_func_call_arguments.cpp b/source/opt/fix_func_call_arguments.cpp
index f3486be..aa45c9f 100644
--- a/source/opt/fix_func_call_arguments.cpp
+++ b/source/opt/fix_func_call_arguments.cpp
@@ -74,16 +74,19 @@
       op_type->result_id(), spv::StorageClass::Function);
   // Create new variable
   builder.SetInsertPoint(variable_insertion_point);
+  // TODO(1841): Handle id overflow.
   Instruction* var =
       builder.AddVariable(varType, uint32_t(spv::StorageClass::Function));
   // Load access chain to the new variable before function call
   builder.SetInsertPoint(func_call_inst);
 
   uint32_t operand_id = operand_inst->result_id();
+  // TODO(1841): Handle id overflow.
   Instruction* load = builder.AddLoad(op_type->result_id(), operand_id);
   builder.AddStore(var->result_id(), load->result_id());
   // Load return value to the acesschain after function call
   builder.SetInsertPoint(next_insert_point);
+  // TODO(1841): Handle id overflow.
   load = builder.AddLoad(op_type->result_id(), var->result_id());
   builder.AddStore(operand_id, load->result_id());
 
diff --git a/source/opt/fix_storage_class.cpp b/source/opt/fix_storage_class.cpp
index b64026e..608285e 100644
--- a/source/opt/fix_storage_class.cpp
+++ b/source/opt/fix_storage_class.cpp
@@ -1,4 +1,6 @@
 // Copyright (c) 2019 Google LLC
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -99,6 +101,7 @@
     case spv::Op::OpCopyMemorySized:
     case spv::Op::OpVariable:
     case spv::Op::OpBitcast:
+    case spv::Op::OpAllocateNodePayloadsAMDX:
       // Nothing to change for these opcode.  The result type is the same
       // regardless of the storage class of the operand.
       return false;
@@ -319,6 +322,7 @@
     switch (type_inst->opcode()) {
       case spv::Op::OpTypeArray:
       case spv::Op::OpTypeRuntimeArray:
+      case spv::Op::OpTypeNodePayloadArrayAMDX:
       case spv::Op::OpTypeMatrix:
       case spv::Op::OpTypeVector:
       case spv::Op::OpTypeCooperativeMatrixKHR:
diff --git a/source/opt/fold.cpp b/source/opt/fold.cpp
index 942da68..64c947f 100644
--- a/source/opt/fold.cpp
+++ b/source/opt/fold.cpp
@@ -50,7 +50,7 @@
       if (s_operand == std::numeric_limits<int32_t>::min()) {
         return s_operand;
       }
-      return -s_operand;
+      return static_cast<uint32_t>(-s_operand);
     }
     case spv::Op::OpNot:
       return ~operand;
@@ -597,6 +597,9 @@
   const analysis::Constant* folded_const = nullptr;
   for (auto rule : GetConstantFoldingRules().GetRulesForInstruction(inst)) {
     folded_const = rule(context_, inst, constants);
+    if (folded_const == nullptr && inst->context()->id_overflow()) {
+      return nullptr;
+    }
     if (folded_const != nullptr) {
       Instruction* const_inst =
           const_mgr->GetDefiningInstruction(folded_const, inst->type_id());
diff --git a/source/opt/fold_spec_constant_op_and_composite_pass.cpp b/source/opt/fold_spec_constant_op_and_composite_pass.cpp
index ddfe59f..edcf551 100644
--- a/source/opt/fold_spec_constant_op_and_composite_pass.cpp
+++ b/source/opt/fold_spec_constant_op_and_composite_pass.cpp
@@ -1,4 +1,5 @@
 // Copyright (c) 2016 Google Inc.
+// Copyright (c) 2025 Arm Ltd.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -31,21 +32,20 @@
   // instructions, records their values in two internal maps: id_to_const_val_
   // and const_val_to_id_ so that we can use them to infer the value of Spec
   // Constants later.
-  // For Spec Constants defined with OpSpecConstantComposite instructions, if
-  // all of their components are Normal Constants, they will be turned into
-  // Normal Constants too. For Spec Constants defined with OpSpecConstantOp
-  // instructions, we check if they only depends on Normal Constants and fold
-  // them when possible. The two maps for Normal Constants: id_to_const_val_
-  // and const_val_to_id_ will be updated along the traversal so that the new
-  // Normal Constants generated from folding can be used to fold following Spec
-  // Constants.
-  // This algorithm depends on the SSA property of SPIR-V when
-  // defining constants. The dependent constants must be defined before the
-  // dependee constants. So a dependent Spec Constant must be defined and
-  // will be processed before its dependee Spec Constant. When we encounter
-  // the dependee Spec Constants, all its dependent constants must have been
-  // processed and all its dependent Spec Constants should have been folded if
-  // possible.
+  // For Spec Constants defined with OpSpecConstantComposite or
+  // OpSpecConstantCompositeReplicateEXT instructions, if all of their
+  // components are Normal Constants, they will be turned into Normal Constants
+  // too. For Spec Constants defined with OpSpecConstantOp instructions, we
+  // check if they only depends on Normal Constants and fold them when possible.
+  // The two maps for Normal Constants: id_to_const_val_ and const_val_to_id_
+  // will be updated along the traversal so that the new Normal Constants
+  // generated from folding can be used to fold following Spec Constants. This
+  // algorithm depends on the SSA property of SPIR-V when defining constants.
+  // The dependent constants must be defined before the dependee constants. So a
+  // dependent Spec Constant must be defined and will be processed before its
+  // dependee Spec Constant. When we encounter the dependee Spec Constants, all
+  // its dependent constants must have been processed and all its dependent Spec
+  // Constants should have been folded if possible.
   Module::inst_iterator next_inst = context()->types_values_begin();
   for (Module::inst_iterator inst_iter = next_inst;
        // Need to re-evaluate the end iterator since we may modify the list of
@@ -54,8 +54,9 @@
     ++next_inst;
     Instruction* inst = &*inst_iter;
     // Collect constant values of normal constants and process the
-    // OpSpecConstantOp and OpSpecConstantComposite instructions if possible.
-    // The constant values will be stored in analysis::Constant instances.
+    // OpSpecConstantOp, OpSpecConstantComposite, and
+    // OpSpecConstantCompositeReplicateEXT instructions if possible. The
+    // constant values will be stored in analysis::Constant instances.
     // OpConstantSampler instruction is not collected here because it cannot be
     // used in OpSpecConstant{Composite|Op} instructions.
     // TODO(qining): If the constant or its type has decoration, we may need
@@ -70,21 +71,29 @@
       case spv::Op::OpConstant:
       case spv::Op::OpConstantNull:
       case spv::Op::OpConstantComposite:
-      case spv::Op::OpSpecConstantComposite: {
+      case spv::Op::OpSpecConstantComposite:
+      case spv::Op::OpSpecConstantCompositeReplicateEXT: {
         // A Constant instance will be created if the given instruction is a
         // Normal Constant whose value(s) are fixed. Note that for a composite
-        // Spec Constant defined with OpSpecConstantComposite instruction, if
-        // all of its components are Normal Constants already, the Spec
-        // Constant will be turned in to a Normal Constant. In that case, a
-        // Constant instance should also be created successfully and recorded
-        // in the id_to_const_val_ and const_val_to_id_ mapps.
+        // Spec Constant defined with OpSpecConstantComposite or
+        // OpSpecConstantCompositeReplicateEXT instruction, if all of its
+        // components are Normal Constants already, the Spec Constant will be
+        // turned in to a Normal Constant. In that case, a Constant instance
+        // should also be created successfully and recorded in the
+        // id_to_const_val_ and const_val_to_id_ mapps.
         if (auto const_value = const_mgr->GetConstantFromInst(inst)) {
-          // Need to replace the OpSpecConstantComposite instruction with a
-          // corresponding OpConstantComposite instruction.
+          // Need to replace the OpSpecConstantComposite or
+          // OpSpecConstantCompositeReplicateEXT instruction with a
+          // corresponding OpConstantComposite or
+          // OpConstantCompositeReplicateEXT instruction.
           if (opcode == spv::Op::OpSpecConstantComposite) {
             inst->SetOpcode(spv::Op::OpConstantComposite);
             modified = true;
           }
+          if (opcode == spv::Op::OpSpecConstantCompositeReplicateEXT) {
+            inst->SetOpcode(spv::Op::OpConstantCompositeReplicateEXT);
+            modified = true;
+          }
           const_mgr->MapConstantToInst(const_value, inst);
         }
         break;
@@ -96,9 +105,16 @@
       // Constants will be added to id_to_const_val_ and const_val_to_id_ so
       // that we can use the new Normal Constants when folding following Spec
       // Constants.
-      case spv::Op::OpSpecConstantOp:
-        modified |= ProcessOpSpecConstantOp(&inst_iter);
+      case spv::Op::OpSpecConstantOp: {
+        const auto status = ProcessOpSpecConstantOp(&inst_iter);
+        if (status == Status::Failure) {
+          return Status::Failure;
+        }
+        if (status == Status::SuccessWithChange) {
+          modified = true;
+        }
         break;
+      }
       default:
         break;
     }
@@ -106,7 +122,7 @@
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
-bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp(
+Pass::Status FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp(
     Module::inst_iterator* pos) {
   Instruction* inst = &**pos;
   Instruction* folded_inst = nullptr;
@@ -116,10 +132,17 @@
          "SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER type");
 
   folded_inst = FoldWithInstructionFolder(pos);
+  if (context()->id_overflow()) {
+    return Status::Failure;
+  }
+
   if (!folded_inst) {
     folded_inst = DoComponentWiseOperation(pos);
+    if (context()->id_overflow()) {
+      return Status::Failure;
+    }
   }
-  if (!folded_inst) return false;
+  if (!folded_inst) return Status::SuccessWithoutChange;
 
   // Replace the original constant with the new folded constant, kill the
   // original constant.
@@ -127,7 +150,7 @@
   uint32_t old_id = inst->result_id();
   context()->ReplaceAllUsesWith(old_id, new_id);
   context()->KillDef(old_id);
-  return true;
+  return Status::SuccessWithChange;
 }
 
 Instruction* FoldSpecConstantOpAndCompositePass::FoldWithInstructionFolder(
@@ -186,7 +209,11 @@
 
   if (need_to_clone) {
     new_const_inst = new_const_inst->Clone(context());
-    new_const_inst->SetResultId(TakeNextId());
+    uint32_t new_id = TakeNextId();
+    if (new_id == 0) {
+      return nullptr;
+    }
+    new_const_inst->SetResultId(new_id);
     new_const_inst->InsertAfter(insert_pos);
     get_def_use_mgr()->AnalyzeInstDefUse(new_const_inst);
   }
diff --git a/source/opt/fold_spec_constant_op_and_composite_pass.h b/source/opt/fold_spec_constant_op_and_composite_pass.h
index 9a8fb40..d48809c 100644
--- a/source/opt/fold_spec_constant_op_and_composite_pass.h
+++ b/source/opt/fold_spec_constant_op_and_composite_pass.h
@@ -19,6 +19,7 @@
 #include <unordered_map>
 #include <vector>
 
+#include "source/diagnostic.h"
 #include "source/opt/constants.h"
 #include "source/opt/def_use_manager.h"
 #include "source/opt/ir_context.h"
@@ -45,14 +46,14 @@
  private:
   // Processes the OpSpecConstantOp instruction pointed by the given
   // instruction iterator, folds it to normal constants if possible. Returns
-  // true if the spec constant is folded to normal constants. New instructions
-  // will be inserted before the OpSpecConstantOp instruction pointed by the
-  // instruction iterator. The instruction iterator, which is passed by
-  // pointer, will still point to the original OpSpecConstantOp instruction. If
-  // folding is done successfully, the original OpSpecConstantOp instruction
-  // will be changed to Nop and new folded instruction will be inserted before
-  // it.
-  bool ProcessOpSpecConstantOp(Module::inst_iterator* pos);
+  // kSuccess if the spec constant is folded to normal constants. New
+  // instructions will be inserted before the OpSpecConstantOp instruction
+  // pointed by the instruction iterator. The instruction iterator, which is
+  // passed by pointer, will still point to the original OpSpecConstantOp
+  // instruction. If folding is done successfully, the original OpSpecConstantOp
+  // instruction will be changed to Nop and new folded instruction will be
+  // inserted before it. Returns kFail if an id overflow occurs.
+  Status ProcessOpSpecConstantOp(Module::inst_iterator* pos);
 
   // Returns the result of folding the OpSpecConstantOp instruction
   // |inst_iter_ptr| using the instruction folder.
@@ -62,7 +63,24 @@
   // pointed by the given instruction iterator to a normal constant defining
   // instruction. Returns the pointer to the new constant defining instruction
   // if succeeded, otherwise return nullptr.
+  // instruction if succeeded, otherwise return nullptr.
   Instruction* DoComponentWiseOperation(Module::inst_iterator* inst_iter_ptr);
+
+  // Returns the next available id, or 0 if the id overflows.
+  uint32_t TakeNextId() {
+    uint32_t next_id = context()->TakeNextId();
+    if (next_id == 0) {
+      Fail() << "ID overflow. Try running compact-ids.";
+    }
+    return next_id;
+  }
+
+  // Records failure for the current module and returns a stream for printing
+  // diagnostics.
+  spvtools::DiagnosticStream Fail() {
+    return spvtools::DiagnosticStream({}, context()->consumer(), "",
+                                      SPV_ERROR_INTERNAL);
+  }
 };
 
 }  // namespace opt
diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp
index 5748f97..792b0f1 100644
--- a/source/opt/folding_rules.cpp
+++ b/source/opt/folding_rules.cpp
@@ -16,6 +16,7 @@
 
 #include <limits>
 #include <memory>
+#include <optional>
 #include <utility>
 
 #include "ir_builder.h"
@@ -77,7 +78,10 @@
 
 // Returns the element width of |type|.
 uint32_t ElementWidth(const analysis::Type* type) {
-  if (const analysis::Vector* vec_type = type->AsVector()) {
+  if (const analysis::CooperativeVectorNV* coopvec_type =
+          type->AsCooperativeVectorNV()) {
+    return ElementWidth(coopvec_type->component_type());
+  } else if (const analysis::Vector* vec_type = type->AsVector()) {
     return ElementWidth(vec_type->element_type());
   } else if (const analysis::Float* float_type = type->AsFloat()) {
     return float_type->width();
@@ -112,12 +116,6 @@
   }
 }
 
-// Returns true if `type` is a cooperative matrix.
-bool IsCooperativeMatrix(const analysis::Type* type) {
-  return type->kind() == analysis::Type::kCooperativeMatrixKHR ||
-         type->kind() == analysis::Type::kCooperativeMatrixNV;
-}
-
 const analysis::Constant* ConstInput(
     const std::vector<const analysis::Constant*>& constants) {
   return constants[0] ? constants[0] : constants[1];
@@ -173,12 +171,57 @@
     return GetWordsFromScalarIntConstant(int_constant);
   } else if (const auto* vec_constant = c->AsVectorConstant()) {
     std::vector<uint32_t> words;
+    // Retrieve all the components as 32bit words.
     for (const auto* comp : vec_constant->GetComponents()) {
       auto comp_in_words =
           GetWordsFromNumericScalarOrVectorConstant(const_mgr, comp);
       words.insert(words.end(), comp_in_words.begin(), comp_in_words.end());
     }
-    return words;
+
+    if (ElementWidth(c->type()) >= 32) {
+      return words;
+    }
+    // Check the element width and concactenate if the width is less than 32.
+    if (ElementWidth(c->type()) == 8) {
+      assert(words.size() <= 4);
+      // Each 32-bit word will comprise 4 8-bit integers.
+      // reverse the order when compacting.
+      uint32_t compacted_word = 0;
+      for (int32_t i = static_cast<int32_t>(words.size()) - 1; i >= 0; --i) {
+        compacted_word <<= 8;
+        compacted_word |= (words[i] & 0xFF);
+      }
+      return {compacted_word};
+    } else if (ElementWidth(c->type()) == 16) {
+      assert(words.size() <= 4);
+      std::vector<uint32_t> compacted_words;
+      // Each 32-bit word will comprise 2 16-bit integers.
+      // reverse the order pair-wise when compacting.
+      for (uint32_t i = 0; i < words.size(); i += 2) {
+        uint32_t word1 = words[i];
+        uint32_t word2 = (i + 1 < words.size()) ? words[i + 1] : 0;
+        uint32_t compacted_word = (word2 << 16) | (word1 & 0xFFFF);
+        compacted_words.push_back(compacted_word);
+      }
+      return compacted_words;
+    }
+    assert(false && "Unhandled element width");
+  } else if (c->AsNullConstant()) {
+    uint32_t num_elements = 1;
+
+    if (const auto* vec_type = c->type()->AsVector()) {
+      num_elements = vec_type->element_count();
+    }
+
+    // We need to check the element width to determine how many 32-bit words are
+    // needed.
+    uint32_t element_width = ElementWidth(c->type());
+    if (element_width < 32) {
+      num_elements = (num_elements + 1) / 2;
+    } else if (element_width == 64) {
+      num_elements = num_elements * 2;
+    }
+    return std::vector<uint32_t>(num_elements, 0);
   }
   return {};
 }
@@ -320,7 +363,7 @@
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
 
-    if (IsCooperativeMatrix(type)) {
+    if (type->IsCooperativeMatrix()) {
       return false;
     }
 
@@ -406,7 +449,7 @@
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
 
-    if (IsCooperativeMatrix(type)) {
+    if (type->IsCooperativeMatrix()) {
       return false;
     }
 
@@ -473,7 +516,7 @@
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
 
-    if (IsCooperativeMatrix(type)) {
+    if (type->IsCooperativeMatrix()) {
       return false;
     }
 
@@ -631,6 +674,15 @@
     case spv::Op::OpISub:
       FOLD_OP(-);
       break;
+    case spv::Op::OpBitwiseXor:
+      FOLD_OP(^);
+      break;
+    case spv::Op::OpBitwiseOr:
+      FOLD_OP(|);
+      break;
+    case spv::Op::OpBitwiseAnd:
+      FOLD_OP(&);
+      break;
     default:
       assert(false && "Unexpected operation");
       break;
@@ -709,7 +761,7 @@
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
 
-    if (IsCooperativeMatrix(type)) {
+    if (type->IsCooperativeMatrix()) {
       return false;
     }
 
@@ -768,7 +820,7 @@
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
 
-    if (IsCooperativeMatrix(type)) {
+    if (type->IsCooperativeMatrix()) {
       return false;
     }
 
@@ -846,7 +898,7 @@
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
 
-    if (IsCooperativeMatrix(type)) {
+    if (type->IsCooperativeMatrix()) {
       return false;
     }
 
@@ -876,6 +928,46 @@
   };
 }
 
+// Returns true if |inst| is negation op and is safe to fold.
+static bool IsFoldableNegation(const Instruction* inst) {
+  return (inst->opcode() == spv::Op::OpSNegate ||
+          (inst->opcode() == spv::Op::OpFNegate &&
+           inst->IsFloatingPointFoldingAllowed()));
+}
+
+// Merges multiplies / divisions of two negations.
+// Cases:
+// (-x) * (-y) = x * y
+// (-x) / (-y) = x / y
+FoldingRule MergeDivMulDoubleNegative() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>&) {
+    assert(inst->opcode() == spv::Op::OpFMul ||
+           inst->opcode() == spv::Op::OpVectorTimesScalar ||
+           inst->opcode() == spv::Op::OpFDiv ||
+           inst->opcode() == spv::Op::OpIMul ||
+           inst->opcode() == spv::Op::OpSDiv);
+
+    const analysis::Type* type =
+        context->get_type_mgr()->GetType(inst->type_id());
+
+    bool uses_float = HasFloatingPoint(type);
+    if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false;
+
+    analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+    Instruction* lhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0));
+    Instruction* rhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(1));
+
+    if (IsFoldableNegation(lhs) && IsFoldableNegation(rhs)) {
+      inst->SetInOperands(
+          {{SPV_OPERAND_TYPE_ID, {lhs->GetSingleWordInOperand(0u)}},
+           {SPV_OPERAND_TYPE_ID, {rhs->GetSingleWordInOperand(0u)}}});
+      return true;
+    }
+    return false;
+  };
+}
+
 // Merges consecutive divides if each instruction contains one constant operand.
 // Does not support integer division.
 // Cases:
@@ -891,7 +983,7 @@
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
 
-    if (IsCooperativeMatrix(type)) {
+    if (type->IsCooperativeMatrix()) {
       return false;
     }
 
@@ -969,7 +1061,7 @@
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
 
-    if (IsCooperativeMatrix(type)) {
+    if (type->IsCooperativeMatrix()) {
       return false;
     }
 
@@ -1068,13 +1160,12 @@
   };
 }
 
-// Folds addition of a constant and a negation.
-// Cases:
-// (-x) + 2 = 2 - x
-// 2 + (-x) = 2 - x
+// Folds addition, where one side is a negation.
+// (-x) + y = y - x
+// y + (-x) = y - x
 FoldingRule MergeAddNegateArithmetic() {
   return [](IRContext* context, Instruction* inst,
-            const std::vector<const analysis::Constant*>& constants) {
+            const std::vector<const analysis::Constant*>&) {
     assert(inst->opcode() == spv::Op::OpFAdd ||
            inst->opcode() == spv::Op::OpIAdd);
     const analysis::Type* type =
@@ -1082,73 +1173,65 @@
     bool uses_float = HasFloatingPoint(type);
     if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false;
 
-    const analysis::Constant* const_input1 = ConstInput(constants);
-    if (!const_input1) return false;
-    Instruction* other_inst = NonConstInput(context, constants[0], inst);
-    if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
-      return false;
+    analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+    Instruction* lhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0));
+    Instruction* rhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(1));
 
-    if (other_inst->opcode() == spv::Op::OpSNegate ||
-        other_inst->opcode() == spv::Op::OpFNegate) {
-      inst->SetOpcode(HasFloatingPoint(type) ? spv::Op::OpFSub
-                                             : spv::Op::OpISub);
-      uint32_t const_id = constants[0] ? inst->GetSingleWordInOperand(0u)
-                                       : inst->GetSingleWordInOperand(1u);
-      inst->SetInOperands(
-          {{SPV_OPERAND_TYPE_ID, {const_id}},
-           {SPV_OPERAND_TYPE_ID, {other_inst->GetSingleWordInOperand(0u)}}});
-      return true;
-    }
-    return false;
+    auto TrySubstitute = [inst, uses_float](Instruction* first,
+                                            Instruction* second) {
+      if (IsFoldableNegation(first)) {
+        inst->SetOpcode(uses_float ? spv::Op::OpFSub : spv::Op::OpISub);
+        inst->SetInOperands(
+            {{SPV_OPERAND_TYPE_ID, {second->result_id()}},
+             {SPV_OPERAND_TYPE_ID, {first->GetSingleWordInOperand(0u)}}});
+        return true;
+      }
+      return false;
+    };
+
+    return TrySubstitute(lhs, rhs) || TrySubstitute(rhs, lhs);
   };
 }
 
-// Folds subtraction of a constant and a negation.
+// Folds subtraction, where one side is a negation.
 // Cases:
 // (-x) - 2 = -2 - x
-// 2 - (-x) = x + 2
+// y - (-x) = x + y
 FoldingRule MergeSubNegateArithmetic() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
     assert(inst->opcode() == spv::Op::OpFSub ||
            inst->opcode() == spv::Op::OpISub);
-    analysis::ConstantManager* const_mgr = context->get_constant_mgr();
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
 
-    if (IsCooperativeMatrix(type)) {
-      return false;
-    }
-
     bool uses_float = HasFloatingPoint(type);
     if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false;
 
+    analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+    Instruction* lhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0));
+    Instruction* rhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(1));
+
+    if (IsFoldableNegation(rhs)) {
+      inst->SetOpcode(uses_float ? spv::Op::OpFAdd : spv::Op::OpIAdd);
+      inst->SetInOperands(
+          {{SPV_OPERAND_TYPE_ID, {lhs->result_id()}},
+           {SPV_OPERAND_TYPE_ID, {rhs->GetSingleWordInOperand(0)}}});
+      return true;
+    }
+
+    if (type->IsCooperativeMatrix()) {
+      return false;
+    }
+
     uint32_t width = ElementWidth(type);
     if (width != 32 && width != 64) return false;
 
-    const analysis::Constant* const_input1 = ConstInput(constants);
-    if (!const_input1) return false;
-    Instruction* other_inst = NonConstInput(context, constants[0], inst);
-    if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
-      return false;
-
-    if (other_inst->opcode() == spv::Op::OpSNegate ||
-        other_inst->opcode() == spv::Op::OpFNegate) {
-      uint32_t op1 = 0;
-      uint32_t op2 = 0;
-      spv::Op opcode = inst->opcode();
-      if (constants[0] != nullptr) {
-        op1 = other_inst->GetSingleWordInOperand(0u);
-        op2 = inst->GetSingleWordInOperand(0u);
-        opcode = HasFloatingPoint(type) ? spv::Op::OpFAdd : spv::Op::OpIAdd;
-      } else {
-        op1 = NegateConstant(const_mgr, const_input1);
-        op2 = other_inst->GetSingleWordInOperand(0u);
-      }
-
-      inst->SetOpcode(opcode);
+    if (constants[1] && IsFoldableNegation(lhs)) {
       inst->SetInOperands(
-          {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}});
+          {{SPV_OPERAND_TYPE_ID,
+            {NegateConstant(context->get_constant_mgr(), constants[1])}},
+           {SPV_OPERAND_TYPE_ID, {lhs->GetSingleWordInOperand(0)}}});
       return true;
     }
     return false;
@@ -1169,7 +1252,7 @@
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
 
-    if (IsCooperativeMatrix(type)) {
+    if (type->IsCooperativeMatrix()) {
       return false;
     }
 
@@ -1222,7 +1305,7 @@
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
 
-    if (IsCooperativeMatrix(type)) {
+    if (type->IsCooperativeMatrix()) {
       return false;
     }
 
@@ -1287,7 +1370,7 @@
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
 
-    if (IsCooperativeMatrix(type)) {
+    if (type->IsCooperativeMatrix()) {
       return false;
     }
 
@@ -1358,7 +1441,7 @@
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
 
-    if (IsCooperativeMatrix(type)) {
+    if (type->IsCooperativeMatrix()) {
       return false;
     }
 
@@ -1456,7 +1539,7 @@
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
 
-    if (IsCooperativeMatrix(type)) {
+    if (type->IsCooperativeMatrix()) {
       return false;
     }
 
@@ -1473,11 +1556,13 @@
   };
 }
 
-// Helper function for FactorAddMuls. If |factor0_0| is the same as |factor1_0|,
-// generate |factor0_0| * (|factor0_1| + |factor1_1|).
-bool FactorAddMulsOpnds(uint32_t factor0_0, uint32_t factor0_1,
-                        uint32_t factor1_0, uint32_t factor1_1,
-                        Instruction* inst) {
+// Helper function for FactorAddSubMuls.
+// If |factor0_0| is the same as |factor1_0|, generate:
+//   |factor0_0| * (|factor0_1| + |factor1_1|)
+//   |factor0_0| * (|factor0_1| - |factor1_1|)
+bool FactorAddSubMulsOpnds(uint32_t factor0_0, uint32_t factor0_1,
+                           uint32_t factor1_0, uint32_t factor1_1,
+                           Instruction* inst) {
   IRContext* context = inst->context();
   if (factor0_0 != factor1_0) return false;
   InstructionBuilder ir_builder(
@@ -1485,8 +1570,13 @@
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
   Instruction* new_add_inst = ir_builder.AddBinaryOp(
       inst->type_id(), inst->opcode(), factor0_1, factor1_1);
-  inst->SetOpcode(inst->opcode() == spv::Op::OpFAdd ? spv::Op::OpFMul
-                                                    : spv::Op::OpIMul);
+  if (!new_add_inst) {
+    return false;
+  }
+
+  bool is_float =
+      inst->opcode() == spv::Op::OpFAdd || inst->opcode() == spv::Op::OpFSub;
+  inst->SetOpcode(is_float ? spv::Op::OpFMul : spv::Op::OpIMul);
   inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {factor0_0}},
                        {SPV_OPERAND_TYPE_ID, {new_add_inst->result_id()}}});
   context->UpdateDefUse(inst);
@@ -1494,12 +1584,16 @@
 }
 
 // Perform the following factoring identity, handling all operand order
-// combinations: (a * b) + (a * c) = a * (b + c)
-FoldingRule FactorAddMuls() {
+// combinations:
+//   (a * b) + (a * c) = a * (b + c)
+//   (a * b) - (a * c) = a * (b - c)
+FoldingRule FactorAddSubMuls() {
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>&) {
     assert(inst->opcode() == spv::Op::OpFAdd ||
-           inst->opcode() == spv::Op::OpIAdd);
+           inst->opcode() == spv::Op::OpFSub ||
+           inst->opcode() == spv::Op::OpIAdd ||
+           inst->opcode() == spv::Op::OpISub);
     const analysis::Type* type =
         context->get_type_mgr()->GetType(inst->type_id());
     bool uses_float = HasFloatingPoint(type);
@@ -1530,11 +1624,11 @@
     for (int i = 0; i < 2; i++) {
       for (int j = 0; j < 2; j++) {
         // Check if operand i in add_op0_inst matches operand j in add_op1_inst.
-        if (FactorAddMulsOpnds(add_op0_inst->GetSingleWordInOperand(i),
-                               add_op0_inst->GetSingleWordInOperand(1 - i),
-                               add_op1_inst->GetSingleWordInOperand(j),
-                               add_op1_inst->GetSingleWordInOperand(1 - j),
-                               inst))
+        if (FactorAddSubMulsOpnds(add_op0_inst->GetSingleWordInOperand(i),
+                                  add_op0_inst->GetSingleWordInOperand(1 - i),
+                                  add_op1_inst->GetSingleWordInOperand(j),
+                                  add_op1_inst->GetSingleWordInOperand(1 - j),
+                                  inst))
           return true;
       }
     }
@@ -1542,6 +1636,393 @@
   };
 }
 
+// Reassociate integer instructions where both operands share the same opcode
+// and both source instructions contain a constant.
+// e.g:
+//   (a * C0) * (C1 * b) = (C0 * C1) * (a * b)
+//   (a ^ C0) ^ (b ^ C1) = (C0 ^ C1) ^ (a ^ b)
+//   (C0 | a) | (b | C1) = (C0 | C1) | (a | b)
+//   (a & C0) & (b & C1) = (C0 & C1) & (a & b)
+static const constexpr spv::Op ReassociateNestedGenericIntOps[] = {
+    spv::Op::OpIMul, spv::Op::OpBitwiseOr, spv::Op::OpBitwiseXor,
+    spv::Op::OpBitwiseAnd};
+
+FoldingRule ReassociateNestedGenericInt(spv::Op opcode) {
+  assert(std::find(std::begin(ReassociateNestedGenericIntOps),
+                   std::end(ReassociateNestedGenericIntOps),
+                   opcode) != std::end(ReassociateNestedGenericIntOps) &&
+         "Wrong opcode.");
+
+  return [opcode](IRContext* context, Instruction* inst,
+                  const std::vector<const analysis::Constant*>& constants) {
+    // Handled by other folding rules.
+    if (constants[0] || constants[1]) {
+      return false;
+    }
+
+    if (inst->opcode() != opcode) {
+      return false;
+    }
+
+    const analysis::Type* type =
+        context->get_type_mgr()->GetType(inst->type_id());
+
+    if (type->IsCooperativeMatrix()) {
+      return false;
+    }
+
+    uint32_t width = ElementWidth(type);
+    if (width != 32 && width != 64) return false;
+
+    analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+    Instruction* lhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0));
+    Instruction* rhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(1));
+
+    if (lhs->opcode() != opcode || rhs->opcode() != opcode) {
+      return false;
+    }
+
+    analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+    std::vector<const analysis::Constant*> lhs_constants =
+        const_mgr->GetOperandConstants(lhs);
+    const analysis::Constant* lhs_const = ConstInput(lhs_constants);
+    if (!lhs_const) {
+      return false;
+    }
+
+    std::vector<const analysis::Constant*> rhs_constants =
+        const_mgr->GetOperandConstants(rhs);
+    const analysis::Constant* rhs_const = ConstInput(rhs_constants);
+    if (!rhs_const) {
+      return false;
+    }
+
+    uint32_t merged_constant =
+        PerformOperation(const_mgr, opcode, lhs_const, rhs_const);
+    if (!merged_constant) {
+      return false;
+    }
+
+    InstructionBuilder ir_builder(
+        context, inst,
+        IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+
+    Instruction* new_rhs = ir_builder.AddBinaryOp(
+        inst->type_id(), opcode,
+        NonConstInput(context, lhs_constants[0], lhs)->result_id(),
+        NonConstInput(context, rhs_constants[0], rhs)->result_id());
+
+    if (!new_rhs) {
+      return false;
+    }
+
+    inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {merged_constant}},
+                         {SPV_OPERAND_TYPE_ID, {new_rhs->result_id()}}});
+    return true;
+  };
+}
+
+// Reassociate floating point mul/div instructions, which have mul/div inputs,
+// both of which contain a constant.
+// e.g:
+//   (a * C0) / (C1 / b) =  (C0 / C1) * (a * b)
+//   (C0 / a) * (b / C1) =  (C0 / C1) * (b / a)
+//   (a / C0) / (b * C1) =  (1 / (C0 * C1)) * (a / b)
+FoldingRule ReassociateNestedMulDivFloat() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    assert(inst->opcode() == spv::Op::OpFMul ||
+           inst->opcode() == spv::Op::OpFDiv);
+
+    // Handled by other folding rules.
+    if (constants[0] || constants[1]) {
+      return false;
+    }
+
+    const analysis::Type* type =
+        context->get_type_mgr()->GetType(inst->type_id());
+
+    if (type->IsCooperativeMatrix()) {
+      return false;
+    }
+
+    uint32_t width = ElementWidth(type);
+    if (width != 32 && width != 64) return false;
+
+    if (!inst->IsFloatingPointFoldingAllowed()) return false;
+
+    analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+    Instruction* lhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0));
+    Instruction* rhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(1));
+
+    bool lhs_is_mul = lhs->opcode() == spv::Op::OpFMul;
+    bool lhs_is_div = lhs->opcode() == spv::Op::OpFDiv;
+    bool rhs_is_mul = rhs->opcode() == spv::Op::OpFMul;
+    bool rhs_is_div = rhs->opcode() == spv::Op::OpFDiv;
+    if (!(lhs_is_mul || lhs_is_div) || !(rhs_is_mul || rhs_is_div)) {
+      return false;
+    }
+
+    if (!lhs->IsFloatingPointFoldingAllowed() ||
+        !rhs->IsFloatingPointFoldingAllowed()) {
+      return false;
+    }
+
+    analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+    std::vector<const analysis::Constant*> lhs_constants =
+        const_mgr->GetOperandConstants(lhs);
+    if (!lhs_constants[0] && !lhs_constants[1]) {
+      return false;
+    }
+
+    std::vector<const analysis::Constant*> rhs_constants =
+        const_mgr->GetOperandConstants(rhs);
+    if (!rhs_constants[0] && !rhs_constants[1]) {
+      return false;
+    }
+
+    const analysis::Constant* lhs_const =
+        lhs_constants[0] ? lhs_constants[0] : lhs_constants[1];
+    const analysis::Constant* rhs_const =
+        rhs_constants[0] ? rhs_constants[0] : rhs_constants[1];
+    if (!lhs_const || !rhs_const) return false;
+
+    bool const_lhs_rcp = lhs_constants[0] ? false : lhs_is_div;
+    bool const_rhs_rcp = rhs_constants[0] ? false : rhs_is_div;
+
+    uint32_t non_const_lhs = lhs_constants[0] ? lhs->GetSingleWordInOperand(1)
+                                              : lhs->GetSingleWordInOperand(0);
+    bool non_const_lhs_rcp = lhs_constants[0] ? lhs_is_div : false;
+
+    uint32_t non_const_rhs = rhs_constants[0] ? rhs->GetSingleWordInOperand(1)
+                                              : rhs->GetSingleWordInOperand(0);
+    bool non_const_rhs_rcp = rhs_constants[0] ? rhs_is_div : false;
+
+    // Rcp the rhs if we're actually dividing it.
+    if (inst->opcode() == spv::Op::OpFDiv) {
+      const_rhs_rcp = !const_rhs_rcp;
+      non_const_rhs_rcp = !non_const_rhs_rcp;
+    }
+
+    if (const_lhs_rcp) {
+      lhs_const =
+          const_mgr->FindDeclaredConstant(Reciprocal(const_mgr, lhs_const));
+      if (!lhs_const) {
+        return false;
+      }
+    }
+    if (const_rhs_rcp) {
+      rhs_const =
+          const_mgr->FindDeclaredConstant(Reciprocal(const_mgr, rhs_const));
+      if (!rhs_const) {
+        return false;
+      }
+    }
+
+    uint32_t merged_constant =
+        PerformOperation(const_mgr, spv::Op::OpFMul, lhs_const, rhs_const);
+
+    if (!merged_constant) {
+      return false;
+    }
+
+    spv::Op op = spv::Op::OpNop;
+    Instruction* new_rhs = nullptr;
+
+    InstructionBuilder ir_builder(
+        context, inst,
+        IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+
+    //  a * b => C * (b * a)
+    if (!non_const_lhs_rcp && !non_const_rhs_rcp) {
+      new_rhs = ir_builder.AddBinaryOp(inst->type_id(), spv::Op::OpFMul,
+                                       non_const_lhs, non_const_rhs);
+      op = spv::Op::OpFMul;
+    }
+    // 1/a * b => C * (b / a)
+    else if (non_const_lhs_rcp && !non_const_rhs_rcp) {
+      new_rhs = ir_builder.AddBinaryOp(inst->type_id(), spv::Op::OpFDiv,
+                                       non_const_rhs, non_const_lhs);
+      op = spv::Op::OpFMul;
+    }
+    //  a * 1/b => C * (a / b)
+    else if (!non_const_lhs_rcp && non_const_rhs_rcp) {
+      new_rhs = ir_builder.AddBinaryOp(inst->type_id(), spv::Op::OpFDiv,
+                                       non_const_lhs, non_const_rhs);
+      op = spv::Op::OpFMul;
+    }
+    // 1/a * 1/b => C / (a * b)
+    else {
+      new_rhs = ir_builder.AddBinaryOp(inst->type_id(), spv::Op::OpFMul,
+                                       non_const_lhs, non_const_rhs);
+      op = spv::Op::OpFDiv;
+    }
+
+    if (!new_rhs) {
+      return false;
+    }
+
+    inst->SetOpcode(op);
+    inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {merged_constant}},
+                         {SPV_OPERAND_TYPE_ID, {new_rhs->result_id()}}});
+    return true;
+  };
+}
+
+// Reassociate add/sub instructions, which have add/sub inputs,
+// both of which contain a constant.
+// e.g:
+//   (a + C0) - (C1 - b) =  (C0 - C1) + (a + b)
+//   (C0 - a) + (b - C1) =  (C0 - C1) + (b - a)
+//   (a - C0) - (b + C1) = (-C0 - C1) + (a - b)
+FoldingRule ReassociateNestedAddSub() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    assert(inst->opcode() == spv::Op::OpFAdd ||
+           inst->opcode() == spv::Op::OpIAdd ||
+           inst->opcode() == spv::Op::OpFSub ||
+           inst->opcode() == spv::Op::OpISub);
+
+    // Handled by other folding rules.
+    if (constants[0] || constants[1]) {
+      return false;
+    }
+
+    const analysis::Type* type =
+        context->get_type_mgr()->GetType(inst->type_id());
+
+    if (type->IsCooperativeMatrix()) {
+      return false;
+    }
+
+    uint32_t width = ElementWidth(type);
+    if (width != 32 && width != 64) return false;
+
+    bool uses_float = HasFloatingPoint(type);
+    if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false;
+
+    analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+    Instruction* lhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0));
+    Instruction* rhs = def_use_mgr->GetDef(inst->GetSingleWordInOperand(1));
+
+    spv::Op add_op = uses_float ? spv::Op::OpFAdd : spv::Op::OpIAdd;
+    spv::Op sub_op = uses_float ? spv::Op::OpFSub : spv::Op::OpISub;
+
+    bool lhs_is_add = lhs->opcode() == add_op;
+    bool lhs_is_sub = lhs->opcode() == sub_op;
+    bool rhs_is_add = rhs->opcode() == add_op;
+    bool rhs_is_sub = rhs->opcode() == sub_op;
+    if (!(lhs_is_add || lhs_is_sub) || !(rhs_is_add || rhs_is_sub)) {
+      return false;
+    }
+
+    if (uses_float && (!lhs->IsFloatingPointFoldingAllowed() ||
+                       !rhs->IsFloatingPointFoldingAllowed())) {
+      return false;
+    }
+
+    analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+    std::vector<const analysis::Constant*> lhs_constants =
+        const_mgr->GetOperandConstants(lhs);
+    if (!lhs_constants[0] && !lhs_constants[1]) {
+      return false;
+    }
+
+    std::vector<const analysis::Constant*> rhs_constants =
+        const_mgr->GetOperandConstants(rhs);
+    if (!rhs_constants[0] && !rhs_constants[1]) {
+      return false;
+    }
+
+    const analysis::Constant* lhs_const =
+        lhs_constants[0] ? lhs_constants[0] : lhs_constants[1];
+    const analysis::Constant* rhs_const =
+        rhs_constants[0] ? rhs_constants[0] : rhs_constants[1];
+    if (!lhs_const || !rhs_const) return false;
+
+    bool const_lhs_neg = lhs_constants[0] ? false : lhs_is_sub;
+    bool const_rhs_neg = rhs_constants[0] ? false : rhs_is_sub;
+
+    uint32_t non_const_lhs = lhs_constants[0] ? lhs->GetSingleWordInOperand(1)
+                                              : lhs->GetSingleWordInOperand(0);
+    bool non_const_lhs_neg = lhs_constants[0] ? lhs_is_sub : false;
+
+    uint32_t non_const_rhs = rhs_constants[0] ? rhs->GetSingleWordInOperand(1)
+                                              : rhs->GetSingleWordInOperand(0);
+    bool non_const_rhs_neg = rhs_constants[0] ? rhs_is_sub : false;
+
+    // Negate the rhs if we're actually subtracting it.
+    if (inst->opcode() == spv::Op::OpFSub ||
+        inst->opcode() == spv::Op::OpISub) {
+      const_rhs_neg = !const_rhs_neg;
+      non_const_rhs_neg = !non_const_rhs_neg;
+    }
+
+    if (const_lhs_neg) {
+      lhs_const =
+          const_mgr->FindDeclaredConstant(NegateConstant(const_mgr, lhs_const));
+      if (!lhs_const) {
+        return false;
+      }
+    }
+    if (const_rhs_neg) {
+      rhs_const =
+          const_mgr->FindDeclaredConstant(NegateConstant(const_mgr, rhs_const));
+      if (!rhs_const) {
+        return false;
+      }
+    }
+
+    uint32_t merged_constant =
+        PerformOperation(const_mgr, add_op, lhs_const, rhs_const);
+
+    if (!merged_constant) {
+      return false;
+    }
+
+    spv::Op op = spv::Op::OpNop;
+    Instruction* new_rhs = nullptr;
+
+    InstructionBuilder ir_builder(
+        context, inst,
+        IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+
+    //  a +  b => C + (b + a)
+    if (!non_const_lhs_neg && !non_const_rhs_neg) {
+      new_rhs = ir_builder.AddBinaryOp(inst->type_id(), add_op, non_const_lhs,
+                                       non_const_rhs);
+      op = add_op;
+    }
+    // -a +  b => C + (b - a)
+    else if (non_const_lhs_neg && !non_const_rhs_neg) {
+      new_rhs = ir_builder.AddBinaryOp(inst->type_id(), sub_op, non_const_rhs,
+                                       non_const_lhs);
+      op = add_op;
+    }
+    //  a + -b => C + (a - b)
+    else if (!non_const_lhs_neg && non_const_rhs_neg) {
+      new_rhs = ir_builder.AddBinaryOp(inst->type_id(), sub_op, non_const_lhs,
+                                       non_const_rhs);
+      op = add_op;
+    }
+    // -a + -b => C - (a + b)
+    else {
+      new_rhs = ir_builder.AddBinaryOp(inst->type_id(), add_op, non_const_lhs,
+                                       non_const_rhs);
+      op = sub_op;
+    }
+
+    if (!new_rhs) {
+      return false;
+    }
+
+    inst->SetOpcode(op);
+    inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {merged_constant}},
+                         {SPV_OPERAND_TYPE_ID, {new_rhs->result_id()}}});
+    return true;
+  };
+}
+
 FoldingRule IntMultipleBy1() {
   return [](IRContext*, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
@@ -1712,6 +2193,174 @@
   return type_id;
 }
 
+// If the input to an OpCompositeExtract is an OpCopyLogical, then we can
+// hoist the extraction before the copy.
+bool CopyLogicalFeedingExtract(IRContext* context, Instruction* inst,
+                               const std::vector<const analysis::Constant*>&) {
+  assert(inst->opcode() == spv::Op::OpCompositeExtract &&
+         "Wrong opcode.  Should be OpCompositeExtract.");
+
+  analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+  uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
+  Instruction* cinst = def_use_mgr->GetDef(cid);
+
+  if (cinst->opcode() != spv::Op::OpCopyLogical) {
+    return false;
+  }
+
+  uint32_t original_composite_id = cinst->GetSingleWordInOperand(0);
+  Instruction* original_composite_inst =
+      def_use_mgr->GetDef(original_composite_id);
+
+  std::vector<uint32_t> indices;
+  for (uint32_t i = 1; i < inst->NumInOperands(); ++i) {
+    indices.push_back(inst->GetSingleWordInOperand(i));
+  }
+
+  uint32_t original_element_type_id =
+      GetElementType(original_composite_inst->type_id(), inst->begin() + 3,
+                     inst->end(), def_use_mgr);
+  assert(original_element_type_id != 0 &&
+         "Could not find the element type.  Invalid SPIR-V.");
+
+  InstructionBuilder ir_builder(
+      context, inst,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+
+  Instruction* new_extract = ir_builder.AddCompositeExtract(
+      original_element_type_id, original_composite_id, indices);
+
+  if (original_element_type_id == inst->type_id())
+    inst->SetOpcode(spv::Op::OpCopyObject);
+  else
+    inst->SetOpcode(spv::Op::OpCopyLogical);
+  inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {new_extract->result_id()}}});
+  return true;
+}
+
+// If the input to an OpCompositeExtract is an OpLoad, we can change the
+// load into a load of an OpAccessChain.
+bool LoadFeedingExtract(IRContext* context, Instruction* inst,
+                        const std::vector<const analysis::Constant*>&) {
+  assert(inst->opcode() == spv::Op::OpCompositeExtract &&
+         "Wrong opcode.  Should be OpCompositeExtract.");
+
+  analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+  uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
+  Instruction* cinst = def_use_mgr->GetDef(cid);
+
+  if (cinst->opcode() != spv::Op::OpLoad) {
+    return false;
+  }
+
+  Instruction* composite_type_inst = def_use_mgr->GetDef(cinst->type_id());
+  if (composite_type_inst->opcode() != spv::Op::OpTypeStruct &&
+      composite_type_inst->opcode() != spv::Op::OpTypeArray) {
+    return false;
+  }
+
+  // Check the memory operands.
+  if (cinst->NumInOperands() > 1) {
+    uint32_t memory_access_mask = cinst->GetSingleWordInOperand(1);
+    if (memory_access_mask & uint32_t(spv::MemoryAccessMask::Volatile)) {
+      return false;
+    }
+  }
+
+  uint32_t ptr_id = cinst->GetSingleWordInOperand(0);
+  Instruction* ptr_inst = def_use_mgr->GetDef(ptr_id);
+  Instruction* ptr_type_inst = def_use_mgr->GetDef(ptr_inst->type_id());
+  assert(ptr_type_inst->opcode() == spv::Op::OpTypePointer);
+  spv::StorageClass storage_class =
+      static_cast<spv::StorageClass>(ptr_type_inst->GetSingleWordInOperand(0));
+
+  // If the storage class is Function or Private, we do not want to fold.
+  // These are the storage classes that the local-access-chain-convert pass
+  // works on.
+  if (storage_class == spv::StorageClass::Function ||
+      storage_class == spv::StorageClass::Private) {
+    return false;
+  }
+
+  analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+  analysis::TypeManager* type_mgr = context->get_type_mgr();
+  std::vector<uint32_t> index_ids;
+  for (uint32_t i = 1; i < inst->NumInOperands(); ++i) {
+    uint32_t index = inst->GetSingleWordInOperand(i);
+    const analysis::Constant* index_const =
+        const_mgr->GetConstant(type_mgr->GetUIntType(), {index});
+    index_ids.push_back(
+        const_mgr->GetDefiningInstruction(index_const)->result_id());
+  }
+
+  InstructionBuilder ir_builder(
+      context, cinst,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+
+  uint32_t element_ptr_type_id =
+      type_mgr->FindPointerToType(inst->type_id(), storage_class);
+  if (element_ptr_type_id == 0) {
+    return false;
+  }
+
+  Instruction* access_chain =
+      ir_builder.AddAccessChain(element_ptr_type_id, ptr_id, index_ids);
+  std::vector<Operand> load_operands;
+  load_operands.push_back({SPV_OPERAND_TYPE_ID, {access_chain->result_id()}});
+
+  if (cinst->NumInOperands() > 1) {
+    uint32_t memory_access_mask = cinst->GetSingleWordInOperand(1);
+    load_operands.push_back(
+        {SPV_OPERAND_TYPE_MEMORY_ACCESS, {memory_access_mask}});
+
+    uint32_t current_operand_index = 2;
+    if (memory_access_mask & uint32_t(spv::MemoryAccessMask::Aligned)) {
+      uint32_t original_alignment =
+          cinst->GetSingleWordInOperand(current_operand_index);
+
+      std::vector<uint32_t> extract_indices;
+      for (uint32_t i = 1; i < inst->NumInOperands(); ++i) {
+        extract_indices.push_back(inst->GetSingleWordInOperand(i));
+      }
+
+      std::optional<uint32_t> offset =
+          type_mgr->GetType(cinst->type_id())->GetByteOffset(extract_indices);
+      if (!offset) {
+        return false;
+      }
+
+      uint32_t new_alignment = original_alignment;
+      if (*offset != 0) {
+        uint32_t offset_alignment = *offset & ~(*offset - 1);
+        new_alignment = std::min(original_alignment, offset_alignment);
+      }
+
+      load_operands.push_back(
+          {SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, {new_alignment}});
+      current_operand_index++;
+    }
+
+    // Copy the remaining operands
+    for (; current_operand_index < cinst->NumInOperands();
+         ++current_operand_index) {
+      load_operands.push_back(cinst->GetInOperand(current_operand_index));
+    }
+  }
+
+  uint32_t load_result_id = context->TakeNextId();
+  if (load_result_id == 0) return false;
+
+  std::unique_ptr<Instruction> new_load_inst(
+      new Instruction(context, spv::Op::OpLoad, inst->type_id(), load_result_id,
+                      load_operands));
+  Instruction* new_load = ir_builder.AddInstruction(std::move(new_load_inst));
+
+  inst->SetOpcode(spv::Op::OpCopyObject);
+  inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {new_load->result_id()}}});
+
+  return true;
+}
+
 // Returns true of |inst_1| and |inst_2| have the same indexes that will be used
 // to index into a composite object, excluding the last index.  The two
 // instructions must have the same opcode, and be either OpCompositeExtract or
@@ -1995,6 +2644,15 @@
     bool use_x = false;
 
     assert(a_const->type()->AsFloat());
+
+    const analysis::Type* type =
+        context->get_type_mgr()->GetType(inst->type_id());
+    uint32_t width = ElementWidth(type);
+    if (width != 32 && width != 64) {
+      // We won't support folding half float values.
+      return false;
+    }
+
     double element_value = a_const->GetValueAsDouble();
     if (element_value == 0.0) {
       use_x = true;
@@ -2227,6 +2885,81 @@
   };
 }
 
+// Remove indirect bitcasts which have no effect.
+//   uint32 x;  asuint32(x)            => x
+//   uint32 x;  asuint32(asint32(x))   => x
+//   float32 x; asuint32(asint32(x))   => asuint32(x)
+FoldingRule RedundantBitcast() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>&) {
+    assert(inst->opcode() == spv::Op::OpBitcast);
+
+    analysis::DefUseManager* def_mgr = context->get_def_use_mgr();
+    Instruction* child = def_mgr->GetDef(inst->GetSingleWordInOperand(0));
+
+    if (inst->type_id() == child->type_id()) {
+      inst->SetOpcode(spv::Op::OpCopyObject);
+      inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {child->result_id()}}});
+      return true;
+    }
+
+    if (child->opcode() != spv::Op::OpBitcast) {
+      return false;
+    }
+
+    if (def_mgr->GetDef(child->GetSingleWordInOperand(0))->type_id() ==
+        inst->type_id()) {
+      inst->SetOpcode(spv::Op::OpCopyObject);
+    }
+    inst->SetInOperands(
+        {{SPV_OPERAND_TYPE_ID, {child->GetSingleWordInOperand(0)}}});
+
+    return true;
+  };
+}
+
+FoldingRule BitReverseScalarOrVector() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    assert(inst->opcode() == spv::Op::OpBitReverse && constants.size() == 1);
+    if (constants[0] == nullptr) return false;
+
+    const analysis::Type* type =
+        context->get_type_mgr()->GetType(inst->type_id());
+    assert(!HasFloatingPoint(type) &&
+           "BitReverse cannot be applied to floating point types.");
+    assert((type->AsInteger() || type->AsVector()) &&
+           "BitReverse can only be applied to integer scalars or vectors.");
+    assert((ElementWidth(type) == 32) &&
+           "BitReverse can only be applied to integer types of width 32");
+
+    analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+    std::vector<uint32_t> words =
+        GetWordsFromNumericScalarOrVectorConstant(const_mgr, constants[0]);
+    if (words.size() == 0) return false;
+
+    for (uint32_t& word : words) {
+      // Reverse the bits in each word.
+      word = ((word & 0x55555555) << 1) | ((word >> 1) & 0x55555555);
+      word = ((word & 0x33333333) << 2) | ((word >> 2) & 0x33333333);
+      word = ((word & 0x0F0F0F0F) << 4) | ((word >> 4) & 0x0F0F0F0F);
+      word = ((word & 0x00FF00FF) << 8) | ((word >> 8) & 0x00FF00FF);
+      word = (word << 16) | (word >> 16);
+    }
+
+    const analysis::Constant* bitreversed_constant =
+        ConvertWordsToNumericScalarOrVectorConstant(const_mgr, words, type);
+    if (!bitreversed_constant) return false;
+
+    auto new_feeder_id =
+        const_mgr->GetDefiningInstruction(bitreversed_constant, inst->type_id())
+            ->result_id();
+    inst->SetOpcode(spv::Op::OpCopyObject);
+    inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {new_feeder_id}}});
+    return true;
+  };
+}
+
 FoldingRule RedundantSelect() {
   // An OpSelect instruction where both values are the same or the condition is
   // constant can be replaced by one of the values
@@ -2299,6 +3032,421 @@
   };
 }
 
+std::optional<bool> GetBoolConstantKind(const analysis::Constant* c) {
+  if (!c) {
+    return {};
+  }
+  if (auto composite = c->AsCompositeConstant()) {
+    auto& components = composite->GetComponents();
+    if (components.empty()) {
+      return {};
+    }
+    auto first = GetBoolConstantKind(components[0]);
+    if (!first) {
+      return {};
+    }
+    if (std::all_of(std::begin(components) + 1, std::end(components),
+                    [first](const analysis::Constant* c2) {
+                      return GetBoolConstantKind(c2) == first;
+                    })) {
+      return first;
+    }
+    return {};
+  } else if (c->AsNullConstant()) {
+    return false;
+  } else if (c->AsBoolConstant()) {
+    return c->AsBoolConstant()->value();
+  }
+  return {};
+}
+
+// Fold OpSelect instructions which have constant booleans as their result.
+//   x ? true  : false =  x
+//   x ? false : true  = !x
+FoldingRule FoldConstantBooleanSelect() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    assert(inst->opcode() == spv::Op::OpSelect);
+    assert(inst->NumInOperands() == 3);
+    assert(constants.size() == 3);
+
+    if (!constants[1] || !constants[2]) {
+      return false;
+    }
+
+    analysis::DefUseManager* def_mgr = context->get_def_use_mgr();
+    if (inst->type_id() !=
+        def_mgr->GetDef(inst->GetSingleWordInOperand(0))->type_id()) {
+      return false;
+    }
+
+    std::optional<bool> uniform_true = GetBoolConstantKind(constants[1]);
+    std::optional<bool> uniform_false = GetBoolConstantKind(constants[2]);
+
+    if (!uniform_true || !uniform_false) {
+      return false;
+    }
+
+    if (uniform_true.value() && !uniform_false.value()) {
+      inst->SetOpcode(spv::Op::OpCopyObject);
+      inst->SetInOperands(
+          {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(0)}}});
+      return true;
+    } else if (!uniform_true.value() && uniform_false.value()) {
+      inst->SetOpcode(spv::Op::OpLogicalNot);
+      inst->SetInOperands(
+          {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(0)}}});
+      return true;
+    }
+    return false;
+  };
+}
+
+// Fold OpLogicalAnd instructions which have a constant true on one side.
+//   x && true = x
+//   true && x = x
+FoldingRule RedundantLogicalAnd() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    assert(inst->opcode() == spv::Op::OpLogicalAnd);
+
+    if (GetBoolConstantKind(ConstInput(constants)) ==
+        std::optional<bool>(true)) {
+      Instruction* other_inst = NonConstInput(context, constants[0], inst);
+      inst->SetOpcode(spv::Op::OpCopyObject);
+      inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {other_inst->result_id()}}});
+      return true;
+    }
+    return false;
+  };
+}
+
+// Fold OpLogicalOr instructions which have a constant false on one side.
+//   x || false = x
+//   false || x = x
+FoldingRule RedundantLogicalOr() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    assert(inst->opcode() == spv::Op::OpLogicalOr);
+
+    if (GetBoolConstantKind(ConstInput(constants)) ==
+        std::optional<bool>(false)) {
+      Instruction* other_inst = NonConstInput(context, constants[0], inst);
+      inst->SetOpcode(spv::Op::OpCopyObject);
+      inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {other_inst->result_id()}}});
+      return true;
+    }
+    return false;
+  };
+}
+
+// Fold concurrent OpLogicalNot instructions:
+//   !!x = x
+FoldingRule RedundantLogicalNot() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>&) {
+    assert(inst->opcode() == spv::Op::OpLogicalNot);
+    Instruction* child =
+        context->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0));
+    if (child->opcode() == spv::Op::OpLogicalNot) {
+      inst->SetOpcode(spv::Op::OpCopyObject);
+      inst->SetInOperands(
+          {{SPV_OPERAND_TYPE_ID, {child->GetSingleWordInOperand(0)}}});
+      return true;
+    }
+    return false;
+  };
+}
+
+// Cases handled:
+//  ((a ? C0 : C1) == C2)  =  ((a ? (C0 == C2) : (C1 == C2))
+//  ((a ? C0 : C1) != C2)  =  ((a ? (C0 != C2) : (C1 != C2))
+//  ((a ? C0 : C1) <  C2)  =  ((a ? (C0 <  C2) : (C1 <  C2))
+//  ((a ? C0 : C1) <= C2)  =  ((a ? (C0 <= C2) : (C1 <= C2))
+//  ((a ? C0 : C1) >  C2)  =  ((a ? (C0 >  C2) : (C1 >  C2))
+//  ((a ? C0 : C1) >= C2)  =  ((a ? (C0 >= C2) : (C1 >= C2))
+//  ((a ? C0 : C1) || C2)  =  ((a ? (C0 || C2) : (C1 || C2))
+//  ((a ? C0 : C1) && C2)  =  ((a ? (C0 && C2) : (C1 && C2))
+//  ((a ? C0 : C1) +  C2)  =  ((a ? (C0 +  C2) : (C1 +  C2))
+//  ((a ? C0 : C1) -  C2)  =  ((a ? (C0 -  C2) : (C1 -  C2))
+//  ((a ? C0 : C1) *  C2)  =  ((a ? (C0 *  C2) : (C1 *  C2))
+//  ((a ? C0 : C1) /  C2)  =  ((a ? (C0 /  C2) : (C1 /  C2))
+//  ((a ? C0 : C1) >> C2)  =  ((a ? (C0 >> C2) : (C1 >> C2))
+//  ((a ? C0 : C1) << C2)  =  ((a ? (C0 << C2) : (C1 << C2))
+//  ((a ? C0 : C1) ^  C2)  =  ((a ? (C0 ^  C2) : (C1 ^  C2))
+//  ((a ? C0 : C1) |  C2)  =  ((a ? (C0 |  C2) : (C1 |  C2))
+//  ((a ? C0 : C1) &  C2)  =  ((a ? (C0 &  C2) : (C1 &  C2))
+static const constexpr spv::Op MergeBinaryOpSelectOps[] = {
+    spv::Op::OpLogicalEqual,
+    spv::Op::OpLogicalNotEqual,
+    spv::Op::OpLogicalAnd,
+    spv::Op::OpLogicalOr,
+    spv::Op::OpIEqual,
+    spv::Op::OpINotEqual,
+    spv::Op::OpUGreaterThan,
+    spv::Op::OpSGreaterThan,
+    spv::Op::OpUGreaterThanEqual,
+    spv::Op::OpSGreaterThanEqual,
+    spv::Op::OpULessThan,
+    spv::Op::OpSLessThan,
+    spv::Op::OpULessThanEqual,
+    spv::Op::OpSLessThanEqual,
+    spv::Op::OpFOrdEqual,
+    spv::Op::OpFUnordEqual,
+    spv::Op::OpFOrdNotEqual,
+    spv::Op::OpFUnordNotEqual,
+    spv::Op::OpFOrdLessThan,
+    spv::Op::OpFUnordLessThan,
+    spv::Op::OpFOrdGreaterThan,
+    spv::Op::OpFUnordGreaterThan,
+    spv::Op::OpFOrdLessThanEqual,
+    spv::Op::OpFUnordLessThanEqual,
+    spv::Op::OpFOrdGreaterThanEqual,
+    spv::Op::OpFUnordGreaterThanEqual,
+    spv::Op::OpIAdd,
+    spv::Op::OpFAdd,
+    spv::Op::OpISub,
+    spv::Op::OpFSub,
+    spv::Op::OpIMul,
+    spv::Op::OpFMul,
+    spv::Op::OpUDiv,
+    spv::Op::OpSDiv,
+    spv::Op::OpFDiv,
+    spv::Op::OpVectorTimesScalar,
+    spv::Op::OpShiftRightLogical,
+    spv::Op::OpShiftRightArithmetic,
+    spv::Op::OpShiftLeftLogical,
+    spv::Op::OpBitwiseXor,
+    spv::Op::OpBitwiseOr,
+    spv::Op::OpBitwiseAnd};
+
+FoldingRule MergeBinaryOpSelect(spv::Op opcode) {
+  assert(std::find(std::begin(MergeBinaryOpSelectOps),
+                   std::end(MergeBinaryOpSelectOps),
+                   opcode) != std::end(MergeBinaryOpSelectOps) &&
+         "Wrong opcode.");
+
+  return [opcode](IRContext* context, Instruction* inst,
+                  const std::vector<const analysis::Constant*>& constants) {
+    const analysis::Constant* const_input = ConstInput(constants);
+    if (!const_input) {
+      return false;
+    }
+    Instruction* non_const = NonConstInput(context, constants[0], inst);
+    if (non_const->opcode() != spv::Op::OpSelect) {
+      return false;
+    }
+    std::vector<const analysis::Constant*> select_constants =
+        context->get_constant_mgr()->GetOperandConstants(non_const);
+    if (!select_constants[1] || !select_constants[2]) {
+      return false;
+    }
+
+    InstructionBuilder ir_builder(
+        context, inst,
+        IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+
+    Instruction *lhs, *rhs;
+    if (constants[0]) {
+      lhs = ir_builder.AddBinaryOp(inst->type_id(), opcode,
+                                   inst->GetSingleWordInOperand(0),
+                                   non_const->GetSingleWordInOperand(1));
+      rhs = ir_builder.AddBinaryOp(inst->type_id(), opcode,
+                                   inst->GetSingleWordInOperand(0),
+                                   non_const->GetSingleWordInOperand(2));
+    } else {
+      lhs = ir_builder.AddBinaryOp(inst->type_id(), opcode,
+                                   non_const->GetSingleWordInOperand(1),
+                                   inst->GetSingleWordInOperand(1));
+      rhs = ir_builder.AddBinaryOp(inst->type_id(), opcode,
+                                   non_const->GetSingleWordInOperand(2),
+                                   inst->GetSingleWordInOperand(1));
+    }
+
+    if (!lhs || !rhs) {
+      return false;
+    }
+
+    if (context->get_instruction_folder().FoldInstruction(lhs)) {
+      context->AnalyzeDefUse(lhs);
+      while (lhs->opcode() == spv::Op::OpCopyObject) {
+        lhs =
+            context->get_def_use_mgr()->GetDef(lhs->GetSingleWordInOperand(0));
+      }
+    }
+    if (context->get_instruction_folder().FoldInstruction(rhs)) {
+      context->AnalyzeDefUse(rhs);
+      while (rhs->opcode() == spv::Op::OpCopyObject) {
+        rhs =
+            context->get_def_use_mgr()->GetDef(rhs->GetSingleWordInOperand(0));
+      }
+    }
+    inst->SetOpcode(spv::Op::OpSelect);
+    inst->SetInOperands(
+        {{SPV_OPERAND_TYPE_ID, {non_const->GetSingleWordInOperand(0)}},
+         {SPV_OPERAND_TYPE_ID, {lhs->result_id()}},
+         {SPV_OPERAND_TYPE_ID, {rhs->result_id()}}});
+    return true;
+  };
+}
+
+// Fold OpLogicalNot instructions that follow a comparison,
+// if the comparison is only used by that instruction.
+//
+// !(a == b) = (a != b)
+// !(a != b) = (a == b)
+// !(a < b)  = (a >= b)
+// !(a >= b) = (a < b)
+// !(a > b)  = (a <= b)
+// !(a <= b) = (a > b)
+FoldingRule FoldLogicalNotComparison() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>&) {
+    assert(inst->opcode() == spv::Op::OpLogicalNot);
+    analysis::DefUseManager* def_mgr = context->get_def_use_mgr();
+    Instruction* child =
+        context->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0));
+
+    if (def_mgr->NumUses(child) > 1) {
+      return false;
+    }
+
+    spv::Op new_opcode = spv::Op::OpNop;
+    switch (child->opcode()) {
+      // (a == b) <=> (a != b)
+      case spv::Op::OpIEqual:
+        new_opcode = spv::Op::OpINotEqual;
+        break;
+      case spv::Op::OpINotEqual:
+        new_opcode = spv::Op::OpIEqual;
+        break;
+      case spv::Op::OpFOrdEqual:
+        new_opcode = spv::Op::OpFUnordNotEqual;
+        break;
+      case spv::Op::OpFOrdNotEqual:
+        new_opcode = spv::Op::OpFUnordEqual;
+        break;
+      case spv::Op::OpFUnordEqual:
+        new_opcode = spv::Op::OpFOrdNotEqual;
+        break;
+      case spv::Op::OpFUnordNotEqual:
+        new_opcode = spv::Op::OpFOrdEqual;
+        break;
+      case spv::Op::OpLogicalEqual:
+        new_opcode = spv::Op::OpLogicalNotEqual;
+        break;
+      case spv::Op::OpLogicalNotEqual:
+        new_opcode = spv::Op::OpLogicalEqual;
+        break;
+
+      // (a > b) <=> (a <= b)
+      case spv::Op::OpUGreaterThan:
+        new_opcode = spv::Op::OpULessThanEqual;
+        break;
+      case spv::Op::OpULessThanEqual:
+        new_opcode = spv::Op::OpUGreaterThan;
+        break;
+      case spv::Op::OpSGreaterThan:
+        new_opcode = spv::Op::OpSLessThanEqual;
+        break;
+      case spv::Op::OpSLessThanEqual:
+        new_opcode = spv::Op::OpSGreaterThan;
+        break;
+      case spv::Op::OpFOrdGreaterThan:
+        new_opcode = spv::Op::OpFUnordLessThanEqual;
+        break;
+      case spv::Op::OpFOrdLessThanEqual:
+        new_opcode = spv::Op::OpFUnordGreaterThan;
+        break;
+      case spv::Op::OpFUnordGreaterThan:
+        new_opcode = spv::Op::OpFOrdLessThanEqual;
+        break;
+      case spv::Op::OpFUnordLessThanEqual:
+        new_opcode = spv::Op::OpFOrdGreaterThan;
+        break;
+
+      // (a < b) <=> (a >= b)
+      case spv::Op::OpULessThan:
+        new_opcode = spv::Op::OpUGreaterThanEqual;
+        break;
+      case spv::Op::OpUGreaterThanEqual:
+        new_opcode = spv::Op::OpULessThan;
+        break;
+      case spv::Op::OpSLessThan:
+        new_opcode = spv::Op::OpSGreaterThanEqual;
+        break;
+      case spv::Op::OpSGreaterThanEqual:
+        new_opcode = spv::Op::OpSLessThan;
+        break;
+      case spv::Op::OpFOrdLessThan:
+        new_opcode = spv::Op::OpFUnordGreaterThanEqual;
+        break;
+      case spv::Op::OpFOrdGreaterThanEqual:
+        new_opcode = spv::Op::OpFUnordLessThan;
+        break;
+      case spv::Op::OpFUnordLessThan:
+        new_opcode = spv::Op::OpFOrdGreaterThanEqual;
+        break;
+      case spv::Op::OpFUnordGreaterThanEqual:
+        new_opcode = spv::Op::OpFOrdLessThan;
+        break;
+
+      default:
+        break;
+    }
+
+    if (new_opcode == spv::Op::OpNop) {
+      return false;
+    }
+
+    inst->SetOpcode(new_opcode);
+    inst->SetInOperands(
+        {{SPV_OPERAND_TYPE_ID, {child->GetSingleWordInOperand(0)}},
+         {SPV_OPERAND_TYPE_ID, {child->GetSingleWordInOperand(1)}}});
+
+    return true;
+  };
+}
+
+// (a == true)  =  a
+// (a == false) = !a
+// (a != true)  = !a
+// (a != false) =  a
+FoldingRule RedundantLogicalEqual() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    assert(inst->opcode() == spv::Op::OpLogicalEqual ||
+           inst->opcode() == spv::Op::OpLogicalNotEqual);
+
+    const analysis::Constant* const_input = ConstInput(constants);
+    if (!const_input) {
+      return false;
+    }
+
+    analysis::DefUseManager* def_mgr = context->get_def_use_mgr();
+    if (inst->type_id() !=
+        def_mgr->GetDef(inst->GetSingleWordInOperand(0))->type_id()) {
+      return false;
+    }
+
+    std::optional<bool> uniform_const = GetBoolConstantKind(const_input);
+    if (!uniform_const) {
+      return false;
+    }
+
+    bool direct_copy = inst->opcode() == spv::Op::OpLogicalEqual
+                           ? uniform_const.value()
+                           : !uniform_const.value();
+
+    inst->SetOpcode(direct_copy ? spv::Op::OpCopyObject
+                                : spv::Op::OpLogicalNot);
+    inst->SetInOperands(
+        {{SPV_OPERAND_TYPE_ID,
+          {NonConstInput(context, constants[0], inst)->result_id()}}});
+    return true;
+  };
+}
+
 enum class FloatConstantKind { Unknown, Zero, One };
 
 FloatConstantKind getFloatConstantKind(const analysis::Constant* constant) {
@@ -2451,14 +3599,31 @@
     FloatConstantKind kind0 = getFloatConstantKind(constants[0]);
     FloatConstantKind kind1 = getFloatConstantKind(constants[1]);
 
-    if (kind0 == FloatConstantKind::Zero) {
+    if (kind0 == FloatConstantKind::Zero || kind1 == FloatConstantKind::One) {
       inst->SetOpcode(spv::Op::OpCopyObject);
       inst->SetInOperands(
           {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(0)}}});
       return true;
     }
 
-    if (kind1 == FloatConstantKind::One) {
+    return false;
+  };
+}
+
+FoldingRule RedundantFMod() {
+  return [](IRContext*, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    assert(inst->opcode() == spv::Op::OpFMod &&
+           "Wrong opcode.  Should be OpFMod.");
+    assert(constants.size() == 2);
+
+    if (!inst->IsFloatingPointFoldingAllowed()) {
+      return false;
+    }
+
+    FloatConstantKind kind0 = getFloatConstantKind(constants[0]);
+
+    if (kind0 == FloatConstantKind::Zero) {
       inst->SetOpcode(spv::Op::OpCopyObject);
       inst->SetInOperands(
           {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(0)}}});
@@ -2504,24 +3669,18 @@
   };
 }
 
-// This rule handles addition of zero for integers.
-FoldingRule RedundantIAdd() {
-  return [](IRContext* context, Instruction* inst,
-            const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == spv::Op::OpIAdd &&
-           "Wrong opcode. Should be OpIAdd.");
+// Returns a folding rule that folds the instruction to operand |foldToArg|
+// (0 or 1) if operand |arg| (0 or 1) is a zero constant.
+FoldingRule RedundantBinaryOpWithZeroOperand(uint32_t arg, uint32_t foldToArg) {
+  return [arg, foldToArg](
+             IRContext* context, Instruction* inst,
+             const std::vector<const analysis::Constant*>& constants) {
+    assert(constants.size() == 2);
 
-    uint32_t operand = std::numeric_limits<uint32_t>::max();
-    const analysis::Type* operand_type = nullptr;
-    if (constants[0] && constants[0]->IsZero()) {
-      operand = inst->GetSingleWordInOperand(1);
-      operand_type = constants[0]->type();
-    } else if (constants[1] && constants[1]->IsZero()) {
-      operand = inst->GetSingleWordInOperand(0);
-      operand_type = constants[1]->type();
-    }
+    if (constants[arg] && constants[arg]->IsZero()) {
+      auto operand = inst->GetSingleWordInOperand(foldToArg);
+      auto operand_type = constants[arg]->type();
 
-    if (operand != std::numeric_limits<uint32_t>::max()) {
       const analysis::Type* inst_type =
           context->get_type_mgr()->GetType(inst->type_id());
       if (inst_type->IsSame(operand_type)) {
@@ -2536,6 +3695,421 @@
   };
 }
 
+// This rule handles any of RedundantBinaryRhs0Ops with a 0 or vector 0 on the
+// right-hand side (a | 0 => a).
+static const constexpr spv::Op RedundantBinaryRhs0Ops[] = {
+    spv::Op::OpBitwiseOr,
+    spv::Op::OpBitwiseXor,
+    spv::Op::OpShiftRightLogical,
+    spv::Op::OpShiftRightArithmetic,
+    spv::Op::OpShiftLeftLogical,
+    spv::Op::OpIAdd,
+    spv::Op::OpISub};
+FoldingRule RedundantBinaryRhs0(spv::Op op) {
+  assert(std::find(std::begin(RedundantBinaryRhs0Ops),
+                   std::end(RedundantBinaryRhs0Ops),
+                   op) != std::end(RedundantBinaryRhs0Ops) &&
+         "Wrong opcode.");
+  (void)op;
+  return RedundantBinaryOpWithZeroOperand(1, 0);
+}
+
+// This rule handles any of RedundantBinaryLhs0Ops with a 0 or vector 0 on the
+// left-hand side (0 | a => a).
+static const constexpr spv::Op RedundantBinaryLhs0Ops[] = {
+    spv::Op::OpBitwiseOr, spv::Op::OpBitwiseXor, spv::Op::OpIAdd};
+FoldingRule RedundantBinaryLhs0(spv::Op op) {
+  assert(std::find(std::begin(RedundantBinaryLhs0Ops),
+                   std::end(RedundantBinaryLhs0Ops),
+                   op) != std::end(RedundantBinaryLhs0Ops) &&
+         "Wrong opcode.");
+  (void)op;
+  return RedundantBinaryOpWithZeroOperand(0, 1);
+}
+
+// This rule handles shifts and divisions of 0 or vector 0 by any amount
+// (0 >> a => 0).
+static const constexpr spv::Op RedundantBinaryLhs0To0Ops[] = {
+    spv::Op::OpShiftRightLogical,
+    spv::Op::OpShiftRightArithmetic,
+    spv::Op::OpShiftLeftLogical,
+    spv::Op::OpSDiv,
+    spv::Op::OpUDiv,
+    spv::Op::OpSMod,
+    spv::Op::OpUMod};
+FoldingRule RedundantBinaryLhs0To0(spv::Op op) {
+  assert(std::find(std::begin(RedundantBinaryLhs0To0Ops),
+                   std::end(RedundantBinaryLhs0To0Ops),
+                   op) != std::end(RedundantBinaryLhs0To0Ops) &&
+         "Wrong opcode.");
+  (void)op;
+  return RedundantBinaryOpWithZeroOperand(0, 0);
+}
+
+FoldingRule ReassociateCommutiveOp() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    const analysis::Type* type =
+        context->get_type_mgr()->GetType(inst->type_id());
+    uint32_t width = ElementWidth(type);
+    if (width != 32) return false;
+
+    analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+    const analysis::Constant* const_input1 = ConstInput(constants);
+    if (!const_input1) return false;
+    Instruction* other_inst = NonConstInput(context, constants[0], inst);
+
+    if (other_inst->opcode() == inst->opcode()) {
+      std::vector<const analysis::Constant*> other_constants =
+          const_mgr->GetOperandConstants(other_inst);
+      const analysis::Constant* const_input2 = ConstInput(other_constants);
+      if (!const_input2) return false;
+
+      Instruction* non_const_input =
+          NonConstInput(context, other_constants[0], other_inst);
+      uint32_t merged_id = PerformOperation(const_mgr, inst->opcode(),
+                                            const_input1, const_input2);
+
+      if (merged_id == 0) return false;
+      inst->SetInOperands(
+          {{SPV_OPERAND_TYPE_ID, {non_const_input->result_id()}},
+           {SPV_OPERAND_TYPE_ID, {merged_id}}});
+      return true;
+    }
+
+    return false;
+  };
+}
+
+// A | (b | C) = b | (A | C)
+// A ^ (b ^ C) = b ^ (A ^ C)
+// A & (b & C) = b & (A & C)
+// Where A and C are constants
+static const constexpr spv::Op ReassociateCommutiveBitwiseOps[] = {
+    spv::Op::OpBitwiseOr, spv::Op::OpBitwiseXor, spv::Op::OpBitwiseAnd};
+FoldingRule ReassociateCommutiveBitwise(spv::Op op) {
+  assert(std::find(std::begin(ReassociateCommutiveBitwiseOps),
+                   std::end(ReassociateCommutiveBitwiseOps),
+                   op) != std::end(ReassociateCommutiveBitwiseOps) &&
+         "Wrong opcode.");
+  (void)op;
+  return ReassociateCommutiveOp();
+}
+
+// Returns true if all elements in |c| are 1.
+bool IsAllInt1(const analysis::Constant* c) {
+  if (auto composite = c->AsCompositeConstant()) {
+    auto& components = composite->GetComponents();
+    return std::all_of(std::begin(components), std::end(components), IsAllInt1);
+  } else if (c->AsIntConstant()) {
+    return c->GetSignExtendedValue() == 1;
+  }
+
+  return false;
+}
+
+// This rule handles divisions by 1 or vector 1 (a / 1 => a).
+FoldingRule RedundantSUDiv() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    assert(constants.size() == 2);
+    assert((inst->opcode() == spv::Op::OpUDiv ||
+            inst->opcode() == spv::Op::OpSDiv) &&
+           "Wrong opcode.");
+
+    if (constants[1] && IsAllInt1(constants[1])) {
+      auto operand = inst->GetSingleWordInOperand(0);
+      auto operand_type = constants[1]->type();
+
+      const analysis::Type* inst_type =
+          context->get_type_mgr()->GetType(inst->type_id());
+      if (inst_type->IsSame(operand_type)) {
+        inst->SetOpcode(spv::Op::OpCopyObject);
+      } else {
+        inst->SetOpcode(spv::Op::OpBitcast);
+      }
+      inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {operand}}});
+      return true;
+    }
+    return false;
+  };
+}
+
+// This rule handles modulo from division by 1 or vector 1 (a % 1 => 0).
+FoldingRule RedundantSUMod() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    assert(constants.size() == 2);
+    assert((inst->opcode() == spv::Op::OpUMod ||
+            inst->opcode() == spv::Op::OpSMod) &&
+           "Wrong opcode.");
+
+    if (constants[1] && IsAllInt1(constants[1])) {
+      auto type = context->get_type_mgr()->GetType(inst->type_id());
+      auto zero_id = context->get_constant_mgr()->GetNullConstId(type);
+
+      inst->SetOpcode(spv::Op::OpCopyObject);
+      inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {zero_id}}});
+      return true;
+    }
+    return false;
+  };
+}
+
+// Utility function for applying |callback| to |input1| and |input2|.
+// If they are vectors it applies element wise.
+// The constants |input1| and |input2| must be integers or a vector of integers.
+template <typename Callback>
+void ForEachIntegerConstantPair(analysis::ConstantManager* const_mgr,
+                                const analysis::Constant* input1,
+                                const analysis::Constant* input2,
+                                Callback&& callback) {
+  assert(input1 && input2);
+
+  auto Dispatch = [&callback](const analysis::Constant* lhs,
+                              const analysis::Constant* rhs) {
+    assert(lhs->type()->AsInteger());
+    const analysis::Integer* type = lhs->type()->AsInteger();
+    uint32_t width = type->AsInteger()->width();
+    assert(width == 32 || width == 64);
+    if (width == 32) {
+      callback(lhs->GetU32(), rhs->GetU32());
+    } else {
+      callback(lhs->GetU64(), rhs->GetU64());
+    }
+  };
+
+  const analysis::Type* type = input1->type();
+  if (const analysis::Vector* vector_type = type->AsVector()) {
+    const analysis::Type* ele_type = vector_type->element_type();
+    assert(ele_type->AsInteger());
+    for (uint32_t i = 0; i != vector_type->element_count(); ++i) {
+      const analysis::Constant* input1_comp = nullptr;
+      if (const analysis::VectorConstant* input1_vector =
+              input1->AsVectorConstant()) {
+        input1_comp = input1_vector->GetComponents()[i];
+      } else {
+        assert(input1->AsNullConstant());
+        input1_comp = const_mgr->GetConstant(ele_type, {});
+      }
+
+      const analysis::Constant* input2_comp = nullptr;
+      if (const analysis::VectorConstant* input2_vector =
+              input2->AsVectorConstant()) {
+        input2_comp = input2_vector->GetComponents()[i];
+      } else {
+        assert(input2->AsNullConstant());
+        input2_comp = const_mgr->GetConstant(ele_type, {});
+      }
+
+      assert(ele_type->AsInteger());
+      Dispatch(input1_comp, input2_comp);
+    }
+
+  } else {
+    assert(type->AsInteger());
+    Dispatch(input1, input2);
+  }
+}
+
+// Folds redundant xor and or ops that are part of an and.
+// Cases handled:
+// 0b1110 & (a | 0b0001) = a & 0b1110
+// 0b1110 & (a ^ 0b0001) = a & 0b1110
+// 0b0110 & (a | 0b1110) = 0b0110
+FoldingRule RedundantAndOrXor() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    assert(inst->opcode() == spv::Op::OpBitwiseAnd && "Wrong opcode.");
+    const analysis::Type* type =
+        context->get_type_mgr()->GetType(inst->type_id());
+    uint32_t width = ElementWidth(type);
+    if ((width != 32) && (width != 64)) return false;
+
+    analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+    const analysis::Constant* const_input1 = ConstInput(constants);
+    if (!const_input1) return false;
+    Instruction* other_inst = NonConstInput(context, constants[0], inst);
+
+    if (other_inst->opcode() == spv::Op::OpBitwiseOr ||
+        other_inst->opcode() == spv::Op::OpBitwiseXor) {
+      std::vector<const analysis::Constant*> other_constants =
+          const_mgr->GetOperandConstants(other_inst);
+      const analysis::Constant* const_input2 = ConstInput(other_constants);
+      if (!const_input2) return false;
+
+      bool can_convert_to_const = other_inst->opcode() == spv::Op::OpBitwiseOr;
+      bool can_remove_inner = true;
+
+      ForEachIntegerConstantPair(
+          const_mgr, const_input1, const_input2,
+          [&can_remove_inner, &can_convert_to_const](auto lhs, auto rhs) {
+            // Only convert to const if 'and' is a subset of 'or'
+            can_convert_to_const = can_convert_to_const && ((lhs & rhs) == lhs);
+            // Only remove 'xor'/'or' if no bits intersect with 'and'
+            can_remove_inner = can_remove_inner && ((lhs & rhs) == 0);
+          });
+
+      if (can_convert_to_const) {
+        Instruction* const_inst =
+            const_mgr->GetDefiningInstruction(const_input1);
+        inst->SetOpcode(spv::Op::OpCopyObject);
+        inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {const_inst->result_id()}}});
+        return true;
+      } else if (can_remove_inner) {
+        Instruction* non_const_input =
+            NonConstInput(context, other_constants[0], other_inst);
+        Instruction* const_inst =
+            const_mgr->GetDefiningInstruction(const_input1);
+        inst->SetInOperands(
+            {{SPV_OPERAND_TYPE_ID, {non_const_input->result_id()}},
+             {SPV_OPERAND_TYPE_ID, {const_inst->result_id()}}});
+        return true;
+      }
+    }
+    return false;
+  };
+}
+
+// Folds redundant add and sub ops that are part of an and.
+// Cases handled:
+// 1 & (b + 2) = b & 1
+// 1 & (b - 2) = b & 1
+FoldingRule RedundantAndAddSub() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    assert(inst->opcode() == spv::Op::OpBitwiseAnd && "Wrong opcode.");
+    const analysis::Type* type =
+        context->get_type_mgr()->GetType(inst->type_id());
+    uint32_t width = ElementWidth(type);
+    if ((width != 32) && (width != 64)) return false;
+
+    analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+    const analysis::Constant* const_input1 = ConstInput(constants);
+    if (!const_input1) return false;
+    Instruction* other_inst = NonConstInput(context, constants[0], inst);
+
+    if (other_inst->opcode() != spv::Op::OpIAdd &&
+        other_inst->opcode() != spv::Op::OpISub) {
+      return false;
+    }
+    std::vector<const analysis::Constant*> other_constants =
+        const_mgr->GetOperandConstants(other_inst);
+    const analysis::Constant* const_input2 = ConstInput(other_constants);
+    if (!const_input2) return false;
+
+    // Only valid for subtraction if const is on the right
+    if ((other_inst->opcode() == spv::Op::OpISub) && other_constants[0]) {
+      return false;
+    }
+
+    bool can_remove_inner = true;
+    ForEachIntegerConstantPair(const_mgr, const_input1, const_input2,
+                               [&can_remove_inner](auto and_op, auto add_op) {
+                                 if (can_remove_inner) {
+                                   // Only valid if no bits from the +/- could
+                                   // affect bits from the & operation.
+                                   can_remove_inner =
+                                       utils::LSB(add_op) > and_op;
+                                 }
+                               });
+
+    if (can_remove_inner) {
+      Instruction* non_const_input =
+          NonConstInput(context, other_constants[0], other_inst);
+      Instruction* const_inst = const_mgr->GetDefiningInstruction(const_input1);
+      inst->SetInOperands(
+          {{SPV_OPERAND_TYPE_ID, {non_const_input->result_id()}},
+           {SPV_OPERAND_TYPE_ID, {const_inst->result_id()}}});
+      return true;
+    }
+    return false;
+  };
+}
+
+// Folds redundant shift ops that are part of an and.
+// Cases handled:
+// 1 & (b << 1) = 0
+// 0x80000000 & (b >> 1) = 0
+FoldingRule RedundantAndShift() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    assert(inst->opcode() == spv::Op::OpBitwiseAnd && "Wrong opcode.");
+    const analysis::Type* type =
+        context->get_type_mgr()->GetType(inst->type_id());
+    uint32_t width = ElementWidth(type);
+    if (width != 8 && width != 16 && width != 32 && width != 64) return false;
+    const uint64_t width_mask =
+        (width == 64) ? ~0ull : ((1ull << width) - 1ull);
+
+    analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+    const analysis::Constant* const_input1 = ConstInput(constants);
+    if (!const_input1) return false;
+    Instruction* other_inst = NonConstInput(context, constants[0], inst);
+
+    spv::Op other_op = other_inst->opcode();
+    if (other_op != spv::Op::OpShiftLeftLogical &&
+        other_op != spv::Op::OpShiftRightLogical) {
+      return false;
+    }
+
+    std::vector<const analysis::Constant*> other_constants =
+        const_mgr->GetOperandConstants(other_inst);
+
+    // Only valid if const is on the right.
+    if (other_constants[0]) return false;
+    const analysis::Constant* const_input2 = other_constants[1];
+    if (!const_input2) return false;
+
+    auto get_value_u64 =
+        [](const analysis::Constant* c) -> std::optional<uint64_t> {
+      if (!c) return std::nullopt;
+      const analysis::Integer* int_t = c->type()->AsInteger();
+      if (!int_t) return std::nullopt;
+      return c->GetZeroExtendedValue();
+    };
+
+    auto can_fold_component =
+        [&](const analysis::Constant* mask_const,
+            const analysis::Constant* shift_const) -> std::optional<bool> {
+      auto lhs = get_value_u64(mask_const);
+      auto rhs = get_value_u64(shift_const);
+      if (!lhs || !rhs) return std::nullopt;
+      if (*rhs >= width) return false;
+      uint64_t lhs_masked = *lhs & width_mask;
+      if (other_op == spv::Op::OpShiftRightLogical) {
+        return ((lhs_masked << *rhs) & width_mask) == 0;
+      }
+      return ((lhs_masked >> *rhs) & width_mask) == 0;
+    };
+
+    if (const analysis::Vector* mask_vec = type->AsVector()) {
+      const analysis::Vector* shift_vec = const_input2->type()->AsVector();
+      if (!shift_vec ||
+          shift_vec->element_count() != mask_vec->element_count()) {
+        return false;
+      }
+      const auto mask_components = const_input1->GetVectorComponents(const_mgr);
+      const auto shift_components =
+          const_input2->GetVectorComponents(const_mgr);
+      for (uint32_t i = 0; i != mask_vec->element_count(); ++i) {
+        auto result =
+            can_fold_component(mask_components[i], shift_components[i]);
+        if (!result || !*result) return false;
+      }
+    } else {
+      if (const_input2->type()->AsVector()) return false;
+      auto result = can_fold_component(const_input1, const_input2);
+      if (!result || !*result) return false;
+    }
+
+    auto zero_id = context->get_constant_mgr()->GetNullConstId(type);
+    inst->SetOpcode(spv::Op::OpCopyObject);
+    inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {zero_id}}});
+    return true;
+  };
+}
+
 // This rule look for a dot with a constant vector containing a single 1 and
 // the rest 0s.  This is the same as doing an extract.
 FoldingRule DotProductDoingExtract() {
@@ -2873,7 +4447,27 @@
   // Note that the order in which rules are added to the list matters. If a rule
   // applies to the instruction, the rest of the rules will not be attempted.
   // Take that into consideration.
+  for (auto op : RedundantBinaryRhs0Ops)
+    rules_[op].push_back(RedundantBinaryRhs0(op));
+  for (auto op : RedundantBinaryLhs0Ops)
+    rules_[op].push_back(RedundantBinaryLhs0(op));
+  for (auto op : RedundantBinaryLhs0To0Ops)
+    rules_[op].push_back(RedundantBinaryLhs0To0(op));
+  for (auto op : ReassociateCommutiveBitwiseOps)
+    rules_[op].push_back(ReassociateCommutiveBitwise(op));
+  for (auto op : ReassociateNestedGenericIntOps)
+    rules_[op].push_back(ReassociateNestedGenericInt(op));
+  for (auto op : MergeBinaryOpSelectOps)
+    rules_[op].push_back(MergeBinaryOpSelect(op));
+  rules_[spv::Op::OpSDiv].push_back(RedundantSUDiv());
+  rules_[spv::Op::OpUDiv].push_back(RedundantSUDiv());
+  rules_[spv::Op::OpSMod].push_back(RedundantSUMod());
+  rules_[spv::Op::OpUMod].push_back(RedundantSUMod());
+
   rules_[spv::Op::OpBitcast].push_back(BitCastScalarOrVector());
+  rules_[spv::Op::OpBitcast].push_back(RedundantBitcast());
+
+  rules_[spv::Op::OpBitReverse].push_back(BitReverseScalarOrVector());
 
   rules_[spv::Op::OpCompositeConstruct].push_back(
       CompositeExtractFeedingConstruct);
@@ -2883,6 +4477,8 @@
       CompositeConstructFeedingExtract);
   rules_[spv::Op::OpCompositeExtract].push_back(VectorShuffleFeedingExtract());
   rules_[spv::Op::OpCompositeExtract].push_back(FMixFeedingExtract());
+  rules_[spv::Op::OpCompositeExtract].push_back(CopyLogicalFeedingExtract);
+  rules_[spv::Op::OpCompositeExtract].push_back(LoadFeedingExtract);
 
   rules_[spv::Op::OpCompositeInsert].push_back(
       CompositeInsertToCompositeConstruct);
@@ -2896,18 +4492,27 @@
   rules_[spv::Op::OpFAdd].push_back(MergeAddAddArithmetic());
   rules_[spv::Op::OpFAdd].push_back(MergeAddSubArithmetic());
   rules_[spv::Op::OpFAdd].push_back(MergeGenericAddSubArithmetic());
-  rules_[spv::Op::OpFAdd].push_back(FactorAddMuls());
+  rules_[spv::Op::OpFAdd].push_back(ReassociateNestedAddSub());
+  rules_[spv::Op::OpFAdd].push_back(FactorAddSubMuls());
 
   rules_[spv::Op::OpFDiv].push_back(RedundantFDiv());
   rules_[spv::Op::OpFDiv].push_back(ReciprocalFDiv());
   rules_[spv::Op::OpFDiv].push_back(MergeDivDivArithmetic());
   rules_[spv::Op::OpFDiv].push_back(MergeDivMulArithmetic());
   rules_[spv::Op::OpFDiv].push_back(MergeDivNegateArithmetic());
+  rules_[spv::Op::OpFDiv].push_back(MergeDivMulDoubleNegative());
+  rules_[spv::Op::OpFDiv].push_back(ReassociateNestedMulDivFloat());
+
+  rules_[spv::Op::OpFMod].push_back(RedundantFMod());
 
   rules_[spv::Op::OpFMul].push_back(RedundantFMul());
   rules_[spv::Op::OpFMul].push_back(MergeMulMulArithmetic());
   rules_[spv::Op::OpFMul].push_back(MergeMulDivArithmetic());
   rules_[spv::Op::OpFMul].push_back(MergeMulNegateArithmetic());
+  rules_[spv::Op::OpFMul].push_back(MergeDivMulDoubleNegative());
+  rules_[spv::Op::OpFMul].push_back(ReassociateNestedMulDivFloat());
+
+  rules_[spv::Op::OpVectorTimesScalar].push_back(MergeDivMulDoubleNegative());
 
   rules_[spv::Op::OpFNegate].push_back(MergeNegateArithmetic());
   rules_[spv::Op::OpFNegate].push_back(MergeNegateAddSubArithmetic());
@@ -2917,21 +4522,32 @@
   rules_[spv::Op::OpFSub].push_back(MergeSubNegateArithmetic());
   rules_[spv::Op::OpFSub].push_back(MergeSubAddArithmetic());
   rules_[spv::Op::OpFSub].push_back(MergeSubSubArithmetic());
+  rules_[spv::Op::OpFSub].push_back(ReassociateNestedAddSub());
+  rules_[spv::Op::OpFSub].push_back(FactorAddSubMuls());
 
-  rules_[spv::Op::OpIAdd].push_back(RedundantIAdd());
   rules_[spv::Op::OpIAdd].push_back(MergeAddNegateArithmetic());
   rules_[spv::Op::OpIAdd].push_back(MergeAddAddArithmetic());
   rules_[spv::Op::OpIAdd].push_back(MergeAddSubArithmetic());
   rules_[spv::Op::OpIAdd].push_back(MergeGenericAddSubArithmetic());
-  rules_[spv::Op::OpIAdd].push_back(FactorAddMuls());
+  rules_[spv::Op::OpIAdd].push_back(ReassociateNestedAddSub());
+  rules_[spv::Op::OpIAdd].push_back(FactorAddSubMuls());
+
+  rules_[spv::Op::OpSDiv].push_back(MergeDivMulDoubleNegative());
 
   rules_[spv::Op::OpIMul].push_back(IntMultipleBy1());
   rules_[spv::Op::OpIMul].push_back(MergeMulMulArithmetic());
   rules_[spv::Op::OpIMul].push_back(MergeMulNegateArithmetic());
+  rules_[spv::Op::OpIMul].push_back(MergeDivMulDoubleNegative());
 
   rules_[spv::Op::OpISub].push_back(MergeSubNegateArithmetic());
   rules_[spv::Op::OpISub].push_back(MergeSubAddArithmetic());
   rules_[spv::Op::OpISub].push_back(MergeSubSubArithmetic());
+  rules_[spv::Op::OpISub].push_back(ReassociateNestedAddSub());
+  rules_[spv::Op::OpISub].push_back(FactorAddSubMuls());
+
+  rules_[spv::Op::OpBitwiseAnd].push_back(RedundantAndOrXor());
+  rules_[spv::Op::OpBitwiseAnd].push_back(RedundantAndAddSub());
+  rules_[spv::Op::OpBitwiseAnd].push_back(RedundantAndShift());
 
   rules_[spv::Op::OpPhi].push_back(RedundantPhi());
 
@@ -2940,6 +4556,17 @@
   rules_[spv::Op::OpSNegate].push_back(MergeNegateAddSubArithmetic());
 
   rules_[spv::Op::OpSelect].push_back(RedundantSelect());
+  rules_[spv::Op::OpSelect].push_back(FoldConstantBooleanSelect());
+
+  rules_[spv::Op::OpLogicalAnd].push_back(RedundantLogicalAnd());
+
+  rules_[spv::Op::OpLogicalOr].push_back(RedundantLogicalOr());
+
+  rules_[spv::Op::OpLogicalNot].push_back(RedundantLogicalNot());
+  rules_[spv::Op::OpLogicalNot].push_back(FoldLogicalNotComparison());
+
+  rules_[spv::Op::OpLogicalEqual].push_back(RedundantLogicalEqual());
+  rules_[spv::Op::OpLogicalNotEqual].push_back(RedundantLogicalEqual());
 
   rules_[spv::Op::OpStore].push_back(StoringUndef());
 
diff --git a/source/opt/freeze_spec_constant_value_pass.cpp b/source/opt/freeze_spec_constant_value_pass.cpp
index 3f89e56..5ed8a92 100644
--- a/source/opt/freeze_spec_constant_value_pass.cpp
+++ b/source/opt/freeze_spec_constant_value_pass.cpp
@@ -35,6 +35,10 @@
         inst->SetOpcode(spv::Op::OpConstantFalse);
         modified = true;
         break;
+      case spv::Op::OpSpecConstantDataKHR:
+        inst->SetOpcode(spv::Op::OpConstantDataKHR);
+        modified = true;
+        break;
       case spv::Op::OpDecorate:
         if (spv::Decoration(inst->GetSingleWordInOperand(1)) ==
             spv::Decoration::SpecId) {
diff --git a/source/opt/function.cpp b/source/opt/function.cpp
index 2ee88ec..560953b 100644
--- a/source/opt/function.cpp
+++ b/source/opt/function.cpp
@@ -40,6 +40,10 @@
   clone->blocks_.reserve(blocks_.size());
   for (const auto& b : blocks_) {
     std::unique_ptr<BasicBlock> bb(b->Clone(ctx));
+    if (!bb) {
+      delete clone;
+      return nullptr;
+    }
     clone->AddBasicBlock(std::move(bb));
   }
 
diff --git a/source/opt/function.h b/source/opt/function.h
index 8c0472c..39ecd59 100644
--- a/source/opt/function.h
+++ b/source/opt/function.h
@@ -17,6 +17,7 @@
 
 #include <algorithm>
 #include <functional>
+#include <iterator>
 #include <memory>
 #include <string>
 #include <unordered_set>
@@ -39,6 +40,7 @@
  public:
   using iterator = UptrVectorIterator<BasicBlock>;
   using const_iterator = UptrVectorIterator<BasicBlock, true>;
+  using ParamList = std::vector<std::unique_ptr<Instruction>>;
 
   // Creates a function instance declared by the given OpFunction instruction
   // |def_inst|.
@@ -46,7 +48,7 @@
 
   explicit Function(const Function& f) = delete;
 
-  // Creates a clone of the instruction in the given |context|
+  // Creates a clone of the function in the given |context|
   //
   // The parent module will default to null and needs to be explicitly set by
   // the user.
@@ -77,6 +79,23 @@
   // Does nothing if the function doesn't have such a parameter.
   inline void RemoveParameter(uint32_t id);
 
+  // Rewrites the function parameters by calling a replacer callback.
+  // The replacer takes two parameters: an expiring unique pointer to a current
+  // instruction, and a back-inserter into a new list of unique pointers to
+  // instructions.  The replacer is called for each current parameter, in order.
+  // Not valid to call while also iterating through the parameter list, e.g.
+  // within the ForEachParam method.
+  using RewriteParamFn = std::function<void(
+      std::unique_ptr<Instruction>&&, std::back_insert_iterator<ParamList>&)>;
+  void RewriteParams(RewriteParamFn& replacer) {
+    ParamList new_params;
+    auto appender = std::back_inserter(new_params);
+    for (auto& param : params_) {
+      replacer(std::move(param), appender);
+    }
+    params_ = std::move(new_params);
+  }
+
   // Saves the given function end instruction.
   inline void SetFunctionEnd(std::unique_ptr<Instruction> end_inst);
 
@@ -197,7 +216,7 @@
   // The OpFunction instruction that begins the definition of this function.
   std::unique_ptr<Instruction> def_inst_;
   // All parameters to this function.
-  std::vector<std::unique_ptr<Instruction>> params_;
+  ParamList params_;
   // All debug instructions in this function's header.
   InstructionList debug_insts_in_header_;
   // All basic blocks inside this function in specification order
diff --git a/source/opt/graph.cpp b/source/opt/graph.cpp
new file mode 100644
index 0000000..ba1030d
--- /dev/null
+++ b/source/opt/graph.cpp
@@ -0,0 +1,90 @@
+// Copyright (c) 2022-2025 Arm Ltd.
+//
+// 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/graph.h"
+
+namespace spvtools {
+namespace opt {
+
+Graph* Graph::Clone(IRContext* ctx) const {
+  Graph* clone = new Graph(std::unique_ptr<Instruction>(DefInst().Clone(ctx)));
+
+  clone->inputs_.reserve(inputs_.size());
+  for (const auto& i : inputs()) {
+    clone->AddInput(std::unique_ptr<Instruction>(i->Clone(ctx)));
+  }
+
+  clone->insts_.reserve(insts_.size());
+  for (const auto& i : instructions()) {
+    clone->AddInstruction(std::unique_ptr<Instruction>(i->Clone(ctx)));
+  }
+
+  clone->outputs_.reserve(outputs_.size());
+  for (const auto& i : outputs()) {
+    clone->AddOutput(std::unique_ptr<Instruction>(i->Clone(ctx)));
+  }
+
+  clone->SetGraphEnd(std::unique_ptr<Instruction>(EndInst()->Clone(ctx)));
+
+  return clone;
+}
+
+void Graph::ForEachInst(const std::function<void(Instruction*)>& f,
+                        bool run_on_debug_line_insts,
+                        bool run_on_non_semantic_insts) {
+  (void)run_on_debug_line_insts;
+  (void)run_on_non_semantic_insts;
+
+  f(def_inst_.get());
+
+  for (auto& inst : inputs_) {
+    f(inst.get());
+  }
+
+  for (auto& inst : insts_) {
+    f(inst.get());
+  }
+
+  for (auto& inst : outputs_) {
+    f(inst.get());
+  }
+
+  f(end_inst_.get());
+}
+
+void Graph::ForEachInst(const std::function<void(const Instruction*)>& f,
+                        bool run_on_debug_line_insts,
+                        bool run_on_non_semantic_insts) const {
+  (void)run_on_debug_line_insts;
+  (void)run_on_non_semantic_insts;
+
+  f(def_inst_.get());
+
+  for (auto& inst : inputs_) {
+    f(inst.get());
+  }
+
+  for (auto& inst : insts_) {
+    f(inst.get());
+  }
+
+  for (auto& inst : outputs_) {
+    f(inst.get());
+  }
+
+  f(end_inst_.get());
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/graph.h b/source/opt/graph.h
new file mode 100644
index 0000000..3b533b2
--- /dev/null
+++ b/source/opt/graph.h
@@ -0,0 +1,124 @@
+// Copyright (c) 2022-2025 Arm Ltd.
+//
+// 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_GRAPH_H_
+#define SOURCE_OPT_GRAPH_H_
+
+#include "source/opt/instruction.h"
+
+namespace spvtools {
+namespace opt {
+
+struct Graph {
+  // Creates a graph instance declared by the given OpGraph instruction
+  // |def_inst|.
+  inline explicit Graph(std::unique_ptr<Instruction> def_inst);
+  explicit Graph(const Graph& f) = delete;
+
+  // Creates a clone of the graph in the given |context|
+  //
+  // The parent module will default to null and needs to be explicitly set by
+  // the user.
+  Graph* Clone(IRContext*) const;
+
+  // The OpGraph instruction that begins the definition of this graph.
+  Instruction& DefInst() { return *def_inst_; }
+  const Instruction& DefInst() const { return *def_inst_; }
+
+  // Appends an input to this graph.
+  inline void AddInput(std::unique_ptr<Instruction> inst);
+
+  // Appends an instruction to this graph.
+  inline void AddInstruction(std::unique_ptr<Instruction> inst);
+
+  // Appends an output to this graph.
+  inline void AddOutput(std::unique_ptr<Instruction> inst);
+
+  // Saves the given graph end instruction.
+  void SetGraphEnd(std::unique_ptr<Instruction> end_inst);
+
+  // Returns the given graph end instruction.
+  inline Instruction* EndInst() { return end_inst_.get(); }
+  inline const Instruction* EndInst() const { return end_inst_.get(); }
+
+  // Returns graph's id
+  inline uint32_t result_id() const { return def_inst_->result_id(); }
+
+  // Returns graph's return type id
+  inline uint32_t type_id() const { return def_inst_->type_id(); }
+
+  // Return a read-only reference to the instructions that define the body of
+  // the graph.
+  const std::vector<std::unique_ptr<Instruction>>& instructions() const {
+    return insts_;
+  }
+
+  // Return a read-only reference to the instructions that define the inputs
+  // of the graph.
+  const std::vector<std::unique_ptr<Instruction>>& inputs() const {
+    return inputs_;
+  }
+
+  // Return a read-only reference to the instructions that define the outputs
+  // of the graph.
+  const std::vector<std::unique_ptr<Instruction>>& outputs() const {
+    return outputs_;
+  }
+
+  // Runs the given function |f| on instructions in this graph, in order,
+  // and optionally on debug line instructions that might precede them and
+  // non-semantic instructions that succceed the function.
+  void ForEachInst(const std::function<void(Instruction*)>& f,
+                   bool run_on_debug_line_insts = false,
+                   bool run_on_non_semantic_insts = false);
+  void ForEachInst(const std::function<void(const Instruction*)>& f,
+                   bool run_on_debug_line_insts = false,
+                   bool run_on_non_semantic_insts = false) const;
+
+ private:
+  // The OpGraph instruction that begins the definition of this graph.
+  std::unique_ptr<Instruction> def_inst_;
+  // All inputs to this graph.
+  std::vector<std::unique_ptr<Instruction>> inputs_;
+  // All instructions describing this graph
+  std::vector<std::unique_ptr<Instruction>> insts_;
+  // All outputs of this graph.
+  std::vector<std::unique_ptr<Instruction>> outputs_;
+  // The OpGraphEnd instruction.
+  std::unique_ptr<Instruction> end_inst_;
+};
+
+inline Graph::Graph(std::unique_ptr<Instruction> def_inst)
+    : def_inst_(std::move(def_inst)) {}
+
+inline void Graph::AddInput(std::unique_ptr<Instruction> inst) {
+  inputs_.emplace_back(std::move(inst));
+}
+
+inline void Graph::AddInstruction(std::unique_ptr<Instruction> inst) {
+  insts_.emplace_back(std::move(inst));
+}
+
+inline void Graph::AddOutput(std::unique_ptr<Instruction> inst) {
+  outputs_.emplace_back(std::move(inst));
+}
+
+inline void Graph::SetGraphEnd(std::unique_ptr<Instruction> end_inst) {
+  end_inst_ = std::move(end_inst);
+}
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // SOURCE_OPT_GRAPH_H_
diff --git a/source/opt/graphics_robust_access_pass.cpp b/source/opt/graphics_robust_access_pass.cpp
index e765c39..15076ec 100644
--- a/source/opt/graphics_robust_access_pass.cpp
+++ b/source/opt/graphics_robust_access_pass.cpp
@@ -283,9 +283,14 @@
   // use 0 for %min_value).
   auto clamp_index = [&inst, type_mgr, this, &replace_index](
                          uint32_t operand_index, Instruction* old_value,
-                         Instruction* min_value, Instruction* max_value) {
+                         Instruction* min_value,
+                         Instruction* max_value) -> spv_result_t {
     auto* clamp_inst =
         MakeSClampInst(*type_mgr, old_value, min_value, max_value, &inst);
+    if (clamp_inst == nullptr) {
+      Fail();
+      return SPV_ERROR_INTERNAL;
+    }
     return replace_index(operand_index, clamp_inst);
   };
 
@@ -304,7 +309,11 @@
 
     if (count <= 1) {
       // Replace the index with 0.
-      return replace_index(operand_index, GetValueForType(0, index_type));
+      Instruction* new_value = GetValueForType(0, index_type);
+      if (new_value == nullptr) {
+        return Fail();
+      }
+      return replace_index(operand_index, new_value);
     }
 
     uint64_t maxval = count - 1;
@@ -318,8 +327,15 @@
     // Determine the type for |maxval|.
     uint32_t next_id = context()->module()->IdBound();
     analysis::Integer signed_type_for_query(maxval_width, true);
-    auto* maxval_type =
-        type_mgr->GetRegisteredType(&signed_type_for_query)->AsInteger();
+    auto* maxval_type_registered =
+        type_mgr->GetRegisteredType(&signed_type_for_query);
+    if (maxval_type_registered == nullptr) {
+      return Fail();
+    }
+    auto* maxval_type = maxval_type_registered->AsInteger();
+    if (maxval_type == nullptr) {
+      return Fail();
+    }
     if (next_id != context()->module()->IdBound()) {
       module_status_.modified = true;
     }
@@ -352,15 +368,22 @@
         value = int_index_constant->GetS64BitValue();
       }
       if (value < 0) {
-        return replace_index(operand_index, GetValueForType(0, index_type));
+        Instruction* new_value = GetValueForType(0, index_type);
+        if (new_value == nullptr) {
+          return Fail();
+        }
+        return replace_index(operand_index, new_value);
       } else if (uint64_t(value) <= maxval) {
         // Nothing to do.
         return SPV_SUCCESS;
       } else {
         // Replace with maxval.
         assert(count > 0);  // Already took care of this case above.
-        return replace_index(operand_index,
-                             GetValueForType(maxval, maxval_type));
+        Instruction* new_value = GetValueForType(maxval, maxval_type);
+        if (new_value == nullptr) {
+          return Fail();
+        }
+        return replace_index(operand_index, new_value);
       }
     } else {
       // Generate a clamp instruction.
@@ -389,6 +412,9 @@
         }
         index_inst = WidenInteger(index_type->IsSigned(), maxval_width,
                                   index_inst, &inst);
+        if (index_inst == nullptr) {
+          return Fail();
+        }
       }
 
       // Finally, clamp the index.
@@ -438,28 +464,51 @@
       if (index_type->width() < target_width) {
         // Access chain indices are treated as signed integers.
         index_inst = WidenInteger(true, target_width, index_inst, &inst);
+        if (index_inst == nullptr) {
+          return Fail();
+        }
       } else if (count_type->width() < target_width) {
         // Assume type sizes are treated as unsigned.
         count_inst = WidenInteger(false, target_width, count_inst, &inst);
+        if (count_inst == nullptr) {
+          return Fail();
+        }
       }
       // Compute count - 1.
       // It doesn't matter if 1 is signed or unsigned.
       auto* one = GetValueForType(1, wider_type);
-      auto* count_minus_1 = InsertInst(
-          &inst, spv::Op::OpISub, type_mgr->GetId(wider_type), TakeNextId(),
-          {{SPV_OPERAND_TYPE_ID, {count_inst->result_id()}},
-           {SPV_OPERAND_TYPE_ID, {one->result_id()}}});
+      if (!one) {
+        return Fail();
+      }
+      auto* count_minus_1 =
+          InsertInst(&inst, spv::Op::OpISub, type_mgr->GetId(wider_type),
+                     context()->TakeNextId(),
+                     {{SPV_OPERAND_TYPE_ID, {count_inst->result_id()}},
+                      {SPV_OPERAND_TYPE_ID, {one->result_id()}}});
+      if (count_minus_1 == nullptr) {
+        return Fail();
+      }
       auto* zero = GetValueForType(0, wider_type);
+      if (!zero) {
+        return Fail();
+      }
       // Make sure we clamp to an upper bound that is at most the signed max
       // for the target type.
       const uint64_t max_signed_value =
           ((uint64_t(1) << (target_width - 1)) - 1);
+      Instruction* max_signed_inst =
+          GetValueForType(max_signed_value, wider_type);
+      if (!max_signed_inst) {
+        return Fail();
+      }
       // Use unsigned-min to ensure that the result is always non-negative.
       // That ensures we satisfy the invariant for SClamp, where the "min"
       // argument we give it (zero), is no larger than the third argument.
       auto* upper_bound =
-          MakeUMinInst(*type_mgr, count_minus_1,
-                       GetValueForType(max_signed_value, wider_type), &inst);
+          MakeUMinInst(*type_mgr, count_minus_1, max_signed_inst, &inst);
+      if (upper_bound == nullptr) {
+        return Fail();
+      }
       // Now clamp the index to this upper bound.
       return clamp_index(operand_index, index_inst, zero, upper_bound);
     }
@@ -485,7 +534,7 @@
       case spv::Op::OpTypeVector:  // Use component count
       {
         const uint32_t count = pointee_type->GetSingleWordOperand(2);
-        clamp_to_literal_count(idx, count);
+        if (clamp_to_literal_count(idx, count) != SPV_SUCCESS) return;
         pointee_type = GetDef(pointee_type->GetSingleWordOperand(1));
       } break;
 
@@ -493,7 +542,7 @@
         // The array length can be a spec constant, so go through the general
         // case.
         Instruction* array_len = GetDef(pointee_type->GetSingleWordOperand(2));
-        clamp_to_count(idx, array_len);
+        if (clamp_to_count(idx, array_len) != SPV_SUCCESS) return;
         pointee_type = GetDef(pointee_type->GetSingleWordOperand(1));
       } break;
 
@@ -537,7 +586,7 @@
         if (!array_len) {  // We've already signaled an error.
           return;
         }
-        clamp_to_count(idx, array_len);
+        if (clamp_to_count(idx, array_len) != SPV_SUCCESS) return;
         if (module_status_.failed) return;
         pointee_type = GetDef(pointee_type->GetSingleWordOperand(1));
       } break;
@@ -563,7 +612,10 @@
     }
     if (module_status_.glsl_insts_id == 0) {
       // Make a new import instruction.
-      module_status_.glsl_insts_id = TakeNextId();
+      module_status_.glsl_insts_id = context()->TakeNextId();
+      if (module_status_.glsl_insts_id == 0) {
+        return 0;
+      }
       std::vector<uint32_t> words = spvtools::utils::MakeVector(glsl);
       auto import_inst = MakeUnique<Instruction>(
           context(), spv::Op::OpExtInstImport, 0, module_status_.glsl_insts_id,
@@ -602,7 +654,10 @@
   auto* type_mgr = context()->get_type_mgr();
   auto* unsigned_type = type_mgr->GetRegisteredType(&unsigned_type_for_query);
   auto type_id = context()->get_type_mgr()->GetId(unsigned_type);
-  auto conversion_id = TakeNextId();
+  auto conversion_id = context()->TakeNextId();
+  if (conversion_id == 0) {
+    return nullptr;
+  }
   auto* conversion = InsertInst(
       before_inst, (sign_extend ? spv::Op::OpSConvert : spv::Op::OpUConvert),
       type_id, conversion_id, {{SPV_OPERAND_TYPE_ID, {value->result_id()}}});
@@ -616,7 +671,13 @@
   // the function so we force a deterministic ordering in case both of them need
   // to take a new ID.
   const uint32_t glsl_insts_id = GetGlslInsts();
-  uint32_t smin_id = TakeNextId();
+  if (glsl_insts_id == 0) {
+    return nullptr;
+  }
+  uint32_t smin_id = context()->TakeNextId();
+  if (smin_id == 0) {
+    return nullptr;
+  }
   const auto xwidth = tm.GetType(x->type_id())->AsInteger()->width();
   const auto ywidth = tm.GetType(y->type_id())->AsInteger()->width();
   assert(xwidth == ywidth);
@@ -640,7 +701,13 @@
   // the function so we force a deterministic ordering in case both of them need
   // to take a new ID.
   const uint32_t glsl_insts_id = GetGlslInsts();
-  uint32_t clamp_id = TakeNextId();
+  if (glsl_insts_id == 0) {
+    return nullptr;
+  }
+  uint32_t clamp_id = context()->TakeNextId();
+  if (clamp_id == 0) {
+    return nullptr;
+  }
   const auto xwidth = tm.GetType(x->type_id())->AsInteger()->width();
   const auto minwidth = tm.GetType(min->type_id())->AsInteger()->width();
   const auto maxwidth = tm.GetType(max->type_id())->AsInteger()->width();
@@ -755,7 +822,11 @@
               base_ptr_type->storage_class());
 
           // Create the instruction and insert it.
-          const auto new_access_chain_id = TakeNextId();
+          const auto new_access_chain_id = context()->TakeNextId();
+          if (new_access_chain_id == 0) {
+            Fail();
+            return nullptr;
+          }
           auto* new_access_chain =
               InsertInst(current_access_chain, current_access_chain->opcode(),
                          new_access_chain_type_id, new_access_chain_id, ops);
@@ -784,7 +855,11 @@
       uint32_t(struct_type->element_types().size() - 1);
   // Create the length-of-array instruction before the original access chain,
   // but after the generation of the pointer to the struct.
-  const auto array_len_id = TakeNextId();
+  const auto array_len_id = context()->TakeNextId();
+  if (array_len_id == 0) {
+    Fail();
+    return nullptr;
+  }
   analysis::Integer uint_type_for_query(32, false);
   auto* uint_type = type_mgr->GetRegisteredType(&uint_type_for_query);
   auto* array_len = InsertInst(
@@ -935,12 +1010,18 @@
     return type_mgr->GetRegisteredType(&proposed);
   }();
 
-  const uint32_t image_id = TakeNextId();
+  const uint32_t image_id = context()->TakeNextId();
+  if (image_id == 0) {
+    return Fail();
+  }
   auto* image =
       InsertInst(image_texel_pointer, spv::Op::OpLoad, image_type_id, image_id,
                  {{SPV_OPERAND_TYPE_ID, {image_ptr->result_id()}}});
 
-  const uint32_t query_size_id = TakeNextId();
+  const uint32_t query_size_id = context()->TakeNextId();
+  if (query_size_id == 0) {
+    return Fail();
+  }
   auto* query_size =
       InsertInst(image_texel_pointer, spv::Op::OpImageQuerySize,
                  type_mgr->GetTypeInstruction(query_size_type), query_size_id,
@@ -968,7 +1049,10 @@
         query_size_type, {component_1_id, component_1_id, component_6_id});
     auto* multiplicand_inst =
         constant_mgr->GetDefiningInstruction(multiplicand);
-    const auto query_size_including_faces_id = TakeNextId();
+    const auto query_size_including_faces_id = context()->TakeNextId();
+    if (query_size_including_faces_id == 0) {
+      return Fail();
+    }
     query_size_including_faces = InsertInst(
         image_texel_pointer, spv::Op::OpIMul,
         type_mgr->GetTypeInstruction(query_size_type),
@@ -992,7 +1076,10 @@
                 query_size_type,
                 std::vector<uint32_t>(query_num_components, component_0_id));
 
-  const uint32_t query_max_including_faces_id = TakeNextId();
+  const uint32_t query_max_including_faces_id = context()->TakeNextId();
+  if (query_max_including_faces_id == 0) {
+    return Fail();
+  }
   auto* query_max_including_faces = InsertInst(
       image_texel_pointer, spv::Op::OpISub,
       type_mgr->GetTypeInstruction(query_size_type),
@@ -1005,18 +1092,27 @@
   auto* clamp_coord = MakeSClampInst(
       *type_mgr, coord, constant_mgr->GetDefiningInstruction(coordinate_0),
       query_max_including_faces, image_texel_pointer);
+  if (clamp_coord == nullptr) {
+    return Fail();
+  }
   image_texel_pointer->SetInOperand(1, {clamp_coord->result_id()});
 
   // Clamp the sample index
   if (multisampled) {
     // Get the sample count via OpImageQuerySamples
-    const auto query_samples_id = TakeNextId();
+    const auto query_samples_id = context()->TakeNextId();
+    if (query_samples_id == 0) {
+      return Fail();
+    }
     auto* query_samples = InsertInst(
         image_texel_pointer, spv::Op::OpImageQuerySamples,
         constant_mgr->GetDefiningInstruction(component_0)->type_id(),
         query_samples_id, {{SPV_OPERAND_TYPE_ID, {image->result_id()}}});
 
-    const auto max_samples_id = TakeNextId();
+    const auto max_samples_id = context()->TakeNextId();
+    if (max_samples_id == 0) {
+      return Fail();
+    }
     auto* max_samples = InsertInst(image_texel_pointer, spv::Op::OpImageQuerySamples,
                                    query_samples->type_id(), max_samples_id,
                                    {{SPV_OPERAND_TYPE_ID, {query_samples_id}},
@@ -1025,6 +1121,9 @@
     auto* clamp_samples = MakeSClampInst(
         *type_mgr, samples, constant_mgr->GetDefiningInstruction(coordinate_0),
         max_samples, image_texel_pointer);
+    if (clamp_samples == nullptr) {
+      return Fail();
+    }
     image_texel_pointer->SetInOperand(2, {clamp_samples->result_id()});
 
   } else {
@@ -1041,6 +1140,9 @@
 opt::Instruction* GraphicsRobustAccessPass::InsertInst(
     opt::Instruction* where_inst, spv::Op opcode, uint32_t type_id,
     uint32_t result_id, const Instruction::OperandList& operands) {
+  if (result_id == 0) {
+    return nullptr;
+  }
   module_status_.modified = true;
   auto* result = where_inst->InsertBefore(
       MakeUnique<Instruction>(context(), opcode, type_id, result_id, operands));
diff --git a/source/opt/if_conversion.cpp b/source/opt/if_conversion.cpp
index 5912cf1..2b71626 100644
--- a/source/opt/if_conversion.cpp
+++ b/source/opt/if_conversion.cpp
@@ -126,6 +126,7 @@
           condition = SplatCondition(vec_data_ty, condition, &builder);
         }
 
+        // TODO(1841): Handle id overflow.
         Instruction* select = builder.AddSelect(phi->type_id(), condition,
                                                 true_value->result_id(),
                                                 false_value->result_id());
@@ -205,6 +206,7 @@
   uint32_t bool_vec_id =
       context()->get_type_mgr()->GetTypeInstruction(&bool_vec_ty);
   std::vector<uint32_t> ids(vec_data_ty->element_count(), cond);
+  // TODO(1841): Handle id overflow.
   return builder->AddCompositeConstruct(bool_vec_id, ids)->result_id();
 }
 
diff --git a/source/opt/inline_exhaustive_pass.cpp b/source/opt/inline_exhaustive_pass.cpp
index bef4501..c04fb04 100644
--- a/source/opt/inline_exhaustive_pass.cpp
+++ b/source/opt/inline_exhaustive_pass.cpp
@@ -16,6 +16,7 @@
 
 #include "source/opt/inline_exhaustive_pass.h"
 
+#include <iterator>
 #include <utility>
 
 namespace spvtools {
@@ -27,6 +28,10 @@
   for (auto bi = func->begin(); bi != func->end(); ++bi) {
     for (auto ii = bi->begin(); ii != bi->end();) {
       if (IsInlinableFunctionCall(&*ii)) {
+        // Save instruction before the call to avoid redundant re-scanning.
+        Instruction* prev_inst =
+            (ii == bi->begin()) ? nullptr : &*std::prev(ii);
+
         // Inline call.
         std::vector<std::unique_ptr<BasicBlock>> newBlocks;
         std::vector<std::unique_ptr<Instruction>> newVars;
@@ -47,14 +52,19 @@
         // Insert new function variables.
         if (newVars.size() > 0)
           func->begin()->begin().InsertBefore(std::move(newVars));
-        // Restart inlining at beginning of calling block.
-        ii = bi->begin();
+        // Restart inlining at the first instruction of the inlined code.
+        ii = prev_inst ? ++InstructionList::iterator(prev_inst) : bi->begin();
         modified = true;
       } else {
         ++ii;
       }
     }
   }
+
+  if (modified) {
+    FixDebugDeclares(func);
+  }
+
   return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
 }
 
diff --git a/source/opt/inline_opaque_pass.cpp b/source/opt/inline_opaque_pass.cpp
index 90a4c22..0831c25 100644
--- a/source/opt/inline_opaque_pass.cpp
+++ b/source/opt/inline_opaque_pass.cpp
@@ -16,6 +16,7 @@
 
 #include "source/opt/inline_opaque_pass.h"
 
+#include <iterator>
 #include <utility>
 
 namespace spvtools {
@@ -67,6 +68,10 @@
   for (auto bi = func->begin(); bi != func->end(); ++bi) {
     for (auto ii = bi->begin(); ii != bi->end();) {
       if (IsInlinableFunctionCall(&*ii) && HasOpaqueArgsOrReturn(&*ii)) {
+        // Save instruction before the call to avoid redundant re-scanning.
+        Instruction* prev_inst =
+            (ii == bi->begin()) ? nullptr : &*std::prev(ii);
+
         // Inline call.
         std::vector<std::unique_ptr<BasicBlock>> newBlocks;
         std::vector<std::unique_ptr<Instruction>> newVars;
@@ -79,18 +84,27 @@
         if (newBlocks.size() > 1) UpdateSucceedingPhis(newBlocks);
         // Replace old calling block with new block(s).
         bi = bi.Erase();
+
+        for (auto& bb : newBlocks) {
+          bb->SetParent(func);
+        }
         bi = bi.InsertBefore(&newBlocks);
         // Insert new function variables.
         if (newVars.size() > 0)
           func->begin()->begin().InsertBefore(std::move(newVars));
-        // Restart inlining at beginning of calling block.
-        ii = bi->begin();
+        // Restart inlining at the first instruction of the inlined code.
+        ii = prev_inst ? ++InstructionList::iterator(prev_inst) : bi->begin();
         modified = true;
       } else {
         ++ii;
       }
     }
   }
+
+  if (modified) {
+    FixDebugDeclares(func);
+  }
+
   return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
 }
 
diff --git a/source/opt/inline_pass.cpp b/source/opt/inline_pass.cpp
index 3186433..dbbb31f 100644
--- a/source/opt/inline_pass.cpp
+++ b/source/opt/inline_pass.cpp
@@ -30,6 +30,8 @@
 constexpr int kSpvFunctionCallFunctionId = 2;
 constexpr int kSpvFunctionCallArgumentId = 3;
 constexpr int kSpvReturnValueId = 0;
+constexpr int kSpvDebugDeclareVarInIdx = 3;
+constexpr int kSpvAccessChainBaseInIdx = 0;
 }  // namespace
 
 uint32_t InlinePass::AddPointerToType(uint32_t type_id,
@@ -422,8 +424,8 @@
   while (callee_inst_itr != callee_first_block->end()) {
     // Don't inline function definition links, the calling function is not a
     // definition.
-    if (callee_inst_itr->GetShader100DebugOpcode() ==
-        NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
+    if (callee_inst_itr->GetShaderDebugOpcode() ==
+        NonSemanticShaderDebugInfoDebugFunctionDefinition) {
       ++callee_inst_itr;
       continue;
     }
@@ -460,8 +462,8 @@
          ++inst_itr) {
       // Don't inline function definition links, the calling function is not a
       // definition
-      if (inst_itr->GetShader100DebugOpcode() ==
-          NonSemanticShaderDebugInfo100DebugFunctionDefinition)
+      if (inst_itr->GetShaderDebugOpcode() ==
+          NonSemanticShaderDebugInfoDebugFunctionDefinition)
         continue;
       if (!InlineSingleInstruction(
               callee2caller, new_blk_ptr.get(), &*inst_itr,
@@ -858,5 +860,92 @@
 
 InlinePass::InlinePass() {}
 
+void InlinePass::FixDebugDeclares(Function* func) {
+  std::map<uint32_t, Instruction*> access_chains;
+  std::vector<Instruction*> debug_declare_insts;
+
+  func->ForEachInst([&access_chains, &debug_declare_insts](Instruction* inst) {
+    if (inst->opcode() == spv::Op::OpAccessChain) {
+      access_chains[inst->result_id()] = inst;
+    }
+    if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) {
+      debug_declare_insts.push_back(inst);
+    }
+  });
+
+  for (auto& inst : debug_declare_insts) {
+    FixDebugDeclare(inst, access_chains);
+  }
+}
+
+void InlinePass::FixDebugDeclare(
+    Instruction* dbg_declare_inst,
+    const std::map<uint32_t, Instruction*>& access_chains) {
+  do {
+    uint32_t var_id =
+        dbg_declare_inst->GetSingleWordInOperand(kSpvDebugDeclareVarInIdx);
+
+    // The def-use chains are not kept up to date while inlining, so we need to
+    // get the variable by traversing the functions.
+    auto it = access_chains.find(var_id);
+    if (it == access_chains.end()) {
+      return;
+    }
+    Instruction* access_chain = it->second;
+
+    // If the variable id in the debug declare is an access chain, it is
+    // invalid. it needs to be fixed up. The debug declare will be updated so
+    // that its Var operand becomes the base of the access chain. The indexes of
+    // the access chain are prepended before the indexes of the debug declare.
+
+    // DebugDeclare Indexes must be constant integers. If any access chain
+    // index is non-constant (e.g. the result of an OpLoad), we cannot
+    // produce a valid DebugDeclare. Kill it rather than emit invalid SPIR-V.
+    bool has_non_constant_index = false;
+    for (uint32_t i = kSpvAccessChainBaseInIdx + 1;
+         i < access_chain->NumInOperands(); ++i) {
+      uint32_t idx_id = access_chain->GetSingleWordInOperand(i);
+      bool found_constant = false;
+      for (auto& inst : context()->module()->types_values()) {
+        if (inst.result_id() == idx_id) {
+          found_constant = spvOpcodeIsConstant(inst.opcode());
+          break;
+        }
+      }
+      if (!found_constant) {
+        has_non_constant_index = true;
+        break;
+      }
+    }
+    if (has_non_constant_index) {
+      context()->KillInst(dbg_declare_inst);
+      return;
+    }
+
+    std::vector<Operand> operands;
+    for (int i = 0; i < kSpvDebugDeclareVarInIdx; i++) {
+      operands.push_back(dbg_declare_inst->GetInOperand(i));
+    }
+
+    uint32_t access_chain_base =
+        access_chain->GetSingleWordInOperand(kSpvAccessChainBaseInIdx);
+    operands.push_back(Operand(SPV_OPERAND_TYPE_ID, {access_chain_base}));
+    operands.push_back(
+        dbg_declare_inst->GetInOperand(kSpvDebugDeclareVarInIdx + 1));
+
+    for (uint32_t i = kSpvAccessChainBaseInIdx + 1;
+         i < access_chain->NumInOperands(); ++i) {
+      operands.push_back(access_chain->GetInOperand(i));
+    }
+
+    for (uint32_t i = kSpvDebugDeclareVarInIdx + 2;
+         i < dbg_declare_inst->NumInOperands(); ++i) {
+      operands.push_back(dbg_declare_inst->GetInOperand(i));
+    }
+
+    dbg_declare_inst->SetInOperands(std::move(operands));
+  } while (true);
+}
+
 }  // namespace opt
 }  // namespace spvtools
diff --git a/source/opt/inline_pass.h b/source/opt/inline_pass.h
index 1c9d60e..7bea31d 100644
--- a/source/opt/inline_pass.h
+++ b/source/opt/inline_pass.h
@@ -150,6 +150,12 @@
   // Initialize state for optimization of |module|
   void InitializeInline();
 
+  // Fixes invalid debug declare functions in `func` that were caused by
+  // inlining. This function cannot be called while in the middle of inlining
+  // because it needs to be able to find the instructions that define an
+  // id.
+  void FixDebugDeclares(Function* func);
+
   // Map from function's result id to function.
   std::unordered_map<uint32_t, Function*> id2function_;
 
@@ -241,6 +247,11 @@
   // structural dominance.
   void UpdateSingleBlockLoopContinueTarget(
       uint32_t new_id, std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
+
+  // Replaces the `var` operand of `dbg_declare_inst` and updates the indexes
+  // accordingly, if it is the id of an access chain in `access_chains`.
+  void FixDebugDeclare(Instruction* dbg_declare_inst,
+                       const std::map<uint32_t, Instruction*>& access_chains);
 };
 
 }  // namespace opt
diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp
index aa4ae26..346eed6 100644
--- a/source/opt/instruction.cpp
+++ b/source/opt/instruction.cpp
@@ -32,7 +32,7 @@
 constexpr uint32_t kVariableStorageClassIndex = 0;
 constexpr uint32_t kTypeImageSampledIndex = 5;
 
-// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100
+// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo
 // extension instructions.
 constexpr uint32_t kExtInstSetIdInIdx = 0;
 constexpr uint32_t kExtInstInstructionInIdx = 1;
@@ -168,7 +168,13 @@
   clone->dbg_line_insts_ = dbg_line_insts_;
   for (auto& i : clone->dbg_line_insts_) {
     i.unique_id_ = c->TakeNextUniqueId();
-    if (i.IsDebugLineInst()) i.SetResultId(c->TakeNextId());
+    if (i.IsDebugLineInst()) {
+      uint32_t new_id = c->TakeNextId();
+      if (new_id == 0) {
+        return nullptr;
+      }
+      i.SetResultId(new_id);
+    }
   }
   clone->dbg_scope_ = dbg_scope_;
   return clone;
@@ -246,12 +252,12 @@
     switch (base_inst->opcode()) {
       case spv::Op::OpAccessChain:
       case spv::Op::OpInBoundsAccessChain:
+      case spv::Op::OpUntypedAccessChainKHR:
       case spv::Op::OpPtrAccessChain:
       case spv::Op::OpInBoundsPtrAccessChain:
       case spv::Op::OpImageTexelPointer:
       case spv::Op::OpCopyObject:
-        // All of these instructions have the base pointer use a base pointer
-        // in in-operand 0.
+        // All of these instructions have their base pointer in in-operand 0.
         base = base_inst->GetSingleWordInOperand(0);
         base_inst = context()->get_def_use_mgr()->GetDef(base);
         break;
@@ -547,45 +553,57 @@
   clear_dbg_line_insts();
 }
 
-void Instruction::UpdateDebugInfoFrom(const Instruction* from) {
-  if (from == nullptr) return;
+bool Instruction::UpdateDebugInfoFrom(const Instruction* from,
+                                      const Instruction* line) {
+  if (from == nullptr) return true;
   ClearDbgLineInsts();
-  if (!from->dbg_line_insts().empty())
-    AddDebugLine(&from->dbg_line_insts().back());
+  const Instruction* fromLine = line != nullptr ? line : from;
+  if (!fromLine->dbg_line_insts().empty()) {
+    if (!AddDebugLine(&fromLine->dbg_line_insts().back())) {
+      return false;
+    }
+  }
   SetDebugScope(from->GetDebugScope());
   if (!IsLineInst() &&
       context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) {
     context()->get_debug_info_mgr()->AnalyzeDebugInst(this);
   }
+  return true;
 }
 
-void Instruction::AddDebugLine(const Instruction* inst) {
+bool Instruction::AddDebugLine(const Instruction* inst) {
   dbg_line_insts_.push_back(*inst);
   dbg_line_insts_.back().unique_id_ = context()->TakeNextUniqueId();
-  if (inst->IsDebugLineInst())
-    dbg_line_insts_.back().SetResultId(context_->TakeNextId());
+  if (inst->IsDebugLineInst()) {
+    uint32_t new_id = context()->TakeNextId();
+    if (new_id == 0) {
+      return false;
+    }
+    dbg_line_insts_.back().SetResultId(new_id);
+  }
   if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse))
     context()->get_def_use_mgr()->AnalyzeInstDefUse(&dbg_line_insts_.back());
+  return true;
 }
 
 bool Instruction::IsDebugLineInst() const {
-  NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode();
-  return ((ext_opt == NonSemanticShaderDebugInfo100DebugLine) ||
-          (ext_opt == NonSemanticShaderDebugInfo100DebugNoLine));
+  NonSemanticShaderDebugInfoInstructions ext_opt = GetShaderDebugOpcode();
+  return ((ext_opt == NonSemanticShaderDebugInfoDebugLine) ||
+          (ext_opt == NonSemanticShaderDebugInfoDebugNoLine));
 }
 
 bool Instruction::IsLineInst() const { return IsLine() || IsNoLine(); }
 
 bool Instruction::IsLine() const {
   if (opcode() == spv::Op::OpLine) return true;
-  NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode();
-  return ext_opt == NonSemanticShaderDebugInfo100DebugLine;
+  NonSemanticShaderDebugInfoInstructions ext_opt = GetShaderDebugOpcode();
+  return ext_opt == NonSemanticShaderDebugInfoDebugLine;
 }
 
 bool Instruction::IsNoLine() const {
   if (opcode() == spv::Op::OpNoLine) return true;
-  NonSemanticShaderDebugInfo100Instructions ext_opt = GetShader100DebugOpcode();
-  return ext_opt == NonSemanticShaderDebugInfo100DebugNoLine;
+  NonSemanticShaderDebugInfoInstructions ext_opt = GetShaderDebugOpcode();
+  return ext_opt == NonSemanticShaderDebugInfoDebugNoLine;
 }
 
 Instruction* Instruction::InsertBefore(std::unique_ptr<Instruction>&& inst) {
@@ -656,7 +674,8 @@
 }
 
 OpenCLDebugInfo100Instructions Instruction::GetOpenCL100DebugOpcode() const {
-  if (opcode() != spv::Op::OpExtInst) {
+  if (opcode() != spv::Op::OpExtInst &&
+      opcode() != spv::Op::OpExtInstWithForwardRefsKHR) {
     return OpenCLDebugInfo100InstructionsMax;
   }
 
@@ -673,38 +692,40 @@
       GetSingleWordInOperand(kExtInstInstructionInIdx));
 }
 
-NonSemanticShaderDebugInfo100Instructions Instruction::GetShader100DebugOpcode()
+NonSemanticShaderDebugInfoInstructions Instruction::GetShaderDebugOpcode()
     const {
-  if (opcode() != spv::Op::OpExtInst) {
-    return NonSemanticShaderDebugInfo100InstructionsMax;
+  if (opcode() != spv::Op::OpExtInst &&
+      opcode() != spv::Op::OpExtInstWithForwardRefsKHR) {
+    return NonSemanticShaderDebugInfoInstructionsMax;
   }
 
-  if (!context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) {
-    return NonSemanticShaderDebugInfo100InstructionsMax;
+  if (!context()->get_feature_mgr()->GetExtInstImportId_ShaderDebugInfo()) {
+    return NonSemanticShaderDebugInfoInstructionsMax;
   }
 
   if (GetSingleWordInOperand(kExtInstSetIdInIdx) !=
-      context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo()) {
-    return NonSemanticShaderDebugInfo100InstructionsMax;
+      context()->get_feature_mgr()->GetExtInstImportId_ShaderDebugInfo()) {
+    return NonSemanticShaderDebugInfoInstructionsMax;
   }
 
   uint32_t opcode = GetSingleWordInOperand(kExtInstInstructionInIdx);
-  if (opcode >= NonSemanticShaderDebugInfo100InstructionsMax) {
-    return NonSemanticShaderDebugInfo100InstructionsMax;
+  if (opcode >= NonSemanticShaderDebugInfoInstructionsMax) {
+    return NonSemanticShaderDebugInfoInstructionsMax;
   }
 
-  return NonSemanticShaderDebugInfo100Instructions(opcode);
+  return NonSemanticShaderDebugInfoInstructions(opcode);
 }
 
 CommonDebugInfoInstructions Instruction::GetCommonDebugOpcode() const {
-  if (opcode() != spv::Op::OpExtInst) {
+  if (opcode() != spv::Op::OpExtInst &&
+      opcode() != spv::Op::OpExtInstWithForwardRefsKHR) {
     return CommonDebugInfoInstructionsMax;
   }
 
   const uint32_t opencl_set_id =
       context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo();
   const uint32_t shader_set_id =
-      context()->get_feature_mgr()->GetExtInstImportId_Shader100DebugInfo();
+      context()->get_feature_mgr()->GetExtInstImportId_ShaderDebugInfo();
 
   if (!opencl_set_id && !shader_set_id) {
     return CommonDebugInfoInstructionsMax;
@@ -769,7 +790,7 @@
   // Even if the type of the instruction is foldable, its operands may not be
   // foldable (e.g., comparisons of 64bit types).  Check that all operand types
   // are foldable before accepting the instruction.
-  return WhileEachInOperand([&folder, this](const uint32_t* op_id) {
+  return WhileEachInId([&folder, this](const uint32_t* op_id) {
     Instruction* def_inst = context()->get_def_use_mgr()->GetDef(*op_id);
     Instruction* def_inst_type =
         context()->get_def_use_mgr()->GetDef(def_inst->type_id());
@@ -791,7 +812,7 @@
   // Even if the type of the instruction is foldable, its operands may not be
   // foldable (e.g., comparisons of 64bit types).  Check that all operand types
   // are foldable before accepting the instruction.
-  return WhileEachInOperand([&folder, this](const uint32_t* op_id) {
+  return WhileEachInId([&folder, this](const uint32_t* op_id) {
     Instruction* def_inst = context()->get_def_use_mgr()->GetDef(*op_id);
     Instruction* def_inst_type =
         context()->get_def_use_mgr()->GetDef(def_inst->type_id());
@@ -1034,6 +1055,12 @@
     return true;
   }
 
+  if (IsNonSemanticInstruction() &&
+      (GetShaderDebugOpcode() == NonSemanticShaderDebugInfoDebugDeclare ||
+       GetShaderDebugOpcode() == NonSemanticShaderDebugInfoDebugValue)) {
+    return true;
+  }
+
   switch (opcode()) {
     case spv::Op::OpDPdx:
     case spv::Op::OpDPdy:
diff --git a/source/opt/instruction.h b/source/opt/instruction.h
index c2617fb..9ab9f0e 100644
--- a/source/opt/instruction.h
+++ b/source/opt/instruction.h
@@ -22,7 +22,6 @@
 #include <utility>
 #include <vector>
 
-#include "NonSemanticShaderDebugInfo100.h"
 #include "OpenCLDebugInfo100.h"
 #include "source/binary.h"
 #include "source/common_debug_info.h"
@@ -35,6 +34,7 @@
 #include "source/util/small_vector.h"
 #include "source/util/string_utils.h"
 #include "spirv-tools/libspirv.h"
+#include "spirv/unified1/NonSemanticShaderDebugInfo.h"
 
 constexpr uint32_t kNoDebugScope = 0;
 constexpr uint32_t kNoInlinedAt = 0;
@@ -318,19 +318,19 @@
   inline void SetDebugScope(const DebugScope& scope);
   inline const DebugScope& GetDebugScope() const { return dbg_scope_; }
   // Add debug line inst. Renew result id if Debug[No]Line
-  void AddDebugLine(const Instruction* inst);
+  bool AddDebugLine(const Instruction* inst);
   // Updates DebugInlinedAt of DebugScope and OpLine.
   void UpdateDebugInlinedAt(uint32_t new_inlined_at);
   // Clear line-related debug instructions attached to this instruction
   // along with def-use entries.
   void ClearDbgLineInsts();
-  // Return true if Shader100:Debug[No]Line
+  // Return true if NSDI:Debug[No]Line
   bool IsDebugLineInst() const;
-  // Return true if Op[No]Line or Shader100:Debug[No]Line
+  // Return true if Op[No]Line or NSDI:Debug[No]Line
   bool IsLineInst() const;
-  // Return true if OpLine or Shader100:DebugLine
+  // Return true if OpLine or NSDI:DebugLine
   bool IsLine() const;
-  // Return true if OpNoLine or Shader100:DebugNoLine
+  // Return true if OpNoLine or NSDI:DebugNoLine
   bool IsNoLine() const;
   inline uint32_t GetDebugInlinedAt() const {
     return dbg_scope_.GetInlinedAt();
@@ -338,7 +338,8 @@
   // Updates lexical scope of DebugScope and OpLine.
   void UpdateLexicalScope(uint32_t scope);
   // Updates OpLine and DebugScope based on the information of |from|.
-  void UpdateDebugInfoFrom(const Instruction* from);
+  bool UpdateDebugInfoFrom(const Instruction* from,
+                           const Instruction* line = nullptr);
   // Remove the |index|-th operand
   void RemoveOperand(uint32_t index) {
     operands_.erase(operands_.begin() + index);
@@ -580,13 +581,13 @@
   // OpenCLDebugInfo100InstructionsMax.
   OpenCLDebugInfo100Instructions GetOpenCL100DebugOpcode() const;
 
-  // Returns debug opcode of an NonSemantic.Shader.DebugInfo.100 instruction. If
-  // it is not an NonSemantic.Shader.DebugInfo.100 instruction, just return
-  // NonSemanticShaderDebugInfo100InstructionsMax.
-  NonSemanticShaderDebugInfo100Instructions GetShader100DebugOpcode() const;
+  // Returns debug opcode of a NonSemantic.Shader.DebugInfo instruction. If
+  // it is not a NonSemantic.Shader.DebugInfo instruction, just return
+  // NonSemanticShaderDebugInfoInstructionsMax.
+  NonSemanticShaderDebugInfoInstructions GetShaderDebugOpcode() const;
 
   // Returns debug opcode of an OpenCL.100.DebugInfo or
-  // NonSemantic.Shader.DebugInfo.100 instruction. Since these overlap, we
+  // NonSemantic.Shader.DebugInfo instruction. Since these overlap, we
   // return the OpenCLDebugInfo code
   CommonDebugInfoInstructions GetCommonDebugOpcode() const;
 
@@ -595,10 +596,9 @@
     return GetOpenCL100DebugOpcode() != OpenCLDebugInfo100InstructionsMax;
   }
 
-  // Returns true if it is an NonSemantic.Shader.DebugInfo.100 instruction.
-  bool IsShader100DebugInstr() const {
-    return GetShader100DebugOpcode() !=
-           NonSemanticShaderDebugInfo100InstructionsMax;
+  // Returns true if it is a NonSemantic.Shader.DebugInfo instruction.
+  bool IsShaderDebugInstr() const {
+    return GetShaderDebugOpcode() != NonSemanticShaderDebugInfoInstructionsMax;
   }
   bool IsCommonDebugInstr() const {
     return GetCommonDebugOpcode() != CommonDebugInfoInstructionsMax;
diff --git a/source/opt/interface_var_sroa.cpp b/source/opt/interface_var_sroa.cpp
index 08477cb..8c0b91f 100644
--- a/source/opt/interface_var_sroa.cpp
+++ b/source/opt/interface_var_sroa.cpp
@@ -239,28 +239,34 @@
       });
 }
 
-bool InterfaceVariableScalarReplacement::ReplaceInterfaceVariableWithScalars(
+Pass::Status
+InterfaceVariableScalarReplacement::ReplaceInterfaceVariableWithScalars(
     Instruction* interface_var, Instruction* interface_var_type,
     uint32_t location, uint32_t component, uint32_t extra_array_length) {
-  NestedCompositeComponents scalar_interface_vars =
+  std::optional<NestedCompositeComponents> scalar_interface_vars =
       CreateScalarInterfaceVarsForReplacement(interface_var_type,
                                               GetStorageClass(interface_var),
                                               extra_array_length);
 
-  AddLocationAndComponentDecorations(scalar_interface_vars, &location,
+  if (!scalar_interface_vars) {
+    return Status::Failure;
+  }
+
+  AddLocationAndComponentDecorations(*scalar_interface_vars, &location,
                                      component);
   KillLocationAndComponentDecorations(interface_var->result_id());
 
-  if (!ReplaceInterfaceVarWith(interface_var, extra_array_length,
-                               scalar_interface_vars)) {
-    return false;
+  Status status = ReplaceInterfaceVarWith(interface_var, extra_array_length,
+                                          *scalar_interface_vars);
+  if (status == Status::Failure) {
+    return status;
   }
 
   context()->KillInst(interface_var);
-  return true;
+  return status;
 }
 
-bool InterfaceVariableScalarReplacement::ReplaceInterfaceVarWith(
+Pass::Status InterfaceVariableScalarReplacement::ReplaceInterfaceVarWith(
     Instruction* interface_var, uint32_t extra_array_length,
     const NestedCompositeComponents& scalar_interface_vars) {
   std::vector<Instruction*> users;
@@ -276,21 +282,24 @@
     // interface variable.
     for (uint32_t index = 0; index < extra_array_length; ++index) {
       std::unordered_map<Instruction*, Instruction*> loads_to_component_values;
-      if (!ReplaceComponentsOfInterfaceVarWith(
-              interface_var, users, scalar_interface_vars,
-              interface_var_component_indices, &index,
-              &loads_to_component_values,
-              &loads_for_access_chain_to_composites)) {
-        return false;
+      Status status = ReplaceComponentsOfInterfaceVarWith(
+          interface_var, users, scalar_interface_vars,
+          interface_var_component_indices, &index, &loads_to_component_values,
+          &loads_for_access_chain_to_composites);
+      if (status == Status::Failure) {
+        return Status::Failure;
       }
       AddComponentsToCompositesForLoads(loads_to_component_values,
                                         &loads_to_composites, 0);
     }
-  } else if (!ReplaceComponentsOfInterfaceVarWith(
-                 interface_var, users, scalar_interface_vars,
-                 interface_var_component_indices, nullptr, &loads_to_composites,
-                 &loads_for_access_chain_to_composites)) {
-    return false;
+  } else {
+    Status status = ReplaceComponentsOfInterfaceVarWith(
+        interface_var, users, scalar_interface_vars,
+        interface_var_component_indices, nullptr, &loads_to_composites,
+        &loads_for_access_chain_to_composites);
+    if (status == Status::Failure) {
+      return Status::Failure;
+    }
   }
 
   ReplaceLoadWithCompositeConstruct(context(), loads_to_composites);
@@ -298,7 +307,7 @@
                                     loads_for_access_chain_to_composites);
 
   KillInstructionsAndUsers(users);
-  return true;
+  return Status::SuccessWithChange;
 }
 
 void InterfaceVariableScalarReplacement::AddLocationAndComponentDecorations(
@@ -318,7 +327,8 @@
   }
 }
 
-bool InterfaceVariableScalarReplacement::ReplaceComponentsOfInterfaceVarWith(
+Pass::Status
+InterfaceVariableScalarReplacement::ReplaceComponentsOfInterfaceVarWith(
     Instruction* interface_var,
     const std::vector<Instruction*>& interface_var_users,
     const NestedCompositeComponents& scalar_interface_vars,
@@ -329,15 +339,16 @@
         loads_for_access_chain_to_composites) {
   if (!scalar_interface_vars.HasMultipleComponents()) {
     for (Instruction* interface_var_user : interface_var_users) {
-      if (!ReplaceComponentOfInterfaceVarWith(
-              interface_var, interface_var_user,
-              scalar_interface_vars.GetComponentVariable(),
-              interface_var_component_indices, extra_array_index,
-              loads_to_composites, loads_for_access_chain_to_composites)) {
-        return false;
+      Status status = ReplaceComponentOfInterfaceVarWith(
+          interface_var, interface_var_user,
+          scalar_interface_vars.GetComponentVariable(),
+          interface_var_component_indices, extra_array_index,
+          loads_to_composites, loads_for_access_chain_to_composites);
+      if (status == Status::Failure) {
+        return Status::Failure;
       }
     }
-    return true;
+    return Status::SuccessWithChange;
   }
   return ReplaceMultipleComponentsOfInterfaceVarWith(
       interface_var, interface_var_users, scalar_interface_vars.GetComponents(),
@@ -345,27 +356,28 @@
       loads_for_access_chain_to_composites);
 }
 
-bool InterfaceVariableScalarReplacement::
-    ReplaceMultipleComponentsOfInterfaceVarWith(
-        Instruction* interface_var,
-        const std::vector<Instruction*>& interface_var_users,
-        const std::vector<NestedCompositeComponents>& components,
-        std::vector<uint32_t>& interface_var_component_indices,
-        const uint32_t* extra_array_index,
-        std::unordered_map<Instruction*, Instruction*>* loads_to_composites,
-        std::unordered_map<Instruction*, Instruction*>*
-            loads_for_access_chain_to_composites) {
+Pass::Status
+InterfaceVariableScalarReplacement::ReplaceMultipleComponentsOfInterfaceVarWith(
+    Instruction* interface_var,
+    const std::vector<Instruction*>& interface_var_users,
+    const std::vector<NestedCompositeComponents>& components,
+    std::vector<uint32_t>& interface_var_component_indices,
+    const uint32_t* extra_array_index,
+    std::unordered_map<Instruction*, Instruction*>* loads_to_composites,
+    std::unordered_map<Instruction*, Instruction*>*
+        loads_for_access_chain_to_composites) {
   for (uint32_t i = 0; i < components.size(); ++i) {
     interface_var_component_indices.push_back(i);
     std::unordered_map<Instruction*, Instruction*> loads_to_component_values;
     std::unordered_map<Instruction*, Instruction*>
         loads_for_access_chain_to_component_values;
-    if (!ReplaceComponentsOfInterfaceVarWith(
-            interface_var, interface_var_users, components[i],
-            interface_var_component_indices, extra_array_index,
-            &loads_to_component_values,
-            &loads_for_access_chain_to_component_values)) {
-      return false;
+    Status status = ReplaceComponentsOfInterfaceVarWith(
+        interface_var, interface_var_users, components[i],
+        interface_var_component_indices, extra_array_index,
+        &loads_to_component_values,
+        &loads_for_access_chain_to_component_values);
+    if (status == Status::Failure) {
+      return Status::Failure;
     }
     interface_var_component_indices.pop_back();
 
@@ -378,10 +390,11 @@
     AddComponentsToCompositesForLoads(loads_to_component_values,
                                       loads_to_composites, depth_to_component);
   }
-  return true;
+  return Status::SuccessWithChange;
 }
 
-bool InterfaceVariableScalarReplacement::ReplaceComponentOfInterfaceVarWith(
+Pass::Status
+InterfaceVariableScalarReplacement::ReplaceComponentOfInterfaceVarWith(
     Instruction* interface_var, Instruction* interface_var_user,
     Instruction* scalar_var,
     const std::vector<uint32_t>& interface_var_component_indices,
@@ -395,42 +408,49 @@
     StoreComponentOfValueToScalarVar(value_id, interface_var_component_indices,
                                      scalar_var, extra_array_index,
                                      interface_var_user);
-    return true;
+    return Status::SuccessWithChange;
   }
   if (opcode == spv::Op::OpLoad) {
     Instruction* scalar_load =
         LoadScalarVar(scalar_var, extra_array_index, interface_var_user);
+    if (scalar_load == nullptr) {
+      return Status::Failure;
+    }
     loads_to_component_values->insert({interface_var_user, scalar_load});
-    return true;
+    return Status::SuccessWithChange;
   }
 
   // Copy OpName and annotation instructions only once. Therefore, we create
   // them only for the first element of the extra array.
-  if (extra_array_index && *extra_array_index != 0) return true;
+  if (extra_array_index && *extra_array_index != 0)
+    return Status::SuccessWithChange;
 
   if (opcode == spv::Op::OpDecorateId || opcode == spv::Op::OpDecorateString ||
       opcode == spv::Op::OpDecorate) {
     CloneAnnotationForVariable(interface_var_user, scalar_var->result_id());
-    return true;
+    return Status::SuccessWithChange;
   }
 
   if (opcode == spv::Op::OpName) {
     std::unique_ptr<Instruction> new_inst(interface_var_user->Clone(context()));
     new_inst->SetInOperand(0, {scalar_var->result_id()});
     context()->AddDebug2Inst(std::move(new_inst));
-    return true;
+    return Status::SuccessWithChange;
   }
 
   if (opcode == spv::Op::OpEntryPoint) {
-    return ReplaceInterfaceVarInEntryPoint(interface_var, interface_var_user,
-                                           scalar_var->result_id());
+    if (ReplaceInterfaceVarInEntryPoint(interface_var, interface_var_user,
+                                        scalar_var->result_id())) {
+      return Status::SuccessWithChange;
+    }
+    return Status::Failure;
   }
 
   if (opcode == spv::Op::OpAccessChain) {
     ReplaceAccessChainWith(interface_var_user, interface_var_component_indices,
                            scalar_var,
                            loads_for_access_chain_to_component_values);
-    return true;
+    return Status::SuccessWithChange;
   }
 
   std::string message("Unhandled instruction");
@@ -440,7 +460,7 @@
       "\nfor interface variable scalar replacement\n  " +
       interface_var->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
   context()->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str());
-  return false;
+  return Status::Failure;
 }
 
 void InterfaceVariableScalarReplacement::UseBaseAccessChainForAccessChain(
@@ -470,10 +490,14 @@
   uint32_t ptr_type_id =
       GetPointerType(*component_type_id, GetStorageClass(var));
 
-  std::unique_ptr<Instruction> new_access_chain(new Instruction(
-      context(), spv::Op::OpAccessChain, ptr_type_id, TakeNextId(),
-      std::initializer_list<Operand>{
-          {SPV_OPERAND_TYPE_ID, {var->result_id()}}}));
+  uint32_t new_id = TakeNextId();
+  if (new_id == 0) {
+    return nullptr;
+  }
+  std::unique_ptr<Instruction> new_access_chain(
+      new Instruction(context(), spv::Op::OpAccessChain, ptr_type_id, new_id,
+                      std::initializer_list<Operand>{
+                          {SPV_OPERAND_TYPE_ID, {var->result_id()}}}));
   for (uint32_t index_id : index_ids) {
     new_access_chain->AddOperand({SPV_OPERAND_TYPE_ID, {index_id}});
   }
@@ -490,12 +514,16 @@
   uint32_t ptr_type_id =
       GetPointerType(component_type_id, GetStorageClass(var));
   uint32_t index_id = context()->get_constant_mgr()->GetUIntConstId(index);
-  std::unique_ptr<Instruction> new_access_chain(new Instruction(
-      context(), spv::Op::OpAccessChain, ptr_type_id, TakeNextId(),
-      std::initializer_list<Operand>{
-          {SPV_OPERAND_TYPE_ID, {var->result_id()}},
-          {SPV_OPERAND_TYPE_ID, {index_id}},
-      }));
+  uint32_t new_id = TakeNextId();
+  if (new_id == 0) {
+    return nullptr;
+  }
+  std::unique_ptr<Instruction> new_access_chain(
+      new Instruction(context(), spv::Op::OpAccessChain, ptr_type_id, new_id,
+                      std::initializer_list<Operand>{
+                          {SPV_OPERAND_TYPE_ID, {var->result_id()}},
+                          {SPV_OPERAND_TYPE_ID, {index_id}},
+                      }));
   Instruction* inst = new_access_chain.get();
   context()->get_def_use_mgr()->AnalyzeInstDefUse(inst);
   insert_before->InsertBefore(std::move(new_access_chain));
@@ -617,6 +645,9 @@
     component_type_id = ty_mgr->GetTypeInstruction(array_type->element_type());
     ptr = CreateAccessChainWithIndex(component_type_id, scalar_var,
                                      *extra_array_index, insert_before);
+    if (ptr == nullptr) {
+      return;
+    }
   }
 
   StoreComponentOfValueTo(component_type_id, value_id, component_indices, ptr,
@@ -635,6 +666,9 @@
     component_type_id = ty_mgr->GetTypeInstruction(array_type->element_type());
     ptr = CreateAccessChainWithIndex(component_type_id, scalar_var,
                                      *extra_array_index, insert_before);
+    if (ptr == nullptr) {
+      return nullptr;
+    }
   }
 
   return CreateLoad(component_type_id, ptr, insert_before);
@@ -642,8 +676,12 @@
 
 Instruction* InterfaceVariableScalarReplacement::CreateLoad(
     uint32_t type_id, Instruction* ptr, Instruction* insert_before) {
+  uint32_t new_id = TakeNextId();
+  if (new_id == 0) {
+    return nullptr;
+  }
   std::unique_ptr<Instruction> load(
-      new Instruction(context(), spv::Op::OpLoad, type_id, TakeNextId(),
+      new Instruction(context(), spv::Op::OpLoad, type_id, new_id,
                       std::initializer_list<Operand>{
                           {SPV_OPERAND_TYPE_ID, {ptr->result_id()}}}));
   Instruction* load_inst = load.get();
@@ -658,6 +696,9 @@
     const uint32_t* extra_array_index, Instruction* insert_before) {
   std::unique_ptr<Instruction> composite_extract(CreateCompositeExtract(
       component_type_id, value_id, component_indices, extra_array_index));
+  if (composite_extract == nullptr) {
+    return;
+  }
 
   std::unique_ptr<Instruction> new_store(
       new Instruction(context(), spv::Op::OpStore));
@@ -677,6 +718,9 @@
     uint32_t type_id, uint32_t composite_id,
     const std::vector<uint32_t>& indexes, const uint32_t* extra_first_index) {
   uint32_t component_id = TakeNextId();
+  if (component_id == 0) {
+    return nullptr;
+  }
   Instruction* composite_extract = new Instruction(
       context(), spv::Op::OpCompositeExtract, type_id, component_id,
       std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {composite_id}}});
@@ -716,6 +760,9 @@
   if (!indexes.empty()) {
     ptr = CreateAccessChainToVar(component_type_id, var, indexes, insert_before,
                                  &component_type_id);
+    if (ptr == nullptr) {
+      return nullptr;
+    }
   }
 
   return CreateLoad(component_type_id, ptr, insert_before);
@@ -730,7 +777,10 @@
     type_id = GetComponentTypeOfArrayMatrix(def_use_mgr, load->type_id(),
                                             depth_to_component);
   }
-  uint32_t new_id = context()->TakeNextId();
+  uint32_t new_id = TakeNextId();
+  if (new_id == 0) {
+    return nullptr;
+  }
   std::unique_ptr<Instruction> new_composite_construct(new Instruction(
       context(), spv::Op::OpCompositeConstruct, type_id, new_id, {}));
   Instruction* composite_construct = new_composite_construct.get();
@@ -767,6 +817,10 @@
     if (itr == loads_to_composites->end()) {
       composite_construct =
           CreateCompositeConstructForComponentOfLoad(load, depth_to_component);
+      if (composite_construct == nullptr) {
+        assert(false && "Could not create composite construct");
+        return;
+      }
       loads_to_composites->insert({load, composite_construct});
     } else {
       composite_construct = itr->second;
@@ -795,7 +849,7 @@
   return context()->get_type_mgr()->GetTypeInstruction(&ptr_type);
 }
 
-InterfaceVariableScalarReplacement::NestedCompositeComponents
+std::optional<InterfaceVariableScalarReplacement::NestedCompositeComponents>
 InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForArray(
     Instruction* interface_var_type, spv::StorageClass storage_class,
     uint32_t extra_array_length) {
@@ -807,16 +861,19 @@
 
   NestedCompositeComponents scalar_vars;
   while (array_length > 0) {
-    NestedCompositeComponents scalar_vars_for_element =
+    std::optional<NestedCompositeComponents> scalar_vars_for_element =
         CreateScalarInterfaceVarsForReplacement(elem_type, storage_class,
                                                 extra_array_length);
-    scalar_vars.AddComponent(scalar_vars_for_element);
+    if (!scalar_vars_for_element) {
+      return std::nullopt;
+    }
+    scalar_vars.AddComponent(*scalar_vars_for_element);
     --array_length;
   }
   return scalar_vars;
 }
 
-InterfaceVariableScalarReplacement::NestedCompositeComponents
+std::optional<InterfaceVariableScalarReplacement::NestedCompositeComponents>
 InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForMatrix(
     Instruction* interface_var_type, spv::StorageClass storage_class,
     uint32_t extra_array_length) {
@@ -830,16 +887,19 @@
 
   NestedCompositeComponents scalar_vars;
   while (column_count > 0) {
-    NestedCompositeComponents scalar_vars_for_column =
+    std::optional<NestedCompositeComponents> scalar_vars_for_column =
         CreateScalarInterfaceVarsForReplacement(column_type, storage_class,
                                                 extra_array_length);
-    scalar_vars.AddComponent(scalar_vars_for_column);
+    if (!scalar_vars_for_column) {
+      return std::nullopt;
+    }
+    scalar_vars.AddComponent(*scalar_vars_for_column);
     --column_count;
   }
   return scalar_vars;
 }
 
-InterfaceVariableScalarReplacement::NestedCompositeComponents
+std::optional<InterfaceVariableScalarReplacement::NestedCompositeComponents>
 InterfaceVariableScalarReplacement::CreateScalarInterfaceVarsForReplacement(
     Instruction* interface_var_type, spv::StorageClass storage_class,
     uint32_t extra_array_length) {
@@ -864,6 +924,9 @@
   uint32_t ptr_type_id =
       context()->get_type_mgr()->FindPointerToType(type_id, storage_class);
   uint32_t id = TakeNextId();
+  if (id == 0) {
+    return std::nullopt;
+  }
   std::unique_ptr<Instruction> variable(
       new Instruction(context(), spv::Op::OpVariable, ptr_type_id, id,
                       std::initializer_list<Operand>{
@@ -953,9 +1016,9 @@
       continue;
     }
 
-    if (!ReplaceInterfaceVariableWithScalars(interface_var, interface_var_type,
-                                             location, component,
-                                             extra_array_length)) {
+    if (ReplaceInterfaceVariableWithScalars(
+            interface_var, interface_var_type, location, component,
+            extra_array_length) == Pass::Status::Failure) {
       return Pass::Status::Failure;
     }
     status = Pass::Status::SuccessWithChange;
diff --git a/source/opt/interface_var_sroa.h b/source/opt/interface_var_sroa.h
index 45ed371..743cdfb 100644
--- a/source/opt/interface_var_sroa.h
+++ b/source/opt/interface_var_sroa.h
@@ -15,6 +15,7 @@
 #ifndef SOURCE_OPT_INTERFACE_VAR_SROA_H_
 #define SOURCE_OPT_INTERFACE_VAR_SROA_H_
 
+#include <optional>
 #include <unordered_set>
 
 #include "source/opt/pass.h"
@@ -100,25 +101,26 @@
   // If |extra_array_length| is 0, it means |interface_var| has a Patch
   // decoration. Otherwise, |extra_array_length| denotes the length of the extra
   // array of |interface_var|.
-  bool ReplaceInterfaceVariableWithScalars(Instruction* interface_var,
-                                           Instruction* interface_var_type,
-                                           uint32_t location,
-                                           uint32_t component,
-                                           uint32_t extra_array_length);
+  Status ReplaceInterfaceVariableWithScalars(Instruction* interface_var,
+                                             Instruction* interface_var_type,
+                                             uint32_t location,
+                                             uint32_t component,
+                                             uint32_t extra_array_length);
 
   // Creates scalar variables with the storage classe |storage_class| to replace
   // an interface variable whose type is |interface_var_type|. If
   // |extra_array_length| is not zero, adds the extra arrayness to the created
   // scalar variables.
-  NestedCompositeComponents CreateScalarInterfaceVarsForReplacement(
-      Instruction* interface_var_type, spv::StorageClass storage_class,
-      uint32_t extra_array_length);
+  std::optional<NestedCompositeComponents>
+  CreateScalarInterfaceVarsForReplacement(Instruction* interface_var_type,
+                                          spv::StorageClass storage_class,
+                                          uint32_t extra_array_length);
 
   // Creates scalar variables with the storage classe |storage_class| to replace
   // the interface variable whose type is OpTypeArray |interface_var_type| with.
   // If |extra_array_length| is not zero, adds the extra arrayness to all the
   // scalar variables.
-  NestedCompositeComponents CreateScalarInterfaceVarsForArray(
+  std::optional<NestedCompositeComponents> CreateScalarInterfaceVarsForArray(
       Instruction* interface_var_type, spv::StorageClass storage_class,
       uint32_t extra_array_length);
 
@@ -126,7 +128,7 @@
   // the interface variable whose type is OpTypeMatrix |interface_var_type|
   // with. If |extra_array_length| is not zero, adds the extra arrayness to all
   // the scalar variables.
-  NestedCompositeComponents CreateScalarInterfaceVarsForMatrix(
+  std::optional<NestedCompositeComponents> CreateScalarInterfaceVarsForMatrix(
       Instruction* interface_var_type, spv::StorageClass storage_class,
       uint32_t extra_array_length);
 
@@ -142,7 +144,7 @@
   // |extra_arrayness| is the extra arrayness of the interface variable.
   // |scalar_interface_vars| contains the nested variables to replace the
   // interface variable with.
-  bool ReplaceInterfaceVarWith(
+  Status ReplaceInterfaceVarWith(
       Instruction* interface_var, uint32_t extra_arrayness,
       const NestedCompositeComponents& scalar_interface_vars);
 
@@ -155,7 +157,7 @@
   // construct instructions to be replaced with load instructions of access
   // chain instructions in |interface_var_users| via
   // |loads_for_access_chain_to_composites|.
-  bool ReplaceComponentsOfInterfaceVarWith(
+  Status ReplaceComponentsOfInterfaceVarWith(
       Instruction* interface_var,
       const std::vector<Instruction*>& interface_var_users,
       const NestedCompositeComponents& scalar_interface_vars,
@@ -174,7 +176,7 @@
   // via |loads_to_composites|. Returns composite construct instructions to be
   // replaced with load instructions of access chain instructions in
   // |interface_var_users| via |loads_for_access_chain_to_composites|.
-  bool ReplaceMultipleComponentsOfInterfaceVarWith(
+  Status ReplaceMultipleComponentsOfInterfaceVarWith(
       Instruction* interface_var,
       const std::vector<Instruction*>& interface_var_users,
       const std::vector<NestedCompositeComponents>& components,
@@ -192,7 +194,7 @@
   // |loads_to_component_values|. If |interface_var_user| is an access chain,
   // returns the component value for loads of |interface_var_user| via
   // |loads_for_access_chain_to_component_values|.
-  bool ReplaceComponentOfInterfaceVarWith(
+  Status ReplaceComponentOfInterfaceVarWith(
       Instruction* interface_var, Instruction* interface_var_user,
       Instruction* scalar_var,
       const std::vector<uint32_t>& interface_var_component_indices,
@@ -389,6 +391,9 @@
   // A set of interface variables without the extra arrayness for any of the
   // entry points.
   std::unordered_set<Instruction*> vars_without_extra_arrayness;
+
+  // Returns the next available id, or 0 if the id overflows.
+  uint32_t TakeNextId() { return context()->TakeNextId(); }
 };
 
 }  // namespace opt
diff --git a/source/opt/invocation_interlock_placement_pass.cpp b/source/opt/invocation_interlock_placement_pass.cpp
index 642e2d2..b31697c 100644
--- a/source/opt/invocation_interlock_placement_pass.cpp
+++ b/source/opt/invocation_interlock_placement_pass.cpp
@@ -26,10 +26,10 @@
 #include <vector>
 
 #include "source/enum_set.h"
-#include "source/enum_string_mapping.h"
 #include "source/opt/ir_context.h"
 #include "source/opt/reflect.h"
 #include "source/spirv_target_env.h"
+#include "source/table2.h"
 #include "source/util/string_utils.h"
 
 namespace spvtools {
@@ -294,8 +294,12 @@
 BasicBlock* InvocationInterlockPlacementPass::splitEdge(BasicBlock* block,
                                                         uint32_t succ_id) {
   // Create a new block to replace the critical edge.
+  uint32_t new_id = context()->TakeNextId();
+  if (new_id == 0) {
+    return nullptr;
+  }
   auto new_succ_temp = MakeUnique<BasicBlock>(
-      MakeUnique<Instruction>(context(), spv::Op::OpLabel, 0, TakeNextId(),
+      MakeUnique<Instruction>(context(), spv::Op::OpLabel, 0, new_id,
                               std::initializer_list<Operand>{}));
   auto* new_succ = new_succ_temp.get();
 
@@ -325,7 +329,7 @@
   return new_succ;
 }
 
-bool InvocationInterlockPlacementPass::placeInstructionsForEdge(
+Pass::Status InvocationInterlockPlacementPass::placeInstructionsForEdge(
     BasicBlock* block, uint32_t next_id, BlockSet& inside,
     BlockSet& previous_inside, spv::Op opcode, bool reverse_cfg) {
   bool modified = false;
@@ -372,31 +376,45 @@
         new_branch = splitEdge(cfg()->block(next_id), block->id());
       }
 
+      if (!new_branch) {
+        return Status::Failure;
+      }
+
       auto inst = new Instruction(context(), opcode);
       inst->InsertBefore(&*new_branch->tail());
     }
   }
 
-  return modified;
+  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
-bool InvocationInterlockPlacementPass::placeInstructions(BasicBlock* block) {
-  bool modified = false;
+Pass::Status InvocationInterlockPlacementPass::placeInstructions(
+    BasicBlock* block) {
+  Status status = Status::SuccessWithoutChange;
 
-  block->ForEachSuccessorLabel([this, block, &modified](uint32_t succ_id) {
-    modified |= placeInstructionsForEdge(
+  block->ForEachSuccessorLabel([this, block, &status](uint32_t succ_id) {
+    if (status == Status::Failure) {
+      return;
+    }
+    Status edge_status = placeInstructionsForEdge(
         block, succ_id, after_begin_, predecessors_after_begin_,
         spv::Op::OpBeginInvocationInterlockEXT, /* reverse_cfg= */ true);
-    modified |= placeInstructionsForEdge(cfg()->block(succ_id), block->id(),
-                                         before_end_, successors_before_end_,
-                                         spv::Op::OpEndInvocationInterlockEXT,
-                                         /* reverse_cfg= */ false);
+    status = CombineStatus(status, edge_status);
+    if (status == Status::Failure) {
+      return;
+    }
+
+    edge_status = placeInstructionsForEdge(cfg()->block(succ_id), block->id(),
+                                           before_end_, successors_before_end_,
+                                           spv::Op::OpEndInvocationInterlockEXT,
+                                           /* reverse_cfg= */ false);
+    status = CombineStatus(status, edge_status);
   });
 
-  return modified;
+  return status;
 }
 
-bool InvocationInterlockPlacementPass::processFragmentShaderEntry(
+Pass::Status InvocationInterlockPlacementPass::processFragmentShaderEntry(
     Function* entry_func) {
   bool modified = false;
 
@@ -417,9 +435,15 @@
 
   for (BasicBlock* block : original_blocks) {
     modified |= removeUnneededInstructions(block);
-    modified |= placeInstructions(block);
+    Status place_status = placeInstructions(block);
+    if (place_status == Status::Failure) {
+      return Status::Failure;
+    }
+    if (place_status == Status::SuccessWithChange) {
+      modified = true;
+    }
   }
-  return modified;
+  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
 bool InvocationInterlockPlacementPass::isFragmentShaderInterlockEnabled() {
@@ -452,7 +476,7 @@
     return Status::SuccessWithoutChange;
   }
 
-  bool modified = false;
+  Status status = Status::SuccessWithoutChange;
 
   std::unordered_set<Function*> entry_points;
   for (Instruction& entry_inst : context()->module()->entry_points()) {
@@ -466,7 +490,9 @@
     Function* func = &*fi;
     recordBeginOrEndInFunction(func);
     if (!entry_points.count(func) && extracted_functions_.count(func)) {
-      modified |= removeBeginAndEndInstructionsFromFunction(func);
+      if (removeBeginAndEndInstructionsFromFunction(func)) {
+        status = Status::SuccessWithChange;
+      }
     }
   }
 
@@ -482,11 +508,14 @@
       continue;
     }
 
-    modified |= processFragmentShaderEntry(entry_func);
+    Status frag_status = processFragmentShaderEntry(entry_func);
+    if (frag_status == Status::Failure) {
+      return Status::Failure;
+    }
+    status = CombineStatus(status, frag_status);
   }
 
-  return modified ? Pass::Status::SuccessWithChange
-                  : Pass::Status::SuccessWithoutChange;
+  return status;
 }
 
 }  // namespace opt
diff --git a/source/opt/invocation_interlock_placement_pass.h b/source/opt/invocation_interlock_placement_pass.h
index 4e85be8..497af28 100644
--- a/source/opt/invocation_interlock_placement_pass.h
+++ b/source/opt/invocation_interlock_placement_pass.h
@@ -120,14 +120,14 @@
   // For the edge from block to next_id, places a begin or end instruction on
   // the edge, based on the direction we are walking the CFG, specified in
   // reverse_cfg.
-  bool placeInstructionsForEdge(BasicBlock* block, uint32_t next_id,
-                                BlockSet& inside, BlockSet& previous_inside,
-                                spv::Op opcode, bool reverse_cfg);
+  Status placeInstructionsForEdge(BasicBlock* block, uint32_t next_id,
+                                  BlockSet& inside, BlockSet& previous_inside,
+                                  spv::Op opcode, bool reverse_cfg);
   // Calls placeInstructionsForEdge for each edge in block.
-  bool placeInstructions(BasicBlock* block);
+  Status placeInstructions(BasicBlock* block);
 
   // Processes a single fragment shader entry function.
-  bool processFragmentShaderEntry(Function* entry_func);
+  Status processFragmentShaderEntry(Function* entry_func);
 
   // Returns whether the module has the SPV_EXT_fragment_shader_interlock
   // extension and one of the FragmentShader*InterlockEXT capabilities.
diff --git a/source/opt/ir_builder.h b/source/opt/ir_builder.h
index f3e0afc..529bd8f 100644
--- a/source/opt/ir_builder.h
+++ b/source/opt/ir_builder.h
@@ -15,6 +15,7 @@
 #ifndef SOURCE_OPT_IR_BUILDER_H_
 #define SOURCE_OPT_IR_BUILDER_H_
 
+#include <cassert>
 #include <limits>
 #include <memory>
 #include <utility>
@@ -165,10 +166,14 @@
     for (size_t i = 0; i < operands.size(); i++) {
       ops.push_back({SPV_OPERAND_TYPE_ID, {operands[i]}});
     }
-    // TODO(1841): Handle id overflow.
-    std::unique_ptr<Instruction> new_inst(new Instruction(
-        GetContext(), opcode, type_id,
-        result != 0 ? result : GetContext()->TakeNextId(), ops));
+    if (result == 0) {
+      result = GetContext()->TakeNextId();
+      if (result == 0) {
+        return nullptr;
+      }
+    }
+    std::unique_ptr<Instruction> new_inst(
+        new Instruction(GetContext(), opcode, type_id, result, ops));
     return AddInstruction(std::move(new_inst));
   }
 
@@ -296,9 +301,12 @@
   // The id |op1| is the left hand side of the operation.
   // The id |op2| is the right hand side of the operation.
   Instruction* AddIAdd(uint32_t type, uint32_t op1, uint32_t op2) {
-    // TODO(1841): Handle id overflow.
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
     std::unique_ptr<Instruction> inst(new Instruction(
-        GetContext(), spv::Op::OpIAdd, type, GetContext()->TakeNextId(),
+        GetContext(), spv::Op::OpIAdd, type, result_id,
         {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}));
     return AddInstruction(std::move(inst));
   }
@@ -310,9 +318,12 @@
   Instruction* AddULessThan(uint32_t op1, uint32_t op2) {
     analysis::Bool bool_type;
     uint32_t type = GetContext()->get_type_mgr()->GetId(&bool_type);
-    // TODO(1841): Handle id overflow.
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
     std::unique_ptr<Instruction> inst(new Instruction(
-        GetContext(), spv::Op::OpULessThan, type, GetContext()->TakeNextId(),
+        GetContext(), spv::Op::OpULessThan, type, result_id,
         {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}));
     return AddInstruction(std::move(inst));
   }
@@ -324,9 +335,12 @@
   Instruction* AddSLessThan(uint32_t op1, uint32_t op2) {
     analysis::Bool bool_type;
     uint32_t type = GetContext()->get_type_mgr()->GetId(&bool_type);
-    // TODO(1841): Handle id overflow.
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
     std::unique_ptr<Instruction> inst(new Instruction(
-        GetContext(), spv::Op::OpSLessThan, type, GetContext()->TakeNextId(),
+        GetContext(), spv::Op::OpSLessThan, type, result_id,
         {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}));
     return AddInstruction(std::move(inst));
   }
@@ -354,9 +368,12 @@
   // bool) for |type|.
   Instruction* AddSelect(uint32_t type, uint32_t cond, uint32_t true_value,
                          uint32_t false_value) {
-    // TODO(1841): Handle id overflow.
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
     std::unique_ptr<Instruction> select(new Instruction(
-        GetContext(), spv::Op::OpSelect, type, GetContext()->TakeNextId(),
+        GetContext(), spv::Op::OpSelect, type, result_id,
         std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {cond}},
                                        {SPV_OPERAND_TYPE_ID, {true_value}},
                                        {SPV_OPERAND_TYPE_ID, {false_value}}}));
@@ -380,10 +397,12 @@
       ops.emplace_back(SPV_OPERAND_TYPE_ID,
                        std::initializer_list<uint32_t>{id});
     }
-    // TODO(1841): Handle id overflow.
-    std::unique_ptr<Instruction> construct(
-        new Instruction(GetContext(), spv::Op::OpCompositeConstruct, type,
-                        GetContext()->TakeNextId(), ops));
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
+    std::unique_ptr<Instruction> construct(new Instruction(
+        GetContext(), spv::Op::OpCompositeConstruct, type, result_id, ops));
     return AddInstruction(std::move(construct));
   }
 
@@ -465,10 +484,12 @@
       operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}});
     }
 
-    // TODO(1841): Handle id overflow.
-    std::unique_ptr<Instruction> new_inst(
-        new Instruction(GetContext(), spv::Op::OpCompositeExtract, type,
-                        GetContext()->TakeNextId(), operands));
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
+    std::unique_ptr<Instruction> new_inst(new Instruction(
+        GetContext(), spv::Op::OpCompositeExtract, type, result_id, operands));
     return AddInstruction(std::move(new_inst));
   }
 
@@ -480,8 +501,11 @@
     return AddInstruction(std::move(select));
   }
 
-  Instruction* AddAccessChain(uint32_t type_id, uint32_t base_ptr_id,
-                              std::vector<uint32_t> ids) {
+  Instruction* AddOpcodeAccessChain(spv::Op opcode, uint32_t type_id,
+                                    uint32_t base_ptr_id,
+                                    const std::vector<uint32_t>& ids) {
+    assert(opcode == spv::Op::OpAccessChain ||
+           opcode == spv::Op::OpInBoundsAccessChain);
     std::vector<Operand> operands;
     operands.push_back({SPV_OPERAND_TYPE_ID, {base_ptr_id}});
 
@@ -489,13 +513,26 @@
       operands.push_back({SPV_OPERAND_TYPE_ID, {index_id}});
     }
 
-    // TODO(1841): Handle id overflow.
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
     std::unique_ptr<Instruction> new_inst(
-        new Instruction(GetContext(), spv::Op::OpAccessChain, type_id,
-                        GetContext()->TakeNextId(), operands));
+        new Instruction(GetContext(), opcode, type_id, result_id, operands));
     return AddInstruction(std::move(new_inst));
   }
 
+  Instruction* AddAccessChain(uint32_t type_id, uint32_t base_ptr_id,
+                              const std::vector<uint32_t>& ids) {
+    return AddOpcodeAccessChain(spv::Op::OpAccessChain, type_id, base_ptr_id,
+                                ids);
+  }
+  Instruction* AddInBoundsAccessChain(uint32_t type_id, uint32_t base_ptr_id,
+                                      const std::vector<uint32_t>& ids) {
+    return AddOpcodeAccessChain(spv::Op::OpInBoundsAccessChain, type_id,
+                                base_ptr_id, ids);
+  }
+
   Instruction* AddLoad(uint32_t type_id, uint32_t base_ptr_id,
                        uint32_t alignment = 0) {
     std::vector<Operand> operands;
@@ -507,19 +544,36 @@
       operands.push_back({SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, {alignment}});
     }
 
-    // TODO(1841): Handle id overflow.
-    std::unique_ptr<Instruction> new_inst(
-        new Instruction(GetContext(), spv::Op::OpLoad, type_id,
-                        GetContext()->TakeNextId(), operands));
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
+    std::unique_ptr<Instruction> new_inst(new Instruction(
+        GetContext(), spv::Op::OpLoad, type_id, result_id, operands));
+    return AddInstruction(std::move(new_inst));
+  }
+
+  Instruction* AddCopyObject(uint32_t type_id, uint32_t value_id) {
+    std::vector<Operand> operands{{SPV_OPERAND_TYPE_ID, {value_id}}};
+
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
+    std::unique_ptr<Instruction> new_inst(new Instruction(
+        GetContext(), spv::Op::OpCopyObject, type_id, result_id, operands));
     return AddInstruction(std::move(new_inst));
   }
 
   Instruction* AddVariable(uint32_t type_id, uint32_t storage_class) {
     std::vector<Operand> operands;
-    operands.push_back({SPV_OPERAND_TYPE_ID, {storage_class}});
-    std::unique_ptr<Instruction> new_inst(
-        new Instruction(GetContext(), spv::Op::OpVariable, type_id,
-                        GetContext()->TakeNextId(), operands));
+    operands.push_back({SPV_OPERAND_TYPE_STORAGE_CLASS, {storage_class}});
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
+    std::unique_ptr<Instruction> new_inst(new Instruction(
+        GetContext(), spv::Op::OpVariable, type_id, result_id, operands));
     return AddInstruction(std::move(new_inst));
   }
 
@@ -572,6 +626,26 @@
     return AddInstruction(std::move(new_inst));
   }
 
+  Instruction* AddDecoration(uint32_t target_id, spv::Decoration d,
+                             const std::vector<uint32_t>& literals) {
+    std::vector<Operand> operands;
+    operands.push_back({SPV_OPERAND_TYPE_ID, {target_id}});
+    operands.push_back({SPV_OPERAND_TYPE_DECORATION, {uint32_t(d)}});
+    for (uint32_t literal : literals) {
+      operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {literal}});
+    }
+
+    std::unique_ptr<Instruction> new_inst(
+        new Instruction(GetContext(), spv::Op::OpDecorate, 0, 0, operands));
+    // Decorations are annotation instructions.  Add it via the IR context,
+    // so the decoration manager will be updated.
+    // Decorations don't belong to basic blocks, so there is no need
+    // to update the instruction to block mapping.
+    Instruction* result = new_inst.get();
+    GetContext()->AddAnnotationInst(std::move(new_inst));
+    return result;
+  }
+
   Instruction* AddNaryExtendedInstruction(
       uint32_t result_type, uint32_t set, uint32_t instruction,
       const std::vector<uint32_t>& ext_operands) {
@@ -593,6 +667,23 @@
     return AddInstruction(std::move(new_inst));
   }
 
+  Instruction* AddSampledImage(uint32_t sampled_image_type_id,
+                               uint32_t image_id, uint32_t sampler_id) {
+    std::vector<Operand> operands;
+    operands.push_back({SPV_OPERAND_TYPE_ID, {image_id}});
+    operands.push_back({SPV_OPERAND_TYPE_ID, {sampler_id}});
+
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
+
+    std::unique_ptr<Instruction> new_inst(
+        new Instruction(GetContext(), spv::Op::OpSampledImage,
+                        sampled_image_type_id, result_id, operands));
+    return AddInstruction(std::move(new_inst));
+  }
+
   // Inserts the new instruction before the insertion point.
   Instruction* AddInstruction(std::unique_ptr<Instruction>&& insn) {
     Instruction* insn_ptr = &*insert_before_.InsertBefore(std::move(insn));
diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp
index 1cf0d74..6a66d2b 100644
--- a/source/opt/ir_context.cpp
+++ b/source/opt/ir_context.cpp
@@ -1,4 +1,6 @@
 // Copyright (c) 2017 Google Inc.
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -31,8 +33,8 @@
 constexpr int kEntryPointFunctionIdInIdx = 1;
 constexpr int kEntryPointExecutionModelInIdx = 0;
 
-// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo.100
-// extension instructions.
+// Constants for OpenCL.DebugInfo.100 / NonSemantic.Shader.DebugInfo extension
+// instructions.
 constexpr uint32_t kDebugFunctionOperandFunctionIndex = 13;
 constexpr uint32_t kDebugGlobalVariableOperandVariableIndex = 11;
 }  // namespace
@@ -91,6 +93,9 @@
   if (set & kAnalysisLiveness) {
     BuildLivenessManager();
   }
+  if (set & kAnalysisIdToGraphMapping) {
+    BuildIdToGraphMapping();
+  }
 }
 
 void IRContext::InvalidateAnalysesExceptFor(
@@ -162,6 +167,9 @@
   if (analyses_to_invalidate & kAnalysisDebugInfo) {
     debug_info_mgr_.reset(nullptr);
   }
+  if (analyses_to_invalidate & kAnalysisIdToGraphMapping) {
+    id_to_graph_.clear();
+  }
 
   valid_analyses_ = Analysis(valid_analyses_ & ~analyses_to_invalidate);
 }
@@ -175,6 +183,8 @@
 
   KillOperandFromDebugInstructions(inst);
 
+  KillRelatedDebugScopes(inst);
+
   if (AreAnalysesValid(kAnalysisDefUse)) {
     analysis::DefUseManager* def_use_mgr = get_def_use_mgr();
     def_use_mgr->ClearInst(inst);
@@ -199,7 +209,9 @@
     constant_mgr_->RemoveId(inst->result_id());
   }
   if (inst->opcode() == spv::Op::OpCapability ||
-      inst->opcode() == spv::Op::OpExtension) {
+      inst->opcode() == spv::Op::OpConditionalCapabilityINTEL ||
+      inst->opcode() == spv::Op::OpExtension ||
+      inst->opcode() == spv::Op::OpConditionalExtensionINTEL) {
     // We reset the feature manager, instead of updating it, because it is just
     // as much work.  We would have to remove all capabilities implied by this
     // capability that are not also implied by the remaining OpCapability
@@ -380,6 +392,7 @@
     }
   }
 
+  return true;
   if (AreAnalysesValid(kAnalysisIdToFuncMapping)) {
     for (auto& fn : *module_) {
       if (id_to_func_[fn.result_id()] != &fn) {
@@ -388,6 +401,14 @@
     }
   }
 
+  if (AreAnalysesValid(kAnalysisIdToGraphMapping)) {
+    for (auto& g : module_->graphs()) {
+      if (id_to_graph_[g->DefInst().result_id()] != g.get()) {
+        return false;
+      }
+    }
+  }
+
   if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) {
     for (auto& func : *module()) {
       for (auto& block : func) {
@@ -396,8 +417,9 @@
                 return false;
               }
               return true;
-            }))
+            })) {
           return false;
+        }
       }
     }
   }
@@ -512,6 +534,20 @@
   }
 }
 
+void IRContext::KillRelatedDebugScopes(Instruction* inst) {
+  // Extension has been fully unloaded, remove debug scope from every
+  // instruction.
+  if (inst->opcode() == spv::Op::OpExtInstImport) {
+    const std::string extension_name = inst->GetInOperand(0).AsString();
+    if (extension_name.compare(0, 29, "NonSemantic.Shader.DebugInfo.") == 0 ||
+        extension_name == "OpenCL.DebugInfo.100") {
+      module()->ForEachInst([](Instruction* child) {
+        child->SetDebugScope(DebugScope(kNoDebugScope, kNoInlinedAt));
+      });
+    }
+  }
+}
+
 void IRContext::AddCombinatorsForCapability(uint32_t capability) {
   spv::Capability cap = spv::Capability(capability);
   if (cap == spv::Capability::Shader) {
@@ -537,11 +573,14 @@
          (uint32_t)spv::Op::OpTypeAccelerationStructureKHR,
          (uint32_t)spv::Op::OpTypeRayQueryKHR,
          (uint32_t)spv::Op::OpTypeHitObjectNV,
+         (uint32_t)spv::Op::OpTypeHitObjectEXT,
          (uint32_t)spv::Op::OpTypeArray,
          (uint32_t)spv::Op::OpTypeRuntimeArray,
+         (uint32_t)spv::Op::OpTypeNodePayloadArrayAMDX,
          (uint32_t)spv::Op::OpTypeStruct,
          (uint32_t)spv::Op::OpTypeOpaque,
          (uint32_t)spv::Op::OpTypePointer,
+         (uint32_t)spv::Op::OpTypeUntypedPointerKHR,
          (uint32_t)spv::Op::OpTypeFunction,
          (uint32_t)spv::Op::OpTypeEvent,
          (uint32_t)spv::Op::OpTypeDeviceEvent,
@@ -550,10 +589,12 @@
          (uint32_t)spv::Op::OpTypePipe,
          (uint32_t)spv::Op::OpTypeForwardPointer,
          (uint32_t)spv::Op::OpVariable,
+         (uint32_t)spv::Op::OpUntypedVariableKHR,
          (uint32_t)spv::Op::OpImageTexelPointer,
          (uint32_t)spv::Op::OpLoad,
          (uint32_t)spv::Op::OpAccessChain,
          (uint32_t)spv::Op::OpInBoundsAccessChain,
+         (uint32_t)spv::Op::OpUntypedAccessChainKHR,
          (uint32_t)spv::Op::OpArrayLength,
          (uint32_t)spv::Op::OpVectorExtractDynamic,
          (uint32_t)spv::Op::OpVectorInsertDynamic,
@@ -561,6 +602,7 @@
          (uint32_t)spv::Op::OpCompositeConstruct,
          (uint32_t)spv::Op::OpCompositeExtract,
          (uint32_t)spv::Op::OpCompositeInsert,
+         (uint32_t)spv::Op::OpCopyLogical,
          (uint32_t)spv::Op::OpCopyObject,
          (uint32_t)spv::Op::OpTranspose,
          (uint32_t)spv::Op::OpSampledImage,
@@ -905,11 +947,13 @@
         return 0;
       }
     }
+    if (reg_type == nullptr) return 0;  // Error
+
     uint32_t type_id = type_mgr->GetTypeInstruction(reg_type);
     uint32_t varTyPtrId =
         type_mgr->FindPointerToType(type_id, spv::StorageClass::Input);
-    // TODO(1841): Handle id overflow.
     var_id = TakeNextId();
+    if (var_id == 0) return 0;  // Error
     std::unique_ptr<Instruction> newVarOp(
         new Instruction(this, spv::Op::OpVariable, varTyPtrId, var_id,
                         {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h
index 3857696..f4a69fc 100644
--- a/source/opt/ir_context.h
+++ b/source/opt/ir_context.h
@@ -27,7 +27,6 @@
 #include <vector>
 
 #include "source/assembly_grammar.h"
-#include "source/enum_string_mapping.h"
 #include "source/opt/cfg.h"
 #include "source/opt/constants.h"
 #include "source/opt/debug_info_manager.h"
@@ -44,6 +43,7 @@
 #include "source/opt/struct_cfg_analysis.h"
 #include "source/opt/type_manager.h"
 #include "source/opt/value_number_table.h"
+#include "source/table2.h"
 #include "source/util/make_unique.h"
 #include "source/util/string_utils.h"
 
@@ -84,7 +84,8 @@
     kAnalysisTypes = 1 << 15,
     kAnalysisDebugInfo = 1 << 16,
     kAnalysisLiveness = 1 << 17,
-    kAnalysisEnd = 1 << 18
+    kAnalysisIdToGraphMapping = 1 << 18,
+    kAnalysisEnd = 1 << 19
   };
 
   using ProcessFunction = std::function<bool(Function*)>;
@@ -109,7 +110,8 @@
         id_to_name_(nullptr),
         max_id_bound_(kDefaultMaxIdBound),
         preserve_bindings_(false),
-        preserve_spec_constants_(false) {
+        preserve_spec_constants_(false),
+        id_overflow_(false) {
     SetContextMessageConsumer(syntax_context_, consumer_);
     module_->SetContext(this);
   }
@@ -127,7 +129,8 @@
         id_to_name_(nullptr),
         max_id_bound_(kDefaultMaxIdBound),
         preserve_bindings_(false),
-        preserve_spec_constants_(false) {
+        preserve_spec_constants_(false),
+        id_overflow_(false) {
     SetContextMessageConsumer(syntax_context_, consumer_);
     module_->SetContext(this);
     InitializeCombinators();
@@ -505,6 +508,9 @@
   // Change operands of debug instruction to DebugInfoNone.
   void KillOperandFromDebugInstructions(Instruction* inst);
 
+  // Remove the debug scope from any instruction related to |inst|.
+  void KillRelatedDebugScopes(Instruction* inst);
+
   // Returns the next unique id for use by an instruction.
   inline uint32_t TakeNextUniqueId() {
     assert(unique_id_ != std::numeric_limits<uint32_t>::max());
@@ -563,6 +569,7 @@
   inline uint32_t TakeNextId() {
     uint32_t next_id = module()->TakeNextIdBound();
     if (next_id == 0) {
+      id_overflow_ = true;
       if (consumer()) {
         std::string message = "ID overflow. Try running compact-ids.";
         consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str());
@@ -583,6 +590,13 @@
     return next_id;
   }
 
+  // Returns true if an ID overflow has occurred since the last time the flag
+  // was cleared.
+  bool id_overflow() const { return id_overflow_; }
+
+  // Clears the ID overflow flag.
+  void clear_id_overflow() { id_overflow_ = false; }
+
   FeatureManager* get_feature_mgr() {
     if (!feature_mgr_.get()) {
       AnalyzeFeatures();
@@ -641,6 +655,23 @@
     return GetFunction(inst->result_id());
   }
 
+  // Returns the graph whose id is |id|, if one exists.  Returns |nullptr|
+  // otherwise.
+  Graph* GetGraph(uint32_t id) {
+    if (!AreAnalysesValid(kAnalysisIdToGraphMapping)) {
+      BuildIdToGraphMapping();
+    }
+    auto entry = id_to_graph_.find(id);
+    return (entry != id_to_graph_.end()) ? entry->second : nullptr;
+  }
+
+  Graph* GetGraph(Instruction* inst) {
+    if (inst->opcode() != spv::Op::OpGraphARM) {
+      return nullptr;
+    }
+    return GetGraph(inst->result_id());
+  }
+
   // Add to |todo| all ids of functions called directly from |func|.
   void AddCalls(const Function* func, std::queue<uint32_t>* todo);
 
@@ -719,6 +750,15 @@
     valid_analyses_ = valid_analyses_ | kAnalysisIdToFuncMapping;
   }
 
+  // Builds the instruction-graph map for the whole module.
+  void BuildIdToGraphMapping() {
+    id_to_graph_.clear();
+    for (auto& g : module_->graphs()) {
+      id_to_graph_[g->DefInst().result_id()] = g.get();
+    }
+    valid_analyses_ = valid_analyses_ | kAnalysisIdToGraphMapping;
+  }
+
   void BuildDecorationManager() {
     decoration_mgr_ = MakeUnique<analysis::DecorationManager>(module());
     valid_analyses_ = valid_analyses_ | kAnalysisDecorations;
@@ -872,6 +912,13 @@
   // iterators to traverse instructions.
   std::unordered_map<uint32_t, Function*> id_to_func_;
 
+  // A map from ids to the graph they define. This mapping is
+  // built on-demand when GetGraph() is called.
+  //
+  // NOTE: Do not traverse this map. Ever. Use the graph iterators to
+  // traverse instructions.
+  std::unordered_map<uint32_t, Graph*> id_to_graph_;
+
   // A bitset indicating which analyzes are currently valid.
   Analysis valid_analyses_;
 
@@ -930,6 +977,9 @@
   // Whether all specialization constants within |module_|
   // should be preserved.
   bool preserve_spec_constants_;
+
+  // Set to true if TakeNextId() fails.
+  bool id_overflow_;
 };
 
 inline IRContext::Analysis operator|(IRContext::Analysis lhs,
diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp
index a785048..c0af91b 100644
--- a/source/opt/ir_loader.cpp
+++ b/source/opt/ir_loader.cpp
@@ -46,10 +46,10 @@
   if (inst->ext_inst_type != SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100)
     return false;
   const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
-  const NonSemanticShaderDebugInfo100Instructions ext_inst_key =
-      NonSemanticShaderDebugInfo100Instructions(ext_inst_index);
-  return ext_inst_key == NonSemanticShaderDebugInfo100DebugLine ||
-         ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine;
+  const NonSemanticShaderDebugInfoInstructions ext_inst_key =
+      NonSemanticShaderDebugInfoInstructions(ext_inst_index);
+  return ext_inst_key == NonSemanticShaderDebugInfoDebugLine ||
+         ext_inst_key == NonSemanticShaderDebugInfoDebugNoLine;
 }
 
 bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
@@ -178,12 +178,48 @@
     last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
     last_line_inst_.reset();
     dbg_line_info_.clear();
+  } else if (opcode == spv::Op::OpGraphARM) {
+    if (graph_ != nullptr) {
+      Error(consumer_, src, loc, "graph inside graph");
+      return false;
+    }
+    graph_ = MakeUnique<Graph>(std::move(spv_inst));
+  } else if (opcode == spv::Op::OpGraphEndARM) {
+    if (graph_ == nullptr) {
+      Error(consumer_, src, loc,
+            "OpGraphEndARM without corresponding OpGraphARM");
+      return false;
+    }
+    graph_->SetGraphEnd(std::move(spv_inst));
+    module_->AddGraph(std::move(graph_));
+    graph_ = nullptr;
+  } else if (opcode == spv::Op::OpGraphConstantARM) {
+    module_->AddGlobalValue(std::move(spv_inst));
+  } else if (graph_ != nullptr) {
+    if (opcode == spv::Op::OpGraphInputARM) {
+      graph_->AddInput(std::move(spv_inst));
+    } else if (opcode == spv::Op::OpGraphSetOutputARM) {
+      graph_->AddOutput(std::move(spv_inst));
+    } else {
+      switch (opcode) {
+        case spv::Op::OpExtInst:
+        case spv::Op::OpCompositeExtract:
+          graph_->AddInstruction(std::move(spv_inst));
+          break;
+        default:
+          Errorf(consumer_, src, loc,
+                 "unhandled instruction (opcode %d) inside graph", opcode);
+          return false;
+      }
+    }
   } else {
     if (function_ == nullptr) {  // Outside function definition
       SPIRV_ASSERT(consumer_, block_ == nullptr);
-      if (opcode == spv::Op::OpCapability) {
+      if (opcode == spv::Op::OpCapability ||
+          opcode == spv::Op::OpConditionalCapabilityINTEL) {
         module_->AddCapability(std::move(spv_inst));
-      } else if (opcode == spv::Op::OpExtension) {
+      } else if (opcode == spv::Op::OpExtension ||
+                 opcode == spv::Op::OpConditionalExtensionINTEL) {
         module_->AddExtension(std::move(spv_inst));
       } else if (opcode == spv::Op::OpExtInstImport) {
         module_->AddExtInstImport(std::move(spv_inst));
@@ -193,6 +229,8 @@
         module_->SetSampledImageAddressMode(std::move(spv_inst));
       } else if (opcode == spv::Op::OpEntryPoint) {
         module_->AddEntryPoint(std::move(spv_inst));
+      } else if (opcode == spv::Op::OpGraphEntryPointARM) {
+        module_->AddGraphEntryPoint(std::move(spv_inst));
       } else if (opcode == spv::Op::OpExecutionMode ||
                  opcode == spv::Op::OpExecutionModeId) {
         module_->AddExecutionMode(std::move(spv_inst));
@@ -207,6 +245,7 @@
       } else if (IsTypeInst(opcode)) {
         module_->AddType(std::move(spv_inst));
       } else if (IsConstantInst(opcode) || opcode == spv::Op::OpVariable ||
+                 opcode == spv::Op::OpUntypedVariableKHR ||
                  opcode == spv::Op::OpUndef) {
         module_->AddGlobalValue(std::move(spv_inst));
       } else if (spvIsExtendedInstruction(opcode) &&
@@ -267,14 +306,14 @@
           }
         } else if (inst->ext_inst_type ==
                    SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
-          const NonSemanticShaderDebugInfo100Instructions ext_inst_key =
-              NonSemanticShaderDebugInfo100Instructions(ext_inst_index);
+          const NonSemanticShaderDebugInfoInstructions ext_inst_key =
+              NonSemanticShaderDebugInfoInstructions(ext_inst_index);
           switch (ext_inst_key) {
-            case NonSemanticShaderDebugInfo100DebugDeclare:
-            case NonSemanticShaderDebugInfo100DebugValue:
-            case NonSemanticShaderDebugInfo100DebugScope:
-            case NonSemanticShaderDebugInfo100DebugNoScope:
-            case NonSemanticShaderDebugInfo100DebugFunctionDefinition: {
+            case NonSemanticShaderDebugInfoDebugDeclare:
+            case NonSemanticShaderDebugInfoDebugValue:
+            case NonSemanticShaderDebugInfoDebugScope:
+            case NonSemanticShaderDebugInfoDebugNoScope:
+            case NonSemanticShaderDebugInfoDebugFunctionDefinition: {
               if (block_ == nullptr) {  // Inside function but outside blocks
                 Errorf(consumer_, src, loc,
                        "Debug info extension instruction found inside function "
diff --git a/source/opt/ir_loader.h b/source/opt/ir_loader.h
index 16bc2c7..a8d2119 100644
--- a/source/opt/ir_loader.h
+++ b/source/opt/ir_loader.h
@@ -20,6 +20,7 @@
 #include <vector>
 
 #include "source/opt/basic_block.h"
+#include "source/opt/graph.h"
 #include "source/opt/instruction.h"
 #include "source/opt/module.h"
 #include "spirv-tools/libspirv.hpp"
@@ -80,6 +81,8 @@
   std::unique_ptr<Function> function_;
   // The current BasicBlock under construction.
   std::unique_ptr<BasicBlock> block_;
+  // The current Graph under construction.
+  std::unique_ptr<Graph> graph_;
   // Line related debug instructions accumulated thus far.
   std::vector<Instruction> dbg_line_info_;
   // If doing extra line tracking, this is the line instruction that should be
diff --git a/source/opt/legalize_multidim_array_pass.cpp b/source/opt/legalize_multidim_array_pass.cpp
new file mode 100644
index 0000000..cd207b0
--- /dev/null
+++ b/source/opt/legalize_multidim_array_pass.cpp
@@ -0,0 +1,275 @@
+// Copyright (c) 2026 Google LLC
+//
+// 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/legalize_multidim_array_pass.h"
+
+#include "source/opt/constants.h"
+#include "source/opt/desc_sroa_util.h"
+#include "source/opt/ir_builder.h"
+#include "source/opt/ir_context.h"
+#include "source/opt/type_manager.h"
+
+namespace spvtools {
+namespace opt {
+
+Pass::Status LegalizeMultidimArrayPass::Process() {
+  std::vector<Instruction*> vars_to_legalize;
+
+  for (auto& var : context()->types_values()) {
+    if (var.opcode() != spv::Op::OpVariable) continue;
+    if (!IsMultidimArrayOfResources(&var)) continue;
+    if (!CanLegalize(&var)) {
+      context()->EmitErrorMessage("Unable to legalize multidimensional array: ",
+                                  &var);
+      return Status::Failure;
+    }
+    vars_to_legalize.push_back(&var);
+  }
+
+  if (vars_to_legalize.empty()) return Status::SuccessWithoutChange;
+
+  for (auto* var : vars_to_legalize) {
+    uint32_t old_ptr_type_id = var->type_id();
+    uint32_t new_ptr_type_id = FlattenArrayType(var);
+    if (new_ptr_type_id == 0) return Status::Failure;
+    if (!RewriteAccessChains(var, old_ptr_type_id)) return Status::Failure;
+  }
+
+  return Status::SuccessWithChange;
+}
+
+bool LegalizeMultidimArrayPass::IsMultidimArrayOfResources(Instruction* var) {
+  if (!descsroautil::IsDescriptorArray(context(), var)) return false;
+
+  uint32_t type_id = var->type_id();
+  Instruction* type_inst = context()->get_def_use_mgr()->GetDef(type_id);
+  uint32_t pointee_type_id = type_inst->GetSingleWordInOperand(1);
+  std::vector<uint32_t> dims;
+  uint32_t element_type_id = 0;
+  GetArrayDimensions(pointee_type_id, &dims, &element_type_id);
+
+  return dims.size() > 1;
+}
+
+void LegalizeMultidimArrayPass::GetArrayDimensions(uint32_t type_id,
+                                                   std::vector<uint32_t>* dims,
+                                                   uint32_t* element_type_id) {
+  assert(dims != nullptr && "dims cannot be null.");
+  dims->clear();
+
+  Instruction* type_inst = context()->get_def_use_mgr()->GetDef(type_id);
+  while (type_inst->opcode() == spv::Op::OpTypeArray) {
+    uint32_t length_id = type_inst->GetSingleWordInOperand(1);
+    Instruction* length_inst = context()->get_def_use_mgr()->GetDef(length_id);
+    // Assume OpConstant. According to the spec the length could also be an
+    // OpSpecConstantOp. However, DXC will not generate that type of code. The
+    // code to handle spec constants will be much more complicated.
+    assert(length_inst->opcode() == spv::Op::OpConstant);
+    uint32_t length = length_inst->GetSingleWordInOperand(0);
+    dims->push_back(length);
+    type_id = type_inst->GetSingleWordInOperand(0);
+    type_inst = context()->get_def_use_mgr()->GetDef(type_id);
+  }
+  *element_type_id = type_id;
+}
+
+uint32_t LegalizeMultidimArrayPass::FlattenArrayType(Instruction* var) {
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  analysis::ConstantManager* constant_mgr = context()->get_constant_mgr();
+
+  uint32_t ptr_type_id = var->type_id();
+  Instruction* ptr_type_inst =
+      context()->get_def_use_mgr()->GetDef(ptr_type_id);
+  uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
+
+  std::vector<uint32_t> dims;
+  uint32_t element_type_id = 0;
+  GetArrayDimensions(pointee_type_id, &dims, &element_type_id);
+
+  uint32_t total_elements = 1;
+  for (uint32_t dim : dims) {
+    total_elements *= dim;
+  }
+
+  const analysis::Constant* total_elements_const =
+      constant_mgr->GetIntConst(total_elements, 32, false);
+
+  Instruction* total_elements_inst =
+      constant_mgr->GetDefiningInstruction(total_elements_const);
+  uint32_t total_elements_id = total_elements_inst->result_id();
+
+  // Create new OpTypeArray.
+  analysis::Type* element_type = type_mgr->GetType(element_type_id);
+  analysis::Array::LengthInfo length_info = {
+      total_elements_id,
+      {analysis::Array::LengthInfo::kConstant, total_elements}};
+  analysis::Array new_array_type(element_type, length_info);
+  uint32_t new_array_type_id = type_mgr->GetTypeInstruction(&new_array_type);
+
+  // Create new OpTypePointer.
+  spv::StorageClass sc =
+      static_cast<spv::StorageClass>(ptr_type_inst->GetSingleWordInOperand(0));
+  analysis::Pointer new_ptr_type(type_mgr->GetType(new_array_type_id), sc);
+  uint32_t new_ptr_type_id = type_mgr->GetTypeInstruction(&new_ptr_type);
+
+  var->SetResultType(new_ptr_type_id);
+  context()->UpdateDefUse(var);
+
+  // Move the var after the new pointer type to avoid a def-before-use.
+  var->InsertAfter(get_def_use_mgr()->GetDef(new_ptr_type_id));
+
+  return new_ptr_type_id;
+}
+
+bool LegalizeMultidimArrayPass::RewriteAccessChains(Instruction* var,
+                                                    uint32_t old_ptr_type_id) {
+  uint32_t var_id = var->result_id();
+  std::vector<Instruction*> users;
+  // Use a worklist to handle transitive uses (e.g. through OpCopyObject)
+  std::vector<Instruction*> worklist;
+
+  context()->get_def_use_mgr()->ForEachUser(
+      var_id, [&worklist](Instruction* user) { worklist.push_back(user); });
+
+  Instruction* old_ptr_type_inst =
+      context()->get_def_use_mgr()->GetDef(old_ptr_type_id);
+  uint32_t old_pointee_type_id = old_ptr_type_inst->GetSingleWordInOperand(1);
+  std::vector<uint32_t> dims;
+  uint32_t element_type_id = 0;
+  GetArrayDimensions(old_pointee_type_id, &dims, &element_type_id);
+  assert(dims.size() != 0 &&
+         "This variable should have been rejected earlier.");
+
+  // Calculate strides once
+  std::vector<uint32_t> strides(dims.size());
+  strides[dims.size() - 1] = 1;
+  for (int i = static_cast<int>(dims.size()) - 2; i >= 0; --i) {
+    strides[i] = strides[i + 1] * dims[i + 1];
+  }
+
+  // Pre-calculate uint type id
+  uint32_t uint_type_id = context()->get_type_mgr()->GetUIntTypeId();
+  if (uint_type_id == 0) return false;
+
+  while (!worklist.empty()) {
+    Instruction* user = worklist.back();
+    worklist.pop_back();
+
+    if (user->opcode() == spv::Op::OpAccessChain ||
+        user->opcode() == spv::Op::OpInBoundsAccessChain) {
+      uint32_t num_indices = user->NumInOperands() - 1;
+      assert(num_indices >= dims.size());
+
+      InstructionBuilder builder(context(), user, IRContext::kAnalysisDefUse);
+
+      uint32_t linearized_idx_id = 0;
+      for (uint32_t i = 0; i < dims.size(); ++i) {
+        uint32_t idx_id = user->GetSingleWordInOperand(i + 1);
+
+        uint32_t term_id = idx_id;
+        if (strides[i] != 1) {
+          const analysis::Constant* stride_const =
+              context()->get_constant_mgr()->GetConstant(
+                  context()->get_type_mgr()->GetType(uint_type_id),
+                  {strides[i]});
+          Instruction* stride_inst =
+              context()->get_constant_mgr()->GetDefiningInstruction(
+                  stride_const);
+
+          Instruction* mul_inst = builder.AddBinaryOp(
+              uint_type_id, spv::Op::OpIMul, idx_id, stride_inst->result_id());
+          if (mul_inst == nullptr) return false;
+          term_id = mul_inst->result_id();
+        }
+
+        if (linearized_idx_id == 0) {
+          linearized_idx_id = term_id;
+        } else {
+          Instruction* add_inst = builder.AddBinaryOp(
+              uint_type_id, spv::Op::OpIAdd, linearized_idx_id, term_id);
+          if (add_inst == nullptr) return false;
+          linearized_idx_id = add_inst->result_id();
+        }
+      }
+
+      // Create new AccessChain.
+      Instruction::OperandList new_operands;
+      new_operands.push_back(user->GetInOperand(0));
+      new_operands.push_back({SPV_OPERAND_TYPE_ID, {linearized_idx_id}});
+      for (uint32_t i = static_cast<uint32_t>(dims.size()); i < num_indices;
+           ++i) {
+        new_operands.push_back(user->GetInOperand(i + 1));
+      }
+      user->SetInOperands(std::move(new_operands));
+      context()->UpdateDefUse(user);
+    } else if (user->opcode() == spv::Op::OpCopyObject) {
+      // The type of the variable has changed so the result type of the
+      // OpCopyObject will change as well.
+
+      uint32_t operand_id = user->GetSingleWordInOperand(0);
+      Instruction* operand_inst =
+          context()->get_def_use_mgr()->GetDef(operand_id);
+      user->SetResultType(operand_inst->type_id());
+      context()->UpdateDefUse(user);
+
+      // Add users of this copy to worklist
+      context()->get_def_use_mgr()->ForEachUser(
+          user->result_id(),
+          [&worklist](Instruction* u) { worklist.push_back(u); });
+    }
+  }
+  return true;
+}
+
+bool LegalizeMultidimArrayPass::CheckUse(Instruction* inst,
+                                         uint32_t max_depth) {
+  if (inst->opcode() == spv::Op::OpAccessChain ||
+      inst->opcode() == spv::Op::OpInBoundsAccessChain) {
+    uint32_t num_indices = inst->NumInOperands() - 1;
+    return num_indices >= max_depth;
+  } else if (inst->opcode() == spv::Op::OpCopyObject) {
+    bool ok = true;
+    return !context()->get_def_use_mgr()->WhileEachUser(
+        inst->result_id(),
+        [&](Instruction* u) { return !CheckUse(u, max_depth); });
+    return ok;
+  } else if (inst->IsDecoration() || inst->opcode() == spv::Op::OpName ||
+             inst->opcode() == spv::Op::OpMemberName) {
+    // Metadata is fine.
+    return true;
+  }
+
+  // Direct use of array or partial array without AccessChain is not allowed.
+  return false;
+}
+
+bool LegalizeMultidimArrayPass::CanLegalize(Instruction* var) {
+  bool ok = true;
+  uint32_t ptr_type_id = var->type_id();
+  Instruction* ptr_type_inst =
+      context()->get_def_use_mgr()->GetDef(ptr_type_id);
+  uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
+  std::vector<uint32_t> dims;
+  uint32_t element_type_id = 0;
+  GetArrayDimensions(pointee_type_id, &dims, &element_type_id);
+
+  context()->get_def_use_mgr()->ForEachUser(
+      var->result_id(), [&](Instruction* u) {
+        if (!CheckUse(u, static_cast<uint32_t>(dims.size()))) ok = false;
+      });
+  return ok;
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/legalize_multidim_array_pass.h b/source/opt/legalize_multidim_array_pass.h
new file mode 100644
index 0000000..61c912d
--- /dev/null
+++ b/source/opt/legalize_multidim_array_pass.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2026 Google LLC
+//
+// 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_LEGALIZE_MULTIDIM_ARRAY_PASS_H_
+#define SOURCE_OPT_LEGALIZE_MULTIDIM_ARRAY_PASS_H_
+
+#include "source/opt/pass.h"
+
+namespace spvtools {
+namespace opt {
+
+// Pass to legalize multidimensional arrays of resources for Vulkan.
+// It transforms multidimensional arrays into single-dimensional ones.
+class LegalizeMultidimArrayPass : public Pass {
+ public:
+  const char* name() const override { return "legalize-multidim-array"; }
+  Status Process() override;
+
+ private:
+  // Returns true if |var| is a multidimensional array of resources.
+  bool IsMultidimArrayOfResources(Instruction* var);
+
+  // Flattens the multidimensional array type of |var| and returns the new type
+  // id.
+  uint32_t FlattenArrayType(Instruction* var);
+
+  // Rewrites all access chains that use |var|.
+  bool RewriteAccessChains(Instruction* var, uint32_t old_ptr_type_id);
+
+  // Returns true if all uses of |var| can be legalized.
+  bool CanLegalize(Instruction* var);
+
+  // Recursively checks if the uses of |inst| can be legalized.
+  bool CheckUse(Instruction* inst, uint32_t max_depth);
+
+  // Returns the dimensions of the array type |type_id|.
+  void GetArrayDimensions(uint32_t type_id, std::vector<uint32_t>* dims,
+                          uint32_t* element_type_id);
+};
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // SOURCE_OPT_LEGALIZE_MULTIDIM_ARRAY_PASS_H_
diff --git a/source/opt/licm_pass.cpp b/source/opt/licm_pass.cpp
index f2a6e4d..996762b 100644
--- a/source/opt/licm_pass.cpp
+++ b/source/opt/licm_pass.cpp
@@ -118,7 +118,6 @@
 }
 
 bool LICMPass::HoistInstruction(Loop* loop, Instruction* inst) {
-  // TODO(1841): Handle failure to create pre-header.
   BasicBlock* pre_header_bb = loop->GetOrCreatePreHeaderBlock();
   if (!pre_header_bb) {
     return false;
diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp
index 91ea7c6..44e3645 100644
--- a/source/opt/local_access_chain_convert_pass.cpp
+++ b/source/opt/local_access_chain_convert_pass.cpp
@@ -1,6 +1,8 @@
 // Copyright (c) 2017 The Khronos Group Inc.
 // Copyright (c) 2017 Valve Corporation
 // Copyright (c) 2017 LunarG Inc.
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -352,15 +354,15 @@
     if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
       return false;
   }
-  // only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise
-  // around unknown extended
-  // instruction sets even if they are non-semantic
+  // Only allow NonSemantic.Shader.DebugInfo (any version); we cannot safely
+  // optimise around unknown extended instruction sets even if non-semantic.
   for (auto& inst : context()->module()->ext_inst_imports()) {
     assert(inst.opcode() == spv::Op::OpExtInstImport &&
            "Expecting an import of an extension's instruction set.");
     const std::string extension_name = inst.GetInOperand(0).AsString();
     if (spvtools::utils::starts_with(extension_name, "NonSemantic.") &&
-        extension_name != "NonSemantic.Shader.DebugInfo.100") {
+        !spvtools::utils::starts_with(extension_name,
+                                      "NonSemantic.Shader.DebugInfo.")) {
       return false;
     }
   }
@@ -397,41 +399,84 @@
 
 void LocalAccessChainConvertPass::InitExtensions() {
   extensions_allowlist_.clear();
-  extensions_allowlist_.insert(
-      {"SPV_AMD_shader_explicit_vertex_parameter",
-       "SPV_AMD_shader_trinary_minmax", "SPV_AMD_gcn_shader",
-       "SPV_KHR_shader_ballot", "SPV_AMD_shader_ballot",
-       "SPV_AMD_gpu_shader_half_float", "SPV_KHR_shader_draw_parameters",
-       "SPV_KHR_subgroup_vote", "SPV_KHR_8bit_storage", "SPV_KHR_16bit_storage",
-       "SPV_KHR_device_group", "SPV_KHR_multiview",
-       "SPV_NVX_multiview_per_view_attributes", "SPV_NV_viewport_array2",
-       "SPV_NV_stereo_view_rendering", "SPV_NV_sample_mask_override_coverage",
-       "SPV_NV_geometry_shader_passthrough", "SPV_AMD_texture_gather_bias_lod",
-       "SPV_KHR_storage_buffer_storage_class",
-       // SPV_KHR_variable_pointers
-       //   Currently do not support extended pointer expressions
-       "SPV_AMD_gpu_shader_int16", "SPV_KHR_post_depth_coverage",
-       "SPV_KHR_shader_atomic_counter_ops", "SPV_EXT_shader_stencil_export",
-       "SPV_EXT_shader_viewport_index_layer",
-       "SPV_AMD_shader_image_load_store_lod", "SPV_AMD_shader_fragment_mask",
-       "SPV_EXT_fragment_fully_covered", "SPV_AMD_gpu_shader_half_float_fetch",
-       "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1",
-       "SPV_GOOGLE_user_type", "SPV_NV_shader_subgroup_partitioned",
-       "SPV_EXT_demote_to_helper_invocation", "SPV_EXT_descriptor_indexing",
-       "SPV_NV_fragment_shader_barycentric",
-       "SPV_NV_compute_shader_derivatives", "SPV_NV_shader_image_footprint",
-       "SPV_NV_shading_rate", "SPV_NV_mesh_shader", "SPV_EXT_mesh_shader",
-       "SPV_NV_ray_tracing", "SPV_KHR_ray_tracing", "SPV_KHR_ray_query",
-       "SPV_EXT_fragment_invocation_density", "SPV_KHR_terminate_invocation",
-       "SPV_KHR_subgroup_uniform_control_flow", "SPV_KHR_integer_dot_product",
-       "SPV_EXT_shader_image_int64", "SPV_KHR_non_semantic_info",
-       "SPV_KHR_uniform_group_instructions",
-       "SPV_KHR_fragment_shader_barycentric", "SPV_KHR_vulkan_memory_model",
-       "SPV_NV_bindless_texture", "SPV_EXT_shader_atomic_float_add",
-       "SPV_EXT_fragment_shader_interlock",
-       "SPV_KHR_compute_shader_derivatives", "SPV_NV_cooperative_matrix",
-       "SPV_KHR_cooperative_matrix", "SPV_KHR_ray_tracing_position_fetch",
-       "SPV_KHR_fragment_shading_rate"});
+  extensions_allowlist_.insert({
+      "SPV_AMD_shader_explicit_vertex_parameter",
+      "SPV_AMD_shader_trinary_minmax",
+      "SPV_AMD_gcn_shader",
+      "SPV_KHR_shader_ballot",
+      "SPV_AMD_shader_ballot",
+      "SPV_AMD_gpu_shader_half_float",
+      "SPV_KHR_shader_draw_parameters",
+      "SPV_KHR_subgroup_vote",
+      "SPV_KHR_8bit_storage",
+      "SPV_KHR_16bit_storage",
+      "SPV_KHR_device_group",
+      "SPV_KHR_multiview",
+      "SPV_NVX_multiview_per_view_attributes",
+      "SPV_NV_viewport_array2",
+      "SPV_NV_stereo_view_rendering",
+      "SPV_NV_sample_mask_override_coverage",
+      "SPV_NV_geometry_shader_passthrough",
+      "SPV_AMD_texture_gather_bias_lod",
+      "SPV_KHR_storage_buffer_storage_class",
+      // SPV_KHR_variable_pointers
+      //   Currently do not support extended pointer expressions
+      "SPV_AMD_gpu_shader_int16",
+      "SPV_KHR_post_depth_coverage",
+      "SPV_KHR_shader_atomic_counter_ops",
+      "SPV_EXT_shader_stencil_export",
+      "SPV_EXT_shader_viewport_index_layer",
+      "SPV_AMD_shader_image_load_store_lod",
+      "SPV_AMD_shader_fragment_mask",
+      "SPV_EXT_fragment_fully_covered",
+      "SPV_AMD_gpu_shader_half_float_fetch",
+      "SPV_GOOGLE_decorate_string",
+      "SPV_GOOGLE_hlsl_functionality1",
+      "SPV_GOOGLE_user_type",
+      "SPV_NV_shader_subgroup_partitioned",
+      "SPV_EXT_demote_to_helper_invocation",
+      "SPV_EXT_descriptor_indexing",
+      "SPV_EXT_descriptor_heap",
+      "SPV_NV_fragment_shader_barycentric",
+      "SPV_NV_compute_shader_derivatives",
+      "SPV_NV_shader_image_footprint",
+      "SPV_NV_shading_rate",
+      "SPV_NV_mesh_shader",
+      "SPV_EXT_mesh_shader",
+      "SPV_NV_ray_tracing",
+      "SPV_KHR_ray_tracing",
+      "SPV_KHR_ray_query",
+      "SPV_EXT_fragment_invocation_density",
+      "SPV_KHR_terminate_invocation",
+      "SPV_KHR_shader_clock",
+      "SPV_KHR_subgroup_uniform_control_flow",
+      "SPV_KHR_integer_dot_product",
+      "SPV_EXT_shader_image_int64",
+      "SPV_KHR_non_semantic_info",
+      "SPV_KHR_uniform_group_instructions",
+      "SPV_KHR_fragment_shader_barycentric",
+      "SPV_KHR_vulkan_memory_model",
+      "SPV_NV_bindless_texture",
+      "SPV_EXT_shader_atomic_float_add",
+      "SPV_EXT_fragment_shader_interlock",
+      "SPV_KHR_compute_shader_derivatives",
+      "SPV_NV_cooperative_matrix",
+      "SPV_KHR_cooperative_matrix",
+      "SPV_KHR_ray_tracing_position_fetch",
+      "SPV_AMDX_shader_enqueue",
+      "SPV_KHR_fragment_shading_rate",
+      "SPV_KHR_quad_control",
+      "SPV_NV_shader_invocation_reorder",
+      "SPV_NV_cluster_acceleration_structure",
+      "SPV_NV_linear_swept_spheres",
+      "SPV_KHR_maximal_reconvergence",
+      "SPV_NV_push_constant_bank",
+      "SPV_EXT_opacity_micromap",
+      "SPV_EXT_shader_invocation_reorder",
+      "SPV_EXT_shader_atomic_float16_add",
+      "SPV_KHR_abort",
+      "SPV_KHR_constant_data",
+  });
 }
 
 bool LocalAccessChainConvertPass::AnyIndexIsOutOfBounds(
diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp
index f9c5a79..9dcef9e 100644
--- a/source/opt/local_single_block_elim_pass.cpp
+++ b/source/opt/local_single_block_elim_pass.cpp
@@ -1,6 +1,8 @@
 // Copyright (c) 2017 The Khronos Group Inc.
 // Copyright (c) 2017 Valve Corporation
 // Copyright (c) 2017 LunarG Inc.
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -185,15 +187,15 @@
     if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
       return false;
   }
-  // only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise
-  // around unknown extended
-  // instruction sets even if they are non-semantic
+  // Only allow NonSemantic.Shader.DebugInfo (any version); we cannot safely
+  // optimise around unknown extended instruction sets even if non-semantic.
   for (auto& inst : context()->module()->ext_inst_imports()) {
     assert(inst.opcode() == spv::Op::OpExtInstImport &&
            "Expecting an import of an extension's instruction set.");
     const std::string extension_name = inst.GetInOperand(0).AsString();
     if (spvtools::utils::starts_with(extension_name, "NonSemantic.") &&
-        extension_name != "NonSemantic.Shader.DebugInfo.100") {
+        !spvtools::utils::starts_with(extension_name,
+                                      "NonSemantic.Shader.DebugInfo.")) {
       return false;
     }
   }
@@ -233,69 +235,85 @@
 
 void LocalSingleBlockLoadStoreElimPass::InitExtensions() {
   extensions_allowlist_.clear();
-  extensions_allowlist_.insert({"SPV_AMD_shader_explicit_vertex_parameter",
-                                "SPV_AMD_shader_trinary_minmax",
-                                "SPV_AMD_gcn_shader",
-                                "SPV_KHR_shader_ballot",
-                                "SPV_AMD_shader_ballot",
-                                "SPV_AMD_gpu_shader_half_float",
-                                "SPV_KHR_shader_draw_parameters",
-                                "SPV_KHR_subgroup_vote",
-                                "SPV_KHR_8bit_storage",
-                                "SPV_KHR_16bit_storage",
-                                "SPV_KHR_device_group",
-                                "SPV_KHR_multiview",
-                                "SPV_NVX_multiview_per_view_attributes",
-                                "SPV_NV_viewport_array2",
-                                "SPV_NV_stereo_view_rendering",
-                                "SPV_NV_sample_mask_override_coverage",
-                                "SPV_NV_geometry_shader_passthrough",
-                                "SPV_AMD_texture_gather_bias_lod",
-                                "SPV_KHR_storage_buffer_storage_class",
-                                "SPV_KHR_variable_pointers",
-                                "SPV_AMD_gpu_shader_int16",
-                                "SPV_KHR_post_depth_coverage",
-                                "SPV_KHR_shader_atomic_counter_ops",
-                                "SPV_EXT_shader_stencil_export",
-                                "SPV_EXT_shader_viewport_index_layer",
-                                "SPV_AMD_shader_image_load_store_lod",
-                                "SPV_AMD_shader_fragment_mask",
-                                "SPV_EXT_fragment_fully_covered",
-                                "SPV_AMD_gpu_shader_half_float_fetch",
-                                "SPV_GOOGLE_decorate_string",
-                                "SPV_GOOGLE_hlsl_functionality1",
-                                "SPV_GOOGLE_user_type",
-                                "SPV_NV_shader_subgroup_partitioned",
-                                "SPV_EXT_demote_to_helper_invocation",
-                                "SPV_EXT_descriptor_indexing",
-                                "SPV_NV_fragment_shader_barycentric",
-                                "SPV_NV_compute_shader_derivatives",
-                                "SPV_NV_shader_image_footprint",
-                                "SPV_NV_shading_rate",
-                                "SPV_NV_mesh_shader",
-                                "SPV_EXT_mesh_shader",
-                                "SPV_NV_ray_tracing",
-                                "SPV_KHR_ray_tracing",
-                                "SPV_KHR_ray_query",
-                                "SPV_EXT_fragment_invocation_density",
-                                "SPV_EXT_physical_storage_buffer",
-                                "SPV_KHR_physical_storage_buffer",
-                                "SPV_KHR_terminate_invocation",
-                                "SPV_KHR_subgroup_uniform_control_flow",
-                                "SPV_KHR_integer_dot_product",
-                                "SPV_EXT_shader_image_int64",
-                                "SPV_KHR_non_semantic_info",
-                                "SPV_KHR_uniform_group_instructions",
-                                "SPV_KHR_fragment_shader_barycentric",
-                                "SPV_KHR_vulkan_memory_model",
-                                "SPV_NV_bindless_texture",
-                                "SPV_EXT_shader_atomic_float_add",
-                                "SPV_EXT_fragment_shader_interlock",
-                                "SPV_KHR_compute_shader_derivatives",
-                                "SPV_NV_cooperative_matrix",
-                                "SPV_KHR_cooperative_matrix",
-                                "SPV_KHR_ray_tracing_position_fetch",
-                                "SPV_KHR_fragment_shading_rate"});
+  extensions_allowlist_.insert({
+      "SPV_AMD_shader_explicit_vertex_parameter",
+      "SPV_AMD_shader_trinary_minmax",
+      "SPV_AMD_gcn_shader",
+      "SPV_KHR_shader_ballot",
+      "SPV_AMD_shader_ballot",
+      "SPV_AMD_gpu_shader_half_float",
+      "SPV_KHR_shader_draw_parameters",
+      "SPV_KHR_subgroup_vote",
+      "SPV_KHR_8bit_storage",
+      "SPV_KHR_16bit_storage",
+      "SPV_KHR_device_group",
+      "SPV_KHR_multiview",
+      "SPV_NVX_multiview_per_view_attributes",
+      "SPV_NV_viewport_array2",
+      "SPV_NV_stereo_view_rendering",
+      "SPV_NV_sample_mask_override_coverage",
+      "SPV_NV_geometry_shader_passthrough",
+      "SPV_AMD_texture_gather_bias_lod",
+      "SPV_KHR_storage_buffer_storage_class",
+      "SPV_KHR_variable_pointers",
+      "SPV_AMD_gpu_shader_int16",
+      "SPV_KHR_post_depth_coverage",
+      "SPV_KHR_shader_atomic_counter_ops",
+      "SPV_EXT_shader_stencil_export",
+      "SPV_EXT_shader_viewport_index_layer",
+      "SPV_AMD_shader_image_load_store_lod",
+      "SPV_AMD_shader_fragment_mask",
+      "SPV_EXT_fragment_fully_covered",
+      "SPV_AMD_gpu_shader_half_float_fetch",
+      "SPV_GOOGLE_decorate_string",
+      "SPV_GOOGLE_hlsl_functionality1",
+      "SPV_GOOGLE_user_type",
+      "SPV_NV_shader_subgroup_partitioned",
+      "SPV_EXT_demote_to_helper_invocation",
+      "SPV_EXT_descriptor_indexing",
+      "SPV_EXT_descriptor_heap",
+      "SPV_NV_fragment_shader_barycentric",
+      "SPV_NV_compute_shader_derivatives",
+      "SPV_NV_shader_image_footprint",
+      "SPV_NV_shading_rate",
+      "SPV_NV_mesh_shader",
+      "SPV_EXT_mesh_shader",
+      "SPV_NV_ray_tracing",
+      "SPV_KHR_ray_tracing",
+      "SPV_KHR_ray_query",
+      "SPV_EXT_fragment_invocation_density",
+      "SPV_EXT_physical_storage_buffer",
+      "SPV_KHR_physical_storage_buffer",
+      "SPV_KHR_terminate_invocation",
+      "SPV_KHR_shader_clock",
+      "SPV_KHR_subgroup_uniform_control_flow",
+      "SPV_KHR_integer_dot_product",
+      "SPV_EXT_shader_image_int64",
+      "SPV_KHR_non_semantic_info",
+      "SPV_KHR_uniform_group_instructions",
+      "SPV_KHR_fragment_shader_barycentric",
+      "SPV_KHR_vulkan_memory_model",
+      "SPV_NV_bindless_texture",
+      "SPV_EXT_shader_atomic_float_add",
+      "SPV_EXT_fragment_shader_interlock",
+      "SPV_KHR_compute_shader_derivatives",
+      "SPV_NV_cooperative_matrix",
+      "SPV_KHR_cooperative_matrix",
+      "SPV_KHR_ray_tracing_position_fetch",
+      "SPV_AMDX_shader_enqueue",
+      "SPV_KHR_fragment_shading_rate",
+      "SPV_KHR_quad_control",
+      "SPV_NV_shader_invocation_reorder",
+      "SPV_NV_cluster_acceleration_structure",
+      "SPV_NV_linear_swept_spheres",
+      "SPV_KHR_maximal_reconvergence",
+      "SPV_NV_push_constant_bank",
+      "SPV_EXT_opacity_micromap",
+      "SPV_EXT_shader_invocation_reorder",
+      "SPV_EXT_shader_atomic_float16_add",
+      "SPV_KHR_abort",
+      "SPV_KHR_constant_data",
+  });
 }
 
 }  // namespace opt
diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp
index 38fa14e..a3b2949 100644
--- a/source/opt/local_single_store_elim_pass.cpp
+++ b/source/opt/local_single_store_elim_pass.cpp
@@ -1,6 +1,8 @@
 // Copyright (c) 2017 The Khronos Group Inc.
 // Copyright (c) 2017 Valve Corporation
 // Copyright (c) 2017 LunarG Inc.
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -48,15 +50,15 @@
     if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
       return false;
   }
-  // only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise
-  // around unknown extended
-  // instruction sets even if they are non-semantic
+  // Only allow NonSemantic.Shader.DebugInfo (any version); we cannot safely
+  // optimise around unknown extended instruction sets even if non-semantic.
   for (auto& inst : context()->module()->ext_inst_imports()) {
     assert(inst.opcode() == spv::Op::OpExtInstImport &&
            "Expecting an import of an extension's instruction set.");
     const std::string extension_name = inst.GetInOperand(0).AsString();
     if (spvtools::utils::starts_with(extension_name, "NonSemantic.") &&
-        extension_name != "NonSemantic.Shader.DebugInfo.100") {
+        !spvtools::utils::starts_with(extension_name,
+                                      "NonSemantic.Shader.DebugInfo.")) {
       return false;
     }
   }
@@ -86,66 +88,84 @@
 }
 
 void LocalSingleStoreElimPass::InitExtensionAllowList() {
-  extensions_allowlist_.insert({"SPV_AMD_shader_explicit_vertex_parameter",
-                                "SPV_AMD_shader_trinary_minmax",
-                                "SPV_AMD_gcn_shader",
-                                "SPV_KHR_shader_ballot",
-                                "SPV_AMD_shader_ballot",
-                                "SPV_AMD_gpu_shader_half_float",
-                                "SPV_KHR_shader_draw_parameters",
-                                "SPV_KHR_subgroup_vote",
-                                "SPV_KHR_8bit_storage",
-                                "SPV_KHR_16bit_storage",
-                                "SPV_KHR_device_group",
-                                "SPV_KHR_multiview",
-                                "SPV_NVX_multiview_per_view_attributes",
-                                "SPV_NV_viewport_array2",
-                                "SPV_NV_stereo_view_rendering",
-                                "SPV_NV_sample_mask_override_coverage",
-                                "SPV_NV_geometry_shader_passthrough",
-                                "SPV_AMD_texture_gather_bias_lod",
-                                "SPV_KHR_storage_buffer_storage_class",
-                                "SPV_KHR_variable_pointers",
-                                "SPV_AMD_gpu_shader_int16",
-                                "SPV_KHR_post_depth_coverage",
-                                "SPV_KHR_shader_atomic_counter_ops",
-                                "SPV_EXT_shader_stencil_export",
-                                "SPV_EXT_shader_viewport_index_layer",
-                                "SPV_AMD_shader_image_load_store_lod",
-                                "SPV_AMD_shader_fragment_mask",
-                                "SPV_EXT_fragment_fully_covered",
-                                "SPV_AMD_gpu_shader_half_float_fetch",
-                                "SPV_GOOGLE_decorate_string",
-                                "SPV_GOOGLE_hlsl_functionality1",
-                                "SPV_NV_shader_subgroup_partitioned",
-                                "SPV_EXT_descriptor_indexing",
-                                "SPV_NV_fragment_shader_barycentric",
-                                "SPV_NV_compute_shader_derivatives",
-                                "SPV_NV_shader_image_footprint",
-                                "SPV_NV_shading_rate",
-                                "SPV_NV_mesh_shader",
-                                "SPV_EXT_mesh_shader",
-                                "SPV_NV_ray_tracing",
-                                "SPV_KHR_ray_query",
-                                "SPV_EXT_fragment_invocation_density",
-                                "SPV_EXT_physical_storage_buffer",
-                                "SPV_KHR_physical_storage_buffer",
-                                "SPV_KHR_terminate_invocation",
-                                "SPV_KHR_subgroup_uniform_control_flow",
-                                "SPV_KHR_integer_dot_product",
-                                "SPV_EXT_shader_image_int64",
-                                "SPV_KHR_non_semantic_info",
-                                "SPV_KHR_uniform_group_instructions",
-                                "SPV_KHR_fragment_shader_barycentric",
-                                "SPV_KHR_vulkan_memory_model",
-                                "SPV_NV_bindless_texture",
-                                "SPV_EXT_shader_atomic_float_add",
-                                "SPV_EXT_fragment_shader_interlock",
-                                "SPV_KHR_compute_shader_derivatives",
-                                "SPV_NV_cooperative_matrix",
-                                "SPV_KHR_cooperative_matrix",
-                                "SPV_KHR_ray_tracing_position_fetch",
-                                "SPV_KHR_fragment_shading_rate"});
+  extensions_allowlist_.insert({
+      "SPV_AMD_shader_explicit_vertex_parameter",
+      "SPV_AMD_shader_trinary_minmax",
+      "SPV_AMD_gcn_shader",
+      "SPV_KHR_shader_ballot",
+      "SPV_AMD_shader_ballot",
+      "SPV_AMD_gpu_shader_half_float",
+      "SPV_KHR_shader_draw_parameters",
+      "SPV_KHR_subgroup_vote",
+      "SPV_KHR_8bit_storage",
+      "SPV_KHR_16bit_storage",
+      "SPV_KHR_device_group",
+      "SPV_KHR_multiview",
+      "SPV_NVX_multiview_per_view_attributes",
+      "SPV_NV_viewport_array2",
+      "SPV_NV_stereo_view_rendering",
+      "SPV_NV_sample_mask_override_coverage",
+      "SPV_NV_geometry_shader_passthrough",
+      "SPV_AMD_texture_gather_bias_lod",
+      "SPV_KHR_storage_buffer_storage_class",
+      "SPV_KHR_variable_pointers",
+      "SPV_AMD_gpu_shader_int16",
+      "SPV_KHR_post_depth_coverage",
+      "SPV_KHR_shader_atomic_counter_ops",
+      "SPV_EXT_shader_stencil_export",
+      "SPV_EXT_shader_viewport_index_layer",
+      "SPV_AMD_shader_image_load_store_lod",
+      "SPV_AMD_shader_fragment_mask",
+      "SPV_EXT_fragment_fully_covered",
+      "SPV_AMD_gpu_shader_half_float_fetch",
+      "SPV_GOOGLE_decorate_string",
+      "SPV_GOOGLE_hlsl_functionality1",
+      "SPV_GOOGLE_user_type",
+      "SPV_NV_shader_subgroup_partitioned",
+      "SPV_EXT_descriptor_indexing",
+      "SPV_EXT_descriptor_heap",
+      "SPV_NV_fragment_shader_barycentric",
+      "SPV_NV_compute_shader_derivatives",
+      "SPV_NV_shader_image_footprint",
+      "SPV_NV_shading_rate",
+      "SPV_NV_mesh_shader",
+      "SPV_EXT_mesh_shader",
+      "SPV_NV_ray_tracing",
+      "SPV_KHR_ray_tracing",
+      "SPV_KHR_ray_query",
+      "SPV_EXT_fragment_invocation_density",
+      "SPV_EXT_physical_storage_buffer",
+      "SPV_KHR_physical_storage_buffer",
+      "SPV_KHR_terminate_invocation",
+      "SPV_KHR_shader_clock",
+      "SPV_KHR_subgroup_uniform_control_flow",
+      "SPV_KHR_integer_dot_product",
+      "SPV_EXT_shader_image_int64",
+      "SPV_KHR_non_semantic_info",
+      "SPV_KHR_uniform_group_instructions",
+      "SPV_KHR_fragment_shader_barycentric",
+      "SPV_KHR_vulkan_memory_model",
+      "SPV_NV_bindless_texture",
+      "SPV_EXT_shader_atomic_float_add",
+      "SPV_EXT_fragment_shader_interlock",
+      "SPV_KHR_compute_shader_derivatives",
+      "SPV_NV_cooperative_matrix",
+      "SPV_KHR_cooperative_matrix",
+      "SPV_KHR_ray_tracing_position_fetch",
+      "SPV_AMDX_shader_enqueue",
+      "SPV_KHR_fragment_shading_rate",
+      "SPV_KHR_quad_control",
+      "SPV_NV_shader_invocation_reorder",
+      "SPV_NV_cluster_acceleration_structure",
+      "SPV_NV_linear_swept_spheres",
+      "SPV_KHR_maximal_reconvergence",
+      "SPV_NV_push_constant_bank",
+      "SPV_EXT_opacity_micromap",
+      "SPV_EXT_shader_invocation_reorder",
+      "SPV_EXT_shader_atomic_float16_add",
+      "SPV_KHR_abort",
+      "SPV_KHR_constant_data",
+  });
 }
 bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {
   std::vector<Instruction*> users;
diff --git a/source/opt/loop_descriptor.cpp b/source/opt/loop_descriptor.cpp
index cbfc2e7..b0064b4 100644
--- a/source/opt/loop_descriptor.cpp
+++ b/source/opt/loop_descriptor.cpp
@@ -25,6 +25,7 @@
 #include "source/opt/dominator_tree.h"
 #include "source/opt/ir_context.h"
 #include "source/opt/iterator.h"
+#include "source/opt/pass.h"
 #include "source/opt/tree_iterator.h"
 #include "source/util/make_unique.h"
 
@@ -278,6 +279,9 @@
 
   CFG* cfg = context_->cfg();
   loop_header_ = cfg->SplitLoopHeader(loop_header_);
+  if (loop_header_ == nullptr) {
+    return nullptr;
+  }
   return loop_preheader_;
 }
 
@@ -920,18 +924,19 @@
   return induction;
 }
 
-bool LoopDescriptor::CreatePreHeaderBlocksIfMissing() {
-  auto modified = false;
+LoopDescriptor::Status LoopDescriptor::CreatePreHeaderBlocksIfMissing() {
+  bool modified = false;
 
   for (auto& loop : *this) {
     if (!loop.GetPreHeaderBlock()) {
+      if (!loop.GetOrCreatePreHeaderBlock()) {
+        return Status::Failure;
+      }
       modified = true;
-      // TODO(1841): Handle failure to create pre-header.
-      loop.GetOrCreatePreHeaderBlock();
     }
   }
 
-  return modified;
+  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
 // Add and remove loops which have been marked for addition and removal to
diff --git a/source/opt/loop_descriptor.h b/source/opt/loop_descriptor.h
index d451496..bd93bfa 100644
--- a/source/opt/loop_descriptor.h
+++ b/source/opt/loop_descriptor.h
@@ -28,6 +28,7 @@
 #include "source/opt/dominator_analysis.h"
 #include "source/opt/module.h"
 #include "source/opt/tree_iterator.h"
+#include "source/util/status.h"
 
 namespace spvtools {
 namespace opt {
@@ -425,6 +426,9 @@
   using pre_iterator = TreeDFIterator<Loop>;
   using const_pre_iterator = TreeDFIterator<const Loop>;
 
+  // The status of processing a module.
+  using Status = utils::Status;
+
   // Creates a loop object for all loops found in |f|.
   LoopDescriptor(IRContext* context, const Function* f);
 
@@ -506,9 +510,11 @@
     loops_to_add_.emplace_back(std::make_pair(parent, std::move(loop_to_add)));
   }
 
-  // Checks all loops in |this| and will create pre-headers for all loops
-  // that don't have one. Returns |true| if any blocks were created.
-  bool CreatePreHeaderBlocksIfMissing();
+  // Creates pre-header blocks for all loops in the function that do not have
+  // one. Returns `LoopDescriptor::Status::kSuccessWithChange` if any change is
+  // made, `LoopDescriptor::Status::kSuccessWithoutChange` if no change is made,
+  // and `LoopDescriptor::Status::kFailure` if it fails to create a pre-header.
+  Status CreatePreHeaderBlocksIfMissing();
 
   // Should be called to preserve the LoopAnalysis after loops have been marked
   // for addition with AddLoop or MarkLoopForRemoval.
diff --git a/source/opt/loop_fission.cpp b/source/opt/loop_fission.cpp
index 1bbe4ba..fbbee11 100644
--- a/source/opt/loop_fission.cpp
+++ b/source/opt/loop_fission.cpp
@@ -362,14 +362,19 @@
   LoopUtils util{context_, loop_};
   LoopUtils::LoopCloningResult clone_results;
   Loop* cloned_loop = util.CloneAndAttachLoopToHeader(&clone_results);
+  if (!cloned_loop) {
+    return nullptr;
+  }
 
   // Update the OpLoopMerge in the cloned loop.
   cloned_loop->UpdateLoopMergeInst();
 
   // Add the loop_ to the module.
-  // TODO(1841): Handle failure to create pre-header.
-  Function::iterator it =
-      util.GetFunction()->FindBlock(loop_->GetOrCreatePreHeaderBlock()->id());
+  BasicBlock* pre_header = loop_->GetOrCreatePreHeaderBlock();
+  if (!pre_header) {
+    return nullptr;
+  }
+  Function::iterator it = util.GetFunction()->FindBlock(pre_header->id());
   util.GetFunction()->AddBasicBlocks(clone_results.cloned_bb_.begin(),
                                      clone_results.cloned_bb_.end(), ++it);
   loop_->SetPreHeaderBlock(cloned_loop->GetMergeBlock());
@@ -478,6 +483,9 @@
 
         if (impl.CanPerformSplit()) {
           Loop* second_loop = impl.SplitLoop();
+          if (!second_loop) {
+            return Status::Failure;
+          }
           changed = true;
           context()->InvalidateAnalysesExceptFor(
               IRContext::kAnalysisLoopAnalysis);
diff --git a/source/opt/loop_fusion_pass.cpp b/source/opt/loop_fusion_pass.cpp
index 097430f..c8e74c8 100644
--- a/source/opt/loop_fusion_pass.cpp
+++ b/source/opt/loop_fusion_pass.cpp
@@ -22,23 +22,27 @@
 namespace opt {
 
 Pass::Status LoopFusionPass::Process() {
-  bool modified = false;
+  Status status = Status::SuccessWithoutChange;
   Module* module = context()->module();
 
   // Process each function in the module
   for (Function& f : *module) {
-    modified |= ProcessFunction(&f);
+    status = CombineStatus(status, ProcessFunction(&f));
+    if (status == Status::Failure) return Status::Failure;
   }
 
-  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
+  return status;
 }
 
-bool LoopFusionPass::ProcessFunction(Function* function) {
+Pass::Status LoopFusionPass::ProcessFunction(Function* function) {
   LoopDescriptor& ld = *context()->GetLoopDescriptor(function);
 
   // If a loop doesn't have a preheader needs then it needs to be created. Make
   // sure to return Status::SuccessWithChange in that case.
-  auto modified = ld.CreatePreHeaderBlocksIfMissing();
+  bool modified = false;
+  auto status = ld.CreatePreHeaderBlocksIfMissing();
+  if (status == LoopDescriptor::Status::Failure) return Status::Failure;
+  modified = status == LoopDescriptor::Status::SuccessWithChange;
 
   // TODO(tremmelg): Could the only loop that |loop| could possibly be fused be
   // picked out so don't have to check every loop
@@ -55,13 +59,13 @@
           fusion.Fuse();
           // Recurse, as the current iterators will have been invalidated.
           ProcessFunction(function);
-          return true;
+          return Status::SuccessWithChange;
         }
       }
     }
   }
 
-  return modified;
+  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
 }  // namespace opt
diff --git a/source/opt/loop_fusion_pass.h b/source/opt/loop_fusion_pass.h
index 9d5b7cc..9c1f567 100644
--- a/source/opt/loop_fusion_pass.h
+++ b/source/opt/loop_fusion_pass.h
@@ -39,7 +39,7 @@
  private:
   // Fuse loops in |function| if compatible, legal and the fused loop won't use
   // too many registers.
-  bool ProcessFunction(Function* function);
+  Status ProcessFunction(Function* function);
 
   // The maximum number of registers a fused loop is allowed to use.
   size_t max_registers_per_loop_;
diff --git a/source/opt/loop_peeling.cpp b/source/opt/loop_peeling.cpp
index 25c6db1..e7f3876 100644
--- a/source/opt/loop_peeling.cpp
+++ b/source/opt/loop_peeling.cpp
@@ -45,7 +45,7 @@
 
 size_t LoopPeelingPass::code_grow_threshold_ = 1000;
 
-void LoopPeeling::DuplicateAndConnectLoop(
+bool LoopPeeling::DuplicateAndConnectLoop(
     LoopUtils::LoopCloningResult* clone_results) {
   CFG& cfg = *context_->cfg();
   analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr();
@@ -53,12 +53,17 @@
   assert(CanPeelLoop() && "Cannot peel loop!");
 
   std::vector<BasicBlock*> ordered_loop_blocks;
-  // TODO(1841): Handle failure to create pre-header.
   BasicBlock* pre_header = loop_->GetOrCreatePreHeaderBlock();
+  if (!pre_header) {
+    return false;
+  }
 
   loop_->ComputeLoopStructuredOrder(&ordered_loop_blocks);
 
   cloned_loop_ = loop_utils_.CloneLoop(clone_results, ordered_loop_blocks);
+  if (!cloned_loop_) {
+    return false;
+  }
 
   // Add the basic block to the function.
   Function::iterator it =
@@ -146,17 +151,21 @@
 
   // Force the creation of a new preheader for the original loop and set it as
   // the merge block for the cloned loop.
-  // TODO(1841): Handle failure to create pre-header.
-  cloned_loop_->SetMergeBlock(loop_->GetOrCreatePreHeaderBlock());
+  BasicBlock* new_pre_header = loop_->GetOrCreatePreHeaderBlock();
+  if (!new_pre_header) {
+    return false;
+  }
+  cloned_loop_->SetMergeBlock(new_pre_header);
+  return true;
 }
 
-void LoopPeeling::InsertCanonicalInductionVariable(
+bool LoopPeeling::InsertCanonicalInductionVariable(
     LoopUtils::LoopCloningResult* clone_results) {
   if (original_loop_canonical_induction_variable_) {
     canonical_induction_variable_ =
         context_->get_def_use_mgr()->GetDef(clone_results->value_map_.at(
             original_loop_canonical_induction_variable_->result_id()));
-    return;
+    return true;
   }
 
   BasicBlock::iterator insert_point = GetClonedLoop()->GetLatchBlock()->tail();
@@ -168,19 +177,25 @@
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
   Instruction* uint_1_cst =
       builder.GetIntConstant<uint32_t>(1, int_type_->IsSigned());
+  if (!uint_1_cst) return false;
   // Create the increment.
   // Note that we do "1 + 1" here, one of the operand should the phi
   // value but we don't have it yet. The operand will be set latter.
   Instruction* iv_inc = builder.AddIAdd(
       uint_1_cst->type_id(), uint_1_cst->result_id(), uint_1_cst->result_id());
+  if (!iv_inc) return false;
 
   builder.SetInsertPoint(&*GetClonedLoop()->GetHeaderBlock()->begin());
 
+  Instruction* initial_value =
+      builder.GetIntConstant<uint32_t>(0, int_type_->IsSigned());
+  if (!initial_value) return false;
+
   canonical_induction_variable_ = builder.AddPhi(
       uint_1_cst->type_id(),
-      {builder.GetIntConstant<uint32_t>(0, int_type_->IsSigned())->result_id(),
-       GetClonedLoop()->GetPreHeaderBlock()->id(), iv_inc->result_id(),
-       GetClonedLoop()->GetLatchBlock()->id()});
+      {initial_value->result_id(), GetClonedLoop()->GetPreHeaderBlock()->id(),
+       iv_inc->result_id(), GetClonedLoop()->GetLatchBlock()->id()});
+  if (!canonical_induction_variable_) return false;
   // Connect everything.
   iv_inc->SetInOperand(0, {canonical_induction_variable_->result_id()});
 
@@ -191,6 +206,7 @@
   if (do_while_form_) {
     canonical_induction_variable_ = iv_inc;
   }
+  return true;
 }
 
 void LoopPeeling::GetIteratorUpdateOperations(
@@ -308,7 +324,7 @@
   }
 }
 
-void LoopPeeling::FixExitCondition(
+bool LoopPeeling::FixExitCondition(
     const std::function<uint32_t(Instruction*)>& condition_builder) {
   CFG& cfg = *context_->cfg();
 
@@ -329,7 +345,11 @@
     --insert_point;
   }
 
-  exit_condition->SetInOperand(0, {condition_builder(&*insert_point)});
+  uint32_t new_cond_id = condition_builder(&*insert_point);
+  if (new_cond_id == 0) {
+    return false;
+  }
+  exit_condition->SetInOperand(0, {new_cond_id});
 
   uint32_t to_continue_block_idx =
       GetClonedLoop()->IsInsideLoop(exit_condition->GetSingleWordInOperand(1))
@@ -341,6 +361,7 @@
 
   // Update def/use manager.
   context_->get_def_use_mgr()->AnalyzeInstUse(exit_condition);
+  return true;
 }
 
 BasicBlock* LoopPeeling::CreateBlockBefore(BasicBlock* bb) {
@@ -348,10 +369,13 @@
   CFG& cfg = *context_->cfg();
   assert(cfg.preds(bb->id()).size() == 1 && "More than one predecessor");
 
-  // TODO(1841): Handle id overflow.
+  uint32_t new_id = context_->TakeNextId();
+  if (new_id == 0) {
+    return nullptr;
+  }
   std::unique_ptr<BasicBlock> new_bb =
-      MakeUnique<BasicBlock>(std::unique_ptr<Instruction>(new Instruction(
-          context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {})));
+      MakeUnique<BasicBlock>(std::unique_ptr<Instruction>(
+          new Instruction(context_, spv::Op::OpLabel, 0, new_id, {})));
   // Update the loop descriptor.
   Loop* in_loop = (*loop_utils_.GetLoopDescriptor())[bb];
   if (in_loop) {
@@ -394,8 +418,10 @@
 
 BasicBlock* LoopPeeling::ProtectLoop(Loop* loop, Instruction* condition,
                                      BasicBlock* if_merge) {
-  // TODO(1841): Handle failure to create pre-header.
   BasicBlock* if_block = loop->GetOrCreatePreHeaderBlock();
+  if (!if_block) {
+    return nullptr;
+  }
   // Will no longer be a pre-header because of the if.
   loop->SetPreHeaderBlock(nullptr);
   // Kill the branch to the header.
@@ -411,48 +437,63 @@
   return if_block;
 }
 
-void LoopPeeling::PeelBefore(uint32_t peel_factor) {
+bool LoopPeeling::PeelBefore(uint32_t peel_factor) {
   assert(CanPeelLoop() && "Cannot peel loop");
   LoopUtils::LoopCloningResult clone_results;
 
   // Clone the loop and insert the cloned one before the loop.
-  DuplicateAndConnectLoop(&clone_results);
+  if (!DuplicateAndConnectLoop(&clone_results)) {
+    return false;
+  }
 
   // Add a canonical induction variable "canonical_induction_variable_".
-  InsertCanonicalInductionVariable(&clone_results);
+  if (!InsertCanonicalInductionVariable(&clone_results)) {
+    return false;
+  }
 
   InstructionBuilder builder(
       context_, &*cloned_loop_->GetPreHeaderBlock()->tail(),
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
   Instruction* factor =
       builder.GetIntConstant(peel_factor, int_type_->IsSigned());
+  if (!factor) return false;
 
   Instruction* has_remaining_iteration = builder.AddLessThan(
       factor->result_id(), loop_iteration_count_->result_id());
+  if (!has_remaining_iteration) return false;
   Instruction* max_iteration = builder.AddSelect(
       factor->type_id(), has_remaining_iteration->result_id(),
       factor->result_id(), loop_iteration_count_->result_id());
+  if (!max_iteration) return false;
 
   // Change the exit condition of the cloned loop to be (exit when become
   // false):
   //  "canonical_induction_variable_" < min("factor", "loop_iteration_count_")
-  FixExitCondition([max_iteration, this](Instruction* insert_before_point) {
-    return InstructionBuilder(context_, insert_before_point,
-                              IRContext::kAnalysisDefUse |
-                                  IRContext::kAnalysisInstrToBlockMapping)
-        .AddLessThan(canonical_induction_variable_->result_id(),
-                     max_iteration->result_id())
-        ->result_id();
-  });
+  if (!FixExitCondition(
+          [max_iteration, this](Instruction* insert_before_point) {
+            Instruction* new_cond =
+                InstructionBuilder(context_, insert_before_point,
+                                   IRContext::kAnalysisDefUse |
+                                       IRContext::kAnalysisInstrToBlockMapping)
+                    .AddLessThan(canonical_induction_variable_->result_id(),
+                                 max_iteration->result_id());
+            return new_cond ? new_cond->result_id() : 0;
+          })) {
+    return false;
+  }
 
   // "Protect" the second loop: the second loop can only be executed if
   // |has_remaining_iteration| is true (i.e. factor < loop_iteration_count_).
   BasicBlock* if_merge_block = loop_->GetMergeBlock();
-  loop_->SetMergeBlock(CreateBlockBefore(loop_->GetMergeBlock()));
+  BasicBlock* new_merge_block = CreateBlockBefore(loop_->GetMergeBlock());
+  if (!new_merge_block) return false;
+  loop_->SetMergeBlock(new_merge_block);
   // Prevent the second loop from being executed if we already executed all the
   // required iterations.
   BasicBlock* if_block =
       ProtectLoop(loop_, has_remaining_iteration, if_merge_block);
+  if (!if_block) return false;
+
   // Patch the phi of the merge block.
   if_merge_block->ForEachPhiInst(
       [&clone_results, if_block, this](Instruction* phi) {
@@ -471,14 +512,17 @@
   context_->InvalidateAnalysesExceptFor(
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping |
       IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisCFG);
+  return true;
 }
 
-void LoopPeeling::PeelAfter(uint32_t peel_factor) {
+bool LoopPeeling::PeelAfter(uint32_t peel_factor) {
   assert(CanPeelLoop() && "Cannot peel loop");
   LoopUtils::LoopCloningResult clone_results;
 
   // Clone the loop and insert the cloned one before the loop.
-  DuplicateAndConnectLoop(&clone_results);
+  if (!DuplicateAndConnectLoop(&clone_results)) {
+    return false;
+  }
 
   // Add a canonical induction variable "canonical_induction_variable_".
   InsertCanonicalInductionVariable(&clone_results);
@@ -488,40 +532,55 @@
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
   Instruction* factor =
       builder.GetIntConstant(peel_factor, int_type_->IsSigned());
+  if (!factor) return false;
 
   Instruction* has_remaining_iteration = builder.AddLessThan(
       factor->result_id(), loop_iteration_count_->result_id());
+  if (!has_remaining_iteration) return false;
 
   // Change the exit condition of the cloned loop to be (exit when become
   // false):
   //  "canonical_induction_variable_" + "factor" < "loop_iteration_count_"
-  FixExitCondition([factor, this](Instruction* insert_before_point) {
-    InstructionBuilder cond_builder(
-        context_, insert_before_point,
-        IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-    // Build the following check: canonical_induction_variable_ + factor <
-    // iteration_count
-    return cond_builder
-        .AddLessThan(cond_builder
-                         .AddIAdd(canonical_induction_variable_->type_id(),
-                                  canonical_induction_variable_->result_id(),
-                                  factor->result_id())
-                         ->result_id(),
-                     loop_iteration_count_->result_id())
-        ->result_id();
-  });
+  if (!FixExitCondition([factor,
+                         this](Instruction* insert_before_point) -> uint32_t {
+        InstructionBuilder cond_builder(
+            context_, insert_before_point,
+            IRContext::kAnalysisDefUse |
+                IRContext::kAnalysisInstrToBlockMapping);
+        // Build the following check: canonical_induction_variable_ + factor <
+        // iteration_count
+        Instruction* add = cond_builder.AddIAdd(
+            canonical_induction_variable_->type_id(),
+            canonical_induction_variable_->result_id(), factor->result_id());
+        if (!add) return 0;
+        Instruction* new_cond = cond_builder.AddLessThan(
+            add->result_id(), loop_iteration_count_->result_id());
+        return new_cond ? new_cond->result_id() : 0;
+      })) {
+    return false;
+  }
 
   // "Protect" the first loop: the first loop can only be executed if
   // factor < loop_iteration_count_.
 
   // The original loop's pre-header was the cloned loop merge block.
-  GetClonedLoop()->SetMergeBlock(
-      CreateBlockBefore(GetOriginalLoop()->GetPreHeaderBlock()));
+  BasicBlock* pre_header = GetOriginalLoop()->GetPreHeaderBlock();
+  if (!pre_header) {
+    return false;
+  }
+  BasicBlock* new_merge_block = CreateBlockBefore(pre_header);
+  if (!new_merge_block) {
+    return false;
+  }
+  GetClonedLoop()->SetMergeBlock(new_merge_block);
   // Use the second loop preheader as if merge block.
 
   // Prevent the first loop if only the peeled loop needs it.
   BasicBlock* if_block = ProtectLoop(cloned_loop_, has_remaining_iteration,
                                      GetOriginalLoop()->GetPreHeaderBlock());
+  if (!if_block) {
+    return false;
+  }
 
   // Patch the phi of the header block.
   // We added an if to enclose the first loop and because the phi node are
@@ -529,8 +588,10 @@
   // dominate the preheader.
   // We had to the preheader (our if merge block) the required phi instruction
   // and patch the header phi.
+  bool ok = true;
   GetOriginalLoop()->GetHeaderBlock()->ForEachPhiInst(
-      [&clone_results, if_block, this](Instruction* phi) {
+      [&clone_results, if_block, &ok, this](Instruction* phi) {
+        if (!ok) return;
         analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr();
 
         auto find_value_idx = [](Instruction* phi_inst, Loop* loop) {
@@ -554,15 +615,21 @@
                              find_value_idx(phi, GetOriginalLoop())),
                          GetClonedLoop()->GetMergeBlock()->id(),
                          cloned_preheader_value, if_block->id()});
+        if (!new_phi) {
+          ok = false;
+          return;
+        }
 
         phi->SetInOperand(find_value_idx(phi, GetOriginalLoop()),
                           {new_phi->result_id()});
         def_use_mgr->AnalyzeInstUse(phi);
       });
+  if (!ok) return false;
 
   context_->InvalidateAnalysesExceptFor(
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping |
       IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisCFG);
+  return true;
 }
 
 Pass::Status LoopPeelingPass::Process() {
@@ -571,13 +638,19 @@
 
   // Process each function in the module
   for (Function& f : *module) {
-    modified |= ProcessFunction(&f);
+    Pass::Status status = ProcessFunction(&f);
+    if (status == Status::Failure) {
+      return Status::Failure;
+    }
+    if (status == Status::SuccessWithChange) {
+      modified = true;
+    }
   }
 
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
-bool LoopPeelingPass::ProcessFunction(Function* f) {
+Pass::Status LoopPeelingPass::ProcessFunction(Function* f) {
   bool modified = false;
   LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(f);
 
@@ -593,41 +666,56 @@
     CodeMetrics loop_size;
     loop_size.Analyze(*loop);
 
-    auto try_peel = [&loop_size, &modified, this](Loop* loop_to_peel) -> Loop* {
+    auto try_peel = [&loop_size, &modified, this](
+                        Loop* loop_to_peel) -> std::pair<Pass::Status, Loop*> {
       if (!loop_to_peel->IsLCSSA()) {
-        LoopUtils(context(), loop_to_peel).MakeLoopClosedSSA();
+        if (!LoopUtils(context(), loop_to_peel).MakeLoopClosedSSA()) {
+          return {Pass::Status::Failure, nullptr};
+        }
       }
 
-      bool peeled_loop;
+      Pass::Status status;
       Loop* still_peelable_loop;
-      std::tie(peeled_loop, still_peelable_loop) =
+      std::tie(status, still_peelable_loop) =
           ProcessLoop(loop_to_peel, &loop_size);
 
-      if (peeled_loop) {
+      if (status == Pass::Status::SuccessWithChange) {
         modified = true;
       }
 
-      return still_peelable_loop;
+      return {status, still_peelable_loop};
     };
 
-    Loop* still_peelable_loop = try_peel(loop);
+    Pass::Status status;
+    Loop* still_peelable_loop;
+    std::tie(status, still_peelable_loop) = try_peel(loop);
+
+    if (status == Pass::Status::Failure) {
+      return Pass::Status::Failure;
+    }
+
     // The pass is working out the maximum factor by which a loop can be peeled.
     // If the loop can potentially be peeled again, then there is only one
     // possible direction, so only one call is still needed.
     if (still_peelable_loop) {
-      try_peel(loop);
+      std::tie(status, still_peelable_loop) = try_peel(still_peelable_loop);
+      if (status == Pass::Status::Failure) {
+        return Pass::Status::Failure;
+      }
     }
   }
 
-  return modified;
+  return modified ? Pass::Status::SuccessWithChange
+                  : Pass::Status::SuccessWithoutChange;
 }
 
-std::pair<bool, Loop*> LoopPeelingPass::ProcessLoop(Loop* loop,
-                                                    CodeMetrics* loop_size) {
+std::tuple<Pass::Status, Loop*> LoopPeelingPass::ProcessLoop(
+    Loop* loop, CodeMetrics* loop_size) {
   ScalarEvolutionAnalysis* scev_analysis =
       context()->GetScalarEvolutionAnalysis();
   // Default values for bailing out.
-  std::pair<bool, Loop*> bail_out{false, nullptr};
+  std::tuple<Pass::Status, Loop*> bail_out{Pass::Status::SuccessWithoutChange,
+                                           nullptr};
 
   BasicBlock* exit_block = loop->FindConditionBlock();
   if (!exit_block) {
@@ -744,7 +832,9 @@
   Loop* extra_opportunity = nullptr;
 
   if (direction == PeelDirection::kBefore) {
-    peeler.PeelBefore(factor);
+    if (!peeler.PeelBefore(factor)) {
+      return {Pass::Status::Failure, nullptr};
+    }
     if (stats_) {
       stats_->peeled_loops_.emplace_back(loop, PeelDirection::kBefore, factor);
     }
@@ -753,7 +843,9 @@
       extra_opportunity = peeler.GetOriginalLoop();
     }
   } else {
-    peeler.PeelAfter(factor);
+    if (!peeler.PeelAfter(factor)) {
+      return {Pass::Status::Failure, nullptr};
+    }
     if (stats_) {
       stats_->peeled_loops_.emplace_back(loop, PeelDirection::kAfter, factor);
     }
@@ -763,7 +855,7 @@
     }
   }
 
-  return {true, extra_opportunity};
+  return {Pass::Status::SuccessWithChange, extra_opportunity};
 }
 
 uint32_t LoopPeelingPass::LoopPeelingInfo::GetFirstLoopInvariantOperand(
diff --git a/source/opt/loop_peeling.h b/source/opt/loop_peeling.h
index 2a55fe4..a794793 100644
--- a/source/opt/loop_peeling.h
+++ b/source/opt/loop_peeling.h
@@ -148,11 +148,11 @@
 
   // Moves the execution of the |factor| first iterations of the loop into a
   // dedicated loop.
-  void PeelBefore(uint32_t factor);
+  bool PeelBefore(uint32_t factor);
 
   // Moves the execution of the |factor| last iterations of the loop into a
   // dedicated loop.
-  void PeelAfter(uint32_t factor);
+  bool PeelAfter(uint32_t factor);
 
   // Returns the cloned loop.
   Loop* GetClonedLoop() { return cloned_loop_; }
@@ -184,19 +184,19 @@
   // Duplicate |loop_| and place the new loop before the cloned loop. Iterating
   // values from the cloned loop are then connected to the original loop as
   // initializer.
-  void DuplicateAndConnectLoop(LoopUtils::LoopCloningResult* clone_results);
+  bool DuplicateAndConnectLoop(LoopUtils::LoopCloningResult* clone_results);
 
   // Insert the canonical induction variable into the first loop as a simplified
-  // counter.
-  void InsertCanonicalInductionVariable(
+  // counter. Returns true on success.
+  bool InsertCanonicalInductionVariable(
       LoopUtils::LoopCloningResult* clone_results);
 
   // Fixes the exit condition of the before loop. The function calls
   // |condition_builder| to get the condition to use in the conditional branch
   // of the loop exit. The loop will be exited if the condition evaluate to
   // true. |condition_builder| takes an Instruction* that represent the
-  // insertion point.
-  void FixExitCondition(
+  // insertion point. Returns true on success.
+  bool FixExitCondition(
       const std::function<uint32_t(Instruction*)>& condition_builder);
 
   // Gathers all operations involved in the update of |iterator| into
@@ -321,10 +321,14 @@
     ScalarEvolutionAnalysis* scev_analysis_;
     size_t loop_max_iterations_;
   };
-  // Peel profitable loops in |f|.
-  bool ProcessFunction(Function* f);
-  // Peel |loop| if profitable.
-  std::pair<bool, Loop*> ProcessLoop(Loop* loop, CodeMetrics* loop_size);
+  // Peel profitable loops in |f|. Returns Pass::Status::Failure if an error
+  // occurs.
+  Pass::Status ProcessFunction(Function* f);
+  // Peel |loop| if profitable. Returns Pass::Status::Failure if an error
+  // occurs. Returns {Pass::Status::SuccessWithChange, Loop*} if the loop is
+  // peeled and there is another peeling opportunity.
+  std::tuple<Pass::Status, Loop*> ProcessLoop(Loop* loop,
+                                              CodeMetrics* loop_size);
 
   static size_t code_grow_threshold_;
   LoopPeelingStats* stats_;
diff --git a/source/opt/loop_unroller.cpp b/source/opt/loop_unroller.cpp
index d9e34f2..8765b16 100644
--- a/source/opt/loop_unroller.cpp
+++ b/source/opt/loop_unroller.cpp
@@ -178,7 +178,7 @@
 
   // Unroll the |loop| by given |factor| by copying the whole body |factor|
   // times. The resulting basicblock structure will remain a loop.
-  void PartiallyUnroll(Loop*, size_t factor);
+  bool PartiallyUnroll(Loop*, size_t factor);
 
   // If partially unrolling the |loop| would leave the loop with too many bodies
   // for its number of iterations then this method should be used. This method
@@ -186,12 +186,12 @@
   // successor of the original's merge block. The original loop will have its
   // condition changed to loop over the residual part and the duplicate will be
   // partially unrolled. The resulting structure will be two loops.
-  void PartiallyUnrollResidualFactor(Loop* loop, size_t factor);
+  bool PartiallyUnrollResidualFactor(Loop* loop, size_t factor);
 
   // Fully unroll the |loop| by copying the full body by the total number of
   // loop iterations, folding all conditions, and removing the backedge from the
   // continue block to the header.
-  void FullyUnroll(Loop* loop);
+  bool FullyUnroll(Loop* loop);
 
   // Get the ID of the variable in the |phi| paired with |label|.
   uint32_t GetPhiDefID(const Instruction* phi, uint32_t label) const;
@@ -203,7 +203,7 @@
   // Remove the OpConditionalBranch instruction inside |conditional_block| used
   // to branch to either exit or continue the loop and replace it with an
   // unconditional OpBranch to block |new_target|.
-  void FoldConditionBlock(BasicBlock* condtion_block, uint32_t new_target);
+  bool FoldConditionBlock(BasicBlock* condtion_block, uint32_t new_target);
 
   // Add all blocks_to_add_ to function_ at the |insert_point|.
   void AddBlocksToFunction(const BasicBlock* insert_point);
@@ -211,7 +211,7 @@
   // Duplicates the |old_loop|, cloning each body and remapping the ids without
   // removing instructions or changing relative structure. Result will be stored
   // in |new_loop|.
-  void DuplicateLoop(Loop* old_loop, Loop* new_loop);
+  bool DuplicateLoop(Loop* old_loop, Loop* new_loop);
 
   inline size_t GetLoopIterationCount() const {
     return number_of_loop_iterations_;
@@ -241,7 +241,7 @@
   // to old
   // ids. |loop| is used to identify special loop blocks (header, continue,
   // etc).
-  void AssignNewResultIds(BasicBlock* basic_block);
+  bool AssignNewResultIds(BasicBlock* basic_block);
 
   // Using the map built by AssignNewResultIds, replace the uses in |inst|
   // by the id that the use maps to.
@@ -258,18 +258,18 @@
   // the old |loop| continue block and the new body will link to the |loop|
   // header via the new continue block. |eliminate_conditions| is used to decide
   // whether or not to fold all the condition blocks other than the last one.
-  void CopyBody(Loop* loop, bool eliminate_conditions);
+  bool CopyBody(Loop* loop, bool eliminate_conditions);
 
   // Copy a given |block_to_copy| in the |loop| and record the mapping of the
   // old/new ids. |preserve_instructions| determines whether or not the method
   // will modify (other than result_id) instructions which are copied.
-  void CopyBasicBlock(Loop* loop, const BasicBlock* block_to_copy,
+  bool CopyBasicBlock(Loop* loop, const BasicBlock* block_to_copy,
                       bool preserve_instructions);
 
   // The actual implementation of the unroll step. Unrolls |loop| by given
   // |factor| by copying the body by |factor| times. Also propagates the
   // induction variable value throughout the copies.
-  void Unroll(Loop* loop, size_t factor);
+  bool Unroll(Loop* loop, size_t factor);
 
   // Fills the loop_blocks_inorder_ field with the ordered list of basic blocks
   // as computed by the method ComputeLoopOrderedBlocks.
@@ -376,11 +376,12 @@
 // loop it creates two loops and unrolls one and adjusts the condition on the
 // other. The end result being that the new loop pair iterates over the correct
 // number of bodies.
-void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop,
+bool LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop,
                                                           size_t factor) {
-  // TODO(1841): Handle id overflow.
-  std::unique_ptr<Instruction> new_label{new Instruction(
-      context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {})};
+  uint32_t new_label_id = context_->TakeNextId();
+  if (new_label_id == 0) return false;
+  std::unique_ptr<Instruction> new_label{
+      new Instruction(context_, spv::Op::OpLabel, 0, new_label_id, {})};
   std::unique_ptr<BasicBlock> new_exit_bb{new BasicBlock(std::move(new_label))};
   new_exit_bb->SetParent(&function_);
 
@@ -401,7 +402,9 @@
   // Clear the basic blocks of the new loop.
   new_loop->ClearBlocks();
 
-  DuplicateLoop(loop, new_loop.get());
+  if (!DuplicateLoop(loop, new_loop.get())) {
+    return false;
+  }
 
   // Add the blocks to the function.
   AddBlocksToFunction(loop->GetMergeBlock());
@@ -416,7 +419,9 @@
   loop_induction_variable_ = state_.new_phi;
   // Unroll the new loop by the factor with the usual -1 to account for the
   // existing block iteration.
-  Unroll(new_loop.get(), factor);
+  if (!Unroll(new_loop.get(), factor)) {
+    return false;
+  }
 
   LinkLastPhisToStart(new_loop.get());
   AddBlocksToLoop(new_loop.get());
@@ -460,6 +465,10 @@
     new_constant = builder.GetUintConstant(static_cast<int32_t>(remainder));
   }
 
+  if (!new_constant) {
+    return false;
+  }
+
   uint32_t constant_id = new_constant->result_id();
 
   // Update the condition check.
@@ -477,6 +486,9 @@
   for (size_t index = 0; index < new_inductions.size(); ++index) {
     Instruction* new_induction = new_inductions[index];
     Instruction* old_induction = old_inductions[index];
+    if (!new_induction || !old_induction) {
+      return false;
+    }
     // Get the index of the loop initalizer, the value coming in from the
     // preheader.
     uint32_t initalizer_index =
@@ -512,6 +524,7 @@
   loop_descriptor.AddLoop(std::move(new_loop), loop->GetParent());
 
   RemoveDeadInstructions();
+  return true;
 }
 
 // Mark this loop as DontUnroll as it will already be unrolled and it may not
@@ -528,7 +541,7 @@
 // Duplicate the |loop| body |factor| - 1 number of times while keeping the loop
 // backedge intact. This will leave the loop with |factor| number of bodies
 // after accounting for the initial body.
-void LoopUnrollerUtilsImpl::Unroll(Loop* loop, size_t factor) {
+bool LoopUnrollerUtilsImpl::Unroll(Loop* loop, size_t factor) {
   // If we unroll a loop partially it will not be safe to unroll it further.
   // This is due to the current method of calculating the number of loop
   // iterations.
@@ -539,8 +552,11 @@
   state_ = LoopUnrollState{loop_induction_variable_, loop->GetLatchBlock(),
                            loop_condition_block_, std::move(inductions)};
   for (size_t i = 0; i < factor - 1; ++i) {
-    CopyBody(loop, true);
+    if (!CopyBody(loop, true)) {
+      return false;
+    }
   }
+  return true;
 }
 
 void LoopUnrollerUtilsImpl::RemoveDeadInstructions() {
@@ -560,6 +576,10 @@
   loop->GetInductionVariables(inductions);
 
   for (size_t index = 0; index < inductions.size(); ++index) {
+    // We don't want the decorations that applied to the induction variable
+    // to be applied to the value that replace it.
+    context_->KillNamesAndDecorates(state_.previous_phis_[index]);
+
     uint32_t trip_step_id = GetPhiDefID(state_.previous_phis_[index],
                                         state_.previous_latch_block_->id());
     context_->ReplaceAllUsesWith(inductions[index]->result_id(), trip_step_id);
@@ -569,12 +589,16 @@
 
 // Fully unroll the loop by partially unrolling it by the number of loop
 // iterations minus one for the body already accounted for.
-void LoopUnrollerUtilsImpl::FullyUnroll(Loop* loop) {
+bool LoopUnrollerUtilsImpl::FullyUnroll(Loop* loop) {
   // We unroll the loop by number of iterations in the loop.
-  Unroll(loop, number_of_loop_iterations_);
+  if (!Unroll(loop, number_of_loop_iterations_)) {
+    return false;
+  }
 
   // The first condition block is preserved until now so it can be copied.
-  FoldConditionBlock(loop_condition_block_, 1);
+  if (!FoldConditionBlock(loop_condition_block_, 1)) {
+    return false;
+  }
 
   // Delete the OpLoopMerge and remove the backedge to the header.
   CloseUnrolledLoop(loop);
@@ -598,6 +622,7 @@
   context_->InvalidateAnalysesExceptFor(
       IRContext::Analysis::kAnalysisLoopAnalysis |
       IRContext::Analysis::kAnalysisDefUse);
+  return true;
 }
 
 void LoopUnrollerUtilsImpl::KillDebugDeclares(BasicBlock* bb) {
@@ -618,10 +643,11 @@
 // and the id mapping in the state. |preserve_instructions| is used to determine
 // whether or not this function should edit instructions other than the
 // |result_id|.
-void LoopUnrollerUtilsImpl::CopyBasicBlock(Loop* loop, const BasicBlock* itr,
+bool LoopUnrollerUtilsImpl::CopyBasicBlock(Loop* loop, const BasicBlock* itr,
                                            bool preserve_instructions) {
   // Clone the block exactly, including the IDs.
   BasicBlock* basic_block = itr->Clone(context_);
+  if (!basic_block) return false;
   basic_block->SetParent(itr->GetParent());
 
   // We do not want to duplicate DebugDeclare.
@@ -629,7 +655,9 @@
 
   // Assign each result a new unique ID and keep a mapping of the old ids to
   // the new ones.
-  AssignNewResultIds(basic_block);
+  if (!AssignNewResultIds(basic_block)) {
+    return false;
+  }
 
   // If this is the continue block we are copying.
   if (itr == loop->GetContinueBlock()) {
@@ -668,13 +696,16 @@
 
   // Keep tracking the old block via a map.
   state_.new_blocks[itr->id()] = basic_block;
+  return true;
 }
 
-void LoopUnrollerUtilsImpl::CopyBody(Loop* loop, bool eliminate_conditions) {
+bool LoopUnrollerUtilsImpl::CopyBody(Loop* loop, bool eliminate_conditions) {
   // Copy each basic block in the loop, give them new ids, and save state
   // information.
   for (const BasicBlock* itr : loop_blocks_inorder_) {
-    CopyBasicBlock(loop, itr, false);
+    if (!CopyBasicBlock(loop, itr, false)) {
+      return false;
+    }
   }
 
   // Set the previous latch block to point to the new header.
@@ -713,7 +744,9 @@
 
   if (eliminate_conditions &&
       state_.new_condition_block != loop_condition_block_) {
-    FoldConditionBlock(state_.new_condition_block, 1);
+    if (!FoldConditionBlock(state_.new_condition_block, 1)) {
+      return false;
+    }
   }
 
   // Only reference to the header block is the backedge in the latch block,
@@ -729,6 +762,7 @@
 
   // Swap the state so the new is now the previous.
   state_.NextIterationState();
+  return true;
 }
 
 uint32_t LoopUnrollerUtilsImpl::GetPhiDefID(const Instruction* phi,
@@ -742,7 +776,7 @@
   return 0;
 }
 
-void LoopUnrollerUtilsImpl::FoldConditionBlock(BasicBlock* condition_block,
+bool LoopUnrollerUtilsImpl::FoldConditionBlock(BasicBlock* condition_block,
                                                uint32_t operand_label) {
   // Remove the old conditional branch to the merge and continue blocks.
   Instruction& old_branch = *condition_block->tail();
@@ -759,8 +793,13 @@
           IRContext::Analysis::kAnalysisInstrToBlockMapping);
   Instruction* new_branch = builder.AddBranch(new_target);
 
-  if (!lines.empty()) new_branch->AddDebugLine(&lines.back());
+  if (!lines.empty()) {
+    if (!new_branch->AddDebugLine(&lines.back())) {
+      return false;
+    }
+  }
   new_branch->SetDebugScope(scope);
+  return true;
 }
 
 void LoopUnrollerUtilsImpl::CloseUnrolledLoop(Loop* loop) {
@@ -807,19 +846,24 @@
 }
 
 // Uses the first loop to create a copy of the loop with new IDs.
-void LoopUnrollerUtilsImpl::DuplicateLoop(Loop* old_loop, Loop* new_loop) {
+bool LoopUnrollerUtilsImpl::DuplicateLoop(Loop* old_loop, Loop* new_loop) {
   std::vector<BasicBlock*> new_block_order;
 
   // Copy every block in the old loop.
   for (const BasicBlock* itr : loop_blocks_inorder_) {
-    CopyBasicBlock(old_loop, itr, true);
+    if (!CopyBasicBlock(old_loop, itr, true)) {
+      return false;
+    }
     new_block_order.push_back(blocks_to_add_.back().get());
   }
 
   // Clone the merge block, give it a new id and record it in the state.
   BasicBlock* new_merge = old_loop->GetMergeBlock()->Clone(context_);
+  if (!new_merge) return false;
   new_merge->SetParent(old_loop->GetMergeBlock()->GetParent());
-  AssignNewResultIds(new_merge);
+  if (!AssignNewResultIds(new_merge)) {
+    return false;
+  }
   state_.new_blocks[old_loop->GetMergeBlock()->id()] = new_merge;
 
   // Remap the operands of every instruction in the loop to point to the new
@@ -836,6 +880,7 @@
   new_loop->SetContinueBlock(state_.new_continue_block);
   new_loop->SetLatchBlock(state_.new_latch_block);
   new_loop->SetMergeBlock(new_merge);
+  return true;
 }
 
 // Whenever the utility copies a block it stores it in a temporary buffer, this
@@ -858,13 +903,15 @@
 
 // Assign all result_ids in |basic_block| instructions to new IDs and preserve
 // the mapping of new ids to old ones.
-void LoopUnrollerUtilsImpl::AssignNewResultIds(BasicBlock* basic_block) {
+bool LoopUnrollerUtilsImpl::AssignNewResultIds(BasicBlock* basic_block) {
   analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr();
 
   // Label instructions aren't covered by normal traversal of the
   // instructions.
-  // TODO(1841): Handle id overflow.
   uint32_t new_label_id = context_->TakeNextId();
+  if (new_label_id == 0) {
+    return false;
+  }
 
   // Assign a new id to the label.
   state_.new_inst[basic_block->GetLabelInst()->result_id()] = new_label_id;
@@ -884,10 +931,18 @@
     }
 
     // Give the instruction a new id.
-    // TODO(1841): Handle id overflow.
-    inst.SetResultId(context_->TakeNextId());
+    uint32_t new_id = context_->TakeNextId();
+    if (new_id == 0) {
+      return false;
+    }
+    inst.SetResultId(new_id);
     def_use_mgr->AnalyzeInstDef(&inst);
 
+    // All decorations that can apply to an instruction in a function body
+    // modify the behaviour of the instruction, and should be on the
+    // new instruction to keep the same results.
+    context_->get_decoration_mgr()->CloneDecorations(old_id, new_id);
+
     // Save the mapping of old_id -> new_id.
     state_.new_inst[old_id] = inst.result_id();
     // Check if this instruction is the induction variable.
@@ -897,6 +952,7 @@
     }
     state_.ids_to_new_inst[inst.result_id()] = &inst;
   }
+  return true;
 }
 
 void LoopUnrollerUtilsImpl::RemapOperands(Instruction* inst) {
@@ -957,12 +1013,13 @@
 
 // Duplicate the |loop| body |factor| number of times while keeping the loop
 // backedge intact.
-void LoopUnrollerUtilsImpl::PartiallyUnroll(Loop* loop, size_t factor) {
-  Unroll(loop, factor);
+bool LoopUnrollerUtilsImpl::PartiallyUnroll(Loop* loop, size_t factor) {
+  if (!Unroll(loop, factor)) return false;
   LinkLastPhisToStart(loop);
   AddBlocksToLoop(loop);
   AddBlocksToFunction(loop->GetMergeBlock());
   RemoveDeadInstructions();
+  return true;
 }
 
 /*
@@ -1067,7 +1124,9 @@
   // If the unrolling factor is larger than or the same size as the loop just
   // fully unroll the loop.
   if (factor >= unroller.GetLoopIterationCount()) {
-    unroller.FullyUnroll(loop_);
+    if (!unroller.FullyUnroll(loop_)) {
+      return false;
+    }
     return true;
   }
 
@@ -1076,9 +1135,13 @@
   // remaining part. We add one when calucating the remainder to take into
   // account the one iteration already in the loop.
   if (unroller.GetLoopIterationCount() % factor != 0) {
-    unroller.PartiallyUnrollResidualFactor(loop_, factor);
+    if (!unroller.PartiallyUnrollResidualFactor(loop_, factor)) {
+      return false;
+    }
   } else {
-    unroller.PartiallyUnroll(loop_, factor);
+    if (!unroller.PartiallyUnroll(loop_, factor)) {
+      return false;
+    }
   }
 
   return true;
@@ -1094,7 +1157,9 @@
                                  loop_->GetHeaderBlock()->GetParent()};
 
   unroller.Init(loop_);
-  unroller.FullyUnroll(loop_);
+  if (!unroller.FullyUnroll(loop_)) {
+    return false;
+  }
 
   return true;
 }
@@ -1127,15 +1192,25 @@
       }
 
       if (fully_unroll_) {
-        loop_utils.FullyUnroll();
+        if (!loop_utils.FullyUnroll()) {
+          return Status::Failure;
+        }
+        changed = true;
       } else {
-        loop_utils.PartiallyUnroll(unroll_factor_);
+        if (!loop_utils.PartiallyUnroll(unroll_factor_)) {
+          return Status::Failure;
+        }
+        changed = true;
       }
-      changed = true;
     }
     LD->PostModificationCleanup();
   }
 
+  if (changed) {
+    context()->InvalidateAnalysesExceptFor(
+        IRContext::Analysis::kAnalysisLoopAnalysis);
+  }
+
   return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
diff --git a/source/opt/loop_unswitch_pass.cpp b/source/opt/loop_unswitch_pass.cpp
index 41f1a80..b49f243 100644
--- a/source/opt/loop_unswitch_pass.cpp
+++ b/source/opt/loop_unswitch_pass.cpp
@@ -92,12 +92,16 @@
   // position |ip|. This function preserves the def/use and instr to block
   // managers.
   BasicBlock* CreateBasicBlock(Function::iterator ip) {
+    uint32_t new_label_id = TakeNextId();
+    if (new_label_id == 0) {
+      return nullptr;
+    }
+
     analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr();
 
-    // TODO(1841): Handle id overflow.
     BasicBlock* bb = &*ip.InsertBefore(std::unique_ptr<BasicBlock>(
         new BasicBlock(std::unique_ptr<Instruction>(new Instruction(
-            context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {})))));
+            context_, spv::Op::OpLabel, 0, new_label_id, {})))));
     bb->SetParent(function_);
     def_use_mgr->AnalyzeInstDef(bb->GetLabelInst());
     context_->set_instr_block(bb->GetLabelInst(), bb);
@@ -135,7 +139,7 @@
   }
 
   // Unswitches |loop_|.
-  void PerformUnswitch() {
+  bool PerformUnswitch() {
     assert(CanUnswitchLoop() &&
            "Cannot unswitch if there is not constant condition");
     assert(loop_->GetPreHeaderBlock() && "This loop has no pre-header block");
@@ -165,6 +169,9 @@
         if_merge_block
             ? CreateBasicBlock(FindBasicBlockPosition(if_merge_block))
             : nullptr;
+    if (if_merge_block && !loop_merge_block) {
+      return false;
+    }
     if (loop_merge_block) {
       // Add the instruction and update managers.
       InstructionBuilder builder(
@@ -174,17 +181,24 @@
       builder.SetInsertPoint(&*loop_merge_block->begin());
       cfg.RegisterBlock(loop_merge_block);
       def_use_mgr->AnalyzeInstDef(loop_merge_block->GetLabelInst());
-      // Update CFG.
+      bool ok = true;
       if_merge_block->ForEachPhiInst(
-          [loop_merge_block, &builder, this](Instruction* phi) {
+          [loop_merge_block, &ok, &builder, this](Instruction* phi) -> bool {
             Instruction* cloned = phi->Clone(context_);
-            cloned->SetResultId(TakeNextId());
+            uint32_t new_id = TakeNextId();
+            if (new_id == 0) {
+              ok = false;
+              return false;
+            }
+            cloned->SetResultId(new_id);
             builder.AddInstruction(std::unique_ptr<Instruction>(cloned));
             phi->SetInOperand(0, {cloned->result_id()});
             phi->SetInOperand(1, {loop_merge_block->id()});
             for (uint32_t j = phi->NumInOperands() - 1; j > 1; j--)
               phi->RemoveInOperand(j);
+            return true;
           });
+      if (!ok) return false;
       // Copy the predecessor list (will get invalidated otherwise).
       std::vector<uint32_t> preds = cfg.preds(if_merge_block->id());
       for (uint32_t pid : preds) {
@@ -227,6 +241,9 @@
     // we need to create a dedicated block for the if.
     BasicBlock* loop_pre_header =
         CreateBasicBlock(++FindBasicBlockPosition(if_block));
+    if (!loop_pre_header) {
+      return false;
+    }
     InstructionBuilder(
         context_, loop_pre_header,
         IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping)
@@ -308,6 +325,12 @@
       // specific value.
       original_loop_constant_value =
           GetValueForDefaultPathForSwitch(iv_condition);
+      if (!original_loop_constant_value) {
+        return false;
+      }
+      if (!original_loop_constant_value) {
+        return false;
+      }
 
       for (uint32_t i = 2; i < iv_condition->NumInOperands(); i += 2) {
         constant_branch.emplace_back(
@@ -341,6 +364,9 @@
 
       Loop* cloned_loop =
           loop_utils.CloneLoop(&clone_result, ordered_loop_blocks_);
+      if (!cloned_loop) {
+        return false;
+      }
       specialisation_pair.second = cloned_loop->GetPreHeaderBlock();
 
       ////////////////////////////////////
@@ -416,6 +442,7 @@
 
     context_->InvalidateAnalysesExceptFor(
         IRContext::Analysis::kAnalysisLoopAnalysis);
+    return true;
   }
 
  private:
@@ -434,10 +461,7 @@
   std::vector<BasicBlock*> ordered_loop_blocks_;
 
   // Returns the next usable id for the context.
-  uint32_t TakeNextId() {
-    // TODO(1841): Handle id overflow.
-    return context_->TakeNextId();
-  }
+  uint32_t TakeNextId() { return context_->TakeNextId(); }
 
   // Simplifies |loop| assuming the instruction |to_version_insn| takes the
   // value |cst_value|. |block_range| is an iterator range returning the loop
@@ -573,13 +597,15 @@
 
   // Process each function in the module
   for (Function& f : *module) {
-    modified |= ProcessFunction(&f);
+    Pass::Status status = ProcessFunction(&f);
+    if (status == Status::Failure) return Status::Failure;
+    if (status == Status::SuccessWithChange) modified = true;
   }
 
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
-bool LoopUnswitchPass::ProcessFunction(Function* f) {
+Pass::Status LoopUnswitchPass::ProcessFunction(Function* f) {
   bool modified = false;
   std::unordered_set<Loop*> processed_loop;
 
@@ -597,17 +623,21 @@
       LoopUnswitch unswitcher(context(), f, &loop, &loop_descriptor);
       while (unswitcher.CanUnswitchLoop()) {
         if (!loop.IsLCSSA()) {
-          LoopUtils(context(), &loop).MakeLoopClosedSSA();
+          if (!LoopUtils(context(), &loop).MakeLoopClosedSSA()) {
+            return Status::Failure;
+          }
+        }
+        if (!unswitcher.PerformUnswitch()) {
+          return Status::Failure;
         }
         modified = true;
         loop_changed = true;
-        unswitcher.PerformUnswitch();
       }
       if (loop_changed) break;
     }
   }
 
-  return modified;
+  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
 }  // namespace opt
diff --git a/source/opt/loop_unswitch_pass.h b/source/opt/loop_unswitch_pass.h
index 4f7295d..66a757f 100644
--- a/source/opt/loop_unswitch_pass.h
+++ b/source/opt/loop_unswitch_pass.h
@@ -34,7 +34,8 @@
   Pass::Status Process() override;
 
  private:
-  bool ProcessFunction(Function* f);
+  // Process the given function.
+  Pass::Status ProcessFunction(Function* f);
 };
 
 }  // namespace opt
diff --git a/source/opt/loop_utils.cpp b/source/opt/loop_utils.cpp
index 20494e1..ced68de 100644
--- a/source/opt/loop_utils.cpp
+++ b/source/opt/loop_utils.cpp
@@ -68,7 +68,7 @@
     // block. This operation does not update the def/use manager, instead it
     // records what needs to be updated. The actual update is performed by
     // UpdateManagers.
-    void RewriteUse(BasicBlock* bb, Instruction* user, uint32_t operand_index) {
+    bool RewriteUse(BasicBlock* bb, Instruction* user, uint32_t operand_index) {
       assert(
           (user->opcode() != spv::Op::OpPhi || bb != GetParent(user)) &&
           "The root basic block must be the incoming edge if |user| is a phi "
@@ -79,9 +79,13 @@
              "phi instruction");
 
       Instruction* new_def = GetOrBuildIncoming(bb->id());
+      if (!new_def) {
+        return false;
+      }
 
       user->SetOperand(operand_index, {new_def->result_id()});
       rewritten_.insert(user);
+      return true;
     }
 
     // In-place update of some managers (avoid full invalidation).
@@ -120,6 +124,9 @@
                                  IRContext::kAnalysisInstrToBlockMapping);
       Instruction* incoming_phi =
           builder.AddPhi(def_insn_.type_id(), incomings);
+      if (!incoming_phi) {
+        return nullptr;
+      }
 
       rewritten_.insert(incoming_phi);
       return incoming_phi;
@@ -139,6 +146,9 @@
                                  IRContext::kAnalysisInstrToBlockMapping);
       Instruction* incoming_phi =
           builder.AddPhi(def_insn_.type_id(), incomings);
+      if (!incoming_phi) {
+        return nullptr;
+      }
 
       rewritten_.insert(incoming_phi);
       return incoming_phi;
@@ -270,7 +280,7 @@
 // Make the set |blocks| closed SSA. The set is closed SSA if all the uses
 // outside the set are phi instructions in exiting basic block set (hold by
 // |lcssa_rewriter|).
-inline void MakeSetClosedSSA(IRContext* context, Function* function,
+inline bool MakeSetClosedSSA(IRContext* context, Function* function,
                              const std::unordered_set<uint32_t>& blocks,
                              const std::unordered_set<BasicBlock*>& exit_bb,
                              LCSSARewriter* lcssa_rewriter) {
@@ -285,18 +295,18 @@
     if (!DominatesAnExit(bb, exit_bb, dom_tree)) continue;
     for (Instruction& inst : *bb) {
       LCSSARewriter::UseRewriter rewriter(lcssa_rewriter, inst);
-      def_use_manager->ForEachUse(
+      bool success = def_use_manager->WhileEachUse(
           &inst, [&blocks, &rewriter, &exit_bb, context](
                      Instruction* use, uint32_t operand_index) {
             BasicBlock* use_parent = context->get_instr_block(use);
             assert(use_parent);
-            if (blocks.count(use_parent->id())) return;
+            if (blocks.count(use_parent->id())) return true;
 
             if (use->opcode() == spv::Op::OpPhi) {
               // If the use is a Phi instruction and the incoming block is
               // coming from the loop, then that's consistent with LCSSA form.
               if (exit_bb.count(use_parent)) {
-                return;
+                return true;
               } else {
                 // That's not an exit block, but the user is a phi instruction.
                 // Consider the incoming branch only.
@@ -306,16 +316,20 @@
             }
             // Rewrite the use. Note that this call does not invalidate the
             // def/use manager. So this operation is safe.
-            rewriter.RewriteUse(use_parent, use, operand_index);
+            return rewriter.RewriteUse(use_parent, use, operand_index);
           });
+      if (!success) {
+        return false;
+      }
       rewriter.UpdateManagers();
     }
   }
+  return true;
 }
 
 }  // namespace
 
-void LoopUtils::CreateLoopDedicatedExits() {
+bool LoopUtils::CreateLoopDedicatedExits() {
   Function* function = loop_->GetHeaderBlock()->GetParent();
   LoopDescriptor& loop_desc = *context_->GetLoopDescriptor(function);
   CFG& cfg = *context_->cfg();
@@ -351,10 +365,13 @@
     assert(insert_pt != function->end() && "Basic Block not found");
 
     // Create the dedicate exit basic block.
-    // TODO(1841): Handle id overflow.
-    BasicBlock& exit = *insert_pt.InsertBefore(std::unique_ptr<BasicBlock>(
-        new BasicBlock(std::unique_ptr<Instruction>(new Instruction(
-            context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {})))));
+    uint32_t exit_id = context_->TakeNextId();
+    if (exit_id == 0) {
+      return false;
+    }
+    BasicBlock& exit = *insert_pt.InsertBefore(
+        std::unique_ptr<BasicBlock>(new BasicBlock(std::unique_ptr<Instruction>(
+            new Instruction(context_, spv::Op::OpLabel, 0, exit_id, {})))));
     exit.SetParent(function);
 
     // Redirect in loop predecessors to |exit| block.
@@ -380,7 +397,7 @@
     // We also reset the insert point so all instructions are inserted before
     // the branch.
     builder.SetInsertPoint(builder.AddBranch(non_dedicate->id()));
-    non_dedicate->ForEachPhiInst(
+    bool succeeded = non_dedicate->WhileEachPhiInst(
         [&builder, &exit, def_use_mgr, this](Instruction* phi) {
           // New phi operands for this instruction.
           std::vector<uint32_t> new_phi_op;
@@ -400,6 +417,9 @@
 
           // Build the new phi instruction dedicated exit block.
           Instruction* exit_phi = builder.AddPhi(phi->type_id(), exit_phi_op);
+          if (!exit_phi) {
+            return false;
+          }
           // Build the new incoming branch.
           new_phi_op.push_back(exit_phi->result_id());
           new_phi_op.push_back(exit.id());
@@ -412,7 +432,9 @@
             phi->RemoveInOperand(j);
           // Update the def/use manager for this |phi|.
           def_use_mgr->AnalyzeInstUse(phi);
+          return true;
         });
+    if (!succeeded) return false;
     // Update the CFG.
     cfg.RegisterBlock(&exit);
     cfg.RemoveNonExistingEdges(non_dedicate->id());
@@ -431,10 +453,13 @@
         PreservedAnalyses | IRContext::kAnalysisCFG |
         IRContext::Analysis::kAnalysisLoopAnalysis);
   }
+  return true;
 }
 
-void LoopUtils::MakeLoopClosedSSA() {
-  CreateLoopDedicatedExits();
+bool LoopUtils::MakeLoopClosedSSA() {
+  if (!CreateLoopDedicatedExits()) {
+    return false;
+  }
 
   Function* function = loop_->GetHeaderBlock()->GetParent();
   CFG& cfg = *context_->cfg();
@@ -452,8 +477,10 @@
 
   LCSSARewriter lcssa_rewriter(context_, dom_tree, exit_bb,
                                loop_->GetMergeBlock());
-  MakeSetClosedSSA(context_, function, loop_->GetBlocks(), exit_bb,
-                   &lcssa_rewriter);
+  if (!MakeSetClosedSSA(context_, function, loop_->GetBlocks(), exit_bb,
+                        &lcssa_rewriter)) {
+    return false;
+  }
 
   // Make sure all defs post-dominated by the merge block have their last use no
   // further than the merge block.
@@ -466,14 +493,17 @@
     exit_bb.insert(loop_->GetMergeBlock());
     // LCSSARewriter is reusable here only because it forces the creation of a
     // phi instruction in the merge block.
-    MakeSetClosedSSA(context_, function, merging_bb_id, exit_bb,
-                     &lcssa_rewriter);
+    if (!MakeSetClosedSSA(context_, function, merging_bb_id, exit_bb,
+                          &lcssa_rewriter)) {
+      return false;
+    }
   }
 
   context_->InvalidateAnalysesExceptFor(
       IRContext::Analysis::kAnalysisCFG |
       IRContext::Analysis::kAnalysisDominatorAnalysis |
       IRContext::Analysis::kAnalysisLoopAnalysis);
+  return true;
 }
 
 Loop* LoopUtils::CloneLoop(LoopCloningResult* cloning_result) const {
@@ -488,12 +518,18 @@
 
 Loop* LoopUtils::CloneAndAttachLoopToHeader(LoopCloningResult* cloning_result) {
   // Clone the loop.
-  Loop* new_loop = CloneLoop(cloning_result);
+  Loop* cloned_loop = CloneLoop(cloning_result);
+  if (!cloned_loop) {
+    return nullptr;
+  }
 
   // Create a new exit block/label for the new loop.
-  // TODO(1841): Handle id overflow.
-  std::unique_ptr<Instruction> new_label{new Instruction(
-      context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {})};
+  uint32_t new_label_id = context_->TakeNextId();
+  if (new_label_id == 0) {
+    return nullptr;
+  }
+  std::unique_ptr<Instruction> new_label{
+      new Instruction(context_, spv::Op::OpLabel, 0, new_label_id, {})};
   std::unique_ptr<BasicBlock> new_exit_bb{new BasicBlock(std::move(new_label))};
   new_exit_bb->SetParent(loop_->GetMergeBlock()->GetParent());
 
@@ -520,7 +556,7 @@
   }
 
   const uint32_t old_header = loop_->GetHeaderBlock()->id();
-  const uint32_t new_header = new_loop->GetHeaderBlock()->id();
+  const uint32_t new_header = cloned_loop->GetHeaderBlock()->id();
   analysis::DefUseManager* def_use = context_->get_def_use_mgr();
 
   def_use->ForEachUse(old_header,
@@ -529,22 +565,24 @@
                           inst->SetOperand(operand, {new_header});
                       });
 
-  // TODO(1841): Handle failure to create pre-header.
+  BasicBlock* pre_header = loop_->GetOrCreatePreHeaderBlock();
+  if (!pre_header) {
+    return nullptr;
+  }
   def_use->ForEachUse(
-      loop_->GetOrCreatePreHeaderBlock()->id(),
+      pre_header->id(),
       [new_merge_block, this](Instruction* inst, uint32_t operand) {
         if (this->loop_->IsInsideLoop(inst))
           inst->SetOperand(operand, {new_merge_block});
-
       });
-  new_loop->SetMergeBlock(new_exit_bb.get());
+  cloned_loop->SetMergeBlock(new_exit_bb.get());
 
-  new_loop->SetPreHeaderBlock(loop_->GetPreHeaderBlock());
+  cloned_loop->SetPreHeaderBlock(loop_->GetPreHeaderBlock());
 
   // Add the new block into the cloned instructions.
   cloning_result->cloned_bb_.push_back(std::move(new_exit_bb));
 
-  return new_loop;
+  return cloned_loop;
 }
 
 Loop* LoopUtils::CloneLoop(
@@ -561,9 +599,13 @@
     // For each basic block in the loop, we clone it and register the mapping
     // between old and new ids.
     BasicBlock* new_bb = old_bb->Clone(context_);
+    if (!new_bb) return nullptr;
     new_bb->SetParent(&function_);
-    // TODO(1841): Handle id overflow.
-    new_bb->GetLabelInst()->SetResultId(context_->TakeNextId());
+    uint32_t new_label_id = context_->TakeNextId();
+    if (new_label_id == 0) {
+      return nullptr;
+    }
+    new_bb->GetLabelInst()->SetResultId(new_label_id);
     def_use_mgr->AnalyzeInstDef(new_bb->GetLabelInst());
     context_->set_instr_block(new_bb->GetLabelInst(), new_bb);
     cloning_result->cloned_bb_.emplace_back(new_bb);
@@ -578,8 +620,11 @@
          new_inst != new_bb->end(); ++new_inst, ++old_inst) {
       cloning_result->ptr_map_[&*new_inst] = &*old_inst;
       if (new_inst->HasResultId()) {
-        // TODO(1841): Handle id overflow.
-        new_inst->SetResultId(context_->TakeNextId());
+        uint32_t new_result_id = context_->TakeNextId();
+        if (new_result_id == 0) {
+          return nullptr;
+        }
+        new_inst->SetResultId(new_result_id);
         cloning_result->value_map_[old_inst->result_id()] =
             new_inst->result_id();
 
diff --git a/source/opt/loop_utils.h b/source/opt/loop_utils.h
index 70060fc..2c96e9e 100644
--- a/source/opt/loop_utils.h
+++ b/source/opt/loop_utils.h
@@ -95,14 +95,14 @@
   //
   // This makes some loop transformations (such as loop unswitch) simpler
   // (removes the needs to take care of exiting variables).
-  void MakeLoopClosedSSA();
+  bool MakeLoopClosedSSA();
 
   // Create dedicate exit basic block. This ensure all exit basic blocks has the
   // loop as sole predecessors.
   // By construction, structured control flow already has a dedicated exit
   // block.
   // Preserves: CFG, def/use and instruction to block mapping.
-  void CreateLoopDedicatedExits();
+  bool CreateLoopDedicatedExits();
 
   // Clone |loop_| and remap its instructions. Newly created blocks
   // will be added to the |cloning_result.cloned_bb_| list, correctly ordered to
@@ -114,6 +114,7 @@
   // The function preserves the def/use, cfg and instr to block analyses.
   // The cloned loop nest will be added to the loop descriptor and will have
   // ownership.
+  // Returns the cloned loop, or nullptr if the loop could not be cloned.
   Loop* CloneLoop(LoopCloningResult* cloning_result,
                   const std::vector<BasicBlock*>& ordered_loop_blocks) const;
   // Clone |loop_| and remap its instructions, as above. Overload to compute
@@ -121,6 +122,7 @@
   Loop* CloneLoop(LoopCloningResult* cloning_result) const;
 
   // Clone the |loop_| and make the new loop branch to the second loop on exit.
+  // Returns the cloned loop, or nullptr if the loop could not be cloned.
   Loop* CloneAndAttachLoopToHeader(LoopCloningResult* cloning_result);
 
   // Perform a partial unroll of |loop| by given |factor|. This will copy the
diff --git a/source/opt/mem_pass.cpp b/source/opt/mem_pass.cpp
index 65f45ec..4d061ff 100644
--- a/source/opt/mem_pass.cpp
+++ b/source/opt/mem_pass.cpp
@@ -72,7 +72,8 @@
 
 bool MemPass::IsNonPtrAccessChain(const spv::Op opcode) const {
   return opcode == spv::Op::OpAccessChain ||
-         opcode == spv::Op::OpInBoundsAccessChain;
+         opcode == spv::Op::OpInBoundsAccessChain ||
+         opcode == spv::Op::OpUntypedAccessChainKHR;
 }
 
 bool MemPass::IsPtr(uint32_t ptrId) {
@@ -88,11 +89,14 @@
     ptrInst = get_def_use_mgr()->GetDef(varId);
   }
   const spv::Op op = ptrInst->opcode();
-  if (op == spv::Op::OpVariable || IsNonPtrAccessChain(op)) return true;
+  if (op == spv::Op::OpVariable || op == spv::Op::OpUntypedVariableKHR ||
+      IsNonPtrAccessChain(op))
+    return true;
   const uint32_t varTypeId = ptrInst->type_id();
   if (varTypeId == 0) return false;
   const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
-  return varTypeInst->opcode() == spv::Op::OpTypePointer;
+  return varTypeInst->opcode() == spv::Op::OpTypePointer ||
+         varTypeInst->opcode() == spv::Op::OpTypeUntypedPointerKHR;
 }
 
 Instruction* MemPass::GetPtr(uint32_t ptrId, uint32_t* varId) {
@@ -100,18 +104,29 @@
   Instruction* ptrInst = get_def_use_mgr()->GetDef(*varId);
   Instruction* varInst;
 
-  if (ptrInst->opcode() == spv::Op::OpConstantNull) {
-    *varId = 0;
-    return ptrInst;
+  switch (ptrInst->opcode()) {
+    case spv::Op::OpVariable:
+    case spv::Op::OpUntypedVariableKHR:
+    case spv::Op::OpFunctionParameter:
+      varInst = ptrInst;
+      break;
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpUntypedAccessChainKHR:
+    case spv::Op::OpPtrAccessChain:
+    case spv::Op::OpInBoundsPtrAccessChain:
+    case spv::Op::OpImageTexelPointer:
+    case spv::Op::OpCopyObject:
+      varInst = ptrInst->GetBaseAddress();
+      break;
+    default:
+      *varId = 0;
+      return ptrInst;
+      break;
   }
 
-  if (ptrInst->opcode() != spv::Op::OpVariable &&
-      ptrInst->opcode() != spv::Op::OpFunctionParameter) {
-    varInst = ptrInst->GetBaseAddress();
-  } else {
-    varInst = ptrInst;
-  }
-  if (varInst->opcode() == spv::Op::OpVariable) {
+  if (varInst->opcode() == spv::Op::OpVariable ||
+      varInst->opcode() == spv::Op::OpUntypedVariableKHR) {
     *varId = varInst->result_id();
   } else {
     *varId = 0;
@@ -332,7 +347,7 @@
 //           %50 = OpUndef %int
 //           [ ... ]
 //           %30 = OpPhi %int %int_42 %13 %50 %14 %50 %15
-void MemPass::RemovePhiOperands(
+bool MemPass::RemovePhiOperands(
     Instruction* phi, const std::unordered_set<BasicBlock*>& reachable_blocks) {
   std::vector<Operand> keep_operands;
   uint32_t type_id = 0;
@@ -374,6 +389,7 @@
       if (!undef_id) {
         type_id = arg_def_instr->type_id();
         undef_id = Type2Undef(type_id);
+        if (undef_id == 0) return false;
       }
       keep_operands.push_back(
           Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {undef_id}));
@@ -392,6 +408,7 @@
   context()->ForgetUses(phi);
   phi->ReplaceOperands(keep_operands);
   context()->AnalyzeUses(phi);
+  return true;
 }
 
 void MemPass::RemoveBlock(Function::iterator* bi) {
@@ -414,8 +431,8 @@
   *bi = bi->Erase();
 }
 
-bool MemPass::RemoveUnreachableBlocks(Function* func) {
-  if (func->IsDeclaration()) return false;
+Pass::Status MemPass::RemoveUnreachableBlocks(Function* func) {
+  if (func->IsDeclaration()) return Status::SuccessWithoutChange;
   bool modified = false;
 
   // Mark reachable all blocks reachable from the function's entry block.
@@ -461,9 +478,11 @@
     // If the block is reachable and has Phi instructions, remove all
     // operands from its Phi instructions that reference unreachable blocks.
     // If the block has no Phi instructions, this is a no-op.
-    block.ForEachPhiInst([&reachable_blocks, this](Instruction* phi) {
-      RemovePhiOperands(phi, reachable_blocks);
-    });
+    bool success =
+        block.WhileEachPhiInst([&reachable_blocks, this](Instruction* phi) {
+          return RemovePhiOperands(phi, reachable_blocks);
+        });
+    if (!success) return Status::Failure;
   }
 
   // Erase unreachable blocks.
@@ -476,13 +495,11 @@
     }
   }
 
-  return modified;
+  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
-bool MemPass::CFGCleanup(Function* func) {
-  bool modified = false;
-  modified |= RemoveUnreachableBlocks(func);
-  return modified;
+Pass::Status MemPass::CFGCleanup(Function* func) {
+  return RemoveUnreachableBlocks(func);
 }
 
 void MemPass::CollectTargetVars(Function* func) {
diff --git a/source/opt/mem_pass.h b/source/opt/mem_pass.h
index aef9e5f..496286b 100644
--- a/source/opt/mem_pass.h
+++ b/source/opt/mem_pass.h
@@ -114,7 +114,7 @@
   void DCEInst(Instruction* inst, const std::function<void(Instruction*)>&);
 
   // Call all the cleanup helper functions on |func|.
-  bool CFGCleanup(Function* func);
+  Status CFGCleanup(Function* func);
 
   // Return true if |op| is supported decorate.
   inline bool IsNonTypeDecorate(spv::Op op) const {
@@ -142,15 +142,15 @@
   bool HasOnlySupportedRefs(uint32_t varId);
 
   // Remove all the unreachable basic blocks in |func|.
-  bool RemoveUnreachableBlocks(Function* func);
+  Status RemoveUnreachableBlocks(Function* func);
 
   // Remove the block pointed by the iterator |*bi|. This also removes
   // all the instructions in the pointed-to block.
   void RemoveBlock(Function::iterator* bi);
 
   // Remove Phi operands in |phi| that are coming from blocks not in
-  // |reachable_blocks|.
-  void RemovePhiOperands(
+  // |reachable_blocks|. Returns false if it fails.
+  bool RemovePhiOperands(
       Instruction* phi,
       const std::unordered_set<BasicBlock*>& reachable_blocks);
 
diff --git a/source/opt/merge_return_pass.cpp b/source/opt/merge_return_pass.cpp
index c262ea0..0ec7c37 100644
--- a/source/opt/merge_return_pass.cpp
+++ b/source/opt/merge_return_pass.cpp
@@ -58,7 +58,9 @@
         failed = true;
       }
     } else {
-      MergeReturnBlocks(function, return_blocks);
+      if (!MergeReturnBlocks(function, return_blocks)) {
+        failed = true;
+      }
     }
     return true;
   };
@@ -132,7 +134,9 @@
       state_.pop_back();
     }
 
-    ProcessStructuredBlock(block);
+    if (!ProcessStructuredBlock(block)) {
+      return false;
+    }
 
     // Generate state for next block if warranted
     GenerateState(block);
@@ -167,14 +171,20 @@
   // We have not kept the dominator tree up-to-date.
   // Invalidate it at this point to make sure it will be rebuilt.
   context()->RemoveDominatorAnalysis(function);
-  AddNewPhiNodes();
+  if (!AddNewPhiNodes()) {
+    return false;
+  }
   return true;
 }
 
-void MergeReturnPass::CreateReturnBlock() {
+bool MergeReturnPass::CreateReturnBlock() {
   // Create a label for the new return block
+  uint32_t label_id = TakeNextId();
+  if (label_id == 0) {
+    return false;
+  }
   std::unique_ptr<Instruction> return_label(
-      new Instruction(context(), spv::Op::OpLabel, 0u, TakeNextId(), {}));
+      new Instruction(context(), spv::Op::OpLabel, 0u, label_id, {}));
 
   // Create the new basic block
   std::unique_ptr<BasicBlock> return_block(
@@ -186,14 +196,20 @@
                              final_return_block_);
   assert(final_return_block_->GetParent() == function_ &&
          "The function should have been set when the block was created.");
+  return true;
 }
 
-void MergeReturnPass::CreateReturn(BasicBlock* block) {
-  AddReturnValue();
+bool MergeReturnPass::CreateReturn(BasicBlock* block) {
+  if (!AddReturnValue()) {
+    return false;
+  }
 
   if (return_value_) {
     // Load and return the final return value
     uint32_t loadId = TakeNextId();
+    if (loadId == 0) {
+      return false;
+    }
     block->AddInstruction(MakeUnique<Instruction>(
         context(), spv::Op::OpLoad, function_->type_id(), loadId,
         std::initializer_list<Operand>{
@@ -216,14 +232,21 @@
     context()->AnalyzeDefUse(block->terminator());
     context()->set_instr_block(block->terminator(), block);
   }
+  return true;
 }
 
-void MergeReturnPass::ProcessStructuredBlock(BasicBlock* block) {
+bool MergeReturnPass::ProcessStructuredBlock(BasicBlock* block) {
+  if (block->tail() == block->end()) {
+    return true;
+  }
+
   spv::Op tail_opcode = block->tail()->opcode();
   if (tail_opcode == spv::Op::OpReturn ||
       tail_opcode == spv::Op::OpReturnValue) {
     if (!return_flag_) {
-      AddReturnFlag();
+      if (!AddReturnFlag()) {
+        return false;
+      }
     }
   }
 
@@ -232,15 +255,20 @@
       tail_opcode == spv::Op::OpUnreachable) {
     assert(CurrentState().InBreakable() &&
            "Should be in the placeholder construct.");
-    BranchToBlock(block, CurrentState().BreakMergeId());
+    if (!BranchToBlock(block, CurrentState().BreakMergeId())) {
+      return false;
+    }
     return_blocks_.insert(block->id());
   }
+  return true;
 }
 
-void MergeReturnPass::BranchToBlock(BasicBlock* block, uint32_t target) {
+bool MergeReturnPass::BranchToBlock(BasicBlock* block, uint32_t target) {
   if (block->tail()->opcode() == spv::Op::OpReturn ||
       block->tail()->opcode() == spv::Op::OpReturnValue) {
-    RecordReturned(block);
+    if (!RecordReturned(block)) {
+      return false;
+    }
     RecordReturnValue(block);
   }
 
@@ -248,7 +276,9 @@
   if (target_block->GetLoopMergeInst()) {
     cfg()->SplitLoopHeader(target_block);
   }
-  UpdatePhiNodes(block, target_block);
+  if (!UpdatePhiNodes(block, target_block)) {
+    return false;
+  }
 
   Instruction* return_inst = block->terminator();
   return_inst->SetOpcode(spv::Op::OpBranch);
@@ -256,19 +286,26 @@
   context()->get_def_use_mgr()->AnalyzeInstDefUse(return_inst);
   new_edges_[target_block].insert(block->id());
   cfg()->AddEdge(block->id(), target);
+  return true;
 }
 
-void MergeReturnPass::UpdatePhiNodes(BasicBlock* new_source,
+bool MergeReturnPass::UpdatePhiNodes(BasicBlock* new_source,
                                      BasicBlock* target) {
-  target->ForEachPhiInst([this, new_source](Instruction* inst) {
+  bool succeeded = true;
+  target->ForEachPhiInst([this, new_source, &succeeded](Instruction* inst) {
     uint32_t undefId = Type2Undef(inst->type_id());
+    if (undefId == 0) {
+      succeeded = false;
+      return;
+    }
     inst->AddOperand({SPV_OPERAND_TYPE_ID, {undefId}});
     inst->AddOperand({SPV_OPERAND_TYPE_ID, {new_source->id()}});
     context()->UpdateDefUse(inst);
   });
+  return succeeded;
 }
 
-void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
+bool MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
                                             Instruction& inst) {
   DominatorAnalysis* dom_tree =
       context()->GetDominatorAnalysis(merge_block->GetParent());
@@ -302,7 +339,7 @@
         });
 
     if (users_to_update.empty()) {
-      return;
+      return true;
     }
 
     // There is at least one values that needs to be replaced.
@@ -346,6 +383,9 @@
     if (regenerateInstruction) {
       std::unique_ptr<Instruction> regen_inst(inst.Clone(context()));
       uint32_t new_id = TakeNextId();
+      if (new_id == 0) {
+        return false;
+      }
       regen_inst->SetResultId(new_id);
       Instruction* insert_pos = &*merge_block->begin();
       while (insert_pos->opcode() == spv::Op::OpPhi) {
@@ -355,18 +395,31 @@
       get_def_use_mgr()->AnalyzeInstDefUse(new_phi);
       context()->set_instr_block(new_phi, merge_block);
 
-      new_phi->ForEachInId([dom_tree, merge_block, this](uint32_t* use_id) {
+      bool succeeded = true;
+      new_phi->ForEachInId([dom_tree, merge_block, this,
+                            &succeeded](uint32_t* use_id) {
+        if (!succeeded) {
+          return;
+        }
         Instruction* use = get_def_use_mgr()->GetDef(*use_id);
         BasicBlock* use_bb = context()->get_instr_block(use);
         if (use_bb != nullptr && !dom_tree->Dominates(use_bb, merge_block)) {
-          CreatePhiNodesForInst(merge_block, *use);
+          if (!CreatePhiNodesForInst(merge_block, *use)) {
+            succeeded = false;
+          }
         }
       });
+      if (!succeeded) {
+        return false;
+      }
     } else {
       InstructionBuilder builder(
           context(), &*merge_block->begin(),
           IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
       new_phi = builder.AddPhi(inst.type_id(), phi_operands);
+      if (new_phi == nullptr) {
+        return false;
+      }
     }
     uint32_t result_of_phi = new_phi->result_id();
 
@@ -380,6 +433,7 @@
       context()->AnalyzeUses(user);
     }
   }
+  return true;
 }
 
 bool MergeReturnPass::PredicateBlocks(
@@ -472,6 +526,9 @@
   cfg()->RemoveSuccessorEdges(block);
 
   auto old_body_id = TakeNextId();
+  if (old_body_id == 0) {
+    return false;
+  }
   BasicBlock* old_body = block->SplitBasicBlock(context(), old_body_id, iter);
   predicated->insert(old_body);
 
@@ -508,8 +565,11 @@
   analysis::Bool bool_type;
   uint32_t bool_id = context()->get_type_mgr()->GetId(&bool_type);
   assert(bool_id != 0);
-  uint32_t load_id =
-      builder.AddLoad(bool_id, return_flag_->result_id())->result_id();
+  Instruction* load_inst = builder.AddLoad(bool_id, return_flag_->result_id());
+  if (load_inst == nullptr) {
+    return false;
+  }
+  uint32_t load_id = load_inst->result_id();
 
   // 2. Branch to |merge_block| (true) or |old_body| (false)
   builder.AddConditionalBranch(load_id, merge_block->id(), old_body->id(),
@@ -522,7 +582,9 @@
   }
 
   // 3. Update OpPhi instructions in |merge_block|.
-  UpdatePhiNodes(block, merge_block);
+  if (!UpdatePhiNodes(block, merge_block)) {
+    return false;
+  }
 
   // 4. Update the CFG.  We do this after updating the OpPhi instructions
   // because |UpdatePhiNodes| assumes the edge from |block| has not been added
@@ -535,10 +597,10 @@
   return true;
 }
 
-void MergeReturnPass::RecordReturned(BasicBlock* block) {
+bool MergeReturnPass::RecordReturned(BasicBlock* block) {
   if (block->tail()->opcode() != spv::Op::OpReturn &&
       block->tail()->opcode() != spv::Op::OpReturnValue)
-    return;
+    return true;
 
   assert(return_flag_ && "Did not generate the return flag variable.");
 
@@ -551,6 +613,9 @@
     const analysis::Constant* true_const =
         const_mgr->GetConstant(bool_type, {true});
     constant_true_ = const_mgr->GetDefiningInstruction(true_const);
+    if (!constant_true_) {
+      return false;
+    }
     context()->UpdateDefUse(constant_true_);
   }
 
@@ -564,6 +629,7 @@
       &*block->tail().InsertBefore(std::move(return_store));
   context()->set_instr_block(store_inst, block);
   context()->AnalyzeDefUse(store_inst);
+  return true;
 }
 
 void MergeReturnPass::RecordReturnValue(BasicBlock* block) {
@@ -587,18 +653,21 @@
   context()->AnalyzeDefUse(store_inst);
 }
 
-void MergeReturnPass::AddReturnValue() {
-  if (return_value_) return;
+bool MergeReturnPass::AddReturnValue() {
+  if (return_value_) return true;
 
   uint32_t return_type_id = function_->type_id();
   if (get_def_use_mgr()->GetDef(return_type_id)->opcode() ==
       spv::Op::OpTypeVoid)
-    return;
+    return true;
 
   uint32_t return_ptr_type = context()->get_type_mgr()->FindPointerToType(
       return_type_id, spv::StorageClass::Function);
 
   uint32_t var_id = TakeNextId();
+  if (var_id == 0) {
+    return false;
+  }
   std::unique_ptr<Instruction> returnValue(
       new Instruction(context(), spv::Op::OpVariable, return_ptr_type, var_id,
                       std::initializer_list<Operand>{
@@ -614,27 +683,44 @@
 
   context()->get_decoration_mgr()->CloneDecorations(
       function_->result_id(), var_id, {spv::Decoration::RelaxedPrecision});
+  return true;
 }
 
-void MergeReturnPass::AddReturnFlag() {
-  if (return_flag_) return;
+bool MergeReturnPass::AddReturnFlag() {
+  if (return_flag_) return true;
 
   analysis::TypeManager* type_mgr = context()->get_type_mgr();
   analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
 
   analysis::Bool temp;
   uint32_t bool_id = type_mgr->GetTypeInstruction(&temp);
+  if (bool_id == 0) {
+    return false;
+  }
   analysis::Bool* bool_type = type_mgr->GetType(bool_id)->AsBool();
 
   const analysis::Constant* false_const =
       const_mgr->GetConstant(bool_type, {false});
-  uint32_t const_false_id =
-      const_mgr->GetDefiningInstruction(false_const)->result_id();
+  Instruction* false_inst = const_mgr->GetDefiningInstruction(false_const);
+  if (false_inst == nullptr) {
+    return false;
+  }
+  uint32_t const_false_id = false_inst->result_id();
 
   uint32_t bool_ptr_id =
       type_mgr->FindPointerToType(bool_id, spv::StorageClass::Function);
 
+  if (bool_ptr_id == 0) {
+    return false;
+    ;
+  }
+
   uint32_t var_id = TakeNextId();
+
+  if (var_id == 0) {
+    return false;
+  }
+
   std::unique_ptr<Instruction> returnFlag(new Instruction(
       context(), spv::Op::OpVariable, bool_ptr_id, var_id,
       std::initializer_list<Operand>{{SPV_OPERAND_TYPE_STORAGE_CLASS,
@@ -648,6 +734,7 @@
   return_flag_ = &*entry_block->begin();
   context()->AnalyzeDefUse(return_flag_);
   context()->set_instr_block(return_flag_, entry_block);
+  return true;
 }
 
 std::vector<BasicBlock*> MergeReturnPass::CollectReturnBlocks(
@@ -663,14 +750,16 @@
   return return_blocks;
 }
 
-void MergeReturnPass::MergeReturnBlocks(
+bool MergeReturnPass::MergeReturnBlocks(
     Function* function, const std::vector<BasicBlock*>& return_blocks) {
   if (return_blocks.size() <= 1) {
     // No work to do.
-    return;
+    return true;
   }
 
-  CreateReturnBlock();
+  if (!CreateReturnBlock()) {
+    return false;
+  }
   uint32_t return_id = final_return_block_->id();
   auto ret_block_iter = --function->end();
   // Create the PHI for the merged block (if necessary).
@@ -687,6 +776,9 @@
   if (!phi_ops.empty()) {
     // Need a PHI node to select the correct return value.
     uint32_t phi_result_id = TakeNextId();
+    if (phi_result_id == 0) {
+      return false;
+    }
     uint32_t phi_type_id = function->type_id();
     std::unique_ptr<Instruction> phi_inst(new Instruction(
         context(), spv::Op::OpPhi, phi_type_id, phi_result_id, phi_ops));
@@ -718,18 +810,22 @@
   }
 
   get_def_use_mgr()->AnalyzeInstDefUse(ret_block_iter->GetLabelInst());
+  return true;
 }
 
-void MergeReturnPass::AddNewPhiNodes() {
+bool MergeReturnPass::AddNewPhiNodes() {
   std::list<BasicBlock*> order;
   cfg()->ComputeStructuredOrder(function_, &*function_->begin(), &order);
 
   for (BasicBlock* bb : order) {
-    AddNewPhiNodes(bb);
+    if (!AddNewPhiNodes(bb)) {
+      return false;
+    }
   }
+  return true;
 }
 
-void MergeReturnPass::AddNewPhiNodes(BasicBlock* bb) {
+bool MergeReturnPass::AddNewPhiNodes(BasicBlock* bb) {
   // New phi nodes are needed for any id whose definition used to dominate |bb|,
   // but no longer dominates |bb|.  These are found by walking the dominator
   // tree starting at the original immediate dominator of |bb| and ending at its
@@ -747,16 +843,19 @@
 
   BasicBlock* dominator = dom_tree->ImmediateDominator(bb);
   if (dominator == nullptr) {
-    return;
+    return true;
   }
 
   BasicBlock* current_bb = context()->get_instr_block(original_dominator_[bb]);
   while (current_bb != nullptr && current_bb != dominator) {
     for (Instruction& inst : *current_bb) {
-      CreatePhiNodesForInst(bb, inst);
+      if (!CreatePhiNodesForInst(bb, inst)) {
+        return false;
+      }
     }
     current_bb = dom_tree->ImmediateDominator(current_bb);
   }
+  return true;
 }
 
 void MergeReturnPass::RecordImmediateDominators(Function* function) {
@@ -781,8 +880,12 @@
 }
 
 bool MergeReturnPass::AddSingleCaseSwitchAroundFunction() {
-  CreateReturnBlock();
-  CreateReturn(final_return_block_);
+  if (!CreateReturnBlock()) {
+    return false;
+  }
+  if (!CreateReturn(final_return_block_)) {
+    return false;
+  }
 
   if (context()->AreAnalysesValid(IRContext::kAnalysisCFG)) {
     cfg()->RegisterBlock(final_return_block_);
@@ -828,15 +931,32 @@
 
 bool MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) {
   // Insert the switch before any code is run.  We have to split the entry
-  // block to make sure the OpVariable instructions remain in the entry block.
+  // block to make sure the OpVariable instructions and DebugFunctionDefinition
+  // instructions remain in the entry block.
   BasicBlock* start_block = &*function_->begin();
   auto split_pos = start_block->begin();
   while (split_pos->opcode() == spv::Op::OpVariable) {
     ++split_pos;
   }
 
+  uint32_t new_block_id = TakeNextId();
+  if (new_block_id == 0) {
+    return false;
+  }
   BasicBlock* old_block =
-      start_block->SplitBasicBlock(context(), TakeNextId(), split_pos);
+      start_block->SplitBasicBlock(context(), new_block_id, split_pos);
+
+  // Find DebugFunctionDefinition inst in the old block, and if we can find it,
+  // move it to the entry block. Since DebugFunctionDefinition is not necessary
+  // after OpVariable inst, we have to traverse the whole block to find it.
+  for (auto pos = old_block->begin(); pos != old_block->end(); ++pos) {
+    if (pos->GetShaderDebugOpcode() ==
+        NonSemanticShaderDebugInfoDebugFunctionDefinition) {
+      start_block->AddInstruction(MakeUnique<Instruction>(*pos));
+      pos.Erase();
+      break;
+    }
+  }
 
   // Add the switch to the end of the entry block.
   InstructionBuilder builder(
diff --git a/source/opt/merge_return_pass.h b/source/opt/merge_return_pass.h
index d15db2f..d83ffc3 100644
--- a/source/opt/merge_return_pass.h
+++ b/source/opt/merge_return_pass.h
@@ -149,8 +149,9 @@
 
   // Creates a new basic block with a single return. If |function| returns a
   // value, a phi node is created to select the correct value to return.
-  // Replaces old returns with an unconditional branch to the new block.
-  void MergeReturnBlocks(Function* function,
+  // Replaces old returns with an unconditional branch to the new block. Returns
+  // true if successful.
+  bool MergeReturnBlocks(Function* function,
                          const std::vector<BasicBlock*>& returnBlocks);
 
   // Generate and push new control flow state if |block| contains a merge.
@@ -172,21 +173,22 @@
   //
   // Note this will break the semantics.  To fix this, PredicateBlock will have
   // to be called on the merge block the branch targets.
-  void ProcessStructuredBlock(BasicBlock* block);
+  bool ProcessStructuredBlock(BasicBlock* block);
 
   // Creates a variable used to store whether or not the control flow has
   // traversed a block that used to have a return.  A pointer to the instruction
-  // declaring the variable is stored in |return_flag_|.
-  void AddReturnFlag();
+  // declaring the variable is stored in |return_flag_|. Returns true if it
+  // succeeds.
+  bool AddReturnFlag();
 
   // Creates the variable used to store the return value when passing through
-  // a block that use to contain an OpReturnValue.
-  void AddReturnValue();
+  // a block that use to contain an OpReturnValue. Returns true if it succeeds.
+  bool AddReturnValue();
 
-  // Adds a store that stores true to |return_flag_| immediately before the
-  // terminator of |block|. It is assumed that |AddReturnFlag| has already been
-  // called.
-  void RecordReturned(BasicBlock* block);
+  // Records that |block| used to be a return.  This is done by adding an
+  // instruction to store true to the |return_flag_|. Returns true if it
+  // succeeds.
+  bool RecordReturned(BasicBlock* block);
 
   // Adds an instruction that stores the value being returned in the
   // OpReturnValue in |block|.  The value is stored to |return_value_|, and the
@@ -197,10 +199,10 @@
   // have already been called to create the variable to store to.
   void RecordReturnValue(BasicBlock* block);
 
-  // Adds an unconditional branch in |block| that branches to |target|.  It also
-  // adds stores to |return_flag_| and |return_value_| as needed.
-  // |AddReturnFlag| and |AddReturnValue| must have already been called.
-  void BranchToBlock(BasicBlock* block, uint32_t target);
+  // Replaces the terminator of |block| with a branch to |target|.  If the
+  // terminator was a return, it will first call RecordReturned and
+  // RecordReturnValue. Returns true if it succeeds.
+  bool BranchToBlock(BasicBlock* block, uint32_t target);
 
   // For every basic block that is reachable from |return_block|, extra code is
   // added to jump around any code that should not be executed because the
@@ -231,38 +233,35 @@
 
   // Add an |OpReturn| or |OpReturnValue| to the end of |block|.  If an
   // |OpReturnValue| is needed, the return value is loaded from |return_value_|.
-  void CreateReturn(BasicBlock* block);
+  // Returns true if successful.
+  bool CreateReturn(BasicBlock* block);
 
   // Creates a block at the end of the function that will become the single
   // return block at the end of the pass.
-  void CreateReturnBlock();
+  bool CreateReturnBlock();
 
-  // Creates a Phi node in |merge_block| for the result of |inst|.
-  // Any uses of the result of |inst| that are no longer
-  // dominated by |inst|, are replaced with the result of the new |OpPhi|
-  // instruction.
-  void CreatePhiNodesForInst(BasicBlock* merge_block, Instruction& inst);
+  // For each use of |inst| that is no longer dominated by |inst|, a phi node
+  // is created in |merge_block|.  The original use is replaced by the result
+  // of the phi node. Returns true if it succeeds.
+  bool CreatePhiNodesForInst(BasicBlock* merge_block, Instruction& inst);
 
-  // Add new phi nodes for any id that no longer dominate all of it uses.  A phi
-  // node is added to a block |bb| for an id if the id is defined between the
-  // original immediate dominator of |bb| and its new immediate dominator.  It
-  // is assumed that at this point there are no unreachable blocks in the
-  // control flow graph.
-  void AddNewPhiNodes();
+  // Adds new phi nodes as needed to the function.  This is necessary because
+  // adding the predication code can change the dominator tree.  Returns false
+  // if there is a failure.
+  bool AddNewPhiNodes();
 
-  // Creates any new phi nodes that are needed in |bb|.  |AddNewPhiNodes| must
-  // have already been called on the original dominators of |bb|.
-  void AddNewPhiNodes(BasicBlock* bb);
+  // Adds new phi nodes to |bb| as needed.  This is necessary because adding
+  // the predication code can change the dominator tree.  Returns false if
+  // there is a failure.
+  bool AddNewPhiNodes(BasicBlock* bb);
 
   // Records the terminator of immediate dominator for every basic block in
   // |function|.
   void RecordImmediateDominators(Function* function);
 
-  // Modifies existing OpPhi instruction in |target| block to account for the
-  // new edge from |new_source|.  The value for that edge will be an Undef.
-  //
-  // The CFG must not include the edge from |new_source| to |target| yet.
-  void UpdatePhiNodes(BasicBlock* new_source, BasicBlock* target);
+  // For each OpPhi instruction in |target|, this function adds an operand for
+  // |new_source|.  The value will be OpUndef. Returns true if it succeeds.
+  bool UpdatePhiNodes(BasicBlock* new_source, BasicBlock* target);
 
   StructuredControlState& CurrentState() { return state_.back(); }
 
diff --git a/source/opt/module.cpp b/source/opt/module.cpp
index a9710c6..86f6a28 100644
--- a/source/opt/module.cpp
+++ b/source/opt/module.cpp
@@ -93,6 +93,7 @@
   if (sampled_image_address_mode_)
     sampled_image_address_mode_->ForEachInst(f, run_on_debug_line_insts);
   DELEGATE(entry_points_);
+  DELEGATE(graph_entry_points_);
   DELEGATE(execution_modes_);
   DELEGATE(debugs1_);
   DELEGATE(debugs2_);
@@ -104,6 +105,10 @@
     i->ForEachInst(f, run_on_debug_line_insts,
                    /* run_on_non_semantic_insts = */ true);
   }
+  for (auto& g : graphs_) {
+    g->ForEachInst(f, run_on_debug_line_insts,
+                   /* run_on_non_semantic_insts = */ true);
+  }
 #undef DELEGATE
 }
 
@@ -132,6 +137,12 @@
         f, run_on_debug_line_insts,
         /* run_on_non_semantic_insts = */ true);
   }
+  for (auto& i : graph_entry_points_) DELEGATE(i);
+  for (auto& i : graphs_) {
+    static_cast<const Graph*>(i.get())->ForEachInst(
+        f, run_on_debug_line_insts,
+        /* run_on_non_semantic_insts = */ true);
+  }
   if (run_on_debug_line_insts) {
     for (auto& i : trailing_dbg_line_info_) DELEGATE(i);
   }
@@ -176,16 +187,15 @@
         // If the current instruction does not have the line information,
         // the last line information is not effective any more. Emit OpNoLine
         // or DebugNoLine to specify it.
-        uint32_t shader_set_id = context()
-                                     ->get_feature_mgr()
-                                     ->GetExtInstImportId_Shader100DebugInfo();
+        uint32_t shader_set_id =
+            context()->get_feature_mgr()->GetExtInstImportId_ShaderDebugInfo();
         if (shader_set_id != 0) {
           binary->push_back((5 << 16) |
                             static_cast<uint16_t>(spv::Op::OpExtInst));
           binary->push_back(context()->get_type_mgr()->GetVoidTypeId());
           binary->push_back(context()->TakeNextId());
           binary->push_back(shader_set_id);
-          binary->push_back(NonSemanticShaderDebugInfo100DebugNoLine);
+          binary->push_back(NonSemanticShaderDebugInfoDebugNoLine);
         } else {
           binary->push_back((1 << 16) |
                             static_cast<uint16_t>(spv::Op::OpNoLine));
@@ -206,7 +216,7 @@
       if (scope != last_scope && !between_merge_and_branch) {
         // Can only emit nonsemantic instructions after all phi instructions
         // in a block so don't emit scope instructions before phi instructions
-        // for NonSemantic.Shader.DebugInfo.100.
+        // for NonSemantic.Shader.DebugInfo.
         if (!between_label_and_phi_var ||
             context()
                 ->get_feature_mgr()
diff --git a/source/opt/module.h b/source/opt/module.h
index 98c16dc..0f32995 100644
--- a/source/opt/module.h
+++ b/source/opt/module.h
@@ -23,6 +23,7 @@
 #include <vector>
 
 #include "source/opt/function.h"
+#include "source/opt/graph.h"
 #include "source/opt/instruction.h"
 #include "source/opt/iterator.h"
 
@@ -48,6 +49,8 @@
   using const_iterator = UptrVectorIterator<Function, true>;
   using inst_iterator = InstructionList::iterator;
   using const_inst_iterator = InstructionList::const_iterator;
+  using graph_iterator = UptrVectorIterator<Graph>;
+  using const_graph_iterator = UptrVectorIterator<Graph, true>;
 
   // Creates an empty module with zero'd header.
   Module() : header_({}), contains_debug_info_(false) {}
@@ -90,6 +93,9 @@
   // Appends an entry point instruction to this module.
   inline void AddEntryPoint(std::unique_ptr<Instruction> e);
 
+  // Appends a graph entry point instruction to this module.
+  inline void AddGraphEntryPoint(std::unique_ptr<Instruction> e);
+
   // Appends an execution mode instruction to this module.
   inline void AddExecutionMode(std::unique_ptr<Instruction> e);
 
@@ -108,7 +114,7 @@
   inline void AddDebug3Inst(std::unique_ptr<Instruction> d);
 
   // Appends a debug info extension (OpenCL.DebugInfo.100,
-  // NonSemantic.Shader.DebugInfo.100, or DebugInfo) instruction to this module.
+  // NonSemantic.Shader.DebugInfo, or DebugInfo) instruction to this module.
   inline void AddExtInstDebugInfo(std::unique_ptr<Instruction> d);
 
   // Appends an annotation instruction to this module.
@@ -126,6 +132,9 @@
   // Appends a function to this module.
   inline void AddFunction(std::unique_ptr<Function> f);
 
+  // Appends a graph to this module.
+  inline void AddGraph(std::unique_ptr<Graph> g);
+
   // Sets |contains_debug_info_| as true.
   inline void SetContainsDebugInfo();
   inline bool ContainsDebugInfo() { return contains_debug_info_; }
@@ -220,6 +229,10 @@
   inline IteratorRange<inst_iterator> entry_points();
   inline IteratorRange<const_inst_iterator> entry_points() const;
 
+  // Iterators for graph entry point instructions contained in this module
+  inline IteratorRange<inst_iterator> graph_entry_points();
+  inline IteratorRange<const_inst_iterator> graph_entry_points() const;
+
   // Iterators for execution_modes instructions contained in this module.
   inline inst_iterator execution_mode_begin();
   inline inst_iterator execution_mode_end();
@@ -252,6 +265,9 @@
   inline const_iterator cbegin() const;
   inline const_iterator cend() const;
 
+  // Iterators for graphs contained in this module.
+  inline const std::vector<std::unique_ptr<Graph>>& graphs() const;
+
   // Invokes function |f| on all instructions in this module, and optionally on
   // the debug line instructions that precede them.
   void ForEachInst(const std::function<void(Instruction*)>& f,
@@ -306,6 +322,7 @@
   // A module can only have one optional sampled image addressing mode
   std::unique_ptr<Instruction> sampled_image_address_mode_;
   InstructionList entry_points_;
+  InstructionList graph_entry_points_;
   InstructionList execution_modes_;
   InstructionList debugs1_;
   InstructionList debugs2_;
@@ -315,6 +332,7 @@
   // Type declarations, constants, and global variable declarations.
   InstructionList types_values_;
   std::vector<std::unique_ptr<Function>> functions_;
+  std::vector<std::unique_ptr<Graph>> graphs_;
 
   // If the module ends with Op*Line instruction, they will not be attached to
   // any instruction.  We record them here, so they will not be lost.
@@ -351,6 +369,10 @@
   entry_points_.push_back(std::move(e));
 }
 
+inline void Module::AddGraphEntryPoint(std::unique_ptr<Instruction> e) {
+  graph_entry_points_.push_back(std::move(e));
+}
+
 inline void Module::AddExecutionMode(std::unique_ptr<Instruction> e) {
   execution_modes_.push_back(std::move(e));
 }
@@ -392,6 +414,10 @@
   functions_.emplace_back(std::move(f));
 }
 
+inline void Module::AddGraph(std::unique_ptr<Graph> g) {
+  graphs_.emplace_back(std::move(g));
+}
+
 inline void Module::SetContainsDebugInfo() { contains_debug_info_ = true; }
 
 inline Module::inst_iterator Module::capability_begin() {
@@ -482,6 +508,15 @@
   return make_range(entry_points_.begin(), entry_points_.end());
 }
 
+inline IteratorRange<Module::inst_iterator> Module::graph_entry_points() {
+  return make_range(graph_entry_points_.begin(), graph_entry_points_.end());
+}
+
+inline IteratorRange<Module::const_inst_iterator> Module::graph_entry_points()
+    const {
+  return make_range(graph_entry_points_.begin(), graph_entry_points_.end());
+}
+
 inline Module::inst_iterator Module::execution_mode_begin() {
   return execution_modes_.begin();
 }
@@ -544,6 +579,10 @@
   return make_range(types_values_.begin(), types_values_.end());
 }
 
+inline const std::vector<std::unique_ptr<Graph>>& Module::graphs() const {
+  return graphs_;
+}
+
 inline Module::const_iterator Module::cbegin() const {
   return const_iterator(&functions_, functions_.cbegin());
 }
diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp
index 94b15c6..6986501 100644
--- a/source/opt/optimizer.cpp
+++ b/source/opt/optimizer.cpp
@@ -18,6 +18,7 @@
 #include <charconv>
 #include <memory>
 #include <string>
+#include <system_error>
 #include <unordered_map>
 #include <utility>
 #include <vector>
@@ -148,7 +149,9 @@
           .RegisterPass(CreateLocalSingleStoreElimPass())
           .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
           .RegisterPass(CreateLocalMultiStoreElimPass())
+          .RegisterPass(CreateCombineAccessChainsPass())
           .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
+          .RegisterPass(CreateLegalizeMultidimArrayPass())
           // Propagate constants to get as many constant conditions on branches
           // as possible.
           .RegisterPass(CreateCCPPass())
@@ -188,7 +191,7 @@
       .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
       .RegisterPass(CreateLocalSingleStoreElimPass())
       .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
-      .RegisterPass(CreateScalarReplacementPass())
+      .RegisterPass(CreateScalarReplacementPass(0))
       .RegisterPass(CreateLocalAccessChainConvertPass())
       .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
       .RegisterPass(CreateLocalSingleStoreElimPass())
@@ -202,7 +205,7 @@
       .RegisterPass(CreateRedundancyEliminationPass())
       .RegisterPass(CreateCombineAccessChainsPass())
       .RegisterPass(CreateSimplificationPass())
-      .RegisterPass(CreateScalarReplacementPass())
+      .RegisterPass(CreateScalarReplacementPass(0))
       .RegisterPass(CreateLocalAccessChainConvertPass())
       .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
       .RegisterPass(CreateLocalSingleStoreElimPass())
@@ -398,9 +401,11 @@
     RegisterPass(CreateFoldSpecConstantOpAndCompositePass());
   } else if (pass_name == "loop-unswitch") {
     RegisterPass(CreateLoopUnswitchPass());
+  } else if (pass_name == "legalize-multidim-array") {
+    RegisterPass(CreateLegalizeMultidimArrayPass());
   } else if (pass_name == "scalar-replacement") {
     if (pass_args.size() == 0) {
-      RegisterPass(CreateScalarReplacementPass());
+      RegisterPass(CreateScalarReplacementPass(0));
     } else {
       int limit = -1;
       if (pass_args.find_first_not_of("0123456789") == std::string::npos) {
@@ -636,6 +641,12 @@
     }
   } else if (pass_name == "trim-capabilities") {
     RegisterPass(CreateTrimCapabilitiesPass());
+  } else if (pass_name == "split-combined-image-sampler") {
+    RegisterPass(CreateSplitCombinedImageSamplerPass());
+  } else if (pass_name == "resolve-binding-conflicts") {
+    RegisterPass(CreateResolveBindingConflictsPass());
+  } else if (pass_name == "canonicalize-ids") {
+    RegisterPass(CreateCanonicalizeIdsPass());
   } else {
     Errorf(consumer(), nullptr, {},
            "Unknown flag '--%s'. Use --help for a list of valid flags",
@@ -954,6 +965,11 @@
       MakeUnique<opt::LoopUnswitchPass>());
 }
 
+Optimizer::PassToken CreateLegalizeMultidimArrayPass() {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::LegalizeMultidimArrayPass>());
+}
+
 Optimizer::PassToken CreateRedundancyEliminationPass() {
   return MakeUnique<Optimizer::PassToken::Impl>(
       MakeUnique<opt::RedundancyEliminationPass>());
@@ -1187,6 +1203,21 @@
       MakeUnique<opt::OpExtInstWithForwardReferenceFixupPass>());
 }
 
+Optimizer::PassToken CreateSplitCombinedImageSamplerPass() {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::SplitCombinedImageSamplerPass>());
+}
+
+Optimizer::PassToken CreateResolveBindingConflictsPass() {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::ResolveBindingConflictsPass>());
+}
+
+Optimizer::PassToken CreateCanonicalizeIdsPass() {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::CanonicalizeIdsPass>());
+}
+
 }  // namespace spvtools
 
 extern "C" {
diff --git a/source/opt/pass.cpp b/source/opt/pass.cpp
index 0f260e2..ce37f36 100644
--- a/source/opt/pass.cpp
+++ b/source/opt/pass.cpp
@@ -114,8 +114,12 @@
       assert(length_const->AsIntConstant());
       uint32_t array_length = length_const->AsIntConstant()->GetU32();
       for (uint32_t i = 0; i < array_length; i++) {
+        // TODO(1841): Handle id overflow.
         Instruction* extract = ir_builder.AddCompositeExtract(
             original_element_type_id, object_to_copy->result_id(), {i});
+        if (extract == nullptr) {
+          return 0;
+        }
         uint32_t new_id =
             GenerateCopy(extract, new_element_type_id, insertion_position);
         if (new_id == 0) {
@@ -124,16 +128,24 @@
         element_ids.push_back(new_id);
       }
 
-      return ir_builder.AddCompositeConstruct(new_type_id, element_ids)
-          ->result_id();
+      Instruction* construct =
+          ir_builder.AddCompositeConstruct(new_type_id, element_ids);
+      if (construct == nullptr) {
+        return 0;
+      }
+      return construct->result_id();
     }
     case spv::Op::OpTypeStruct: {
       std::vector<uint32_t> element_ids;
       for (uint32_t i = 0; i < original_type->NumInOperands(); i++) {
         uint32_t orig_member_type_id = original_type->GetSingleWordInOperand(i);
         uint32_t new_member_type_id = new_type->GetSingleWordInOperand(i);
+        // TODO(1841): Handle id overflow.
         Instruction* extract = ir_builder.AddCompositeExtract(
             orig_member_type_id, object_to_copy->result_id(), {i});
+        if (extract == nullptr) {
+          return 0;
+        }
         uint32_t new_id =
             GenerateCopy(extract, new_member_type_id, insertion_position);
         if (new_id == 0) {
@@ -141,8 +153,12 @@
         }
         element_ids.push_back(new_id);
       }
-      return ir_builder.AddCompositeConstruct(new_type_id, element_ids)
-          ->result_id();
+      Instruction* construct =
+          ir_builder.AddCompositeConstruct(new_type_id, element_ids);
+      if (construct == nullptr) {
+        return 0;
+      }
+      return construct->result_id();
     }
     default:
       // If we do not have an aggregate type, then we have a problem.  Either we
diff --git a/source/opt/pass.h b/source/opt/pass.h
index 3e6c4d0..73773e3 100644
--- a/source/opt/pass.h
+++ b/source/opt/pass.h
@@ -25,6 +25,7 @@
 #include "source/opt/def_use_manager.h"
 #include "source/opt/ir_context.h"
 #include "source/opt/module.h"
+#include "source/util/status.h"
 #include "spirv-tools/libspirv.hpp"
 #include "types.h"
 
@@ -46,11 +47,7 @@
   //
   // The numbers for the cases are assigned to make sure that Failure & anything
   // is Failure, SuccessWithChange & any success is SuccessWithChange.
-  enum class Status {
-    Failure = 0x00,
-    SuccessWithChange = 0x10,
-    SuccessWithoutChange = 0x11,
-  };
+  using Status = utils::Status;
 
   using ProcessFunction = std::function<bool(Function*)>;
 
diff --git a/source/opt/passes.h b/source/opt/passes.h
index 3311529..533fc21 100644
--- a/source/opt/passes.h
+++ b/source/opt/passes.h
@@ -21,6 +21,7 @@
 #include "source/opt/amd_ext_to_khr.h"
 #include "source/opt/analyze_live_input_pass.h"
 #include "source/opt/block_merge_pass.h"
+#include "source/opt/canonicalize_ids_pass.h"
 #include "source/opt/ccp_pass.h"
 #include "source/opt/cfg_cleanup_pass.h"
 #include "source/opt/code_sink.h"
@@ -51,6 +52,7 @@
 #include "source/opt/interface_var_sroa.h"
 #include "source/opt/interp_fixup_pass.h"
 #include "source/opt/invocation_interlock_placement_pass.h"
+#include "source/opt/legalize_multidim_array_pass.h"
 #include "source/opt/licm_pass.h"
 #include "source/opt/local_access_chain_convert_pass.h"
 #include "source/opt/local_redundancy_elimination.h"
@@ -74,9 +76,11 @@
 #include "source/opt/remove_unused_interface_variables_pass.h"
 #include "source/opt/replace_desc_array_access_using_var_index.h"
 #include "source/opt/replace_invalid_opc.h"
+#include "source/opt/resolve_binding_conflicts_pass.h"
 #include "source/opt/scalar_replacement_pass.h"
 #include "source/opt/set_spec_constant_default_value_pass.h"
 #include "source/opt/simplification_pass.h"
+#include "source/opt/split_combined_image_sampler_pass.h"
 #include "source/opt/spread_volatile_semantics.h"
 #include "source/opt/ssa_rewrite_pass.h"
 #include "source/opt/strength_reduction_pass.h"
diff --git a/source/opt/private_to_local_pass.cpp b/source/opt/private_to_local_pass.cpp
index 4904e05..ac33dfb 100644
--- a/source/opt/private_to_local_pass.cpp
+++ b/source/opt/private_to_local_pass.cpp
@@ -90,13 +90,13 @@
   Function* target_function = nullptr;
   context()->get_def_use_mgr()->ForEachUser(
       inst.result_id(),
-      [&target_function, &found_first_use, this](Instruction* use) {
+      [&target_function, &found_first_use, inst, this](Instruction* use) {
         BasicBlock* current_block = context()->get_instr_block(use);
         if (current_block == nullptr) {
           return;
         }
 
-        if (!IsValidUse(use)) {
+        if (!IsValidUse(use, inst.result_id())) {
           found_first_use = true;
           target_function = nullptr;
           return;
@@ -153,7 +153,8 @@
   return new_type_id;
 }
 
-bool PrivateToLocalPass::IsValidUse(const Instruction* inst) const {
+bool PrivateToLocalPass::IsValidUse(const Instruction* inst,
+                                    uint32_t private_variable_id) const {
   // The cases in this switch have to match the cases in |UpdateUse|.
   // If we don't know how to update it, it is not valid.
   if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) {
@@ -161,13 +162,14 @@
   }
   switch (inst->opcode()) {
     case spv::Op::OpLoad:
-    case spv::Op::OpStore:
     case spv::Op::OpImageTexelPointer:  // Treat like a load
       return true;
+    case spv::Op::OpStore:
+      return inst->GetOperand(1).AsId() != private_variable_id;
     case spv::Op::OpAccessChain:
       return context()->get_def_use_mgr()->WhileEachUser(
-          inst, [this](const Instruction* user) {
-            if (!IsValidUse(user)) return false;
+          inst, [this, inst](const Instruction* user) {
+            if (!IsValidUse(user, inst->result_id())) return false;
             return true;
           });
     case spv::Op::OpName:
diff --git a/source/opt/private_to_local_pass.h b/source/opt/private_to_local_pass.h
index e96a965..93c1c50 100644
--- a/source/opt/private_to_local_pass.h
+++ b/source/opt/private_to_local_pass.h
@@ -53,7 +53,7 @@
   // Returns true is |inst| is a valid use of a pointer.  In this case, a
   // valid use is one where the transformation is able to rewrite the type to
   // match a change in storage class of the original variable.
-  bool IsValidUse(const Instruction* inst) const;
+  bool IsValidUse(const Instruction* inst, uint32_t private_variable_id) const;
 
   // Given the result id of a pointer type, |old_type_id|, this function
   // returns the id of a the same pointer type except the storage class has
diff --git a/source/opt/propagator.cpp b/source/opt/propagator.cpp
index 9cd6174..67f9132 100644
--- a/source/opt/propagator.cpp
+++ b/source/opt/propagator.cpp
@@ -84,16 +84,19 @@
   return status_changed;
 }
 
-bool SSAPropagator::Simulate(Instruction* instr) {
+Pass::Status SSAPropagator::Simulate(Instruction* instr) {
   bool changed = false;
 
   // Don't bother visiting instructions that should not be simulated again.
   if (!ShouldSimulateAgain(instr)) {
-    return changed;
+    return Pass::Status::SuccessWithoutChange;
   }
 
   BasicBlock* dest_bb = nullptr;
   PropStatus status = visit_fn_(instr, &dest_bb);
+  if (status == SSAPropagator::kFailed) {
+    return Pass::Status::Failure;
+  }
   bool status_changed = SetStatus(instr, status);
 
   if (status == kVarying) {
@@ -112,7 +115,7 @@
         AddControlEdge(e);
       }
     }
-    return false;
+    return Pass::Status::SuccessWithoutChange;
   } else if (status == kInteresting) {
     // Add the SSA edges coming out of this instruction if the propagation
     // status has changed.
@@ -169,12 +172,13 @@
     DontSimulateAgain(instr);
   }
 
-  return changed;
+  return changed ? Pass::Status::SuccessWithChange
+                 : Pass::Status::SuccessWithoutChange;
 }
 
-bool SSAPropagator::Simulate(BasicBlock* block) {
+Pass::Status SSAPropagator::Simulate(BasicBlock* block) {
   if (block == ctx_->cfg()->pseudo_exit_block()) {
-    return false;
+    return Pass::Status::SuccessWithoutChange;
   }
 
   // Always simulate Phi instructions, even if we have simulated this block
@@ -182,17 +186,29 @@
   // incoming edges. When those edges are marked executable, the corresponding
   // operand can be simulated.
   bool changed = false;
-  block->ForEachPhiInst(
-      [&changed, this](Instruction* instr) { changed |= Simulate(instr); });
+  bool succeeded =
+      block->WhileEachPhiInst([&changed, this](Instruction* instr) {
+        auto Status = Simulate(instr);
+        if (Status == Pass::Status::Failure) return false;
+        changed |= Status == Pass::Status::SuccessWithChange;
+        return true;
+      });
+  if (!succeeded) {
+    return Pass::Status::Failure;
+  }
 
   // If this is the first time this block is being simulated, simulate every
   // statement in it.
   if (!BlockHasBeenSimulated(block)) {
-    block->ForEachInst([this, &changed](Instruction* instr) {
-      if (instr->opcode() != spv::Op::OpPhi) {
-        changed |= Simulate(instr);
-      }
+    succeeded = block->WhileEachInst([&changed, this](Instruction* instr) {
+      auto Status = Simulate(instr);
+      if (Status == Pass::Status::Failure) return false;
+      changed |= Status == Pass::Status::SuccessWithChange;
+      return true;
     });
+    if (!succeeded) {
+      return Pass::Status::Failure;
+    }
 
     MarkBlockSimulated(block);
 
@@ -203,7 +219,8 @@
     }
   }
 
-  return changed;
+  return changed ? Pass::Status::SuccessWithChange
+                 : Pass::Status::SuccessWithoutChange;
 }
 
 void SSAPropagator::Initialize(Function* fn) {
@@ -245,7 +262,11 @@
     // follow after all the blocks have been simulated.
     if (!blocks_.empty()) {
       auto block = blocks_.front();
-      changed |= Simulate(block);
+      Pass::Status status = Simulate(block);
+      if (status == Pass::Status::Failure) {
+        return false;
+      }
+      changed |= status == Pass::Status::SuccessWithChange;
       blocks_.pop();
       continue;
     }
@@ -253,7 +274,11 @@
     // Simulate edges from the SSA queue.
     if (!ssa_edge_uses_.empty()) {
       Instruction* instr = ssa_edge_uses_.front();
-      changed |= Simulate(instr);
+      Pass::Status status = Simulate(instr);
+      if (status == Pass::Status::Failure) {
+        return changed;
+      }
+      changed |= status == Pass::Status::SuccessWithChange;
       ssa_edge_uses_.pop();
     }
   }
diff --git a/source/opt/propagator.h b/source/opt/propagator.h
index 71212c9..2727f24 100644
--- a/source/opt/propagator.h
+++ b/source/opt/propagator.h
@@ -25,6 +25,7 @@
 
 #include "source/opt/ir_context.h"
 #include "source/opt/module.h"
+#include "source/opt/pass.h"
 
 namespace spvtools {
 namespace opt {
@@ -182,7 +183,7 @@
  public:
   // Lattice values used for propagation. See class documentation for
   // a description.
-  enum PropStatus { kNotInteresting, kInteresting, kVarying };
+  enum PropStatus { kNotInteresting, kInteresting, kVarying, kFailed };
 
   using VisitFunction = std::function<PropStatus(Instruction*, BasicBlock**)>;
 
@@ -190,7 +191,9 @@
       : ctx_(context), visit_fn_(visit_fn) {}
 
   // Runs the propagator on function |fn|. Returns true if changes were made to
-  // the function. Otherwise, it returns false.
+  // the function. Otherwise, it returns false. The user should check
+  // IRContext::id_overflow() to see if there was an error caused by reaching
+  // the max id.
   bool Run(Function* fn);
 
   // Returns true if the |i|th argument for |phi| comes through a CFG edge that
@@ -218,13 +221,13 @@
 
   // Simulate the execution |block| by calling |visit_fn_| on every instruction
   // in it.
-  bool Simulate(BasicBlock* block);
+  Pass::Status Simulate(BasicBlock* block);
 
   // Simulate the execution of |instr| by replacing all the known values in
   // every operand and determining whether the result is interesting for
   // propagation. This invokes the callback function |visit_fn_| to determine
   // the value computed by |instr|.
-  bool Simulate(Instruction* instr);
+  Pass::Status Simulate(Instruction* instr);
 
   // Returns true if |instr| should be simulated again.
   bool ShouldSimulateAgain(Instruction* instr) const {
diff --git a/source/opt/reduce_load_size.cpp b/source/opt/reduce_load_size.cpp
index 73a90f0..3162d79 100644
--- a/source/opt/reduce_load_size.cpp
+++ b/source/opt/reduce_load_size.cpp
@@ -108,6 +108,7 @@
   Instruction* new_access_chain = ir_builder.AddAccessChain(
       pointer_to_result_type_id,
       composite_inst->GetSingleWordInOperand(kLoadPointerInIdx), ids);
+  // TODO(1841): Handle id overflow.
   Instruction* new_load =
       ir_builder.AddLoad(inst->type_id(), new_access_chain->result_id());
 
diff --git a/source/opt/redundancy_elimination.cpp b/source/opt/redundancy_elimination.cpp
index 398225b..61234fa 100644
--- a/source/opt/redundancy_elimination.cpp
+++ b/source/opt/redundancy_elimination.cpp
@@ -33,12 +33,7 @@
     DominatorTree& dom_tree =
         context()->GetDominatorAnalysis(&func)->GetDomTree();
 
-    // Keeps track of all ids that contain a given value number. We keep
-    // track of multiple values because they could have the same value, but
-    // different decorations.
-    std::map<uint32_t, uint32_t> value_to_ids;
-
-    if (EliminateRedundanciesFrom(dom_tree.GetRoot(), vnTable, value_to_ids)) {
+    if (EliminateRedundanciesFrom(dom_tree.GetRoot(), vnTable)) {
       modified = true;
     }
   }
@@ -46,14 +41,21 @@
 }
 
 bool RedundancyEliminationPass::EliminateRedundanciesFrom(
-    DominatorTreeNode* bb, const ValueNumberTable& vnTable,
-    std::map<uint32_t, uint32_t> value_to_ids) {
-  bool modified = EliminateRedundanciesInBB(bb->bb_, vnTable, &value_to_ids);
-
-  for (auto dominated_bb : bb->children_) {
-    modified |= EliminateRedundanciesFrom(dominated_bb, vnTable, value_to_ids);
+    DominatorTreeNode* bb, const ValueNumberTable& vnTable) {
+  struct State {
+    DominatorTreeNode* node;
+    std::map<uint32_t, uint32_t> value_to_id_map;
+  };
+  std::vector<State> todo;
+  todo.push_back({bb, std::map<uint32_t, uint32_t>()});
+  bool modified = false;
+  for (size_t next_node = 0; next_node < todo.size(); next_node++) {
+    modified |= EliminateRedundanciesInBB(todo[next_node].node->bb_, vnTable,
+                                          &todo[next_node].value_to_id_map);
+    for (DominatorTreeNode* child : todo[next_node].node->children_) {
+      todo.push_back({child, todo[next_node].value_to_id_map});
+    }
   }
-
   return modified;
 }
 }  // namespace opt
diff --git a/source/opt/redundancy_elimination.h b/source/opt/redundancy_elimination.h
index 40451f4..8c6e16a 100644
--- a/source/opt/redundancy_elimination.h
+++ b/source/opt/redundancy_elimination.h
@@ -46,8 +46,7 @@
   //
   // Returns true if at least one instruction is deleted.
   bool EliminateRedundanciesFrom(DominatorTreeNode* bb,
-                                 const ValueNumberTable& vnTable,
-                                 std::map<uint32_t, uint32_t> value_to_ids);
+                                 const ValueNumberTable& vnTable);
 };
 
 }  // namespace opt
diff --git a/source/opt/reflect.h b/source/opt/reflect.h
index ec7c2dd..db43d02 100644
--- a/source/opt/reflect.h
+++ b/source/opt/reflect.h
@@ -44,7 +44,8 @@
           opcode <= spv::Op::OpGroupMemberDecorate) ||
          opcode == spv::Op::OpDecorateId ||
          opcode == spv::Op::OpDecorateStringGOOGLE ||
-         opcode == spv::Op::OpMemberDecorateStringGOOGLE;
+         opcode == spv::Op::OpMemberDecorateStringGOOGLE ||
+         opcode == spv::Op::OpMemberDecorateIdEXT;
 }
 inline bool IsTypeInst(spv::Op opcode) {
   return spvOpcodeGeneratesType(opcode) ||
diff --git a/source/opt/remove_duplicates_pass.cpp b/source/opt/remove_duplicates_pass.cpp
index 0df559b..a388155 100644
--- a/source/opt/remove_duplicates_pass.cpp
+++ b/source/opt/remove_duplicates_pass.cpp
@@ -29,6 +29,7 @@
 
 Pass::Status RemoveDuplicatesPass::Process() {
   bool modified = RemoveDuplicateCapabilities();
+  modified |= RemoveDuplicateExtensions();
   modified |= RemoveDuplicatesExtInstImports();
   modified |= RemoveDuplicateTypes();
   modified |= RemoveDuplicateDecorations();
@@ -36,6 +37,41 @@
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
+bool RemoveDuplicatesPass::RemoveDuplicateExtensions() const {
+  bool modified = false;
+
+  if (context()->extensions().empty()) {
+    return modified;
+  }
+
+  // set of {condition ID, extension name}
+  // ID 0 means unconditional extension, ie., OpExtension, otherwise the ID is
+  // the condition operand of OpConditionalExtensionINTEL.
+  std::set<std::pair<uint32_t, std::string>> extensions;
+  for (auto* inst = &*context()->extension_begin(); inst;) {
+    uint32_t cond_id = 0;
+    uint32_t i_name = 0;
+    if (inst->opcode() == spv::Op::OpConditionalExtensionINTEL) {
+      cond_id = inst->GetOperand(0).AsId();
+      i_name = 1;
+    }
+
+    auto res =
+        extensions.insert({cond_id, inst->GetOperand(i_name).AsString()});
+
+    if (res.second) {
+      // Never seen before, keep it.
+      inst = inst->NextNode();
+    } else {
+      // It's a duplicate, remove it.
+      inst = context()->KillInst(inst);
+      modified = true;
+    }
+  }
+
+  return modified;
+}
+
 bool RemoveDuplicatesPass::RemoveDuplicateCapabilities() const {
   bool modified = false;
 
@@ -43,16 +79,27 @@
     return modified;
   }
 
-  std::unordered_set<uint32_t> capabilities;
-  for (auto* i = &*context()->capability_begin(); i;) {
-    auto res = capabilities.insert(i->GetSingleWordOperand(0u));
+  // set of {condition ID, capability}
+  // ID 0 means unconditional capability, ie., OpCapability, otherwise the ID is
+  // the condition operand of OpConditionalCapabilityINTEL.
+  std::set<std::pair<uint32_t, uint32_t>> capabilities;
+  for (auto* inst = &*context()->capability_begin(); inst;) {
+    uint32_t cond_id = 0;
+    uint32_t i_cap = 0;
+    if (inst->opcode() == spv::Op::OpConditionalCapabilityINTEL) {
+      cond_id = inst->GetOperand(0).AsId();
+      i_cap = 1;
+    }
+
+    auto res =
+        capabilities.insert({cond_id, inst->GetSingleWordOperand(i_cap)});
 
     if (res.second) {
       // Never seen before, keep it.
-      i = i->NextNode();
+      inst = inst->NextNode();
     } else {
       // It's a duplicate, remove it.
-      i = context()->KillInst(i);
+      inst = context()->KillInst(inst);
       modified = true;
     }
   }
diff --git a/source/opt/remove_duplicates_pass.h b/source/opt/remove_duplicates_pass.h
index 038caa8..e5b3925 100644
--- a/source/opt/remove_duplicates_pass.h
+++ b/source/opt/remove_duplicates_pass.h
@@ -37,6 +37,10 @@
   Status Process() override;
 
  private:
+  // Remove duplicate extensions from the module
+  //
+  // Returns true if the module was modified, false otherwise.
+  bool RemoveDuplicateExtensions() const;
   // Remove duplicate capabilities from the module
   //
   // Returns true if the module was modified, false otherwise.
diff --git a/source/opt/remove_unused_interface_variables_pass.cpp b/source/opt/remove_unused_interface_variables_pass.cpp
index c3a4b77..61e6eac 100644
--- a/source/opt/remove_unused_interface_variables_pass.cpp
+++ b/source/opt/remove_unused_interface_variables_pass.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "remove_unused_interface_variables_pass.h"
+
 #include "source/spirv_constant.h"
 namespace spvtools {
 namespace opt {
@@ -33,7 +34,9 @@
         instruction.ForEachInId([&](const uint32_t* id) {
           if (used_variables_.count(*id)) return;
           auto* var = parent_.get_def_use_mgr()->GetDef(*id);
-          if (!var || var->opcode() != spv::Op::OpVariable) return;
+          if (!var || (var->opcode() != spv::Op::OpVariable &&
+                       var->opcode() != spv::Op::OpUntypedVariableKHR))
+            return;
           auto storage_class =
               spv::StorageClass(var->GetSingleWordInOperand(0));
           if (storage_class != spv::StorageClass::Function &&
@@ -55,7 +58,9 @@
 
   void CollectUsedVariables() {
     std::queue<uint32_t> roots;
-    roots.push(entry_.GetSingleWordInOperand(1));
+    const int op_i =
+        entry_.opcode() == spv::Op::OpConditionalEntryPointINTEL ? 2 : 1;
+    roots.push(entry_.GetSingleWordInOperand(op_i));
     parent_.context()->ProcessCallTreeFromRoots(pfn_, &roots);
   }
 
@@ -73,7 +78,9 @@
   }
 
   void Modify() {
-    for (int i = entry_.NumInOperands() - 1; i >= 3; --i)
+    const int min_num_operands =
+        entry_.opcode() == spv::Op::OpConditionalEntryPointINTEL ? 4 : 3;
+    for (int i = entry_.NumInOperands() - 1; i >= min_num_operands; --i)
       entry_.RemoveInOperand(i);
     for (auto id : operands_to_add_) {
       entry_.AddOperand(Operand(SPV_OPERAND_TYPE_ID, {id}));
diff --git a/source/opt/replace_desc_array_access_using_var_index.cpp b/source/opt/replace_desc_array_access_using_var_index.cpp
index 59745e1..619fa93 100644
--- a/source/opt/replace_desc_array_access_using_var_index.cpp
+++ b/source/opt/replace_desc_array_access_using_var_index.cpp
@@ -41,14 +41,15 @@
   Status status = Status::SuccessWithoutChange;
   for (Instruction& var : context()->types_values()) {
     if (descsroautil::IsDescriptorArray(context(), &var)) {
-      if (ReplaceVariableAccessesWithConstantElements(&var))
-        status = Status::SuccessWithChange;
+      Status s = ReplaceVariableAccessesWithConstantElements(&var);
+      if (s == Status::Failure) return Status::Failure;
+      if (s == Status::SuccessWithChange) status = Status::SuccessWithChange;
     }
   }
   return status;
 }
 
-bool ReplaceDescArrayAccessUsingVarIndex::
+Pass::Status ReplaceDescArrayAccessUsingVarIndex::
     ReplaceVariableAccessesWithConstantElements(Instruction* var) const {
   std::vector<Instruction*> work_list;
   get_def_use_mgr()->ForEachUser(var, [&work_list](Instruction* use) {
@@ -66,16 +67,16 @@
   for (Instruction* access_chain : work_list) {
     if (descsroautil::GetAccessChainIndexAsConst(context(), access_chain) ==
         nullptr) {
-      ReplaceAccessChain(var, access_chain);
+      if (!ReplaceAccessChain(var, access_chain)) return Status::Failure;
       updated = true;
     }
   }
   // Note that we do not consider OpLoad and OpCompositeExtract because
   // OpCompositeExtract always has constant literals for indices.
-  return updated;
+  return updated ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
-void ReplaceDescArrayAccessUsingVarIndex::ReplaceAccessChain(
+bool ReplaceDescArrayAccessUsingVarIndex::ReplaceAccessChain(
     Instruction* var, Instruction* access_chain) const {
   uint32_t number_of_elements =
       descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var);
@@ -83,21 +84,23 @@
   if (number_of_elements == 1) {
     UseConstIndexForAccessChain(access_chain, 0);
     get_def_use_mgr()->AnalyzeInstUse(access_chain);
-    return;
+    return true;
   }
-  ReplaceUsersOfAccessChain(access_chain, number_of_elements);
+  return ReplaceUsersOfAccessChain(access_chain, number_of_elements);
 }
 
-void ReplaceDescArrayAccessUsingVarIndex::ReplaceUsersOfAccessChain(
+bool ReplaceDescArrayAccessUsingVarIndex::ReplaceUsersOfAccessChain(
     Instruction* access_chain, uint32_t number_of_elements) const {
   std::vector<Instruction*> final_users;
   CollectRecursiveUsersWithConcreteType(access_chain, &final_users);
   for (auto* inst : final_users) {
     std::deque<Instruction*> insts_to_be_cloned =
         CollectRequiredImageAndAccessInsts(inst);
-    ReplaceNonUniformAccessWithSwitchCase(
-        inst, access_chain, number_of_elements, insts_to_be_cloned);
+    if (!ReplaceNonUniformAccessWithSwitchCase(
+            inst, access_chain, number_of_elements, insts_to_be_cloned))
+      return false;
   }
+  return true;
 }
 
 void ReplaceDescArrayAccessUsingVarIndex::CollectRecursiveUsersWithConcreteType(
@@ -208,17 +211,23 @@
     const std::deque<Instruction*>& insts_to_be_cloned,
     uint32_t branch_target_id,
     std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const {
-  auto* case_block = CreateNewBlock();
-  AddConstElementAccessToCaseBlock(case_block, access_chain, element_index,
-                                   old_ids_to_new_ids);
-  CloneInstsToBlock(case_block, access_chain, insts_to_be_cloned,
-                    old_ids_to_new_ids);
-  AddBranchToBlock(case_block, branch_target_id);
-  UseNewIdsInBlock(case_block, *old_ids_to_new_ids);
-  return case_block;
+  std::unique_ptr<BasicBlock> case_block(CreateNewBlock());
+  if (!case_block) return nullptr;
+
+  if (!AddConstElementAccessToCaseBlock(case_block.get(), access_chain,
+                                        element_index, old_ids_to_new_ids)) {
+    return nullptr;
+  }
+  if (!CloneInstsToBlock(case_block.get(), access_chain, insts_to_be_cloned,
+                         old_ids_to_new_ids)) {
+    return nullptr;
+  }
+  AddBranchToBlock(case_block.get(), branch_target_id);
+  UseNewIdsInBlock(case_block.get(), *old_ids_to_new_ids);
+  return case_block.release();
 }
 
-void ReplaceDescArrayAccessUsingVarIndex::CloneInstsToBlock(
+bool ReplaceDescArrayAccessUsingVarIndex::CloneInstsToBlock(
     BasicBlock* block, Instruction* inst_to_skip_cloning,
     const std::deque<Instruction*>& insts_to_be_cloned,
     std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const {
@@ -227,6 +236,7 @@
     std::unique_ptr<Instruction> clone(inst_to_be_cloned->Clone(context()));
     if (inst_to_be_cloned->HasResultId()) {
       uint32_t new_id = context()->TakeNextId();
+      if (new_id == 0) return false;
       clone->SetResultId(new_id);
       (*old_ids_to_new_ids)[inst_to_be_cloned->result_id()] = new_id;
     }
@@ -234,6 +244,7 @@
     context()->set_instr_block(clone.get(), block);
     block->AddInstruction(std::move(clone));
   }
+  return true;
 }
 
 void ReplaceDescArrayAccessUsingVarIndex::UseNewIdsInBlock(
@@ -250,18 +261,19 @@
   }
 }
 
-void ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase(
+bool ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase(
     Instruction* access_chain_final_user, Instruction* access_chain,
     uint32_t number_of_elements,
     const std::deque<Instruction*>& insts_to_be_cloned) const {
   auto* block = context()->get_instr_block(access_chain_final_user);
   // If the instruction does not belong to a block (i.e. in the case of
   // OpDecorate), no replacement is needed.
-  if (!block) return;
+  if (!block) return true;
 
   // Create merge block and add terminator
   auto* merge_block = SeparateInstructionsIntoNewBlock(
       block, access_chain_final_user->NextNode());
+  if (!merge_block) return false;
 
   auto* function = block->GetParent();
 
@@ -273,6 +285,7 @@
     std::unique_ptr<BasicBlock> case_block(CreateCaseBlock(
         access_chain, idx, insts_to_be_cloned, merge_block->id(),
         &old_ids_to_new_ids_for_cloned_insts));
+    if (!case_block) return false;
     case_block_ids.push_back(case_block->id());
     function->InsertBasicBlockBefore(std::move(case_block), merge_block);
 
@@ -288,6 +301,7 @@
   std::unique_ptr<BasicBlock> default_block(
       CreateDefaultBlock(access_chain_final_user->HasResultId(), &phi_operands,
                          merge_block->id()));
+  if (!default_block) return false;
   uint32_t default_block_id = default_block->id();
   function->InsertBasicBlockBefore(std::move(default_block), merge_block);
 
@@ -301,11 +315,13 @@
   if (!phi_operands.empty()) {
     uint32_t phi_id = CreatePhiInstruction(merge_block, phi_operands,
                                            case_block_ids, default_block_id);
+    if (phi_id == 0) return false;
     context()->ReplaceAllUsesWith(access_chain_final_user->result_id(), phi_id);
   }
 
   // Replace OpPhi incoming block operand that uses |block| with |merge_block|
   ReplacePhiIncomingBlock(block->id(), merge_block->id());
+  return true;
 }
 
 BasicBlock*
@@ -316,13 +332,16 @@
          &*separation_begin != separation_begin_inst) {
     ++separation_begin;
   }
-  return block->SplitBasicBlock(context(), context()->TakeNextId(),
-                                separation_begin);
+  uint32_t new_id = context()->TakeNextId();
+  if (new_id == 0) return nullptr;
+  return block->SplitBasicBlock(context(), new_id, separation_begin);
 }
 
 BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateNewBlock() const {
-  auto* new_block = new BasicBlock(std::unique_ptr<Instruction>(new Instruction(
-      context(), spv::Op::OpLabel, 0, context()->TakeNextId(), {})));
+  uint32_t new_id = context()->TakeNextId();
+  if (new_id == 0) return nullptr;
+  auto* new_block = new BasicBlock(std::unique_ptr<Instruction>(
+      new Instruction(context(), spv::Op::OpLabel, 0, new_id, {})));
   get_def_use_mgr()->AnalyzeInstDefUse(new_block->GetLabelInst());
   context()->set_instr_block(new_block->GetLabelInst(), new_block);
   return new_block;
@@ -336,7 +355,7 @@
                              {const_element_idx_id});
 }
 
-void ReplaceDescArrayAccessUsingVarIndex::AddConstElementAccessToCaseBlock(
+bool ReplaceDescArrayAccessUsingVarIndex::AddConstElementAccessToCaseBlock(
     BasicBlock* case_block, Instruction* access_chain,
     uint32_t const_element_idx,
     std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const {
@@ -344,12 +363,14 @@
   UseConstIndexForAccessChain(access_clone.get(), const_element_idx);
 
   uint32_t new_access_id = context()->TakeNextId();
+  if (new_access_id == 0) return false;
   (*old_ids_to_new_ids)[access_clone->result_id()] = new_access_id;
   access_clone->SetResultId(new_access_id);
   get_def_use_mgr()->AnalyzeInstDefUse(access_clone.get());
 
   context()->set_instr_block(access_clone.get(), case_block);
   case_block->AddInstruction(std::move(access_clone));
+  return true;
 }
 
 void ReplaceDescArrayAccessUsingVarIndex::AddBranchToBlock(
@@ -363,6 +384,7 @@
     bool null_const_for_phi_is_needed, std::vector<uint32_t>* phi_operands,
     uint32_t merge_block_id) const {
   auto* default_block = CreateNewBlock();
+  if (!default_block) return nullptr;
   AddBranchToBlock(default_block, merge_block_id);
   if (!null_const_for_phi_is_needed) return default_block;
 
@@ -413,7 +435,11 @@
                              kAnalysisDefUseAndInstrToBlockMapping};
   uint32_t phi_result_type_id =
       context()->get_def_use_mgr()->GetDef(phi_operands[0])->type_id();
-  auto* phi = builder.AddPhi(phi_result_type_id, incomings);
+  Instruction* phi = builder.AddPhi(phi_result_type_id, incomings);
+  if (!phi) {
+    return 0;
+  }
+  context()->get_def_use_mgr()->AnalyzeInstDefUse(phi);
   return phi->result_id();
 }
 
diff --git a/source/opt/replace_desc_array_access_using_var_index.h b/source/opt/replace_desc_array_access_using_var_index.h
index 51817c1..b385532 100644
--- a/source/opt/replace_desc_array_access_using_var_index.h
+++ b/source/opt/replace_desc_array_access_using_var_index.h
@@ -49,14 +49,13 @@
  private:
   // Replaces all accesses to |var| using variable indices with constant
   // elements of the array |var|. Creates switch-case statements to determine
-  // the value of the variable index for all the possible cases. Returns
-  // whether replacement is done or not.
-  bool ReplaceVariableAccessesWithConstantElements(Instruction* var) const;
+  // the value of the variable index for all the possible cases.
+  Status ReplaceVariableAccessesWithConstantElements(Instruction* var) const;
 
   // Replaces the OpAccessChain or OpInBoundsAccessChain instruction |use| that
   // uses the descriptor variable |var| with the OpAccessChain or
   // OpInBoundsAccessChain instruction with a constant Indexes operand.
-  void ReplaceAccessChain(Instruction* var, Instruction* use) const;
+  bool ReplaceAccessChain(Instruction* var, Instruction* use) const;
 
   // Updates the first Indexes operand of the OpAccessChain or
   // OpInBoundsAccessChain instruction |access_chain| to let it use a constant
@@ -68,7 +67,7 @@
   // |access_chain| that accesses an array descriptor variable using variable
   // indices with constant elements. |number_of_elements| is the number
   // of array elements.
-  void ReplaceUsersOfAccessChain(Instruction* access_chain,
+  bool ReplaceUsersOfAccessChain(Instruction* access_chain,
                                  uint32_t number_of_elements) const;
 
   // Puts all the recursive users of |access_chain| with concrete result types
@@ -102,7 +101,7 @@
   // OpInBoundsAccessChain) will have a constant index for its first index. The
   // OpSwitch instruction will have the cases for the variable index of
   // |access_chain| from 0 to |number_of_elements| - 1.
-  void ReplaceNonUniformAccessWithSwitchCase(
+  bool ReplaceNonUniformAccessWithSwitchCase(
       Instruction* access_chain_final_user, Instruction* access_chain,
       uint32_t number_of_elements,
       const std::deque<Instruction*>& non_uniform_accesses_to_clone) const;
@@ -124,7 +123,7 @@
   // |access_chain| to |case_block|. The clone of |access_chain| will use
   // |const_element_idx| for its first index. |old_ids_to_new_ids| keeps the
   // mapping from the result id of |access_chain| to the result of its clone.
-  void AddConstElementAccessToCaseBlock(
+  bool AddConstElementAccessToCaseBlock(
       BasicBlock* case_block, Instruction* access_chain,
       uint32_t const_element_idx,
       std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const;
@@ -132,7 +131,7 @@
   // Clones all instructions in |insts_to_be_cloned| and put them to |block|.
   // |old_ids_to_new_ids| keeps the mapping from the result id of each
   // instruction of |insts_to_be_cloned| to the result of their clones.
-  void CloneInstsToBlock(
+  bool CloneInstsToBlock(
       BasicBlock* block, Instruction* inst_to_skip_cloning,
       const std::deque<Instruction*>& insts_to_be_cloned,
       std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const;
@@ -183,7 +182,8 @@
   // |case_block_ids| and |default_block_id| as incoming blocks. The size of
   // |phi_operands| must be exactly 1 larger than the size of |case_block_ids|.
   // The last element of |phi_operands| will be used for |default_block_id|. It
-  // adds the phi instruction to the beginning of |parent_block|.
+  // adds the phi instruction to the beginning of |parent_block|. Returns 0 if
+  // it fails to create the Phi instruction.
   uint32_t CreatePhiInstruction(BasicBlock* parent_block,
                                 const std::vector<uint32_t>& phi_operands,
                                 const std::vector<uint32_t>& case_block_ids,
diff --git a/source/opt/replace_invalid_opc.cpp b/source/opt/replace_invalid_opc.cpp
index 1b97c0e..a2a2aee 100644
--- a/source/opt/replace_invalid_opc.cpp
+++ b/source/opt/replace_invalid_opc.cpp
@@ -17,6 +17,8 @@
 #include <bitset>
 #include <vector>
 
+#include "source/table2.h"
+
 namespace spvtools {
 namespace opt {
 
@@ -104,7 +106,7 @@
             uint32_t file_name_id = 0;
             if (last_line_dbg_inst->opcode() == spv::Op::OpLine) {
               file_name_id = last_line_dbg_inst->GetSingleWordInOperand(0);
-            } else {  // Shader100::DebugLine
+            } else {  // NSDI::DebugLine
               uint32_t debug_source_id =
                   last_line_dbg_inst->GetSingleWordInOperand(2);
               Instruction* debug_source_inst =
@@ -207,10 +209,10 @@
 }
 
 std::string ReplaceInvalidOpcodePass::BuildWarningMessage(spv::Op opcode) {
-  spv_opcode_desc opcode_info;
-  context()->grammar().lookupOpcode(opcode, &opcode_info);
+  const spvtools::InstructionDesc* opcode_desc = nullptr;
+  spvtools::LookupOpcode(opcode, &opcode_desc);
   std::string message = "Removing ";
-  message += opcode_info->name;
+  message += opcode_desc->name().data();
   message += " instruction because of incompatible execution model.";
   return message;
 }
diff --git a/source/opt/resolve_binding_conflicts_pass.cpp b/source/opt/resolve_binding_conflicts_pass.cpp
new file mode 100644
index 0000000..18ea2e5
--- /dev/null
+++ b/source/opt/resolve_binding_conflicts_pass.cpp
@@ -0,0 +1,328 @@
+// Copyright (c) 2025 Google LLC
+//
+// 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/resolve_binding_conflicts_pass.h"
+
+#include <algorithm>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "source/opt/decoration_manager.h"
+#include "source/opt/def_use_manager.h"
+#include "source/opt/instruction.h"
+#include "source/opt/ir_builder.h"
+#include "source/opt/ir_context.h"
+#include "spirv/unified1/spirv.h"
+
+namespace spvtools {
+namespace opt {
+
+// A VarBindingInfo contains the binding information for a single resource
+// variable.
+//
+// Exactly one such object is created per resource variable in the
+// module. In particular, when a resource variable is statically used by
+// more than one entry point, those entry points share the same VarBindingInfo
+// object for that variable.
+struct VarBindingInfo {
+  const Instruction* const var;
+  const uint32_t descriptor_set;
+  Instruction* const binding_decoration;
+
+  // Returns the binding number.
+  uint32_t binding() const {
+    return binding_decoration->GetSingleWordInOperand(2);
+  }
+  // Sets the binding number to 'b'.
+  void updateBinding(uint32_t b) { binding_decoration->SetOperand(2, {b}); }
+};
+
+// The bindings in the same descriptor set that are used by an entry point.
+using BindingList = std::vector<VarBindingInfo*>;
+// A map from descriptor set number to the list of bindings in that descriptor
+// set, as used by a particular entry point.
+using DescriptorSets = std::unordered_map<uint32_t, BindingList>;
+
+IRContext::Analysis ResolveBindingConflictsPass::GetPreservedAnalyses() {
+  // All analyses are kept up to date.
+  // At most this modifies the Binding numbers on variables.
+  return IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping |
+         IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators |
+         IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis |
+         IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap |
+         IRContext::kAnalysisScalarEvolution |
+         IRContext::kAnalysisRegisterPressure |
+         IRContext::kAnalysisValueNumberTable |
+         IRContext::kAnalysisStructuredCFG | IRContext::kAnalysisBuiltinVarId |
+         IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisConstants |
+         IRContext::kAnalysisTypes | IRContext::kAnalysisDebugInfo |
+         IRContext::kAnalysisLiveness;
+}
+
+// Orders variable binding info objects.
+// * The binding number is most signficant;
+// * Then a sampler-like object compares greater than non-sampler like object.
+// * Otherwise compare based on variable ID.
+// This provides a total order among bindings in a descriptor set for a valid
+// Vulkan module.
+bool Less(const VarBindingInfo* const lhs, const VarBindingInfo* const rhs) {
+  if (lhs->binding() < rhs->binding()) return true;
+  if (lhs->binding() > rhs->binding()) return false;
+
+  // Examine types.
+  // In valid Vulkan the only conflict can occur between
+  // images and samplers.  We only care about a specific
+  // comparison when one is a image-like thing and the other
+  // is a sampler-like thing of the same shape.  So unwrap
+  // types until we hit one of those two.
+
+  auto* def_use_mgr = lhs->var->context()->get_def_use_mgr();
+
+  // Returns the type found by iteratively following pointer pointee type,
+  // or array element type.
+  auto unwrap = [&def_use_mgr](Instruction* ty) {
+    bool keep_going = true;
+    do {
+      switch (ty->opcode()) {
+        case spv::Op::OpTypePointer:
+          ty = def_use_mgr->GetDef(ty->GetSingleWordInOperand(1));
+          break;
+        case spv::Op::OpTypeArray:
+        case spv::Op::OpTypeRuntimeArray:
+          ty = def_use_mgr->GetDef(ty->GetSingleWordInOperand(0));
+          break;
+        default:
+          keep_going = false;
+          break;
+      }
+    } while (keep_going);
+    return ty;
+  };
+
+  auto* lhs_ty = unwrap(def_use_mgr->GetDef(lhs->var->type_id()));
+  auto* rhs_ty = unwrap(def_use_mgr->GetDef(rhs->var->type_id()));
+  if (lhs_ty->opcode() == rhs_ty->opcode()) {
+    // Pick based on variable ID.
+    return lhs->var->result_id() < rhs->var->result_id();
+  }
+  // A sampler is always greater than an image.
+  if (lhs_ty->opcode() == spv::Op::OpTypeSampler) {
+    return false;
+  }
+  if (rhs_ty->opcode() == spv::Op::OpTypeSampler) {
+    return true;
+  }
+  // Pick based on variable ID.
+  return lhs->var->result_id() < rhs->var->result_id();
+}
+
+// Summarizes the caller-callee relationships between functions in a module.
+class CallGraph {
+ public:
+  // Returns the list of all functions statically reachable from entry points,
+  // where callees precede callers.
+  const std::vector<uint32_t>& CalleesBeforeCallers() const {
+    return visit_order_;
+  }
+  // Returns the list functions called from a given function.
+  const std::unordered_set<uint32_t>& Callees(uint32_t caller) {
+    return calls_[caller];
+  }
+
+  CallGraph(IRContext& context) {
+    // Populate calls_.
+    std::queue<uint32_t> callee_queue;
+    for (const auto& fn : *context.module()) {
+      auto& callees = calls_[fn.result_id()];
+      context.AddCalls(&fn, &callee_queue);
+      while (!callee_queue.empty()) {
+        callees.insert(callee_queue.front());
+        callee_queue.pop();
+      }
+    }
+
+    // Perform depth-first search, starting from each entry point.
+    // Populates visit_order_.
+    for (const auto& ep : context.module()->entry_points()) {
+      Visit(ep.GetSingleWordInOperand(1));
+    }
+  }
+
+ private:
+  // Visits a function, recursively visiting its callees. Adds this ID
+  // to the visit_order after all callees have been visited.
+  void Visit(uint32_t func_id) {
+    if (visited_.count(func_id)) {
+      return;
+    }
+    visited_.insert(func_id);
+    for (auto callee_id : calls_[func_id]) {
+      Visit(callee_id);
+    }
+    visit_order_.push_back(func_id);
+  }
+
+  // Maps the ID of a function to the IDs of functions it calls.
+  std::unordered_map<uint32_t, std::unordered_set<uint32_t>> calls_;
+
+  // IDs of visited functions;
+  std::unordered_set<uint32_t> visited_;
+  // IDs of functions, where callees precede callers.
+  std::vector<uint32_t> visit_order_;
+};
+
+// Returns vector binding info for all resource variables in the module.
+auto GetVarBindings(IRContext& context) {
+  std::vector<VarBindingInfo> vars;
+  auto* deco_mgr = context.get_decoration_mgr();
+  for (auto& inst : context.module()->types_values()) {
+    if (inst.opcode() == spv::Op::OpVariable) {
+      Instruction* descriptor_set_deco = nullptr;
+      Instruction* binding_deco = nullptr;
+      for (auto* deco : deco_mgr->GetDecorationsFor(inst.result_id(), false)) {
+        switch (static_cast<spv::Decoration>(deco->GetSingleWordInOperand(1))) {
+          case spv::Decoration::DescriptorSet:
+            assert(!descriptor_set_deco);
+            descriptor_set_deco = deco;
+            break;
+          case spv::Decoration::Binding:
+            assert(!binding_deco);
+            binding_deco = deco;
+            break;
+          default:
+            break;
+        }
+      }
+      if (descriptor_set_deco && binding_deco) {
+        vars.push_back({&inst, descriptor_set_deco->GetSingleWordInOperand(2),
+                        binding_deco});
+      }
+    }
+  }
+  return vars;
+}
+
+// Merges the bindings from source into sink. Maintains order and uniqueness
+// within a list of bindings.
+void Merge(DescriptorSets& sink, const DescriptorSets& source) {
+  for (auto index_and_bindings : source) {
+    const uint32_t index = index_and_bindings.first;
+    const BindingList& src1 = index_and_bindings.second;
+    const BindingList& src2 = sink[index];
+    BindingList merged;
+    merged.resize(src1.size() + src2.size());
+    auto merged_end = std::merge(src1.begin(), src1.end(), src2.begin(),
+                                 src2.end(), merged.begin(), Less);
+    auto unique_end = std::unique(merged.begin(), merged_end);
+    merged.resize(unique_end - merged.begin());
+    sink[index] = std::move(merged);
+  }
+}
+
+// Resolves conflicts within this binding list, so the binding number on an
+// item is at least one more than the binding number on the previous item.
+// When this does not yet hold, increase the binding number on the second
+// item in the pair. Returns true if any changes were applied.
+bool ResolveConflicts(BindingList& bl) {
+  bool changed = false;
+  for (size_t i = 1; i < bl.size(); i++) {
+    const auto prev_num = bl[i - 1]->binding();
+    if (prev_num >= bl[i]->binding()) {
+      bl[i]->updateBinding(prev_num + 1);
+      changed = true;
+    }
+  }
+  return changed;
+}
+
+Pass::Status ResolveBindingConflictsPass::Process() {
+  // Assumes the descriptor set and binding decorations are not provided
+  // via decoration groups.  Decoration groups were deprecated in SPIR-V 1.3
+  // Revision 6.  I have not seen any compiler generate them. --dneto
+
+  auto vars = GetVarBindings(*context());
+
+  // Maps a function ID to the variables used directly or indirectly by the
+  // function, organized into descriptor sets. Each descriptor set
+  // consists of a BindingList of distinct variables.
+  std::unordered_map<uint32_t, DescriptorSets> used_vars;
+
+  // Determine variables directly used by functions.
+  auto* def_use_mgr = context()->get_def_use_mgr();
+  for (auto& var : vars) {
+    std::unordered_set<uint32_t> visited_functions_for_var;
+    def_use_mgr->ForEachUser(var.var, [&](Instruction* user) {
+      if (auto* block = context()->get_instr_block(user)) {
+        auto* fn = block->GetParent();
+        assert(fn);
+        const auto fn_id = fn->result_id();
+        if (visited_functions_for_var.insert(fn_id).second) {
+          used_vars[fn_id][var.descriptor_set].push_back(&var);
+        }
+      }
+    });
+  }
+
+  // Sort within a descriptor set by binding number.
+  for (auto& sets_for_fn : used_vars) {
+    for (auto& ds : sets_for_fn.second) {
+      BindingList& bindings = ds.second;
+      std::stable_sort(bindings.begin(), bindings.end(), Less);
+    }
+  }
+
+  // Propagate from callees to callers.
+  CallGraph call_graph(*context());
+  for (const uint32_t caller : call_graph.CalleesBeforeCallers()) {
+    DescriptorSets& caller_ds = used_vars[caller];
+    for (const uint32_t callee : call_graph.Callees(caller)) {
+      Merge(caller_ds, used_vars[callee]);
+    }
+  }
+
+  // At this point, the descriptor sets associated with each entry point
+  // capture exactly the set of resource variables statically used
+  // by the static call tree of that entry point.
+
+  // Resolve conflicts.
+  // VarBindingInfo objects may be shared between the bindings lists.
+  // Updating a binding in one list can require updating another list later.
+  // So repeat updates until settling.
+
+  // The union of BindingLists across all entry points.
+  std::vector<BindingList*> ep_bindings;
+
+  for (auto& ep : context()->module()->entry_points()) {
+    for (auto& ds : used_vars[ep.GetSingleWordInOperand(1)]) {
+      BindingList& bindings = ds.second;
+      ep_bindings.push_back(&bindings);
+    }
+  }
+  bool modified = false;
+  bool found_conflict;
+  do {
+    found_conflict = false;
+    for (BindingList* bl : ep_bindings) {
+      found_conflict |= ResolveConflicts(*bl);
+    }
+    modified |= found_conflict;
+  } while (found_conflict);
+
+  return modified ? Pass::Status::SuccessWithChange
+                  : Pass::Status::SuccessWithoutChange;
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/resolve_binding_conflicts_pass.h b/source/opt/resolve_binding_conflicts_pass.h
new file mode 100644
index 0000000..d1ab433
--- /dev/null
+++ b/source/opt/resolve_binding_conflicts_pass.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2025 Google LLC
+//
+// 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 LIBSPIRV_OPT_RESOLVE_BINDING_CONFLICTS_PASS_H_
+#define LIBSPIRV_OPT_RESOLVE_BINDING_CONFLICTS_PASS_H_
+
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "source/diagnostic.h"
+#include "source/opt/pass.h"
+#include "source/util/small_vector.h"
+
+namespace spvtools {
+namespace opt {
+class ResolveBindingConflictsPass : public Pass {
+ public:
+  virtual ~ResolveBindingConflictsPass() override = default;
+  const char* name() const override { return "resolve-binding-conflicts"; }
+  IRContext::Analysis GetPreservedAnalyses() override;
+  Status Process() override;
+};
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // LIBSPIRV_OPT_RESOLVE_BINDING_CONFLICTS_PASS_H_
diff --git a/source/opt/scalar_replacement_pass.cpp b/source/opt/scalar_replacement_pass.cpp
index 38c8aec..cdda380 100644
--- a/source/opt/scalar_replacement_pass.cpp
+++ b/source/opt/scalar_replacement_pass.cpp
@@ -1,4 +1,6 @@
 // Copyright (c) 2017 Google Inc.
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -184,7 +186,7 @@
     Instruction* added_dbg_value =
         context()->get_debug_info_mgr()->AddDebugValueForDecl(
             dbg_decl, /*value_id=*/var->result_id(),
-            /*insert_before=*/insert_before, /*scope_and_line=*/dbg_decl);
+            /*insert_before=*/insert_before, /*line=*/dbg_decl);
 
     if (added_dbg_value == nullptr) return false;
     added_dbg_value->AddOperand(
@@ -473,6 +475,7 @@
 
   if (id == 0) {
     replacements->push_back(nullptr);
+    return;
   }
 
   std::unique_ptr<Instruction> variable(
@@ -486,7 +489,10 @@
   Instruction* inst = &*block->begin();
 
   // If varInst was initialized, make sure to initialize its replacement.
-  GetOrCreateInitialValue(var_inst, index, inst);
+  if (!GetOrCreateInitialValue(var_inst, index, inst)) {
+    replacements->push_back(nullptr);
+    return;
+  }
   get_def_use_mgr()->AnalyzeInstDefUse(inst);
   context()->set_instr_block(inst, block);
 
@@ -507,11 +513,11 @@
   return ptr_type_id;
 }
 
-void ScalarReplacementPass::GetOrCreateInitialValue(Instruction* source,
+bool ScalarReplacementPass::GetOrCreateInitialValue(Instruction* source,
                                                     uint32_t index,
                                                     Instruction* newVar) {
   assert(source->opcode() == spv::Op::OpVariable);
-  if (source->NumInOperands() < 2) return;
+  if (source->NumInOperands() < 2) return true;
 
   uint32_t initId = source->GetSingleWordInOperand(1u);
   uint32_t storageId = GetStorageType(newVar)->result_id();
@@ -523,6 +529,7 @@
     auto iter = type_to_null_.find(storageId);
     if (iter == type_to_null_.end()) {
       newInitId = TakeNextId();
+      if (newInitId == 0) return false;
       type_to_null_[storageId] = newInitId;
       context()->AddGlobalValue(
           MakeUnique<Instruction>(context(), spv::Op::OpConstantNull, storageId,
@@ -535,6 +542,7 @@
   } else if (IsSpecConstantInst(init->opcode())) {
     // Create a new constant extract.
     newInitId = TakeNextId();
+    if (newInitId == 0) return false;
     context()->AddGlobalValue(MakeUnique<Instruction>(
         context(), spv::Op::OpSpecConstantOp, storageId, newInitId,
         std::initializer_list<Operand>{
@@ -559,6 +567,7 @@
   if (newInitId != 0) {
     newVar->AddOperand({SPV_OPERAND_TYPE_ID, {newInitId}});
   }
+  return true;
 }
 
 uint64_t ScalarReplacementPass::GetArrayLength(
@@ -671,7 +680,8 @@
   for (auto inst :
        get_decoration_mgr()->GetDecorationsFor(typeInst->result_id(), false)) {
     uint32_t decoration;
-    if (inst->opcode() == spv::Op::OpDecorate) {
+    if (inst->opcode() == spv::Op::OpDecorate ||
+        inst->opcode() == spv::Op::OpDecorateId) {
       decoration = inst->GetSingleWordInOperand(1u);
     } else {
       assert(inst->opcode() == spv::Op::OpMemberDecorate);
diff --git a/source/opt/scalar_replacement_pass.h b/source/opt/scalar_replacement_pass.h
index c73ecfd..77d5bd5 100644
--- a/source/opt/scalar_replacement_pass.h
+++ b/source/opt/scalar_replacement_pass.h
@@ -33,7 +33,7 @@
 // Documented in optimizer.hpp
 class ScalarReplacementPass : public MemPass {
  private:
-  static constexpr uint32_t kDefaultLimit = 100;
+  static constexpr uint32_t kDefaultLimit = 0;
 
  public:
   ScalarReplacementPass(uint32_t limit = kDefaultLimit)
@@ -199,7 +199,9 @@
   // If there is an initial value for |source| for element |index|, it is
   // appended as an operand on |newVar|. If the initial value is OpUndef, no
   // initial value is added to |newVar|.
-  void GetOrCreateInitialValue(Instruction* source, uint32_t index,
+  //
+  // Returns true if the value was successfully created.
+  bool GetOrCreateInitialValue(Instruction* source, uint32_t index,
                                Instruction* newVar);
 
   // Replaces the load to the entire composite.
diff --git a/source/opt/set_spec_constant_default_value_pass.cpp b/source/opt/set_spec_constant_default_value_pass.cpp
index d2aa9b1..523253f 100644
--- a/source/opt/set_spec_constant_default_value_pass.cpp
+++ b/source/opt/set_spec_constant_default_value_pass.cpp
@@ -16,8 +16,9 @@
 
 #include <algorithm>
 #include <cctype>
+#include <cstdint>
 #include <cstring>
-#include <tuple>
+#include <initializer_list>
 #include <vector>
 
 #include "source/opt/def_use_manager.h"
@@ -127,6 +128,11 @@
       }
       return result;
     }
+  } else if (type->AsArray()) {
+    // This is only for OpSpecConstantDataKHR
+    // Since the length can be a spec constant as well,
+    // we just pass through the bit pattern
+    return std::vector<uint32_t>(input_bit_pattern);
   }
   result.clear();
   return result;
@@ -139,6 +145,7 @@
     case spv::Op::OpSpecConstant:
     case spv::Op::OpSpecConstantFalse:
     case spv::Op::OpSpecConstantTrue:
+    case spv::Op::OpSpecConstantDataKHR:
       return true;
     default:
       return false;
@@ -224,6 +231,9 @@
   constexpr uint32_t kOpDecorateSpecIdNumOperands = 3;
   // The in-operand index of the default value in a OpSpecConstant instruction.
   constexpr uint32_t kOpSpecConstantLiteralInOperandIndex = 0;
+  // The in-operand index of the default value in a OpSpecConstantData
+  // instruction.
+  constexpr uint32_t kOpSpecConstantDataLiteralInOperandIndex = 0;
 
   bool modified = false;
   // Scan through all the annotation instructions to find 'OpDecorate SpecId'
@@ -324,6 +334,23 @@
           modified = true;
         }
         break;
+      case spv::Op::OpSpecConstantDataKHR: {
+        if (spec_inst->GetInOperand(kOpSpecConstantDataLiteralInOperandIndex)
+                .words != bit_pattern) {
+          std::vector<Operand> operands;
+          // keep the result/type
+          operands.push_back(spec_inst->GetOperand(0u));
+          operands.push_back(spec_inst->GetOperand(1u));
+          // the validator is in charge of making sure the length matches
+          for (uint32_t word : bit_pattern) {
+            operands.emplace_back(SPV_OPERAND_TYPE_LITERAL_INTEGER,
+                                  std::initializer_list<uint32_t>{word});
+          }
+          spec_inst->ReplaceOperands(operands);
+          modified = true;
+        }
+        break;
+      }
       default:
         break;
     }
diff --git a/source/opt/split_combined_image_sampler_pass.cpp b/source/opt/split_combined_image_sampler_pass.cpp
new file mode 100644
index 0000000..b5d3d65
--- /dev/null
+++ b/source/opt/split_combined_image_sampler_pass.cpp
@@ -0,0 +1,694 @@
+// Copyright (c) 2025 Google LLC
+//
+// 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/split_combined_image_sampler_pass.h"
+
+#include <algorithm>
+#include <cassert>
+#include <memory>
+
+#include "source/opt/instruction.h"
+#include "source/opt/ir_builder.h"
+#include "source/opt/ir_context.h"
+#include "source/opt/type_manager.h"
+#include "source/opt/types.h"
+#include "source/util/make_unique.h"
+#include "source/util/string_utils.h"
+#include "spirv/unified1/spirv.h"
+
+namespace spvtools {
+namespace opt {
+
+#define CHECK(cond)                                          \
+  {                                                          \
+    if ((cond) != SPV_SUCCESS) return Pass::Status::Failure; \
+  }
+
+#define CHECK_STATUS(cond)                           \
+  {                                                  \
+    if (auto c = (cond); c != SPV_SUCCESS) return c; \
+  }
+
+IRContext::Analysis SplitCombinedImageSamplerPass::GetPreservedAnalyses() {
+  return
+      // def use manager is updated
+      IRContext::kAnalysisDefUse
+
+      // decorations are updated
+      | IRContext::kAnalysisDecorations
+
+      // control flow is not changed
+      | IRContext::kAnalysisCFG           //
+      | IRContext::kAnalysisLoopAnalysis  //
+      | IRContext::kAnalysisStructuredCFG
+
+      // type manager is updated
+      | IRContext::kAnalysisTypes;
+}
+
+Pass::Status SplitCombinedImageSamplerPass::Process() {
+  def_use_mgr_ = context()->get_def_use_mgr();
+  type_mgr_ = context()->get_type_mgr();
+
+  FindCombinedTextureSamplers();
+  if (combined_types_to_remove_.empty() && !sampled_image_used_as_param_) {
+    return Ok();
+  }
+
+  CHECK(RemapFunctions());
+  CHECK(RemapVars());
+  CHECK(RemoveDeadTypes());
+
+  def_use_mgr_ = nullptr;
+  type_mgr_ = nullptr;
+
+  return Ok();
+}
+
+spvtools::DiagnosticStream SplitCombinedImageSamplerPass::Fail() {
+  return std::move(
+      spvtools::DiagnosticStream({}, consumer(), "", SPV_ERROR_INVALID_BINARY)
+      << "split-combined-image-sampler: ");
+}
+
+void SplitCombinedImageSamplerPass::FindCombinedTextureSamplers() {
+  for (auto& inst : context()->types_values()) {
+    RegisterGlobal(inst.result_id());
+    switch (inst.opcode()) {
+      case spv::Op::OpTypeSampler:
+        // Modules can't have duplicate sampler types.
+        assert(!sampler_type_);
+        sampler_type_ = &inst;
+        break;
+
+      case spv::Op::OpTypeSampledImage:
+        if (!first_sampled_image_type_) {
+          first_sampled_image_type_ = &inst;
+        }
+        combined_types_.insert(inst.result_id());
+        def_use_mgr_->WhileEachUser(inst.result_id(), [&](Instruction* i) {
+          sampled_image_used_as_param_ |=
+              i->opcode() == spv::Op::OpTypeFunction;
+          return !sampled_image_used_as_param_;
+        });
+        break;
+
+      case spv::Op::OpTypeArray:
+      case spv::Op::OpTypeRuntimeArray: {
+        auto pointee_id = inst.GetSingleWordInOperand(0);
+        if (combined_types_.find(pointee_id) != combined_types_.end()) {
+          combined_types_.insert(inst.result_id());
+          combined_types_to_remove_.push_back(inst.result_id());
+        }
+      } break;
+
+      case spv::Op::OpTypePointer: {
+        auto sc =
+            static_cast<spv::StorageClass>(inst.GetSingleWordInOperand(0));
+        if (sc == spv::StorageClass::UniformConstant) {
+          auto pointee_id = inst.GetSingleWordInOperand(1);
+          if (combined_types_.find(pointee_id) != combined_types_.end()) {
+            combined_types_.insert(inst.result_id());
+            combined_types_to_remove_.push_back(inst.result_id());
+          }
+        }
+      } break;
+
+      case spv::Op::OpVariable:
+        if (combined_types_.find(inst.type_id()) != combined_types_.end()) {
+          ordered_vars_.push_back(&inst);
+        }
+        break;
+
+      default:
+        break;
+    }
+  }
+}
+
+Instruction* SplitCombinedImageSamplerPass::GetSamplerType() {
+  if (!sampler_type_) {
+    analysis::Sampler s;
+    uint32_t sampler_type_id = type_mgr_->GetTypeInstruction(&s);
+    sampler_type_ = def_use_mgr_->GetDef(sampler_type_id);
+    if (sampler_type_ == nullptr) return nullptr;
+    assert(first_sampled_image_type_);
+    sampler_type_->InsertBefore(first_sampled_image_type_);
+    RegisterNewGlobal(sampler_type_->result_id());
+  }
+  return sampler_type_;
+}
+
+spv_result_t SplitCombinedImageSamplerPass::RemapVars() {
+  for (Instruction* var : ordered_vars_) {
+    CHECK_STATUS(RemapVar(var));
+  }
+  return SPV_SUCCESS;
+}
+
+std::pair<Instruction*, Instruction*> SplitCombinedImageSamplerPass::SplitType(
+    Instruction& combined_kind_type) {
+  if (auto where = type_remap_.find(combined_kind_type.result_id());
+      where != type_remap_.end()) {
+    auto& type_remap = where->second;
+    return {type_remap.image_kind_type, type_remap.sampler_kind_type};
+  }
+
+  switch (combined_kind_type.opcode()) {
+    case spv::Op::OpTypeSampledImage: {
+      auto* image_type =
+          def_use_mgr_->GetDef(combined_kind_type.GetSingleWordInOperand(0));
+      auto* sampler_type = GetSamplerType();
+      if (!sampler_type) return {nullptr, nullptr};
+      type_remap_[combined_kind_type.result_id()] = {&combined_kind_type,
+                                                     image_type, sampler_type};
+      return {image_type, sampler_type};
+      break;
+    }
+    case spv::Op::OpTypePointer: {
+      auto sc = static_cast<spv::StorageClass>(
+          combined_kind_type.GetSingleWordInOperand(0));
+      if (sc == spv::StorageClass::UniformConstant) {
+        auto* pointee =
+            def_use_mgr_->GetDef(combined_kind_type.GetSingleWordInOperand(1));
+        auto [image_pointee, sampler_pointee] = SplitType(*pointee);
+        // These would be null if the pointee is an image type or a sampler
+        // type. Don't decompose them. Currently this method does not check the
+        // assumption that it is being only called on combined types. So code
+        // this defensively.
+        if (image_pointee && sampler_pointee) {
+          auto* ptr_image = MakeUniformConstantPointer(image_pointee);
+          if (!ptr_image) return {nullptr, nullptr};
+          auto* ptr_sampler = MakeUniformConstantPointer(sampler_pointee);
+          if (!ptr_sampler) return {nullptr, nullptr};
+          type_remap_[combined_kind_type.result_id()] = {
+              &combined_kind_type, ptr_image, ptr_sampler};
+          return {ptr_image, ptr_sampler};
+        }
+      }
+      break;
+    }
+    case spv::Op::OpTypeArray: {
+      const auto* array_ty =
+          type_mgr_->GetType(combined_kind_type.result_id())->AsArray();
+      assert(array_ty);
+      const auto* sampled_image_ty = array_ty->element_type()->AsSampledImage();
+      assert(sampled_image_ty);
+
+      const analysis::Type* image_ty = sampled_image_ty->image_type();
+      assert(image_ty);
+      analysis::Array array_image_ty(image_ty, array_ty->length_info());
+      const uint32_t array_image_ty_id =
+          type_mgr_->GetTypeInstruction(&array_image_ty);
+      if (array_image_ty_id == 0) return {nullptr, nullptr};
+      auto* array_image_ty_inst = def_use_mgr_->GetDef(array_image_ty_id);
+      if (!IsKnownGlobal(array_image_ty_id)) {
+        array_image_ty_inst->InsertBefore(&combined_kind_type);
+        RegisterNewGlobal(array_image_ty_id);
+        // GetTypeInstruction also updated the def-use manager.
+      }
+
+      auto* sampler_ty_inst = GetSamplerType();
+      if (!sampler_ty_inst) return {nullptr, nullptr};
+      analysis::Array sampler_array_ty(
+          type_mgr_->GetType(sampler_ty_inst->result_id()),
+          array_ty->length_info());
+      const uint32_t array_sampler_ty_id =
+          type_mgr_->GetTypeInstruction(&sampler_array_ty);
+      if (array_sampler_ty_id == 0) return {nullptr, nullptr};
+      auto* array_sampler_ty_inst = def_use_mgr_->GetDef(array_sampler_ty_id);
+      if (!IsKnownGlobal(array_sampler_ty_id)) {
+        array_sampler_ty_inst->InsertBefore(&combined_kind_type);
+        RegisterNewGlobal(array_sampler_ty_id);
+        // GetTypeInstruction also updated the def-use manager.
+      }
+      return {array_image_ty_inst, array_sampler_ty_inst};
+    }
+    case spv::Op::OpTypeRuntimeArray: {
+      // This is like the sized-array case, but there is no length parameter.
+      auto* array_ty =
+          type_mgr_->GetType(combined_kind_type.result_id())->AsRuntimeArray();
+      assert(array_ty);
+      auto* sampled_image_ty = array_ty->element_type()->AsSampledImage();
+      assert(sampled_image_ty);
+
+      const analysis::Type* image_ty = sampled_image_ty->image_type();
+      assert(image_ty);
+      analysis::RuntimeArray array_image_ty(image_ty);
+      const uint32_t array_image_ty_id =
+          type_mgr_->GetTypeInstruction(&array_image_ty);
+      if (array_image_ty_id == 0) return {nullptr, nullptr};
+      auto* array_image_ty_inst = def_use_mgr_->GetDef(array_image_ty_id);
+      if (!IsKnownGlobal(array_image_ty_id)) {
+        array_image_ty_inst->InsertBefore(&combined_kind_type);
+        RegisterNewGlobal(array_image_ty_id);
+        // GetTypeInstruction also updated the def-use manager.
+      }
+
+      auto* sampler_ty_inst = GetSamplerType();
+      if (!sampler_ty_inst) return {nullptr, nullptr};
+      analysis::RuntimeArray sampler_array_ty(
+          type_mgr_->GetType(sampler_ty_inst->result_id()));
+      const uint32_t array_sampler_ty_id =
+          type_mgr_->GetTypeInstruction(&sampler_array_ty);
+      if (array_sampler_ty_id == 0) return {nullptr, nullptr};
+      auto* array_sampler_ty_inst = def_use_mgr_->GetDef(array_sampler_ty_id);
+      if (!IsKnownGlobal(array_sampler_ty_id)) {
+        array_sampler_ty_inst->InsertBefore(&combined_kind_type);
+        RegisterNewGlobal(array_sampler_ty_id);
+        // GetTypeInstruction also updated the def-use manager.
+      }
+      return {array_image_ty_inst, array_sampler_ty_inst};
+    }
+    default:
+      break;
+  }
+  return {nullptr, nullptr};
+}
+
+spv_result_t SplitCombinedImageSamplerPass::RemapVar(
+    Instruction* combined_var) {
+  InstructionBuilder builder(context(), combined_var,
+                             IRContext::kAnalysisDefUse);
+
+  // Create an image variable, and a sampler variable.
+  auto* combined_var_type = def_use_mgr_->GetDef(combined_var->type_id());
+  auto [ptr_image_ty, ptr_sampler_ty] = SplitType(*combined_var_type);
+  if (!ptr_image_ty || !ptr_sampler_ty) return SPV_ERROR_INTERNAL;
+  Instruction* sampler_var = builder.AddVariable(
+      ptr_sampler_ty->result_id(), SpvStorageClassUniformConstant);
+  if (sampler_var == nullptr) return SPV_ERROR_INTERNAL;
+  Instruction* image_var = builder.AddVariable(ptr_image_ty->result_id(),
+                                               SpvStorageClassUniformConstant);
+  if (image_var == nullptr) return SPV_ERROR_INTERNAL;
+
+  modified_ = true;
+  return RemapUses(combined_var, image_var, sampler_var);
+}
+
+spv_result_t SplitCombinedImageSamplerPass::RemapUses(
+    Instruction* combined, Instruction* image_part, Instruction* sampler_part) {
+  // The instructions to delete.
+  std::unordered_set<Instruction*> dead_insts;
+  // The insertion point should be updated before using this builder.
+  // We needed *something* here.
+  InstructionBuilder builder(context(), combined, IRContext::kAnalysisDefUse);
+
+  // This code must maintain the SPIR-V "Data rule" about sampled image values:
+  //  > All OpSampledImage instructions, or instructions that load an image or
+  //  > sampler reference, must be in the same block in which their Result <id>
+  //  > are consumed.
+  //
+  // When the code below inserts OpSampledImage instructions, it is always
+  // either:
+  // - in the same block as the previous OpSampledImage instruction it is
+  //   replacing, or
+  // - in the same block as the instruction using sampled image value it is
+  //   replacing.
+  //
+  // Assuming that rule is already honoured by the module, these updates will
+  // continue to honour the rule.
+
+  // Represents a single use of a value to be remapped.
+  struct RemapUse {
+    uint32_t used_id;  // The ID that is being used.
+    Instruction* user;
+    uint32_t index;
+    Instruction* image_part;    // The image part of the replacement.
+    Instruction* sampler_part;  // The sampler part of the replacement.
+  };
+  // The work list of uses to be remapped.
+  std::vector<RemapUse> uses;
+
+  // Adds remap records for each use of a value to be remapped.
+  // Also schedules the original value for deletion.
+  auto add_remap = [this, &dead_insts, &uses](Instruction* combined_arg,
+                                              Instruction* image_part_arg,
+                                              Instruction* sampler_part_arg) {
+    const uint32_t used_combined_id = combined_arg->result_id();
+
+    def_use_mgr_->ForEachUse(
+        combined_arg, [&](Instruction* user, uint32_t use_index) {
+          uses.push_back({used_combined_id, user, use_index, image_part_arg,
+                          sampler_part_arg});
+        });
+    dead_insts.insert(combined_arg);
+  };
+
+  add_remap(combined, image_part, sampler_part);
+
+  // Use index-based iteration because we can add to the work list as we go
+  // along, and reallocation would invalidate ordinary iterators.
+  for (size_t use_index = 0; use_index < uses.size(); ++use_index) {
+    auto& use = uses[use_index];
+    switch (use.user->opcode()) {
+      case spv::Op::OpCopyObject: {
+        // Append the uses of this OpCopyObject to the work list.
+        add_remap(use.user, image_part, sampler_part);
+        break;
+      }
+      case spv::Op::OpLoad: {
+        assert(use.index == 2 && "variable used as non-pointer index on load");
+        Instruction* load = use.user;
+
+        // Assume the loaded value is a sampled image.
+        assert(def_use_mgr_->GetDef(load->type_id())->opcode() ==
+               spv::Op::OpTypeSampledImage);
+
+        // Create loads for the image part and sampler part.
+        builder.SetInsertPoint(load);
+        auto* image = builder.AddLoad(PointeeTypeId(use.image_part),
+                                      use.image_part->result_id());
+        if (!image) return SPV_ERROR_INTERNAL;
+        auto* sampler = builder.AddLoad(PointeeTypeId(use.sampler_part),
+                                        use.sampler_part->result_id());
+        if (!sampler) return SPV_ERROR_INTERNAL;
+
+        // Move decorations, such as RelaxedPrecision.
+        auto* deco_mgr = context()->get_decoration_mgr();
+        deco_mgr->CloneDecorations(load->result_id(), image->result_id());
+        deco_mgr->CloneDecorations(load->result_id(), sampler->result_id());
+        deco_mgr->RemoveDecorationsFrom(load->result_id());
+
+        // Create a sampled image from the loads of the two parts.
+        auto* sampled_image = builder.AddSampledImage(
+            load->type_id(), image->result_id(), sampler->result_id());
+        if (!sampled_image) return SPV_ERROR_INTERNAL;
+        // Replace the original sampled image value with the new one.
+        std::unordered_set<Instruction*> users;
+        def_use_mgr_->ForEachUse(
+            load, [&users, sampled_image](Instruction* user, uint32_t index) {
+              user->SetOperand(index, {sampled_image->result_id()});
+              users.insert(user);
+            });
+        for (auto* user : users) {
+          def_use_mgr_->AnalyzeInstUse(user);
+        }
+        dead_insts.insert(load);
+        break;
+      }
+      case spv::Op::OpDecorate: {
+        assert(use.index == 0 && "variable used as non-target index");
+        builder.SetInsertPoint(use.user);
+        spv::Decoration deco{use.user->GetSingleWordInOperand(1)};
+        std::vector<uint32_t> literals;
+        for (uint32_t i = 2; i < use.user->NumInOperands(); i++) {
+          literals.push_back(use.user->GetSingleWordInOperand(i));
+        }
+        builder.AddDecoration(use.image_part->result_id(), deco, literals);
+        builder.AddDecoration(use.sampler_part->result_id(), deco, literals);
+        // KillInst will delete names and decorations, so don't schedule a
+        // deletion of this instruction.
+        break;
+      }
+      case spv::Op::OpEntryPoint: {
+        // The entry point lists variables in the shader interface, i.e.
+        // module-scope variables referenced by the static call tree rooted
+        // at the entry point. (It can be a proper superset).  Before SPIR-V
+        // 1.4, only Input and Output variables are listed; in 1.4 and later,
+        // module-scope variables in all storage classes are listed.
+        // If a combined image+sampler is listed by the entry point, then
+        // the separated image and sampler variables should be.
+        assert(use.index >= 3 &&
+               "variable used in OpEntryPoint but not as an interface ID");
+        use.user->SetOperand(use.index, {use.image_part->result_id()});
+        use.user->InsertOperand(
+            use.user->NumOperands(),
+            {SPV_OPERAND_TYPE_ID, {use.sampler_part->result_id()}});
+        def_use_mgr_->AnalyzeInstUse(use.user);
+        break;
+      }
+      case spv::Op::OpName: {
+        // Synthesize new names from the old.
+        const auto name = use.user->GetOperand(1).AsString();
+        AddOpName(use.image_part->result_id(), name + "_image");
+        AddOpName(use.sampler_part->result_id(), name + "_sampler");
+
+        // KillInst will delete names and decorations, so don't schedule a
+        // deletion of this instruction.
+        break;
+      }
+      case spv::Op::OpFunctionCall: {
+        // Replace each combined arg with two args: the image part, then the
+        // sampler part.
+        // The combined value could have been used twice in the argument list.
+        // Moving things around now will invalidate the 'use' list above.
+        // So don't trust the use index value.
+        auto& call = *use.user;
+        // The insert API only takes absolute arg IDs, not "in" arg IDs.
+        const auto first_arg_operand_index = 3;  // Skip the callee ID
+        for (uint32_t i = first_arg_operand_index; i < call.NumOperands();
+             ++i) {
+          if (use.used_id == call.GetSingleWordOperand(i)) {
+            call.SetOperand(i, {use.sampler_part->result_id()});
+            call.InsertOperand(
+                i, {SPV_OPERAND_TYPE_ID, {use.image_part->result_id()}});
+            ++i;
+          }
+        }
+        def_use_mgr_->AnalyzeInstUse(&call);
+        break;
+      }
+      case spv::Op::OpAccessChain:
+      case spv::Op::OpInBoundsAccessChain: {
+        auto* original_access_chain = use.user;
+        builder.SetInsertPoint(original_access_chain);
+        // It can only be the base pointer
+        assert(use.index == 2);
+
+        // Replace the original access chain with access chains for the image
+        // part and the sampler part.
+        std::vector<uint32_t> indices;
+        for (uint32_t i = 3; i < original_access_chain->NumOperands(); i++) {
+          indices.push_back(original_access_chain->GetSingleWordOperand(i));
+        }
+
+        auto [result_image_part_ty, result_sampler_part_ty] =
+            SplitType(*def_use_mgr_->GetDef(original_access_chain->type_id()));
+        if (!result_image_part_ty || !result_sampler_part_ty)
+          return Fail() << "failed to split type for access chain";
+        auto* result_image_part = builder.AddOpcodeAccessChain(
+            use.user->opcode(), result_image_part_ty->result_id(),
+            use.image_part->result_id(), indices);
+        if (!result_image_part)
+          return Fail() << "failed to create access chain for image part";
+        auto* result_sampler_part = builder.AddOpcodeAccessChain(
+            use.user->opcode(), result_sampler_part_ty->result_id(),
+            use.sampler_part->result_id(), indices);
+        if (!result_sampler_part)
+          return Fail() << "failed to create access chain for sampler part";
+
+        // Remap uses of the original access chain.
+        add_remap(original_access_chain, result_image_part,
+                  result_sampler_part);
+        break;
+      }
+      default: {
+        uint32_t used_type_id = def_use_mgr_->GetDef(use.used_id)->type_id();
+        auto* used_type = def_use_mgr_->GetDef(used_type_id);
+        if (used_type->opcode() == spv::Op::OpTypeSampledImage) {
+          // This value being used is a sampled image value.  But it's
+          // being replaced, so recreate it here.
+          // Example: used by OpImage, OpImageSampleExplicitLod, etc.
+          builder.SetInsertPoint(use.user);
+          auto* sampled_image =
+              builder.AddSampledImage(used_type_id, use.image_part->result_id(),
+                                      use.sampler_part->result_id());
+          use.user->SetOperand(use.index, {sampled_image->result_id()});
+          def_use_mgr_->AnalyzeInstUse(use.user);
+          break;
+        }
+        return Fail() << "unhandled user: " << *use.user;
+      }
+    }
+  }
+
+  for (auto* inst : dead_insts) {
+    KillInst(inst);
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t SplitCombinedImageSamplerPass::RemapFunctions() {
+  // Remap function types. A combined type can appear as a parameter, but not as
+  // the return type.
+  {
+    std::unordered_set<Instruction*> dead_insts;
+    for (auto& inst : context()->types_values()) {
+      if (inst.opcode() != spv::Op::OpTypeFunction) {
+        continue;
+      }
+      analysis::Function* f_ty =
+          type_mgr_->GetType(inst.result_id())->AsFunction();
+      std::vector<const analysis::Type*> new_params;
+      for (const auto* param_ty : f_ty->param_types()) {
+        const auto param_ty_id = type_mgr_->GetId(param_ty);
+        if (combined_types_.find(param_ty_id) != combined_types_.end()) {
+          auto* param_type = def_use_mgr_->GetDef(param_ty_id);
+          auto [image_type, sampler_type] = SplitType(*param_type);
+          if (!image_type || !sampler_type) return SPV_ERROR_INTERNAL;
+          // The image and sampler types must already exist, so there is no
+          // need to move them to the right spot.
+          new_params.push_back(type_mgr_->GetType(image_type->result_id()));
+          new_params.push_back(type_mgr_->GetType(sampler_type->result_id()));
+        } else {
+          new_params.push_back(param_ty);
+        }
+      }
+      if (new_params.size() != f_ty->param_types().size()) {
+        // Replace this type.
+        analysis::Function new_f_ty(f_ty->return_type(), new_params);
+        const uint32_t new_f_ty_id = type_mgr_->GetTypeInstruction(&new_f_ty);
+        std::unordered_set<Instruction*> users;
+        def_use_mgr_->ForEachUse(
+            &inst,
+            [&users, new_f_ty_id](Instruction* user, uint32_t use_index) {
+              user->SetOperand(use_index, {new_f_ty_id});
+              users.insert(user);
+            });
+        for (auto* user : users) {
+          def_use_mgr_->AnalyzeInstUse(user);
+        }
+        dead_insts.insert(&inst);
+      }
+    }
+    for (auto* inst : dead_insts) {
+      KillInst(inst);
+    }
+  }
+
+  // Rewite OpFunctionParameter in function definitions.
+  for (Function& fn : *context()->module()) {
+    // Rewrite the function parameters and record their replacements.
+    struct Replacement {
+      Instruction* combined;
+      Instruction* image;
+      Instruction* sampler;
+    };
+    std::vector<Replacement> replacements;
+    bool error = false;
+
+    Function::RewriteParamFn rewriter =
+        [&](std::unique_ptr<Instruction>&& param,
+            std::back_insert_iterator<Function::ParamList>& appender) {
+          if (error) {
+            return;
+          }
+          if (combined_types_.count(param->type_id()) == 0) {
+            appender = std::move(param);
+            return;
+          }
+
+          // Replace this parameter with two new parameters.
+          auto* combined_inst = param.release();
+          auto* combined_type = def_use_mgr_->GetDef(combined_inst->type_id());
+          auto [image_type, sampler_type] = SplitType(*combined_type);
+          if (!image_type || !sampler_type) {
+            error = true;
+            return;
+          }
+
+          uint32_t image_param_id = context()->TakeNextId();
+          if (image_param_id == 0) {
+            error = true;
+            return;
+          }
+          auto image_param = MakeUnique<Instruction>(
+              context(), spv::Op::OpFunctionParameter, image_type->result_id(),
+              image_param_id, Instruction::OperandList{});
+          uint32_t sampler_param_id = context()->TakeNextId();
+          if (sampler_param_id == 0) {
+            error = true;
+            return;
+          }
+          auto sampler_param = MakeUnique<Instruction>(
+              context(), spv::Op::OpFunctionParameter,
+              sampler_type->result_id(), sampler_param_id,
+              Instruction::OperandList{});
+          replacements.push_back(
+              {combined_inst, image_param.get(), sampler_param.get()});
+          appender = std::move(image_param);
+          appender = std::move(sampler_param);
+        };
+    fn.RewriteParams(rewriter);
+
+    if (error) {
+      return SPV_ERROR_INTERNAL;
+    }
+
+    for (auto& r : replacements) {
+      modified_ = true;
+      def_use_mgr_->AnalyzeInstDefUse(r.image);
+      def_use_mgr_->AnalyzeInstDefUse(r.sampler);
+      CHECK_STATUS(RemapUses(r.combined, r.image, r.sampler));
+    }
+  }
+  return SPV_SUCCESS;
+}
+
+Instruction* SplitCombinedImageSamplerPass::MakeUniformConstantPointer(
+    Instruction* pointee) {
+  uint32_t ptr_id = type_mgr_->FindPointerToType(
+      pointee->result_id(), spv::StorageClass::UniformConstant);
+  if (ptr_id == 0) return nullptr;
+  auto* ptr = def_use_mgr_->GetDef(ptr_id);
+  if (!IsKnownGlobal(ptr_id)) {
+    // The pointer type was created at the end. Put it right after the
+    // pointee.
+    ptr->InsertBefore(pointee);
+    pointee->InsertBefore(ptr);
+    RegisterNewGlobal(ptr_id);
+    // FindPointerToType also updated the def-use manager.
+  }
+  return ptr;
+}
+
+void SplitCombinedImageSamplerPass::AddOpName(uint32_t id,
+                                              const std::string& name) {
+  std::unique_ptr<Instruction> opname{new Instruction{
+      context(),
+      spv::Op::OpName,
+      0u,
+      0u,
+      {{SPV_OPERAND_TYPE_ID, {id}},
+       {SPV_OPERAND_TYPE_LITERAL_STRING,
+        utils::MakeVector<spvtools::opt::Operand::OperandData>(name)}}}};
+
+  context()->AddDebug2Inst(std::move(opname));
+}
+
+spv_result_t SplitCombinedImageSamplerPass::RemoveDeadTypes() {
+  for (auto dead_type_id : combined_types_to_remove_) {
+    if (auto* ty = def_use_mgr_->GetDef(dead_type_id)) {
+      KillInst(ty);
+    }
+  }
+  return SPV_SUCCESS;
+}
+
+void SplitCombinedImageSamplerPass::KillInst(Instruction* inst) {
+  // IRContext::KillInst will remove associated debug instructions and
+  // decorations. It will delete the object only if it is already in a list.
+  const bool was_in_list = inst->IsInAList();
+  context()->KillInst(inst);
+  if (!was_in_list) {
+    // Avoid leaking
+    delete inst;
+  }
+  modified_ = true;
+}
+
+}  // namespace opt
+}  // namespace spvtools
diff --git a/source/opt/split_combined_image_sampler_pass.h b/source/opt/split_combined_image_sampler_pass.h
new file mode 100644
index 0000000..253f34a
--- /dev/null
+++ b/source/opt/split_combined_image_sampler_pass.h
@@ -0,0 +1,169 @@
+// Copyright (c) 2025 Google LLC
+//
+// 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 LIBSPIRV_OPT_SPLIT_COMBINED_IMAGE_SAMPLER_PASS_H_
+#define LIBSPIRV_OPT_SPLIT_COMBINED_IMAGE_SAMPLER_PASS_H_
+
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "source/diagnostic.h"
+#include "source/opt/decoration_manager.h"
+#include "source/opt/def_use_manager.h"
+#include "source/opt/pass.h"
+#include "source/opt/type_manager.h"
+
+namespace spvtools {
+namespace opt {
+
+// Replaces each combined-image sampler variable with an image variable
+// and a sampler variable. Similar for function parameters.
+//
+// Copy the descriptor set and binding number. Vulkan allows this, surprisingly.
+class SplitCombinedImageSamplerPass : public Pass {
+ public:
+  virtual ~SplitCombinedImageSamplerPass() override = default;
+  const char* name() const override { return "split-combined-image-sampler"; }
+  IRContext::Analysis GetPreservedAnalyses() override;
+  Status Process() override;
+
+ private:
+  // Records failure for the current module, and returns a stream
+  // that can be used to provide user error information to the message
+  // consumer.
+  spvtools::DiagnosticStream Fail();
+
+  // Find variables that contain combined texture-samplers, or arrays of them.
+  // Also populate known_globals_.
+  void FindCombinedTextureSamplers();
+
+  // Returns the sampler type. If it does not yet exist, then it is created
+  // and placed before the first sampled image type.
+  Instruction* GetSamplerType();
+
+  // Remaps function types and function declarations.  Each
+  // pointer-to-sampled-image-type operand is replaced with a pair of
+  // pointer-to-image-type and pointer-to-sampler-type pair.
+  // Updates the def-use manager and type manager.
+  spv_result_t RemapFunctions();
+  // Remap resource variables.
+  // Updates the def-use manager.
+  spv_result_t RemapVars();
+  // Remap a single resource variable for combined var.
+  // Updates the def-use manager and the decorations manager.
+  spv_result_t RemapVar(Instruction* combined_var);
+  // Transitively remaps uses of the combined object with uses of the
+  // decomposed image and sampler parts.  The combined object can be sampled
+  // image value, a pointer to one, an array of one, or a pointer to an array
+  // of one. The image and sampler parts have corresponding shapes.
+  // Updates the def-use manager and the decorations manager.
+  spv_result_t RemapUses(Instruction* combined, Instruction* image_part,
+                         Instruction* sampler_part);
+  // Removes types that are no longer referenced.
+  spv_result_t RemoveDeadTypes();
+
+  // Returns the type instruction for a UniformConstant pointer to the given
+  // pointee type. If it does not yet exist, the new type instruction is created
+  // and placed immediately after the pointee type instruction. Updates def-use
+  // and type managers, and the set of known globals.
+  Instruction* MakeUniformConstantPointer(Instruction* pointee);
+
+  // Returns the ID of the pointee type for a pointer value instruction.
+  uint32_t PointeeTypeId(Instruction* ptr_value) {
+    auto* ptr_ty = def_use_mgr_->GetDef(ptr_value->type_id());
+    assert(ptr_ty->opcode() == spv::Op::OpTypePointer);
+    return ptr_ty->GetSingleWordInOperand(1);
+  }
+
+  // Creates a new OpName instruction mapping the given name to the given
+  // string, and adds it to the module at the end of the OpName and OpMemberName
+  // section.
+  void AddOpName(uint32_t id, const std::string& name);
+
+  // Cached from the IRContext. Valid while Process() is running.
+  analysis::DefUseManager* def_use_mgr_ = nullptr;
+  // Cached from the IRContext. Valid while Process() is running.
+  analysis::TypeManager* type_mgr_ = nullptr;
+
+  // Did processing modify the module?
+  bool modified_ = false;
+  Pass::Status Ok() {
+    return modified_ ? Pass::Status::SuccessWithChange
+                     : Pass::Status::SuccessWithoutChange;
+  }
+
+  // The first OpTypeSampledImage instruction in the module, if one exists.
+  Instruction* first_sampled_image_type_ = nullptr;
+  // An OpTypeSampler instruction, if one existed already, or if we created one.
+  Instruction* sampler_type_ = nullptr;
+
+  // The known types and module-scope values.
+  // We use this to know when a new such value was created.
+  std::unordered_set<uint32_t> known_globals_;
+  bool IsKnownGlobal(uint32_t id) const {
+    return known_globals_.find(id) != known_globals_.end();
+  }
+  void RegisterGlobal(uint32_t id) { known_globals_.insert(id); }
+  void RegisterNewGlobal(uint32_t id) {
+    modified_ = true;
+    RegisterGlobal(id);
+  }
+
+  // Deletes an instruction and associated debug and decoration instructions.
+  // Updates the def-use manager.
+  void KillInst(Instruction* inst);
+
+  // Combined types.  The known combined sampled-image type,
+  // and recursively pointers or arrays of them.
+  std::unordered_set<uint32_t> combined_types_;
+  // The pre-existing types this pass should remove: pointer to
+  // combined type, array of combined type, pointer to array of combined type.
+  std::vector<uint32_t> combined_types_to_remove_;
+  // Is an OpTypeSampledImage used as a function parameter? Those should be
+  // transformed.
+  bool sampled_image_used_as_param_ = false;
+
+  // Remaps a combined-kind type to corresponding sampler-kind and image-kind
+  // of type.
+  struct TypeRemapInfo {
+    // The instruction for the combined type, pointer to combined type,
+    // or point to array of combined type.
+    Instruction* combined_kind_type;
+    // The corresponding image type, with the same shape of indirection as the
+    // combined_kind_type.
+    Instruction* image_kind_type;
+    // The corresponding sampler type, with the same shape of indirection as the
+    // combined_kind_type.
+    Instruction* sampler_kind_type;
+  };
+  // Maps the ID of a combined-image-sampler type kind to its corresponding
+  // split parts.
+  std::unordered_map<uint32_t, TypeRemapInfo> type_remap_;
+
+  // Returns the image-like and sampler-like types of the same indirection shape
+  // as the given combined-like type.  If combined_kind_type is not a combined
+  // type or a pointer to one, or an array of one or a pointer to an array of
+  // one, then returns a pair of null pointer. Either both components are
+  // non-null, or both components are null. Updates the def-use manager and the
+  // type manager if new instructions are created.
+  std::pair<Instruction*, Instruction*> SplitType(
+      Instruction& combined_kind_type);
+
+  // The combined-image-sampler variables to be replaced.
+  std::vector<Instruction*> ordered_vars_;
+};
+}  // namespace opt
+}  // namespace spvtools
+#endif  // LIBSPIRV_OPT_SPLIT_COMBINED_IMAGE_SAMPLER_PASS_H_
diff --git a/source/opt/ssa_rewrite_pass.cpp b/source/opt/ssa_rewrite_pass.cpp
index 3eb4ec3..615b129 100644
--- a/source/opt/ssa_rewrite_pass.cpp
+++ b/source/opt/ssa_rewrite_pass.cpp
@@ -87,13 +87,15 @@
   return str.str();
 }
 
-SSARewriter::PhiCandidate& SSARewriter::CreatePhiCandidate(uint32_t var_id,
+SSARewriter::PhiCandidate* SSARewriter::CreatePhiCandidate(uint32_t var_id,
                                                            BasicBlock* bb) {
-  // TODO(1841): Handle id overflow.
   uint32_t phi_result_id = pass_->context()->TakeNextId();
+  if (phi_result_id == 0) {
+    return nullptr;
+  }
   auto result = phi_candidates_.emplace(
       phi_result_id, PhiCandidate(var_id, phi_result_id, bb));
-  PhiCandidate& phi_candidate = result.first->second;
+  PhiCandidate* phi_candidate = &result.first->second;
   return phi_candidate;
 }
 
@@ -268,11 +270,12 @@
     // If there is more than one predecessor, this is a join block which may
     // require a Phi instruction.  This will act as |var_id|'s current
     // definition to break potential cycles.
-    PhiCandidate& phi_candidate = CreatePhiCandidate(var_id, bb);
+    PhiCandidate* phi_candidate = CreatePhiCandidate(var_id, bb);
+    if (phi_candidate == nullptr) return 0;
 
     // Set the value for |bb| to avoid an infinite recursion.
-    WriteVariable(var_id, bb, phi_candidate.result_id());
-    val_id = AddPhiOperands(&phi_candidate);
+    WriteVariable(var_id, bb, phi_candidate->result_id());
+    val_id = AddPhiOperands(phi_candidate);
   }
 
   // If we could not find a store for this variable in the path from the root
diff --git a/source/opt/ssa_rewrite_pass.h b/source/opt/ssa_rewrite_pass.h
index 2470f85..076d9e1 100644
--- a/source/opt/ssa_rewrite_pass.h
+++ b/source/opt/ssa_rewrite_pass.h
@@ -232,7 +232,7 @@
   // during rewriting.
   //
   // Once the candidate Phi is created, it returns its ID.
-  PhiCandidate& CreatePhiCandidate(uint32_t var_id, BasicBlock* bb);
+  PhiCandidate* CreatePhiCandidate(uint32_t var_id, BasicBlock* bb);
 
   // Attempts to remove a trivial Phi candidate |phi_cand|. Trivial Phis are
   // those that only reference themselves and one other value |val| any number
diff --git a/source/opt/strength_reduction_pass.cpp b/source/opt/strength_reduction_pass.cpp
index 16a7869..b9d6265 100644
--- a/source/opt/strength_reduction_pass.cpp
+++ b/source/opt/strength_reduction_pass.cpp
@@ -53,17 +53,15 @@
 
 Pass::Status StrengthReductionPass::Process() {
   // Initialize the member variables on a per module basis.
-  bool modified = false;
   int32_type_id_ = 0;
   uint32_type_id_ = 0;
   std::memset(constant_ids_, 0, sizeof(constant_ids_));
 
   FindIntTypesAndConstants();
-  modified = ScanFunctions();
-  return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
+  return ScanFunctions();
 }
 
-bool StrengthReductionPass::ReplaceMultiplyByPowerOf2(
+Pass::Status StrengthReductionPass::ReplaceMultiplyByPowerOf2(
     BasicBlock::iterator* inst) {
   assert((*inst)->opcode() == spv::Op::OpIMul &&
          "Only works for multiplication of integers.");
@@ -72,7 +70,7 @@
   // Currently only works on 32-bit integers.
   if ((*inst)->type_id() != int32_type_id_ &&
       (*inst)->type_id() != uint32_type_id_) {
-    return modified;
+    return Status::SuccessWithoutChange;
   }
 
   // Check the operands for a constant that is a power of 2.
@@ -87,9 +85,11 @@
         modified = true;
         uint32_t shiftAmount = CountTrailingZeros(constVal);
         uint32_t shiftConstResultId = GetConstantId(shiftAmount);
+        if (shiftConstResultId == 0) return Status::Failure;
 
         // Create the new instruction.
         uint32_t newResultId = TakeNextId();
+        if (newResultId == 0) return Status::Failure;
         std::vector<Operand> newOperands;
         newOperands.push_back((*inst)->GetInOperand(1 - i));
         Operand shiftOperand(spv_operand_type_t::SPV_OPERAND_TYPE_ID,
@@ -117,7 +117,7 @@
     }
   }
 
-  return modified;
+  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
 void StrengthReductionPass::FindIntTypesAndConstants() {
@@ -152,6 +152,7 @@
 
     // Construct the constant.
     uint32_t resultId = TakeNextId();
+    if (resultId == 0) return 0;
     Operand constant(spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
                      {val});
     std::unique_ptr<Instruction> newConstant(new Instruction(
@@ -169,7 +170,7 @@
   return constant_ids_[val];
 }
 
-bool StrengthReductionPass::ScanFunctions() {
+Pass::Status StrengthReductionPass::ScanFunctions() {
   // I did not use |ForEachInst| in the module because the function that acts on
   // the instruction gets a pointer to the instruction.  We cannot use that to
   // insert a new instruction.  I want an iterator.
@@ -178,16 +179,19 @@
     for (auto& bb : func) {
       for (auto inst = bb.begin(); inst != bb.end(); ++inst) {
         switch (inst->opcode()) {
-          case spv::Op::OpIMul:
-            if (ReplaceMultiplyByPowerOf2(&inst)) modified = true;
+          case spv::Op::OpIMul: {
+            Status s = ReplaceMultiplyByPowerOf2(&inst);
+            if (s == Status::Failure) return Status::Failure;
+            if (s == Status::SuccessWithChange) modified = true;
             break;
+          }
           default:
             break;
         }
       }
     }
   }
-  return modified;
+  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
 }  // namespace opt
diff --git a/source/opt/strength_reduction_pass.h b/source/opt/strength_reduction_pass.h
index 1cbbbcc..48e72ab 100644
--- a/source/opt/strength_reduction_pass.h
+++ b/source/opt/strength_reduction_pass.h
@@ -32,7 +32,7 @@
  private:
   // Replaces multiple by power of 2 with an equivalent bit shift.
   // Returns true if something changed.
-  bool ReplaceMultiplyByPowerOf2(BasicBlock::iterator*);
+  Status ReplaceMultiplyByPowerOf2(BasicBlock::iterator*);
 
   // Scan the types and constants in the module looking for the integer
   // types that we are
@@ -47,7 +47,7 @@
 
   // Replaces certain instructions in function bodies with presumably cheaper
   // ones. Returns true if something changed.
-  bool ScanFunctions();
+  Status ScanFunctions();
 
   // Type ids for the types of interest, or 0 if they do not exist.
   uint32_t int32_type_id_;
diff --git a/source/opt/trim_capabilities_pass.cpp b/source/opt/trim_capabilities_pass.cpp
index 34fbc44..47f2cb2 100644
--- a/source/opt/trim_capabilities_pass.cpp
+++ b/source/opt/trim_capabilities_pass.cpp
@@ -26,11 +26,11 @@
 #include <vector>
 
 #include "source/enum_set.h"
-#include "source/enum_string_mapping.h"
 #include "source/ext_inst.h"
 #include "source/opt/ir_context.h"
 #include "source/opt/reflect.h"
 #include "source/spirv_target_env.h"
+#include "source/table2.h"
 #include "source/util/string_utils.h"
 
 namespace spvtools {
@@ -61,6 +61,7 @@
 template <class UnaryPredicate>
 static void DFSWhile(const Instruction* instruction, UnaryPredicate condition) {
   std::stack<uint32_t> instructions_to_visit;
+  std::unordered_set<uint32_t> visited_instructions;
   instructions_to_visit.push(instruction->result_id());
   const auto* def_use_mgr = instruction->context()->get_def_use_mgr();
 
@@ -68,6 +69,11 @@
     const Instruction* item = def_use_mgr->GetDef(instructions_to_visit.top());
     instructions_to_visit.pop();
 
+    // Forward references can be allowed, meaning we can have cycles
+    // between ID uses. Need to keep track of this.
+    if (visited_instructions.count(item->result_id())) continue;
+    visited_instructions.insert(item->result_id());
+
     if (!condition(item)) {
       continue;
     }
@@ -241,6 +247,37 @@
              : std::nullopt;
 }
 
+static std::optional<spv::Capability>
+Handler_OpTypePointer_StorageBuffer16BitAccess(const Instruction* instruction) {
+  assert(instruction->opcode() == spv::Op::OpTypePointer &&
+         "This handler only support OpTypePointer opcodes.");
+
+  // Requires StorageBuffer, ShaderRecordBufferKHR or PhysicalStorageBuffer
+  // storage classes.
+  spv::StorageClass storage_class = spv::StorageClass(
+      instruction->GetSingleWordInOperand(kOpTypePointerStorageClassIndex));
+  if (storage_class != spv::StorageClass::StorageBuffer &&
+      storage_class != spv::StorageClass::ShaderRecordBufferKHR &&
+      storage_class != spv::StorageClass::PhysicalStorageBuffer) {
+    return std::nullopt;
+  }
+
+  const auto* decoration_mgr = instruction->context()->get_decoration_mgr();
+  const bool matchesCondition =
+      AnyTypeOf(instruction, [decoration_mgr](const Instruction* item) {
+        if (!decoration_mgr->HasDecoration(item->result_id(),
+                                           spv::Decoration::Block)) {
+          return false;
+        }
+
+        return AnyTypeOf(item, is16bitType);
+      });
+
+  return matchesCondition
+             ? std::optional(spv::Capability::StorageBuffer16BitAccess)
+             : std::nullopt;
+}
+
 static std::optional<spv::Capability> Handler_OpTypePointer_StorageUniform16(
     const Instruction* instruction) {
   assert(instruction->opcode() == spv::Op::OpTypePointer &&
@@ -388,40 +425,40 @@
 }
 
 // Opcode of interest to determine capabilities requirements.
-constexpr std::array<std::pair<spv::Op, OpcodeHandler>, 13> kOpcodeHandlers{{
+constexpr std::array<std::pair<spv::Op, OpcodeHandler>, 14> kOpcodeHandlers{{
     // clang-format off
-    {spv::Op::OpImageRead,         Handler_OpImageRead_StorageImageReadWithoutFormat},
-    {spv::Op::OpImageWrite,        Handler_OpImageWrite_StorageImageWriteWithoutFormat},
-    {spv::Op::OpImageSparseRead,   Handler_OpImageSparseRead_StorageImageReadWithoutFormat},
-    {spv::Op::OpTypeFloat,         Handler_OpTypeFloat_Float16 },
-    {spv::Op::OpTypeFloat,         Handler_OpTypeFloat_Float64 },
-    {spv::Op::OpTypeImage,         Handler_OpTypeImage_ImageMSArray},
-    {spv::Op::OpTypeInt,           Handler_OpTypeInt_Int16 },
-    {spv::Op::OpTypeInt,           Handler_OpTypeInt_Int64 },
-    {spv::Op::OpTypePointer,       Handler_OpTypePointer_StorageInputOutput16},
-    {spv::Op::OpTypePointer,       Handler_OpTypePointer_StoragePushConstant16},
-    {spv::Op::OpTypePointer,       Handler_OpTypePointer_StorageUniform16},
-    {spv::Op::OpTypePointer,       Handler_OpTypePointer_StorageUniform16},
-    {spv::Op::OpTypePointer,       Handler_OpTypePointer_StorageUniformBufferBlock16},
+    {spv::Op::OpImageRead,                   Handler_OpImageRead_StorageImageReadWithoutFormat},
+    {spv::Op::OpImageWrite,                  Handler_OpImageWrite_StorageImageWriteWithoutFormat},
+    {spv::Op::OpImageSparseRead,             Handler_OpImageSparseRead_StorageImageReadWithoutFormat},
+    {spv::Op::OpTypeFloat,                   Handler_OpTypeFloat_Float16 },
+    {spv::Op::OpTypeFloat,                   Handler_OpTypeFloat_Float64 },
+    {spv::Op::OpTypeImage,                   Handler_OpTypeImage_ImageMSArray},
+    {spv::Op::OpTypeInt,                     Handler_OpTypeInt_Int16 },
+    {spv::Op::OpTypeInt,                     Handler_OpTypeInt_Int64 },
+    {spv::Op::OpTypePointer,                 Handler_OpTypePointer_StorageInputOutput16},
+    {spv::Op::OpTypePointer,                 Handler_OpTypePointer_StoragePushConstant16},
+    {spv::Op::OpTypePointer,                 Handler_OpTypePointer_StorageUniform16},
+    {spv::Op::OpTypePointer,                 Handler_OpTypePointer_StorageUniform16},
+    {spv::Op::OpTypePointer,                 Handler_OpTypePointer_StorageUniformBufferBlock16},
+    {spv::Op::OpTypePointer,                 Handler_OpTypePointer_StorageBuffer16BitAccess},
     // clang-format on
 }};
 
 // ==============  End opcode handler implementations.  =======================
 
 namespace {
-ExtensionSet getExtensionsRelatedTo(const CapabilitySet& capabilities,
-                                    const AssemblyGrammar& grammar) {
+ExtensionSet getExtensionsRelatedTo(const CapabilitySet& capabilities) {
   ExtensionSet output;
-  const spv_operand_desc_t* desc = nullptr;
+  const spvtools::OperandDesc* desc = nullptr;
   for (auto capability : capabilities) {
-    if (SPV_SUCCESS != grammar.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY,
-                                             static_cast<uint32_t>(capability),
-                                             &desc)) {
+    if (SPV_SUCCESS !=
+        spvtools::LookupOperand(SPV_OPERAND_TYPE_CAPABILITY,
+                                static_cast<uint32_t>(capability), &desc)) {
       continue;
     }
 
-    for (uint32_t i = 0; i < desc->numExtensions; ++i) {
-      output.insert(desc->extensions[i]);
+    for (auto extension : desc->extensions()) {
+      output.insert(extension);
     }
   }
 
@@ -475,8 +512,8 @@
     return;
   }
 
-  const spv_opcode_desc_t* desc = {};
-  auto result = context()->grammar().lookupOpcode(opcode, &desc);
+  const spvtools::InstructionDesc* desc;
+  auto result = spvtools::LookupOpcode(opcode, &desc);
   if (result != SPV_SUCCESS) {
     return;
   }
@@ -513,9 +550,9 @@
 
   // case 1: Operand is a single value, can directly lookup.
   if (!spvOperandIsConcreteMask(operand.type)) {
-    const spv_operand_desc_t* desc = {};
-    auto result = context()->grammar().lookupOperand(operand.type,
-                                                     operand.words[0], &desc);
+    const spvtools::OperandDesc* desc = nullptr;
+    auto result =
+        spvtools::LookupOperand(operand.type, operand.words[0], &desc);
     if (result != SPV_SUCCESS) {
       return;
     }
@@ -531,8 +568,8 @@
       continue;
     }
 
-    const spv_operand_desc_t* desc = {};
-    auto result = context()->grammar().lookupOperand(operand.type, mask, &desc);
+    const spvtools::OperandDesc* desc = nullptr;
+    auto result = spvtools::LookupOperand(operand.type, mask, &desc);
     if (result != SPV_SUCCESS) {
       continue;
     }
@@ -561,9 +598,8 @@
   spv_ext_inst_type_t instructionSet =
       spvExtInstImportTypeGet(extInstSet.AsString().c_str());
 
-  spv_ext_inst_desc desc = {};
-  auto result =
-      context()->grammar().lookupExtInst(instructionSet, extInstruction, &desc);
+  const ExtInstDesc* desc = nullptr;
+  auto result = LookupExtInst(instructionSet, extInstruction, &desc);
   if (result != SPV_SUCCESS) {
     return;
   }
@@ -576,7 +612,9 @@
     ExtensionSet* extensions) const {
   // Ignoring OpCapability and OpExtension instructions.
   if (instruction->opcode() == spv::Op::OpCapability ||
-      instruction->opcode() == spv::Op::OpExtension) {
+      instruction->opcode() == spv::Op::OpConditionalCapabilityINTEL ||
+      instruction->opcode() == spv::Op::OpExtension ||
+      instruction->opcode() == spv::Op::OpConditionalExtensionINTEL) {
     return;
   }
 
@@ -595,7 +633,7 @@
   }
 
   // Last case: some complex logic needs to be run to determine capabilities.
-  auto[begin, end] = opcodeHandlers_.equal_range(instruction->opcode());
+  auto [begin, end] = opcodeHandlers_.equal_range(instruction->opcode());
   for (auto it = begin; it != end; it++) {
     const OpcodeHandler handler = it->second;
     auto result = handler(instruction);
@@ -610,8 +648,8 @@
 void TrimCapabilitiesPass::AddExtensionsForOperand(
     const spv_operand_type_t type, const uint32_t value,
     ExtensionSet* extensions) const {
-  const spv_operand_desc_t* desc = nullptr;
-  spv_result_t result = context()->grammar().lookupOperand(type, value, &desc);
+  const spvtools::OperandDesc* desc = nullptr;
+  spv_result_t result = spvtools::LookupOperand(type, value, &desc);
   if (result != SPV_SUCCESS) {
     return;
   }
@@ -686,7 +724,7 @@
 Pass::Status TrimCapabilitiesPass::TrimUnrequiredExtensions(
     const ExtensionSet& required_extensions) const {
   const auto supported_extensions =
-      getExtensionsRelatedTo(supportedCapabilities_, context()->grammar());
+      getExtensionsRelatedTo(supportedCapabilities_);
 
   bool modified_module = false;
   for (auto extension : supported_extensions) {
@@ -718,7 +756,7 @@
     return Status::SuccessWithoutChange;
   }
 
-  auto[required_capabilities, required_extensions] =
+  auto [required_capabilities, required_extensions] =
       DetermineRequiredCapabilitiesAndExtensions();
 
   Pass::Status capStatus = TrimUnrequiredCapabilities(required_capabilities);
diff --git a/source/opt/trim_capabilities_pass.h b/source/opt/trim_capabilities_pass.h
index 1d1183a..06e989b 100644
--- a/source/opt/trim_capabilities_pass.h
+++ b/source/opt/trim_capabilities_pass.h
@@ -28,6 +28,7 @@
 #include "source/opt/module.h"
 #include "source/opt/pass.h"
 #include "source/spirv_target_env.h"
+#include "source/table2.h"
 
 namespace spvtools {
 namespace opt {
@@ -81,6 +82,7 @@
       spv::Capability::FragmentShaderPixelInterlockEXT,
       spv::Capability::FragmentShaderSampleInterlockEXT,
       spv::Capability::FragmentShaderShadingRateInterlockEXT,
+      spv::Capability::Geometry,
       spv::Capability::GroupNonUniform,
       spv::Capability::GroupNonUniformArithmetic,
       spv::Capability::GroupNonUniformClustered,
@@ -99,6 +101,7 @@
       spv::Capability::RayTraversalPrimitiveCullingKHR,
       spv::Capability::Shader,
       spv::Capability::ShaderClockKHR,
+      spv::Capability::StorageBuffer16BitAccess,
       spv::Capability::StorageImageReadWithoutFormat,
       spv::Capability::StorageImageWriteWithoutFormat,
       spv::Capability::StorageInputOutput16,
@@ -106,6 +109,7 @@
       spv::Capability::StorageUniform16,
       spv::Capability::StorageUniformBufferBlock16,
       spv::Capability::VulkanMemoryModelDeviceScope,
+      spv::Capability::QuadControlKHR,
       // clang-format on
   };
 
@@ -127,14 +131,11 @@
 
  private:
   // Inserts every capability listed by `descriptor` this pass supports into
-  // `output`. Expects a Descriptor like `spv_opcode_desc_t` or
-  // `spv_operand_desc_t`.
-  template <class Descriptor>
-  inline void addSupportedCapabilitiesToSet(const Descriptor* const descriptor,
-                                            CapabilitySet* output) const {
-    const uint32_t capabilityCount = descriptor->numCapabilities;
-    for (uint32_t i = 0; i < capabilityCount; ++i) {
-      const auto capability = descriptor->capabilities[i];
+  // `output`.
+  template <typename Descriptor>
+  void addSupportedCapabilitiesToSet(const Descriptor* const descriptor,
+                                     CapabilitySet* output) const {
+    for (auto capability : descriptor->capabilities()) {
       if (supportedCapabilities_.contains(capability)) {
         output->insert(capability);
       }
@@ -142,8 +143,8 @@
   }
 
   // Inserts every extension listed by `descriptor` required by the module into
-  // `output`. Expects a Descriptor like `spv_opcode_desc_t` or
-  // `spv_operand_desc_t`.
+  // `output`. Expects a Descriptor like spvtools::OperandDesc or
+  // spvtools::InstructionDesc.
   template <class Descriptor>
   inline void addSupportedExtensionsToSet(const Descriptor* const descriptor,
                                           ExtensionSet* output) const {
@@ -151,8 +152,8 @@
         spvVersionForTargetEnv(context()->GetTargetEnv())) {
       return;
     }
-    output->insert(descriptor->extensions,
-                   descriptor->extensions + descriptor->numExtensions);
+    output->insert(descriptor->extensions().begin(),
+                   descriptor->extensions().end());
   }
 
   void addInstructionRequirementsForOpcode(spv::Op opcode,
diff --git a/source/opt/type_manager.cpp b/source/opt/type_manager.cpp
index 88106b6..6f9ef0e 100644
--- a/source/opt/type_manager.cpp
+++ b/source/opt/type_manager.cpp
@@ -1,4 +1,6 @@
 // Copyright (c) 2016 Google Inc.
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -235,6 +237,7 @@
     DefineParameterlessCase(AccelerationStructureNV);
     DefineParameterlessCase(RayQueryKHR);
     DefineParameterlessCase(HitObjectNV);
+    DefineParameterlessCase(HitObjectEXT);
 #undef DefineParameterlessCase
     case Type::kInteger:
       typeInst = MakeUnique<Instruction>(
@@ -335,6 +338,17 @@
           std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {subtype}}});
       break;
     }
+    case Type::kNodePayloadArrayAMDX: {
+      uint32_t subtype =
+          GetTypeInstruction(type->AsNodePayloadArrayAMDX()->element_type());
+      if (subtype == 0) {
+        return 0;
+      }
+      typeInst = MakeUnique<Instruction>(
+          context(), spv::Op::OpTypeNodePayloadArrayAMDX, 0, id,
+          std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {subtype}}});
+      break;
+    }
     case Type::kStruct: {
       std::vector<Operand> ops;
       const Struct* structTy = type->AsStruct();
@@ -361,16 +375,21 @@
     }
     case Type::kPointer: {
       const Pointer* pointer = type->AsPointer();
-      uint32_t subtype = GetTypeInstruction(pointer->pointee_type());
-      if (subtype == 0) {
-        return 0;
+      if (pointer->is_untyped()) {
+        typeInst = MakeUnique<Instruction>(
+            context(), spv::Op::OpTypeUntypedPointerKHR, 0, id,
+            std::initializer_list<Operand>{
+                {SPV_OPERAND_TYPE_STORAGE_CLASS,
+                 {static_cast<uint32_t>(pointer->storage_class())}}});
+      } else {
+        uint32_t subtype = GetTypeInstruction(pointer->pointee_type());
+        typeInst = MakeUnique<Instruction>(
+            context(), spv::Op::OpTypePointer, 0, id,
+            std::initializer_list<Operand>{
+                {SPV_OPERAND_TYPE_STORAGE_CLASS,
+                 {static_cast<uint32_t>(pointer->storage_class())}},
+                {SPV_OPERAND_TYPE_ID, {subtype}}});
       }
-      typeInst = MakeUnique<Instruction>(
-          context(), spv::Op::OpTypePointer, 0, id,
-          std::initializer_list<Operand>{
-              {SPV_OPERAND_TYPE_STORAGE_CLASS,
-               {static_cast<uint32_t>(pointer->storage_class())}},
-              {SPV_OPERAND_TYPE_ID, {subtype}}});
       break;
     }
     case Type::kFunction: {
@@ -463,11 +482,78 @@
                                          0, id, operands);
       break;
     }
+    case Type::kCooperativeVectorNV: {
+      auto coop_vec = type->AsCooperativeVectorNV();
+      uint32_t const component_type =
+          GetTypeInstruction(coop_vec->component_type());
+      if (component_type == 0) {
+        return 0;
+      }
+      typeInst = MakeUnique<Instruction>(
+          context(), spv::Op::OpTypeVectorIdEXT, 0, id,
+          std::initializer_list<Operand>{
+              {SPV_OPERAND_TYPE_ID, {component_type}},
+              {SPV_OPERAND_TYPE_ID, {coop_vec->components()}}});
+      break;
+    }
+    case Type::kTensorARM: {
+      auto tensor_type = type->AsTensorARM();
+      uint32_t const element_type =
+          GetTypeInstruction(tensor_type->element_type());
+      if (element_type == 0) {
+        return 0;
+      }
+      if (tensor_type->rank_id() != 0) {
+        if (tensor_type->shape_id() != 0) {
+          typeInst = MakeUnique<Instruction>(
+              context(), spv::Op::OpTypeTensorARM, 0, id,
+              std::initializer_list<Operand>{
+                  {SPV_OPERAND_TYPE_ID, {element_type}},
+                  {SPV_OPERAND_TYPE_ID, {tensor_type->rank_id()}},
+                  {SPV_OPERAND_TYPE_ID, {tensor_type->shape_id()}}});
+        } else {
+          typeInst = MakeUnique<Instruction>(
+              context(), spv::Op::OpTypeTensorARM, 0, id,
+              std::initializer_list<Operand>{
+                  {SPV_OPERAND_TYPE_ID, {element_type}},
+                  {SPV_OPERAND_TYPE_ID, {tensor_type->rank_id()}}});
+        }
+      } else {
+        typeInst =
+            MakeUnique<Instruction>(context(), spv::Op::OpTypeTensorARM, 0, id,
+                                    std::initializer_list<Operand>{
+                                        {SPV_OPERAND_TYPE_ID, {element_type}}});
+      }
+      break;
+    }
+    case Type::kGraphARM: {
+      auto const gty = type->AsGraphARM();
+      std::vector<Operand> ops;
+      ops.push_back(
+          Operand(SPV_OPERAND_TYPE_LITERAL_INTEGER, {gty->num_inputs()}));
+      for (auto iotype : gty->io_types()) {
+        uint32_t iotype_id = GetTypeInstruction(iotype);
+        ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {iotype_id}));
+      }
+      typeInst = MakeUnique<Instruction>(context(), spv::Op::OpTypeGraphARM, 0,
+                                         id, ops);
+      break;
+    }
+    case Type::kBufferEXT: {
+      typeInst = MakeUnique<Instruction>(
+          context(), spv::Op::OpTypeBufferEXT, 0, id,
+          std::initializer_list<Operand>{
+              {SPV_OPERAND_TYPE_STORAGE_CLASS,
+               {static_cast<uint32_t>(type->AsBufferEXT()->storage_class())}}});
+      break;
+    }
     default:
       assert(false && "Unexpected type");
       break;
   }
   context()->AddType(std::move(typeInst));
+  // TODO(dneto): This next call to AnalyzeDefUse is redundant becaues
+  // IRContext::AddType already does it.
   context()->AnalyzeDefUse(&*--context()->types_values_end());
   AttachDecorations(id, type);
   return id;
@@ -577,6 +663,7 @@
     DefineNoSubtypeCase(AccelerationStructureNV);
     DefineNoSubtypeCase(RayQueryKHR);
     DefineNoSubtypeCase(HitObjectNV);
+    DefineNoSubtypeCase(HitObjectEXT);
 #undef DefineNoSubtypeCase
     case Type::kVector: {
       const Vector* vec_ty = type.AsVector();
@@ -623,6 +710,13 @@
           MakeUnique<RuntimeArray>(RebuildType(GetId(ele_ty), *ele_ty));
       break;
     }
+    case Type::kNodePayloadArrayAMDX: {
+      const NodePayloadArrayAMDX* array_ty = type.AsNodePayloadArrayAMDX();
+      const Type* ele_ty = array_ty->element_type();
+      rebuilt_ty =
+          MakeUnique<NodePayloadArrayAMDX>(RebuildType(GetId(ele_ty), *ele_ty));
+      break;
+    }
     case Type::kStruct: {
       const Struct* struct_ty = type.AsStruct();
       std::vector<const Type*> subtypes;
@@ -644,9 +738,13 @@
     }
     case Type::kPointer: {
       const Pointer* pointer_ty = type.AsPointer();
-      const Type* ele_ty = pointer_ty->pointee_type();
-      rebuilt_ty = MakeUnique<Pointer>(RebuildType(GetId(ele_ty), *ele_ty),
-                                       pointer_ty->storage_class());
+      if (pointer_ty->pointee_type()) {
+        const Type* ele_ty = pointer_ty->pointee_type();
+        rebuilt_ty = MakeUnique<Pointer>(RebuildType(GetId(ele_ty), *ele_ty),
+                                         pointer_ty->storage_class());
+      } else {
+        rebuilt_ty = MakeUnique<Pointer>(nullptr, pointer_ty->storage_class());
+      }
       break;
     }
     case Type::kFunction: {
@@ -701,6 +799,36 @@
           tv_type->dim_id(), tv_type->has_dimensions_id(), tv_type->perm());
       break;
     }
+    case Type::kCooperativeVectorNV: {
+      const CooperativeVectorNV* cv_type = type.AsCooperativeVectorNV();
+      const Type* component_type = cv_type->component_type();
+      rebuilt_ty = MakeUnique<CooperativeVectorNV>(
+          RebuildType(GetId(component_type), *component_type),
+          cv_type->components());
+      break;
+    }
+    case Type::kTensorARM: {
+      const TensorARM* tensor_type = type.AsTensorARM();
+      const Type* element_type = tensor_type->element_type();
+      rebuilt_ty = MakeUnique<TensorARM>(
+          RebuildType(GetId(element_type), *element_type),
+          tensor_type->rank_id(), tensor_type->shape_id());
+      break;
+    }
+    case Type::kGraphARM: {
+      const GraphARM* graph_type = type.AsGraphARM();
+      std::vector<const Type*> io_types;
+      for (auto ioty : graph_type->io_types()) {
+        io_types.push_back(RebuildType(GetId(ioty), *ioty));
+      }
+      rebuilt_ty = MakeUnique<GraphARM>(graph_type->num_inputs(), io_types);
+      break;
+    }
+    case Type::kBufferEXT: {
+      const BufferEXT* buffer_type = type.AsBufferEXT();
+      rebuilt_ty = MakeUnique<BufferEXT>(buffer_type->storage_class());
+      break;
+    }
     default:
       assert(false && "Unhandled type");
       return nullptr;
@@ -748,9 +876,13 @@
       type = new Integer(inst.GetSingleWordInOperand(0),
                          inst.GetSingleWordInOperand(1));
       break;
-    case spv::Op::OpTypeFloat:
-      type = new Float(inst.GetSingleWordInOperand(0));
-      break;
+    case spv::Op::OpTypeFloat: {
+      const spv::FPEncoding encoding =
+          inst.NumInOperands() > 1
+              ? static_cast<spv::FPEncoding>(inst.GetSingleWordInOperand(1))
+              : spv::FPEncoding::Max;
+      type = new Float(inst.GetSingleWordInOperand(0), encoding);
+    } break;
     case spv::Op::OpTypeVector:
       type = new Vector(GetType(inst.GetSingleWordInOperand(0)),
                         inst.GetSingleWordInOperand(1));
@@ -837,6 +969,14 @@
         return type;
       }
       break;
+    case spv::Op::OpTypeNodePayloadArrayAMDX:
+      type = new NodePayloadArrayAMDX(GetType(inst.GetSingleWordInOperand(0)));
+      if (id_to_incomplete_type_.count(inst.GetSingleWordInOperand(0))) {
+        incomplete_types_.emplace_back(inst.result_id(), type);
+        id_to_incomplete_type_[inst.result_id()] = type;
+        return type;
+      }
+      break;
     case spv::Op::OpTypeStruct: {
       std::vector<const Type*> element_types;
       bool incomplete_type = false;
@@ -872,6 +1012,11 @@
       id_to_incomplete_type_.erase(inst.result_id());
 
     } break;
+    case spv::Op::OpTypeUntypedPointerKHR: {
+      type = new Pointer(nullptr, static_cast<spv::StorageClass>(
+                                      inst.GetSingleWordInOperand(0)));
+      id_to_incomplete_type_.erase(inst.result_id());
+    } break;
     case spv::Op::OpTypeFunction: {
       bool incomplete_type = false;
       uint32_t return_type_id = inst.GetSingleWordInOperand(0);
@@ -942,12 +1087,19 @@
           inst.GetSingleWordInOperand(1), inst.GetSingleWordInOperand(2),
           inst.GetSingleWordInOperand(3), inst.GetSingleWordInOperand(4));
       break;
+    case spv::Op::OpTypeVectorIdEXT:
+      type = new CooperativeVectorNV(GetType(inst.GetSingleWordInOperand(0)),
+                                     inst.GetSingleWordInOperand(1));
+      break;
     case spv::Op::OpTypeRayQueryKHR:
       type = new RayQueryKHR();
       break;
     case spv::Op::OpTypeHitObjectNV:
       type = new HitObjectNV();
       break;
+    case spv::Op::OpTypeHitObjectEXT:
+      type = new HitObjectEXT();
+      break;
     case spv::Op::OpTypeTensorLayoutNV:
       type = new TensorLayoutNV(inst.GetSingleWordInOperand(0),
                                 inst.GetSingleWordInOperand(1));
@@ -962,6 +1114,36 @@
                               inst.GetSingleWordInOperand(1), perm);
       break;
     }
+    case spv::Op::OpTypeTensorARM: {
+      switch (inst.NumInOperands()) {
+        case 1:
+          type = new TensorARM(GetType(inst.GetSingleWordInOperand(0)));
+          break;
+        case 2:
+          type = new TensorARM(GetType(inst.GetSingleWordInOperand(0)),
+                               inst.GetSingleWordInOperand(1));
+          break;
+        case 3:
+          type = new TensorARM(GetType(inst.GetSingleWordInOperand(0)),
+                               inst.GetSingleWordInOperand(1),
+                               inst.GetSingleWordInOperand(2));
+          break;
+      }
+      break;
+    }
+    case spv::Op::OpTypeGraphARM: {
+      std::vector<const Type*> io_types;
+      for (unsigned i = 1; i < inst.NumInOperands(); i++) {
+        io_types.push_back(GetType(inst.GetSingleWordInOperand(i)));
+      }
+      type = new GraphARM(inst.GetSingleWordInOperand(0), io_types);
+      break;
+    }
+    case spv::Op::OpTypeBufferEXT: {
+      type = new BufferEXT(
+          static_cast<spv::StorageClass>(inst.GetSingleWordInOperand(0)));
+      break;
+    }
     default:
       assert(false && "Type not handled by the type manager.");
       break;
@@ -988,11 +1170,16 @@
   if (!IsAnnotationInst(opcode)) return;
 
   switch (opcode) {
-    case spv::Op::OpDecorate: {
+    case spv::Op::OpDecorate:
+    case spv::Op::OpDecorateId: {
       const auto count = inst.NumOperands();
       std::vector<uint32_t> data;
       for (uint32_t i = 1; i < count; ++i) {
-        data.push_back(inst.GetSingleWordOperand(i));
+        // LinkageAttributes has a literal string as an operand, which is a
+        // varible length word. We cannot assume that all operands are single
+        // word.
+        const Operand::OperandData& words = inst.GetOperand(i).words;
+        data.insert(data.end(), words.begin(), words.end());
       }
       type->AddDecoration(std::move(data));
     } break;
diff --git a/source/opt/type_manager.h b/source/opt/type_manager.h
index 948b691..cadc7c2 100644
--- a/source/opt/type_manager.h
+++ b/source/opt/type_manager.h
@@ -34,7 +34,7 @@
 
 // Hashing functor.
 //
-// All type pointers must be non-null.
+// All type pointers must be non-null to reach here.
 struct HashTypePointer {
   size_t operator()(const Type* type) const {
     assert(type);
@@ -203,7 +203,11 @@
     return GetRegisteredType(&bool_type);
   }
 
-  uint32_t GetBoolTypeId() { return GetTypeInstruction(GetBoolType()); }
+  uint32_t GetBoolTypeId() {
+    Type* bool_type = GetBoolType();
+    if (bool_type == nullptr) return 0;
+    return GetTypeInstruction(bool_type);
+  }
 
   Type* GetVoidType() {
     Void void_type;
diff --git a/source/opt/types.cpp b/source/opt/types.cpp
index 8ccf6c9..fd475a1 100644
--- a/source/opt/types.cpp
+++ b/source/opt/types.cpp
@@ -1,4 +1,6 @@
 // Copyright (c) 2016 Google Inc.
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -90,6 +92,7 @@
     case kStruct:
     case kArray:
     case kRuntimeArray:
+    case kNodePayloadArrayAMDX:
       return false;
     default:
       return true;
@@ -129,8 +132,13 @@
     DeclareKindCase(AccelerationStructureNV);
     DeclareKindCase(CooperativeMatrixNV);
     DeclareKindCase(CooperativeMatrixKHR);
+    DeclareKindCase(CooperativeVectorNV);
     DeclareKindCase(RayQueryKHR);
     DeclareKindCase(HitObjectNV);
+    DeclareKindCase(HitObjectEXT);
+    DeclareKindCase(TensorARM);
+    DeclareKindCase(GraphARM);
+    DeclareKindCase(BufferEXT);
 #undef DeclareKindCase
     default:
       assert(false && "Unhandled type");
@@ -162,6 +170,7 @@
     DeclareKindCase(SampledImage);
     DeclareKindCase(Array);
     DeclareKindCase(RuntimeArray);
+    DeclareKindCase(NodePayloadArrayAMDX);
     DeclareKindCase(Struct);
     DeclareKindCase(Opaque);
     DeclareKindCase(Pointer);
@@ -177,10 +186,15 @@
     DeclareKindCase(AccelerationStructureNV);
     DeclareKindCase(CooperativeMatrixNV);
     DeclareKindCase(CooperativeMatrixKHR);
+    DeclareKindCase(CooperativeVectorNV);
     DeclareKindCase(RayQueryKHR);
     DeclareKindCase(HitObjectNV);
+    DeclareKindCase(HitObjectEXT);
     DeclareKindCase(TensorLayoutNV);
     DeclareKindCase(TensorViewNV);
+    DeclareKindCase(TensorARM);
+    DeclareKindCase(GraphARM);
+    DeclareKindCase(BufferEXT);
 #undef DeclareKindCase
     default:
       assert(false && "Unhandled type");
@@ -220,6 +234,7 @@
     DeclareKindCase(SampledImage);
     DeclareKindCase(Array);
     DeclareKindCase(RuntimeArray);
+    DeclareKindCase(NodePayloadArrayAMDX);
     DeclareKindCase(Struct);
     DeclareKindCase(Opaque);
     DeclareKindCase(Pointer);
@@ -235,10 +250,15 @@
     DeclareKindCase(AccelerationStructureNV);
     DeclareKindCase(CooperativeMatrixNV);
     DeclareKindCase(CooperativeMatrixKHR);
+    DeclareKindCase(CooperativeVectorNV);
     DeclareKindCase(RayQueryKHR);
     DeclareKindCase(HitObjectNV);
+    DeclareKindCase(HitObjectEXT);
     DeclareKindCase(TensorLayoutNV);
     DeclareKindCase(TensorViewNV);
+    DeclareKindCase(TensorARM);
+    DeclareKindCase(GraphARM);
+    DeclareKindCase(BufferEXT);
 #undef DeclareKindCase
     default:
       assert(false && "Unhandled type");
@@ -283,6 +303,78 @@
   }
 }
 
+std::optional<uint32_t> Type::GetByteOffset(
+    const std::vector<uint32_t>& access_chain) const {
+  uint32_t offset = 0;
+  const Type* current_type = this;
+  for (uint32_t index : access_chain) {
+    if (const Struct* struct_type = current_type->AsStruct()) {
+      std::optional<uint32_t> member_offset;
+      for (const auto& deco : struct_type->element_decorations()) {
+        if (deco.first != index) continue;
+        for (const auto& inst : deco.second) {
+          if (inst[0] == uint32_t(spv::Decoration::Offset)) {
+            member_offset = inst[1];
+            break;
+          }
+        }
+      }
+      if (!member_offset) return {};
+      offset += *member_offset;
+      current_type = struct_type->element_types()[index];
+    } else if (const Array* array_type = current_type->AsArray()) {
+      std::optional<uint32_t> array_stride;
+      for (const auto& deco : array_type->decorations()) {
+        if (deco[0] == uint32_t(spv::Decoration::ArrayStride)) {
+          array_stride = deco[1];
+          break;
+        }
+      }
+      if (!array_stride) return {};
+      offset += *array_stride * index;
+      current_type = array_type->element_type();
+    } else if (const RuntimeArray* runtime_array_type =
+                   current_type->AsRuntimeArray()) {
+      std::optional<uint32_t> array_stride;
+      for (const auto& deco : runtime_array_type->decorations()) {
+        if (deco[0] == uint32_t(spv::Decoration::ArrayStride)) {
+          array_stride = deco[1];
+          break;
+        }
+      }
+      if (!array_stride) return {};
+      offset += *array_stride * index;
+      current_type = runtime_array_type->element_type();
+    } else if (const Matrix* matrix_type = current_type->AsMatrix()) {
+      std::optional<uint32_t> matrix_stride;
+      for (const auto& deco : matrix_type->decorations()) {
+        if (deco[0] == uint32_t(spv::Decoration::MatrixStride)) {
+          matrix_stride = deco[1];
+          break;
+        }
+      }
+      if (!matrix_stride) return {};
+      offset += *matrix_stride * index;
+      current_type = matrix_type->element_type();
+    } else if (const Vector* vector_type = current_type->AsVector()) {
+      const Type* component_type = vector_type->element_type();
+      uint32_t component_size = 0;
+      if (component_type->AsInteger()) {
+        component_size = component_type->AsInteger()->width() / 8;
+      } else if (component_type->AsFloat()) {
+        component_size = component_type->AsFloat()->width() / 8;
+      } else {
+        return {};
+      }
+      offset += component_size * index;
+      current_type = component_type;
+    } else {
+      return {};
+    }
+  }
+  return offset;
+}
+
 bool Integer::IsSameImpl(const Type* that, IsSameCache*) const {
   const Integer* it = that->AsInteger();
   return it && width_ == it->width_ && signed_ == it->signed_ &&
@@ -301,17 +393,34 @@
 
 bool Float::IsSameImpl(const Type* that, IsSameCache*) const {
   const Float* ft = that->AsFloat();
-  return ft && width_ == ft->width_ && HasSameDecorations(that);
+  return ft && width_ == ft->width_ && encoding_ == ft->encoding_ &&
+         HasSameDecorations(that);
 }
 
 std::string Float::str() const {
   std::ostringstream oss;
-  oss << "float" << width_;
+  switch (encoding_) {
+    case spv::FPEncoding::BFloat16KHR:
+      assert(width_ == 16);
+      oss << "bfloat16";
+      break;
+    case spv::FPEncoding::Float8E4M3EXT:
+      assert(width_ == 8);
+      oss << "fp8e4m3";
+      break;
+    case spv::FPEncoding::Float8E5M2EXT:
+      assert(width_ == 8);
+      oss << "fp8e5m2";
+      break;
+    default:
+      oss << "float" << width_;
+      break;
+  }
   return oss.str();
 }
 
 size_t Float::ComputeExtraStateHash(size_t hash, SeenTypes*) const {
-  return hash_combine(hash, width_);
+  return hash_combine(hash, width_, encoding_);
 }
 
 Vector::Vector(const Type* type, uint32_t count)
@@ -489,6 +598,34 @@
   element_type_ = type;
 }
 
+NodePayloadArrayAMDX::NodePayloadArrayAMDX(const Type* type)
+    : Type(kNodePayloadArrayAMDX), element_type_(type) {
+  assert(!type->AsVoid());
+}
+
+bool NodePayloadArrayAMDX::IsSameImpl(const Type* that,
+                                      IsSameCache* seen) const {
+  const NodePayloadArrayAMDX* rat = that->AsNodePayloadArrayAMDX();
+  if (!rat) return false;
+  return element_type_->IsSameImpl(rat->element_type_, seen) &&
+         HasSameDecorations(that);
+}
+
+std::string NodePayloadArrayAMDX::str() const {
+  std::ostringstream oss;
+  oss << "[" << element_type_->str() << "]";
+  return oss.str();
+}
+
+size_t NodePayloadArrayAMDX::ComputeExtraStateHash(size_t hash,
+                                                   SeenTypes* seen) const {
+  return element_type_->ComputeHashValue(hash, seen);
+}
+
+void NodePayloadArrayAMDX::ReplaceElementType(const Type* type) {
+  element_type_ = type;
+}
+
 Struct::Struct(const std::vector<const Type*>& types)
     : Type(kStruct), element_types_(types) {
   for (const auto* t : types) {
@@ -576,24 +713,39 @@
   if (!p.second) {
     return true;
   }
-  bool same_pointee = pointee_type_->IsSameImpl(pt->pointee_type_, seen);
-  seen->erase(p.first);
-  if (!same_pointee) {
-    return false;
+  if (pointee_type_ != nullptr && pt->pointee_type_ != nullptr) {
+    bool same_pointee = pointee_type_->IsSameImpl(pt->pointee_type_, seen);
+    seen->erase(p.first);
+    if (!same_pointee) {
+      return false;
+    }
+  } else {
+    seen->erase(p.first);
+    // Either both are untyped or it is mixed typed and untyped.
+    if (pointee_type_ != pt->pointee_type_) {
+      return false;
+    }
   }
   return HasSameDecorations(that);
 }
 
 std::string Pointer::str() const {
   std::ostringstream os;
-  os << pointee_type_->str() << " " << static_cast<uint32_t>(storage_class_)
-     << "*";
+  if (pointee_type_) {
+    os << pointee_type_->str();
+  } else {
+    os << "untyped_ptr";
+  }
+  os << " " << static_cast<uint32_t>(storage_class_) << "*";
   return os.str();
 }
 
 size_t Pointer::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
   hash = hash_combine(hash, uint32_t(storage_class_));
-  return pointee_type_->ComputeHashValue(hash, seen);
+  if (pointee_type_) {
+    hash = pointee_type_->ComputeHashValue(hash, seen);
+  }
+  return hash;
 }
 
 void Pointer::SetPointeeType(const Type* type) { pointee_type_ = type; }
@@ -802,6 +954,145 @@
          has_dimensions_id_ == tv->has_dimensions_id_ && perm_ == tv->perm_;
 }
 
+CooperativeVectorNV::CooperativeVectorNV(const Type* type,
+                                         const uint32_t components)
+    : Type(kCooperativeVectorNV),
+      component_type_(type),
+      components_(components) {
+  assert(type != nullptr);
+  assert(components != 0);
+}
+
+std::string CooperativeVectorNV::str() const {
+  std::ostringstream oss;
+  oss << "<" << component_type_->str() << ", " << components_ << ">";
+  return oss.str();
+}
+
+size_t CooperativeVectorNV::ComputeExtraStateHash(size_t hash,
+                                                  SeenTypes* seen) const {
+  hash = hash_combine(hash, components_);
+  return component_type_->ComputeHashValue(hash, seen);
+}
+
+bool CooperativeVectorNV::IsSameImpl(const Type* that,
+                                     IsSameCache* seen) const {
+  const CooperativeVectorNV* mt = that->AsCooperativeVectorNV();
+  if (!mt) return false;
+  return component_type_->IsSameImpl(mt->component_type_, seen) &&
+         components_ == mt->components_ && HasSameDecorations(that);
+}
+
+TensorARM::TensorARM(const Type* elty, const uint32_t rank,
+                     const uint32_t shape)
+    : Type(kTensorARM), element_type_(elty), rank_id_(rank), shape_id_(shape) {
+  assert(elty != nullptr);
+  if (shape != 0) {
+    assert(rank != 0);
+  }
+}
+
+std::string TensorARM::str() const {
+  std::ostringstream oss;
+  oss << "tensor<" << element_type_->str() << ", id(" << rank_id_ << "), id("
+      << shape_id_ << ")>";
+  return oss.str();
+}
+
+size_t TensorARM::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
+  hash = hash_combine(hash, rank_id_);
+  hash = hash_combine(hash, shape_id_);
+  return element_type_->ComputeHashValue(hash, seen);
+}
+
+bool TensorARM::IsSameImpl(const Type* that, IsSameCache* seen) const {
+  const TensorARM* tt = that->AsTensorARM();
+  if (!tt) return false;
+  return element_type_->IsSameImpl(tt->element_type_, seen) &&
+         rank_id_ == tt->rank_id_ && shape_id_ == tt->shape_id_ &&
+         HasSameDecorations(that);
+}
+
+GraphARM::GraphARM(const uint32_t num_inputs,
+                   const std::vector<const Type*>& io_types)
+    : Type(kGraphARM), num_inputs_(num_inputs), io_types_(io_types) {
+  assert(io_types.size() > 0);
+}
+
+std::string GraphARM::str() const {
+  std::ostringstream oss;
+  oss << "graph<" << num_inputs_;
+  for (auto ioty : io_types_) {
+    oss << "," << ioty->str();
+  }
+  oss << ">";
+  return oss.str();
+}
+
+bool GraphARM::is_shaped() const {
+  // A graph is considered to be shaped if all its interface tensors are shaped
+  for (auto ioty : io_types_) {
+    auto tensor_type = ioty->AsTensorARM();
+    assert(tensor_type);
+    if (!tensor_type->is_shaped()) {
+      return false;
+    }
+  }
+  return true;
+}
+
+size_t GraphARM::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
+  hash = hash_combine(hash, num_inputs_);
+  for (auto ioty : io_types_) {
+    hash = ioty->ComputeHashValue(hash, seen);
+  }
+  return hash;
+}
+
+bool GraphARM::IsSameImpl(const Type* that, IsSameCache* seen) const {
+  const GraphARM* og = that->AsGraphARM();
+  if (!og) {
+    return false;
+  }
+  if (num_inputs_ != og->num_inputs_) {
+    return false;
+  }
+  if (io_types_.size() != og->io_types_.size()) {
+    return false;
+  }
+  for (size_t i = 0; i < io_types_.size(); i++) {
+    if (!io_types_[i]->IsSameImpl(og->io_types_[i], seen)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+BufferEXT::BufferEXT(spv::StorageClass storage_class)
+    : Type(kBufferEXT), storage_class_(storage_class) {}
+
+std::string BufferEXT::str() const {
+  std::ostringstream oss;
+  oss << "buffer<" << static_cast<uint32_t>(storage_class_) << ">";
+  return oss.str();
+}
+
+size_t BufferEXT::ComputeExtraStateHash(size_t hash, SeenTypes*) const {
+  hash = hash_combine(hash, static_cast<uint32_t>(storage_class_));
+  return hash;
+}
+
+bool BufferEXT::IsSameImpl(const Type* that, IsSameCache*) const {
+  const BufferEXT* og = that->AsBufferEXT();
+  if (!og) {
+    return false;
+  }
+  if (storage_class_ != og->storage_class_) {
+    return false;
+  }
+  return true;
+}
+
 }  // namespace analysis
 }  // namespace opt
 }  // namespace spvtools
diff --git a/source/opt/types.h b/source/opt/types.h
index 6092c3c..90c3447 100644
--- a/source/opt/types.h
+++ b/source/opt/types.h
@@ -1,4 +1,6 @@
 // Copyright (c) 2016 Google Inc.
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -19,6 +21,7 @@
 
 #include <map>
 #include <memory>
+#include <optional>
 #include <set>
 #include <string>
 #include <unordered_map>
@@ -46,6 +49,7 @@
 class SampledImage;
 class Array;
 class RuntimeArray;
+class NodePayloadArrayAMDX;
 class Struct;
 class Opaque;
 class Pointer;
@@ -61,10 +65,15 @@
 class AccelerationStructureNV;
 class CooperativeMatrixNV;
 class CooperativeMatrixKHR;
+class CooperativeVectorNV;
 class RayQueryKHR;
 class HitObjectNV;
+class HitObjectEXT;
 class TensorLayoutNV;
 class TensorViewNV;
+class TensorARM;
+class GraphARM;
+class BufferEXT;
 
 // Abstract class for a SPIR-V type. It has a bunch of As<sublcass>() methods,
 // which is used as a way to probe the actual <subclass>.
@@ -89,6 +98,7 @@
     kSampledImage,
     kArray,
     kRuntimeArray,
+    kNodePayloadArrayAMDX,
     kStruct,
     kOpaque,
     kPointer,
@@ -104,10 +114,15 @@
     kAccelerationStructureNV,
     kCooperativeMatrixNV,
     kCooperativeMatrixKHR,
+    kCooperativeVectorNV,
     kRayQueryKHR,
     kHitObjectNV,
+    kHitObjectEXT,
     kTensorLayoutNV,
     kTensorViewNV,
+    kTensorARM,
+    kGraphARM,
+    kBufferEXT,
     kLast
   };
 
@@ -130,6 +145,12 @@
     return IsSameImpl(that, &seen);
   }
 
+  // Returns true if this is a cooperative matrix.
+  bool IsCooperativeMatrix() const {
+    return kind() == analysis::Type::kCooperativeMatrixKHR ||
+           kind() == analysis::Type::kCooperativeMatrixNV;
+  }
+
   // Returns true if this type is exactly the same as |that| type, including
   // decorations.  |seen| is the set of |Pointer*| pair that are currently being
   // compared in a parent call to |IsSameImpl|.
@@ -176,9 +197,16 @@
   // non-composite type.
   uint64_t NumberOfComponents() const;
 
-// A bunch of methods for casting this type to a given type. Returns this if the
-// cast can be done, nullptr otherwise.
-// clang-format off
+  // Returns the byte offset of the member of this type that is identified
+  // by |access_chain|.  The vector |access_chain| is a series of integers that
+  // are used to pick members as in the |OpCompositeExtract| instructions.
+  // Returns {} if the offset cannot be computed.
+  std::optional<uint32_t> GetByteOffset(
+      const std::vector<uint32_t>& access_chain) const;
+
+  // A bunch of methods for casting this type to a given type. Returns this if
+  // the cast can be done, nullptr otherwise.
+  // clang-format off
 #define DeclareCastMethod(target)                  \
   virtual target* As##target() { return nullptr; } \
   virtual const target* As##target() const { return nullptr; }
@@ -193,6 +221,7 @@
   DeclareCastMethod(SampledImage)
   DeclareCastMethod(Array)
   DeclareCastMethod(RuntimeArray)
+  DeclareCastMethod(NodePayloadArrayAMDX)
   DeclareCastMethod(Struct)
   DeclareCastMethod(Opaque)
   DeclareCastMethod(Pointer)
@@ -208,10 +237,15 @@
   DeclareCastMethod(AccelerationStructureNV)
   DeclareCastMethod(CooperativeMatrixNV)
   DeclareCastMethod(CooperativeMatrixKHR)
+  DeclareCastMethod(CooperativeVectorNV)
   DeclareCastMethod(RayQueryKHR)
   DeclareCastMethod(HitObjectNV)
+  DeclareCastMethod(HitObjectEXT)
   DeclareCastMethod(TensorLayoutNV)
   DeclareCastMethod(TensorViewNV)
+  DeclareCastMethod(TensorARM)
+  DeclareCastMethod(GraphARM)
+  DeclareCastMethod(BufferEXT)
 #undef DeclareCastMethod
 
 protected:
@@ -221,7 +255,9 @@
  protected:
   // Decorations attached to this type. Each decoration is encoded as a vector
   // of uint32_t numbers. The first uint32_t number is the decoration value,
-  // and the rest are the parameters to the decoration (if exists).
+  // and the rest are the parameters to the decoration (if any exist).
+  // The parameters can be either all literals or all ids depending on the
+  // decoration value.
   std::vector<std::vector<uint32_t>> decorations_;
 
  private:
@@ -257,7 +293,8 @@
 
 class Float : public Type {
  public:
-  Float(uint32_t w) : Type(kFloat), width_(w) {}
+  Float(uint32_t w, spv::FPEncoding encoding = spv::FPEncoding::Max)
+      : Type(kFloat), width_(w), encoding_(encoding) {}
   Float(const Float&) = default;
 
   std::string str() const override;
@@ -265,13 +302,15 @@
   Float* AsFloat() override { return this; }
   const Float* AsFloat() const override { return this; }
   uint32_t width() const { return width_; }
+  spv::FPEncoding encoding() const { return encoding_; }
 
   size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
 
  private:
   bool IsSameImpl(const Type* that, IsSameCache*) const override;
 
-  uint32_t width_;  // bit width
+  uint32_t width_;            // bit width
+  spv::FPEncoding encoding_;  // FPEncoding
 };
 
 class Vector : public Type {
@@ -440,6 +479,29 @@
   const Type* element_type_;
 };
 
+class NodePayloadArrayAMDX : public Type {
+ public:
+  NodePayloadArrayAMDX(const Type* element_type);
+  NodePayloadArrayAMDX(const NodePayloadArrayAMDX&) = default;
+
+  std::string str() const override;
+  const Type* element_type() const { return element_type_; }
+
+  NodePayloadArrayAMDX* AsNodePayloadArrayAMDX() override { return this; }
+  const NodePayloadArrayAMDX* AsNodePayloadArrayAMDX() const override {
+    return this;
+  }
+
+  size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
+
+  void ReplaceElementType(const Type* element_type);
+
+ private:
+  bool IsSameImpl(const Type* that, IsSameCache*) const override;
+
+  const Type* element_type_;
+};
+
 class Struct : public Type {
  public:
   Struct(const std::vector<const Type*>& element_types);
@@ -514,6 +576,8 @@
   const Type* pointee_type() const { return pointee_type_; }
   spv::StorageClass storage_class() const { return storage_class_; }
 
+  bool is_untyped() const { return pointee_type_ == nullptr; }
+
   Pointer* AsPointer() override { return this; }
   const Pointer* AsPointer() const override { return this; }
 
@@ -712,6 +776,100 @@
   std::vector<uint32_t> perm_;
 };
 
+class CooperativeVectorNV : public Type {
+ public:
+  CooperativeVectorNV(const Type* type, const uint32_t components);
+  CooperativeVectorNV(const CooperativeVectorNV&) = default;
+
+  std::string str() const override;
+
+  CooperativeVectorNV* AsCooperativeVectorNV() override { return this; }
+  const CooperativeVectorNV* AsCooperativeVectorNV() const override {
+    return this;
+  }
+
+  size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
+
+  const Type* component_type() const { return component_type_; }
+  uint32_t components() const { return components_; }
+
+ private:
+  bool IsSameImpl(const Type* that, IsSameCache*) const override;
+
+  const Type* component_type_;
+  const uint32_t components_;
+};
+
+class TensorARM : public Type {
+ public:
+  TensorARM(const Type* elty, const uint32_t rank = 0,
+            const uint32_t shape = 0);
+  TensorARM(const TensorARM&) = default;
+
+  std::string str() const override;
+
+  TensorARM* AsTensorARM() override { return this; }
+  const TensorARM* AsTensorARM() const override { return this; }
+
+  size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
+
+  const Type* element_type() const { return element_type_; }
+  uint32_t rank_id() const { return rank_id_; }
+  uint32_t shape_id() const { return shape_id_; }
+  bool is_ranked() const { return rank_id_ != 0; }
+  bool is_shaped() const { return shape_id_ != 0; }
+
+ private:
+  bool IsSameImpl(const Type* that, IsSameCache*) const override;
+
+  const Type* element_type_;
+  const uint32_t rank_id_;
+  const uint32_t shape_id_;
+};
+
+class GraphARM : public Type {
+ public:
+  GraphARM(const uint32_t num_inputs, const std::vector<const Type*>& io_types);
+  GraphARM(const GraphARM&) = default;
+
+  std::string str() const override;
+
+  GraphARM* AsGraphARM() override { return this; }
+  const GraphARM* AsGraphARM() const override { return this; }
+
+  uint32_t num_inputs() const { return num_inputs_; }
+  const std::vector<const Type*>& io_types() const { return io_types_; }
+  bool is_shaped() const;
+
+  size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
+
+ private:
+  bool IsSameImpl(const Type* that, IsSameCache*) const override;
+
+  const uint32_t num_inputs_;
+  const std::vector<const Type*> io_types_;
+};
+
+class BufferEXT : public Type {
+ public:
+  BufferEXT(spv::StorageClass storage_class_);
+  BufferEXT(const BufferEXT&) = default;
+
+  std::string str() const override;
+
+  BufferEXT* AsBufferEXT() override { return this; }
+  const BufferEXT* AsBufferEXT() const override { return this; }
+
+  spv::StorageClass storage_class() const { return storage_class_; }
+
+  size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
+
+ private:
+  bool IsSameImpl(const Type* that, IsSameCache*) const override;
+
+  const spv::StorageClass storage_class_;
+};
+
 #define DefineParameterlessType(type, name)                                \
   class type : public Type {                                               \
    public:                                                                 \
@@ -744,6 +902,7 @@
 DefineParameterlessType(AccelerationStructureNV, accelerationStructureNV);
 DefineParameterlessType(RayQueryKHR, rayQueryKHR);
 DefineParameterlessType(HitObjectNV, hitObjectNV);
+DefineParameterlessType(HitObjectEXT, hitObjectEXT);
 #undef DefineParameterlessType
 
 }  // namespace analysis
diff --git a/source/opt/upgrade_memory_model.cpp b/source/opt/upgrade_memory_model.cpp
index 1b439a6..4af466d 100644
--- a/source/opt/upgrade_memory_model.cpp
+++ b/source/opt/upgrade_memory_model.cpp
@@ -160,14 +160,38 @@
       }
 
       switch (inst->opcode()) {
-        case spv::Op::OpLoad:
+        case spv::Op::OpLoad: {
+          Instruction* src_pointer = context()->get_def_use_mgr()->GetDef(
+              inst->GetSingleWordInOperand(0u));
+          analysis::Type* src_type =
+              context()->get_type_mgr()->GetType(src_pointer->type_id());
+          auto storage_class = src_type->AsPointer()->storage_class();
+          if (storage_class == spv::StorageClass::Function ||
+              storage_class == spv::StorageClass::Private) {
+            // If the buffer from function variable or private variable, flag
+            // NonPrivatePointer is unnecessary.
+            is_coherent = false;
+          }
           UpgradeFlags(inst, 1u, is_coherent, is_volatile, kVisibility,
                        kMemory);
           break;
-        case spv::Op::OpStore:
+        }
+        case spv::Op::OpStore: {
+          Instruction* src_pointer = context()->get_def_use_mgr()->GetDef(
+              inst->GetSingleWordInOperand(0u));
+          analysis::Type* src_type =
+              context()->get_type_mgr()->GetType(src_pointer->type_id());
+          auto storage_class = src_type->AsPointer()->storage_class();
+          if (storage_class == spv::StorageClass::Function ||
+              storage_class == spv::StorageClass::Private) {
+            // If the buffer from function variable or private variable, flag
+            // NonPrivatePointer is unnecessary.
+            is_coherent = false;
+          }
           UpgradeFlags(inst, 2u, is_coherent, is_volatile, kAvailability,
                        kMemory);
           break;
+        }
         case spv::Op::OpCopyMemory:
         case spv::Op::OpCopyMemorySized:
           start_operand = inst->opcode() == spv::Op::OpCopyMemory ? 2u : 3u;
@@ -366,6 +390,21 @@
         indices.push_back(inst->GetSingleWordInOperand(i));
       }
       break;
+    case spv::Op::OpLoad:
+      if (context()->get_type_mgr()->GetType(inst->type_id())->AsPointer()) {
+        analysis::Integer int_ty(32, false);
+        uint32_t int_id =
+            context()->get_type_mgr()->GetTypeInstruction(&int_ty);
+        const analysis::Constant* constant =
+            context()->get_constant_mgr()->GetConstant(
+                context()->get_type_mgr()->GetType(int_id), {0u});
+        uint32_t constant_id = context()
+                                   ->get_constant_mgr()
+                                   ->GetDefiningInstruction(constant)
+                                   ->result_id();
+
+        indices.push_back(constant_id);
+      }
     default:
       break;
   }
@@ -661,22 +700,29 @@
       roots.push(e.GetSingleWordInOperand(1u));
       if (context()->ProcessCallTreeFromRoots(CollectBarriers, &roots)) {
         for (auto barrier : barriers) {
-          // Add OutputMemoryKHR to the semantics of the barriers.
+          // Add OutputMemoryKHR to the semantics of the non-relaxed barriers.
           uint32_t semantics_id = barrier->GetSingleWordInOperand(2u);
           Instruction* semantics_inst =
               context()->get_def_use_mgr()->GetDef(semantics_id);
           analysis::Type* semantics_type =
               context()->get_type_mgr()->GetType(semantics_inst->type_id());
           uint64_t semantics_value = GetIndexValue(semantics_inst);
-          const analysis::Constant* constant =
-              context()->get_constant_mgr()->GetConstant(
-                  semantics_type,
-                  {static_cast<uint32_t>(semantics_value) |
-                   uint32_t(spv::MemorySemanticsMask::OutputMemoryKHR)});
-          barrier->SetInOperand(2u, {context()
-                                         ->get_constant_mgr()
-                                         ->GetDefiningInstruction(constant)
-                                         ->result_id()});
+          const uint64_t memory_order_mask =
+              uint64_t(spv::MemorySemanticsMask::Acquire |
+                       spv::MemorySemanticsMask::Release |
+                       spv::MemorySemanticsMask::AcquireRelease |
+                       spv::MemorySemanticsMask::SequentiallyConsistent);
+          if (semantics_value & memory_order_mask) {
+            const analysis::Constant* constant =
+                context()->get_constant_mgr()->GetConstant(
+                    semantics_type,
+                    {static_cast<uint32_t>(semantics_value) |
+                     uint32_t(spv::MemorySemanticsMask::OutputMemoryKHR)});
+            barrier->SetInOperand(2u, {context()
+                                           ->get_constant_mgr()
+                                           ->GetDefiningInstruction(constant)
+                                           ->result_id()});
+          }
         }
       }
       barriers.clear();
@@ -758,11 +804,13 @@
   InstructionBuilder builder(
       context(), where,
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  // TODO(1841): Handle id overflow.
   auto extract_0 =
       builder.AddCompositeExtract(element_type_id, ext_inst->result_id(), {0});
   context()->ReplaceAllUsesWith(ext_inst->result_id(), extract_0->result_id());
   // The extract's input was just changed to itself, so fix that.
   extract_0->SetInOperand(0u, {ext_inst->result_id()});
+  // TODO(1841): Handle id overflow.
   auto extract_1 =
       builder.AddCompositeExtract(pointee_type_id, ext_inst->result_id(), {1});
   builder.AddStore(ptr_id, extract_1->result_id());
diff --git a/source/opt/value_number_table.cpp b/source/opt/value_number_table.cpp
index 743dc52..a93d33c 100644
--- a/source/opt/value_number_table.cpp
+++ b/source/opt/value_number_table.cpp
@@ -38,6 +38,45 @@
   return GetValueNumber(context()->get_def_use_mgr()->GetDef(id));
 }
 
+bool ValueNumberTable::IsReadOnlyLoad(Instruction* inst) {
+  if (!inst->IsLoad()) {
+    return false;
+  }
+
+  Instruction* address_def = inst->GetBaseAddress();
+  if (!address_def) {
+    return false;
+  }
+
+  auto cached_result = read_only_variable_cache_.find(address_def->result_id());
+  if (cached_result != read_only_variable_cache_.end()) {
+    return cached_result->second;
+  }
+
+  bool is_read_only = IsReadOnlyVariable(address_def);
+  read_only_variable_cache_[address_def->result_id()] = is_read_only;
+  return is_read_only;
+}
+
+bool ValueNumberTable::IsReadOnlyVariable(Instruction* address_def) {
+  if (address_def->opcode() == spv::Op::OpVariable) {
+    if (address_def->IsReadOnlyPointer()) {
+      return true;
+    }
+  }
+
+  if (address_def->opcode() == spv::Op::OpLoad) {
+    const analysis::Type* address_type =
+        context()->get_type_mgr()->GetType(address_def->type_id());
+    if (address_type->AsSampledImage() != nullptr) {
+      const auto* image_type =
+          address_type->AsSampledImage()->image_type()->AsImage();
+      return image_type->sampled() == 1;
+    }
+  }
+  return false;
+}
+
 uint32_t ValueNumberTable::AssignValueNumber(Instruction* inst) {
   // If it already has a value return that.
   uint32_t value = GetValueNumber(inst);
@@ -45,38 +84,51 @@
     return value;
   }
 
+  auto assign_new_number = [this](Instruction* i) {
+    const auto new_value = TakeNextValueNumber();
+    id_to_value_[i->result_id()] = new_value;
+    return new_value;
+  };
+
   // If the instruction has other side effects, then it must
   // have its own value number.
-  // OpSampledImage and OpImage must remain in the same basic block in which
-  // they are used, because of this we will assign each one it own value number.
   if (!context()->IsCombinatorInstruction(inst) &&
       !inst->IsCommonDebugInstr()) {
-    value = TakeNextValueNumber();
-    id_to_value_[inst->result_id()] = value;
-    return value;
+    return assign_new_number(inst);
   }
 
+  // OpSampledImage and OpImage must remain in the same basic block in which
+  // they are used, because of this we will assign each one it own value number.
   switch (inst->opcode()) {
     case spv::Op::OpSampledImage:
     case spv::Op::OpImage:
     case spv::Op::OpVariable:
-      value = TakeNextValueNumber();
-      id_to_value_[inst->result_id()] = value;
-      return value;
+      return assign_new_number(inst);
     default:
       break;
   }
 
+  // A load that yields an image, sampler, or sampled image must remain in
+  // the same basic block.  So assign it its own value number.
+  if (inst->IsLoad()) {
+    switch (context()->get_def_use_mgr()->GetDef(inst->type_id())->opcode()) {
+      case spv::Op::OpTypeSampledImage:
+      case spv::Op::OpTypeImage:
+      case spv::Op::OpTypeSampler:
+        return assign_new_number(inst);
+      default:
+        break;
+    }
+  }
+
   // If it is a load from memory that can be modified, we have to assume the
   // memory has been modified, so we give it a new value number.
   //
   // Note that this test will also handle volatile loads because they are not
   // read only.  However, if this is ever relaxed because we analyze stores, we
   // will have to add a new case for volatile loads.
-  if (inst->IsLoad() && !inst->IsReadOnlyLoad()) {
-    value = TakeNextValueNumber();
-    id_to_value_[inst->result_id()] = value;
-    return value;
+  if (inst->IsLoad() && !IsReadOnlyLoad(inst)) {
+    return assign_new_number(inst);
   }
 
   analysis::DecorationManager* dec_mgr = context()->get_decoration_mgr();
@@ -130,8 +182,15 @@
     }
   }
 
-  // TODO: Implement a normal form for opcodes that commute like integer
-  // addition.  This will let us know that a+b is the same value as b+a.
+  // Apply normal form, so a+b == b+a
+  if (spvOpcodeIsCommutativeBinaryOperator(value_ins.opcode())) {
+    if (value_ins.GetSingleWordInOperand(0) >
+        value_ins.GetSingleWordInOperand(1)) {
+      value_ins.SetInOperands(
+          {{SPV_OPERAND_TYPE_ID, {value_ins.GetSingleWordInOperand(1)}},
+           {SPV_OPERAND_TYPE_ID, {value_ins.GetSingleWordInOperand(0)}}});
+    }
+  }
 
   // Otherwise, we check if this value has been computed before.
   auto value_iterator = instruction_to_value_.find(value_ins);
diff --git a/source/opt/value_number_table.h b/source/opt/value_number_table.h
index 39129ff..c7c909b 100644
--- a/source/opt/value_number_table.h
+++ b/source/opt/value_number_table.h
@@ -70,6 +70,14 @@
   // Assigns a value number to every result id in the module.
   void BuildDominatorTreeValueNumberTable();
 
+  // Returns true if |inst| is a load from read-only memory. This is a cached
+  // version of |Instruction::IsReadOnlyLoad| that is local to this pass.
+  bool IsReadOnlyLoad(Instruction* inst);
+
+  // Returns true if the variable pointed to by |address_def| is read-only.
+  // This is the part of |IsReadOnlyLoad| that is cached.
+  bool IsReadOnlyVariable(Instruction* address_def);
+
   // Returns the new value number.
   uint32_t TakeNextValueNumber() { return next_value_number_++; }
 
@@ -81,6 +89,10 @@
   std::unordered_map<Instruction, uint32_t, ValueTableHash, ComputeSameValue>
       instruction_to_value_;
   std::unordered_map<uint32_t, uint32_t> id_to_value_;
+  // A cache for the results of |IsReadOnlyVariable|. The key is the base
+  // variable of a load.
+  std::unordered_map<uint32_t, bool> read_only_variable_cache_;
+
   IRContext* context_;
   uint32_t next_value_number_;
 };
diff --git a/source/parsed_operand.cpp b/source/parsed_operand.cpp
index cc33f8b..5b4c68c 100644
--- a/source/parsed_operand.cpp
+++ b/source/parsed_operand.cpp
@@ -43,12 +43,38 @@
         *out << word;
         break;
       case SPV_NUMBER_FLOATING:
-        if (operand.number_bit_width == 16) {
-          *out << spvtools::utils::FloatProxy<spvtools::utils::Float16>(
-              uint16_t(word & 0xFFFF));
-        } else {
-          // Assume 32-bit floats.
-          *out << spvtools::utils::FloatProxy<float>(word);
+        switch (operand.fp_encoding) {
+          case SPV_FP_ENCODING_IEEE754_BINARY16:
+            *out << spvtools::utils::FloatProxy<spvtools::utils::Float16>(
+                uint16_t(word & 0xFFFF));
+            break;
+          case SPV_FP_ENCODING_IEEE754_BINARY32:
+            *out << spvtools::utils::FloatProxy<float>(word);
+            break;
+          case SPV_FP_ENCODING_FLOAT8_E4M3:
+            *out << spvtools::utils::FloatProxy<spvtools::utils::Float8_E4M3>(
+                uint8_t(word & 0xFF));
+            break;
+          case SPV_FP_ENCODING_FLOAT8_E5M2:
+            *out << spvtools::utils::FloatProxy<spvtools::utils::Float8_E5M2>(
+                uint8_t(word & 0xFF));
+            break;
+          case SPV_FP_ENCODING_BFLOAT16:
+            *out << spvtools::utils::FloatProxy<spvtools::utils::BFloat16>(
+                uint16_t(word & 0xFFFF));
+            break;
+          case SPV_FP_ENCODING_UNKNOWN:
+            switch (operand.number_bit_width) {
+              case 16:
+                *out << spvtools::utils::FloatProxy<spvtools::utils::Float16>(
+                    uint16_t(word & 0xFFFF));
+                break;
+              case 32:
+                *out << spvtools::utils::FloatProxy<float>(word);
+                break;
+            }
+          default:
+            break;
         }
         break;
       default:
diff --git a/source/reduce/CMakeLists.txt b/source/reduce/CMakeLists.txt
index 9ebe418..b631e9d 100644
--- a/source/reduce/CMakeLists.txt
+++ b/source/reduce/CMakeLists.txt
@@ -75,11 +75,6 @@
         structured_loop_to_selection_reduction_opportunity_finder.cpp
 )
 
-if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")))
-  # Enable parallel builds across four cores for this lib
-  add_definitions(/MP4)
-endif()
-
 spvtools_pch(SPIRV_TOOLS_REDUCE_SOURCES pch_source_reduce)
 
 add_library(SPIRV-Tools-reduce ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_REDUCE_SOURCES})
diff --git a/source/spirv_target_env.cpp b/source/spirv_target_env.cpp
index 8e1b2dd..fe23fa6 100644
--- a/source/spirv_target_env.cpp
+++ b/source/spirv_target_env.cpp
@@ -142,11 +142,20 @@
 };
 
 // When a new SPIR-V version is released, update this table.
+// Users see this ordered list when running 'spirv-val --help'. Order
+// matters for readability.
 static_assert(spv::Version == 0x10600);
 inline constexpr std::pair<const char*, spv_target_env> spvTargetEnvNameMap[] =
     {
-        {"vulkan1.1spv1.4", SPV_ENV_VULKAN_1_1_SPIRV_1_4},
+        // Do not reorder blindly. The algorithm to find the target looks for
+        // the first entry where the key is a prefix of the string provided by
+        // the user. For example, if the user provides `vulkan1.2spv1.5`, it
+        // will match `vulkan1.2`. If this feature is to work correctly, the
+        // keys must be ordered so that a string is before its prefix. For
+        // example, `vulkan1.1spv1.4` must be before `vulkan1.1`. Otherwise,
+        // `vulkan1.1` will be returned when looking for `vulkan1.1spv1.4`.
         {"vulkan1.0", SPV_ENV_VULKAN_1_0},
+        {"vulkan1.1spv1.4", SPV_ENV_VULKAN_1_1_SPIRV_1_4},
         {"vulkan1.1", SPV_ENV_VULKAN_1_1},
         {"vulkan1.2", SPV_ENV_VULKAN_1_2},
         {"vulkan1.3", SPV_ENV_VULKAN_1_3},
@@ -230,7 +239,7 @@
       // If no match, determine whether the header has ended (in which case,
       // assumption has failed.)
       // Skip until the next line.
-      i = j;
+      i += j;
       for (; i < text.size(); ++i) {
         if (text[i] == '\n') break;
       }
diff --git a/source/spirv_validator_options.cpp b/source/spirv_validator_options.cpp
index b72a644..a9591f6 100644
--- a/source/spirv_validator_options.cpp
+++ b/source/spirv_validator_options.cpp
@@ -126,6 +126,16 @@
   options->allow_localsizeid = val;
 }
 
+void spvValidatorOptionsSetAllowOffsetTextureOperand(
+    spv_validator_options options, bool val) {
+  options->allow_offset_texture_operand = val;
+}
+
+void spvValidatorOptionsSetAllowVulkan32BitBitwise(
+    spv_validator_options options, bool val) {
+  options->allow_vulkan_32_bit_bitwise = val;
+}
+
 void spvValidatorOptionsSetFriendlyNames(spv_validator_options options,
                                          bool val) {
   options->use_friendly_names = val;
diff --git a/source/spirv_validator_options.h b/source/spirv_validator_options.h
index 0145048..9f0c125 100644
--- a/source/spirv_validator_options.h
+++ b/source/spirv_validator_options.h
@@ -48,6 +48,8 @@
         workgroup_scalar_block_layout(false),
         skip_block_layout(false),
         allow_localsizeid(false),
+        allow_offset_texture_operand(false),
+        allow_vulkan_32_bit_bitwise(false),
         before_hlsl_legalization(false),
         use_friendly_names(true) {}
 
@@ -60,6 +62,8 @@
   bool workgroup_scalar_block_layout;
   bool skip_block_layout;
   bool allow_localsizeid;
+  bool allow_offset_texture_operand;
+  bool allow_vulkan_32_bit_bitwise;
   bool before_hlsl_legalization;
   bool use_friendly_names;
 };
diff --git a/source/table.cpp b/source/table.cpp
index 01df1bc..3f31373 100644
--- a/source/table.cpp
+++ b/source/table.cpp
@@ -49,16 +49,7 @@
       return nullptr;
   }
 
-  spv_opcode_table opcode_table;
-  spv_operand_table operand_table;
-  spv_ext_inst_table ext_inst_table;
-
-  spvOpcodeTableGet(&opcode_table, env);
-  spvOperandTableGet(&operand_table, env);
-  spvExtInstTableGet(&ext_inst_table, env);
-
-  return new spv_context_t{env, opcode_table, operand_table, ext_inst_table,
-                           nullptr /* a null default consumer */};
+  return new spv_context_t{env, nullptr /* a null default consumer */};
 }
 
 void spvContextDestroy(spv_context context) { delete context; }
diff --git a/source/table.h b/source/table.h
index 47625c5..e61a4d4 100644
--- a/source/table.h
+++ b/source/table.h
@@ -17,104 +17,14 @@
 
 #include "source/extensions.h"
 #include "source/latest_version_spirv_header.h"
+#include "source/util/index_range.h"
 #include "spirv-tools/libspirv.hpp"
 
-typedef struct spv_opcode_desc_t {
-  const char* name;
-  const spv::Op opcode;
-  const uint32_t numAliases;
-  const char** aliases;
-  const uint32_t numCapabilities;
-  const spv::Capability* capabilities;
-  // operandTypes[0..numTypes-1] describe logical operands for the instruction.
-  // The operand types include result id and result-type id, followed by
-  // the types of arguments.
-  const uint16_t numTypes;
-  spv_operand_type_t operandTypes[16];  // TODO: Smaller/larger?
-  const bool hasResult;  // Does the instruction have a result ID operand?
-  const bool hasType;    // Does the instruction have a type ID operand?
-  // A set of extensions that enable this feature. If empty then this operand
-  // value is in core and its availability is subject to minVersion. The
-  // assembler, binary parser, and disassembler ignore this rule, so you can
-  // freely process invalid modules.
-  const uint32_t numExtensions;
-  const spvtools::Extension* extensions;
-  // Minimal core SPIR-V version required for this feature, if without
-  // extensions. ~0u means reserved for future use. ~0u and non-empty extension
-  // lists means only available in extensions.
-  const uint32_t minVersion;
-  const uint32_t lastVersion;
-} spv_opcode_desc_t;
-
-typedef struct spv_operand_desc_t {
-  const char* name;
-  const uint32_t value;
-  const uint32_t numAliases;
-  const char** aliases;
-  const uint32_t numCapabilities;
-  const spv::Capability* capabilities;
-  // A set of extensions that enable this feature. If empty then this operand
-  // value is in core and its availability is subject to minVersion. The
-  // assembler, binary parser, and disassembler ignore this rule, so you can
-  // freely process invalid modules.
-  const uint32_t numExtensions;
-  const spvtools::Extension* extensions;
-  const spv_operand_type_t operandTypes[16];  // TODO: Smaller/larger?
-  // Minimal core SPIR-V version required for this feature, if without
-  // extensions. ~0u means reserved for future use. ~0u and non-empty extension
-  // lists means only available in extensions.
-  const uint32_t minVersion;
-  const uint32_t lastVersion;
-} spv_operand_desc_t;
-
-typedef struct spv_operand_desc_group_t {
-  const spv_operand_type_t type;
-  const uint32_t count;
-  const spv_operand_desc_t* entries;
-} spv_operand_desc_group_t;
-
-typedef struct spv_ext_inst_desc_t {
-  const char* name;
-  const uint32_t ext_inst;
-  const uint32_t numCapabilities;
-  const spv::Capability* capabilities;
-  const spv_operand_type_t operandTypes[40];  // vksp needs at least 40
-} spv_ext_inst_desc_t;
-
-typedef struct spv_ext_inst_group_t {
-  const spv_ext_inst_type_t type;
-  const uint32_t count;
-  const spv_ext_inst_desc_t* entries;
-} spv_ext_inst_group_t;
-
-typedef struct spv_opcode_table_t {
-  const uint32_t count;
-  const spv_opcode_desc_t* entries;
-} spv_opcode_table_t;
-
-typedef struct spv_operand_table_t {
-  const uint32_t count;
-  const spv_operand_desc_group_t* types;
-} spv_operand_table_t;
-
-typedef struct spv_ext_inst_table_t {
-  const uint32_t count;
-  const spv_ext_inst_group_t* groups;
-} spv_ext_inst_table_t;
-
-typedef const spv_opcode_desc_t* spv_opcode_desc;
-typedef const spv_operand_desc_t* spv_operand_desc;
-typedef const spv_ext_inst_desc_t* spv_ext_inst_desc;
-
-typedef const spv_opcode_table_t* spv_opcode_table;
-typedef const spv_operand_table_t* spv_operand_table;
-typedef const spv_ext_inst_table_t* spv_ext_inst_table;
+// NOTE: Instruction and operand tables have moved to table2.{h|cpp},
+// where they are represented more compactly.
 
 struct spv_context_t {
   const spv_target_env target_env;
-  const spv_opcode_table opcode_table;
-  const spv_operand_table operand_table;
-  const spv_ext_inst_table ext_inst_table;
   spvtools::MessageConsumer consumer;
 };
 
@@ -125,13 +35,4 @@
 void SetContextMessageConsumer(spv_context context, MessageConsumer consumer);
 }  // namespace spvtools
 
-// Populates *table with entries for env.
-spv_result_t spvOpcodeTableGet(spv_opcode_table* table, spv_target_env env);
-
-// Populates *table with entries for env.
-spv_result_t spvOperandTableGet(spv_operand_table* table, spv_target_env env);
-
-// Populates *table with entries for env.
-spv_result_t spvExtInstTableGet(spv_ext_inst_table* table, spv_target_env env);
-
 #endif  // SOURCE_TABLE_H_
diff --git a/source/table2.cpp b/source/table2.cpp
new file mode 100644
index 0000000..dc1bff3
--- /dev/null
+++ b/source/table2.cpp
@@ -0,0 +1,440 @@
+// Copyright (c) 2025 Google LLC
+//
+// 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.
+
+// Compressed grammar tables.
+
+#include "source/table2.h"
+
+#include <algorithm>
+#include <array>
+#include <cstring>
+
+#include "source/extensions.h"
+#include "source/latest_version_spirv_header.h"
+#include "source/spirv_constant.h"
+#include "source/spirv_target_env.h"
+#include "spirv-tools/libspirv.hpp"
+
+namespace spvtools {
+namespace {
+
+// This is used in the source for the generated tables.
+constexpr inline IndexRange IR(uint32_t first, uint32_t count) {
+  return IndexRange{first, count};
+}
+
+struct NameIndex {
+  // Location of the null-terminated name in the global string table kStrings.
+  IndexRange name;
+  // Index of this name's entry in the corresponding by-value table.
+  uint32_t index;
+};
+
+struct NameValue {
+  // Location of the null-terminated name in the global string table kStrings.
+  IndexRange name;
+  // Enum value in the binary format.
+  uint32_t value;
+};
+
+// The generated include file contains variables:
+//
+//   std::array<NameIndex,...> kOperandNames:
+//      Operand names and index, ordered by (operand kind, name)
+//      The index part is the named entry's index in kOperandsByValue array.
+//      Aliases are included as their own entries.
+//
+//   std::array<OperandDesc, ...> kOperandsByValue:
+//      Operand descriptions, ordered by (operand kind, operand enum value).
+//
+//   std::array<NameIndex,...> kInstructionNames:
+//      Instruction names and index, ordered by (name, value)
+//      The index part is the named entry's index in kInstructionDesc array.
+//      Aliases are included as their own entries.
+//
+//   std::array<InstructionDesc, ...> kInstructionDesc
+//      Instruction descriptions, ordered by opcode.
+//
+//   const char kStrings[]
+//      Array of characters, referenced by IndexRanges elsewhere.
+//      Each IndexRange denotes a string.
+//
+//   const IndexRange kAliasSpans[]
+//      Array of IndexRanges, where each represents a string by referencing
+//      the kStrings table.
+//      This array contains all sequences of alias strings used in the grammar.
+//      This table is referenced by an IndexRange elsewhere, i.e. by the
+//      'aliases' field of an instruction or operand description.
+//
+//   const spv::Capability kCapabilitySpans[]
+//      Array of capabilities, referenced by IndexRanges elsewhere.
+//      Contains all sequences of capabilities used in the grammar.
+//
+//   const spvtools::Extension kExtensionSpans[] = {
+//      Array of extensions, referenced by IndexRanges elsewhere.
+//      Contains all sequences of extensions used in the grammar.
+//
+//   const spv_operand_type_t kOperandSpans[] = {
+//      Array of operand types, referenced by IndexRanges elsewhere.
+//      Contains all sequences of operand types used in the grammar.
+
+// Maps an operand kind to NameValue entries for that kind.
+// The result is an IndexRange into kOperandNames, and are sorted
+// by string name within that span.
+IndexRange OperandNameRangeForKind(spv_operand_type_t type);
+
+// Maps an operand kind to possible operands for that kind.
+// The result is an IndexRange into kOperandsByValue, and the operands
+// are sorted by value within that span.
+IndexRange OperandByValueRangeForKind(spv_operand_type_t type);
+
+// Maps an extended instruction set kind to NameValue entries for that kind.
+// The result is an IndexRange into kExtIntNames, and are sorted
+// by string name within that span.
+IndexRange ExtInstNameRangeForKind(spv_ext_inst_type_t type);
+
+// Maps an extended instruction set kind to possible operands for that kind.
+// The result is an IndexRange into kExtInstByValue, and the instructions
+// are sorted by opcode value within that span.
+IndexRange ExtInstByValueRangeForKind(spv_ext_inst_type_t type);
+
+// Returns the name of an extension, as an index into kStrings
+IndexRange ExtensionToIndexRange(Extension extension);
+
+#include "core_tables_body.inc"
+
+// Returns a pointer to the null-terminated C-style string in the global
+// strings table, as referenced by 'ir'.  Assumes the given range is valid.
+const char* getChars(IndexRange ir) {
+  assert(ir.first() < sizeof(kStrings));
+  return ir.apply(kStrings).data();
+}
+
+}  // anonymous namespace
+
+utils::Span<const spv_operand_type_t> OperandDesc::operands() const {
+  return operands_range.apply(kOperandSpans);
+}
+utils::Span<const char> OperandDesc::name() const {
+  return name_range.apply(kStrings);
+}
+utils::Span<const IndexRange> OperandDesc::aliases() const {
+  return name_range.apply(kAliasSpans);
+}
+utils::Span<const spv::Capability> OperandDesc::capabilities() const {
+  return capabilities_range.apply(kCapabilitySpans);
+}
+utils::Span<const spvtools::Extension> OperandDesc::extensions() const {
+  return extensions_range.apply(kExtensionSpans);
+}
+
+utils::Span<const spv_operand_type_t> InstructionDesc::operands() const {
+  return operands_range.apply(kOperandSpans);
+}
+utils::Span<const char> InstructionDesc::name() const {
+  return name_range.apply(kStrings);
+}
+utils::Span<const IndexRange> InstructionDesc::aliases() const {
+  return name_range.apply(kAliasSpans);
+}
+utils::Span<const spv::Capability> InstructionDesc::capabilities() const {
+  return capabilities_range.apply(kCapabilitySpans);
+}
+utils::Span<const spvtools::Extension> InstructionDesc::extensions() const {
+  return extensions_range.apply(kExtensionSpans);
+}
+
+utils::Span<const spv_operand_type_t> ExtInstDesc::operands() const {
+  return operands_range.apply(kOperandSpans);
+}
+utils::Span<const char> ExtInstDesc::name() const {
+  return name_range.apply(kStrings);
+}
+utils::Span<const spv::Capability> ExtInstDesc::capabilities() const {
+  return capabilities_range.apply(kCapabilitySpans);
+}
+
+spv_result_t LookupOpcode(spv::Op opcode, const InstructionDesc** desc) {
+  // Metaphor: Look for the needle in the haystack.
+  const InstructionDesc needle(opcode);
+  auto where = std::lower_bound(
+      kInstructionDesc.begin(), kInstructionDesc.end(), needle,
+      [&](const InstructionDesc& lhs, const InstructionDesc& rhs) {
+        return uint32_t(lhs.opcode) < uint32_t(rhs.opcode);
+      });
+  if (where != kInstructionDesc.end() && where->opcode == opcode) {
+    *desc = &*where;
+    return SPV_SUCCESS;
+  }
+  return SPV_ERROR_INVALID_LOOKUP;
+}
+
+spv_result_t LookupOpcode(const char* name, const InstructionDesc** desc) {
+  // The comparison function knows to use 'name' string to compare against
+  // when the value is kSentinel.
+  const auto kSentinel = uint32_t(-1);
+  const NameIndex needle{{}, kSentinel};
+  auto less = [&](const NameIndex& lhs, const NameIndex& rhs) {
+    const char* lhs_chars = lhs.index == kSentinel ? name : getChars(lhs.name);
+    const char* rhs_chars = rhs.index == kSentinel ? name : getChars(rhs.name);
+    return std::strcmp(lhs_chars, rhs_chars) < 0;
+  };
+
+  auto where = std::lower_bound(kInstructionNames.begin(),
+                                kInstructionNames.end(), needle, less);
+  if (where != kInstructionNames.end() &&
+      std::strcmp(getChars(where->name), name) == 0) {
+    *desc = &kInstructionDesc[where->index];
+    return SPV_SUCCESS;
+  }
+  return SPV_ERROR_INVALID_LOOKUP;
+}
+
+namespace {
+template <typename KEY_TYPE>
+spv_result_t LookupOpcodeForEnvInternal(spv_target_env env, KEY_TYPE key,
+                                        const InstructionDesc** desc) {
+  const InstructionDesc* desc_proxy;
+  auto status = LookupOpcode(key, &desc_proxy);
+  if (status != SPV_SUCCESS) {
+    return status;
+  }
+  const auto& entry = *desc_proxy;
+  const auto version = spvVersionForTargetEnv(env);
+  if ((version >= entry.minVersion && version <= entry.lastVersion) ||
+      entry.extensions_range.count() > 0 ||
+      entry.capabilities_range.count() > 0) {
+    *desc = desc_proxy;
+    return SPV_SUCCESS;
+  }
+  return SPV_ERROR_INVALID_LOOKUP;
+}
+}  // namespace
+
+spv_result_t LookupOpcodeForEnv(spv_target_env env, const char* name,
+                                const InstructionDesc** desc) {
+  return LookupOpcodeForEnvInternal(env, name, desc);
+}
+
+spv_result_t LookupOpcodeForEnv(spv_target_env env, spv::Op opcode,
+                                const InstructionDesc** desc) {
+  return LookupOpcodeForEnvInternal(env, opcode, desc);
+}
+
+spv_result_t LookupOperand(spv_operand_type_t type, uint32_t value,
+                           const OperandDesc** desc) {
+  auto ir = OperandByValueRangeForKind(type);
+  if (ir.empty()) {
+    return SPV_ERROR_INVALID_LOOKUP;
+  }
+
+  auto span = ir.apply(kOperandsByValue.data());
+
+  // Metaphor: Look for the needle in the haystack.
+  // The operand value is the first member.
+  const OperandDesc needle{value};
+  auto where =
+      std::lower_bound(span.begin(), span.end(), needle,
+                       [&](const OperandDesc& lhs, const OperandDesc& rhs) {
+                         return lhs.value < rhs.value;
+                       });
+  if (where != span.end() && where->value == value) {
+    *desc = &*where;
+    return SPV_SUCCESS;
+  }
+  return SPV_ERROR_INVALID_LOOKUP;
+}
+
+spv_result_t LookupOperand(spv_operand_type_t type, const char* name,
+                           size_t name_len, const OperandDesc** desc) {
+  auto ir = OperandNameRangeForKind(type);
+  if (ir.empty()) {
+    return SPV_ERROR_INVALID_LOOKUP;
+  }
+
+  auto span = ir.apply(kOperandNames.data());
+
+  // The comparison function knows to use (name, name_len) as the
+  // string to compare against when the value is kSentinel.
+  const auto kSentinel = uint32_t(-1);
+  const NameIndex needle{{}, kSentinel};
+  // The strings in the global string table are null-terminated, and the count
+  // reflects that.  So always deduct 1 from its length.
+  auto less = [&](const NameIndex& lhs, const NameIndex& rhs) {
+    const char* lhs_chars = lhs.index == kSentinel ? name : getChars(lhs.name);
+    const char* rhs_chars = rhs.index == kSentinel ? name : getChars(rhs.name);
+    const auto content_cmp = std::strncmp(lhs_chars, rhs_chars, name_len);
+    if (content_cmp != 0) {
+      return content_cmp < 0;
+    }
+    const auto lhs_len =
+        lhs.index == kSentinel ? name_len : lhs.name.count() - 1;
+    const auto rhs_len =
+        rhs.index == kSentinel ? name_len : rhs.name.count() - 1;
+    return lhs_len < rhs_len;
+  };
+
+  auto where = std::lower_bound(span.begin(), span.end(), needle, less);
+  if (where != span.end() && where->name.count() - 1 == name_len &&
+      std::strncmp(getChars(where->name), name, name_len) == 0) {
+    *desc = &kOperandsByValue[where->index];
+    return SPV_SUCCESS;
+  }
+  return SPV_ERROR_INVALID_LOOKUP;
+}
+
+spv_result_t LookupExtInst(spv_ext_inst_type_t type, const char* name,
+                           const ExtInstDesc** desc) {
+  auto ir = ExtInstNameRangeForKind(type);
+  if (ir.empty()) {
+    return SPV_ERROR_INVALID_LOOKUP;
+  }
+
+  auto span = ir.apply(kExtInstNames.data());
+
+  // The comparison function knows to use 'name' string to compare against
+  // when the value is kSentinel.
+  const auto kSentinel = uint32_t(-1);
+  const NameIndex needle{{}, kSentinel};
+  auto less = [&](const NameIndex& lhs, const NameIndex& rhs) {
+    const char* lhs_chars = lhs.index == kSentinel ? name : getChars(lhs.name);
+    const char* rhs_chars = rhs.index == kSentinel ? name : getChars(rhs.name);
+    return std::strcmp(lhs_chars, rhs_chars) < 0;
+  };
+
+  auto where = std::lower_bound(span.begin(), span.end(), needle, less);
+  if (where != span.end() && std::strcmp(getChars(where->name), name) == 0) {
+    *desc = &kExtInstByValue[where->index];
+    return SPV_SUCCESS;
+  }
+  return SPV_ERROR_INVALID_LOOKUP;
+}
+
+// Finds the extended instruction description by opcode value.
+// On success, returns SPV_SUCCESS and updates *desc.
+spv_result_t LookupExtInst(spv_ext_inst_type_t type, uint32_t value,
+                           const ExtInstDesc** desc) {
+  auto ir = ExtInstByValueRangeForKind(type);
+  if (ir.empty()) {
+    return SPV_ERROR_INVALID_LOOKUP;
+  }
+
+  auto span = ir.apply(kExtInstByValue.data());
+
+  // Metaphor: Look for the needle in the haystack.
+  // The operand value is the first member.
+  const ExtInstDesc needle(value);
+  auto where =
+      std::lower_bound(span.begin(), span.end(), needle,
+                       [&](const ExtInstDesc& lhs, const ExtInstDesc& rhs) {
+                         return lhs.value < rhs.value;
+                       });
+  if (where != span.end() && where->value == value) {
+    *desc = &*where;
+    return SPV_SUCCESS;
+  }
+  return SPV_ERROR_INVALID_LOOKUP;
+}
+
+const char* ExtensionToString(Extension extension) {
+  return getChars(ExtensionToIndexRange(extension));
+}
+
+bool GetExtensionFromString(const char* name, Extension* extension) {
+  // The comparison function knows to use 'name' string to compare against
+  // when the value is kSentinel.
+  const auto kSentinel = uint32_t(-1);
+  const NameValue needle{{}, kSentinel};
+  auto less = [&](const NameValue& lhs, const NameValue& rhs) {
+    const char* lhs_chars = lhs.value == kSentinel ? name : getChars(lhs.name);
+    const char* rhs_chars = rhs.value == kSentinel ? name : getChars(rhs.name);
+    return std::strcmp(lhs_chars, rhs_chars) < 0;
+  };
+
+  auto where = std::lower_bound(kExtensionNames.begin(), kExtensionNames.end(),
+                                needle, less);
+  if (where != kExtensionNames.end() &&
+      std::strcmp(getChars(where->name), name) == 0) {
+    *extension = static_cast<Extension>(where->value);
+    return true;
+  }
+  return false;
+}
+
+// This is dirty copy of the spirv.hpp11 function
+// TODO - Use a generated version of this function
+const char* StorageClassToString(spv::StorageClass value) {
+  switch (value) {
+    case spv::StorageClass::UniformConstant:
+      return "UniformConstant";
+    case spv::StorageClass::Input:
+      return "Input";
+    case spv::StorageClass::Uniform:
+      return "Uniform";
+    case spv::StorageClass::Output:
+      return "Output";
+    case spv::StorageClass::Workgroup:
+      return "Workgroup";
+    case spv::StorageClass::CrossWorkgroup:
+      return "CrossWorkgroup";
+    case spv::StorageClass::Private:
+      return "Private";
+    case spv::StorageClass::Function:
+      return "Function";
+    case spv::StorageClass::Generic:
+      return "Generic";
+    case spv::StorageClass::PushConstant:
+      return "PushConstant";
+    case spv::StorageClass::AtomicCounter:
+      return "AtomicCounter";
+    case spv::StorageClass::Image:
+      return "Image";
+    case spv::StorageClass::StorageBuffer:
+      return "StorageBuffer";
+    case spv::StorageClass::TileImageEXT:
+      return "TileImageEXT";
+    case spv::StorageClass::TileAttachmentQCOM:
+      return "TileAttachmentQCOM";
+    case spv::StorageClass::NodePayloadAMDX:
+      return "NodePayloadAMDX";
+    case spv::StorageClass::CallableDataKHR:
+      return "CallableDataKHR";
+    case spv::StorageClass::IncomingCallableDataKHR:
+      return "IncomingCallableDataKHR";
+    case spv::StorageClass::RayPayloadKHR:
+      return "RayPayloadKHR";
+    case spv::StorageClass::HitAttributeKHR:
+      return "HitAttributeKHR";
+    case spv::StorageClass::IncomingRayPayloadKHR:
+      return "IncomingRayPayloadKHR";
+    case spv::StorageClass::ShaderRecordBufferKHR:
+      return "ShaderRecordBufferKHR";
+    case spv::StorageClass::PhysicalStorageBuffer:
+      return "PhysicalStorageBuffer";
+    case spv::StorageClass::HitObjectAttributeNV:
+      return "HitObjectAttributeNV";
+    case spv::StorageClass::TaskPayloadWorkgroupEXT:
+      return "TaskPayloadWorkgroupEXT";
+    case spv::StorageClass::CodeSectionINTEL:
+      return "CodeSectionINTEL";
+    case spv::StorageClass::DeviceOnlyINTEL:
+      return "DeviceOnlyINTEL";
+    case spv::StorageClass::HostOnlyINTEL:
+      return "HostOnlyINTEL";
+    default:
+      return "Unknown";
+  }
+}
+
+}  // namespace spvtools
diff --git a/source/table2.h b/source/table2.h
new file mode 100644
index 0000000..1218fb2
--- /dev/null
+++ b/source/table2.h
@@ -0,0 +1,263 @@
+// Copyright (c) 2025 The Khronos Group Inc.
+// Copyright (c) 2025 Google LLC
+//
+// 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_TABLE2_H_
+#define SOURCE_TABLE2_H_
+
+#include "source/latest_version_spirv_header.h"
+#include "source/util/index_range.h"
+#include "spirv-tools/libspirv.hpp"
+
+// Define the objects that describe the grammatical structure of SPIR-V
+// instructions and their operands. The objects are owned by static
+// tables populated at C++ build time from the grammar files from SPIRV-Headers.
+//
+// Clients use freestanding methods to lookup an opcode or an operand, either
+// by numeric value (in the binary), or by name.
+//
+// For historical reasons, the opcode lookup can also use a target enviroment
+// enum to filter for opcodes supported in that environment.
+//
+// It should be very fast for the system loader to load (and possibly relocate)
+// the static tables.  In particular, there should be very few global symbols
+// with independent addresses. Prefer a very few large tables of items rather
+// than dozens or hundreds of global symbols.
+//
+// The overall structure among containers (i.e. skipping scalar data members)
+// is as follows:
+//
+//    An OperandDesc describes an operand.
+//    An InstructionDesc desribes an instruction.
+//    An ExtInstDesc describes an extended intruction.
+//
+//    Both OperandDesc and InstructionDesc have members:
+//      - a name string
+//      - array of alias strings
+//      - array of spv::Capability      (as an enum)
+//      - array of spv_operand_type_t   (as an enum)
+//      - array of spvtools::Extension  (as an enum)
+//      - a minVersion
+//      - a lastVersion
+//
+//    An OperandDesc also has:
+//      - a uint32_t value.
+//
+//    An InstructionDesc also has:
+//      - a spv::Op opcode
+//      - a bool hasResult
+//      - a bool hasType
+//      - a printing class
+//
+//    An ExtInstDesc has:
+//      - a name
+//      - array of spv::Capability      (as an enum)
+//      - array of spv_operand_type_t   (as an enum)
+//
+// The arrays are represented by spans into a global static array, with one
+// array for each of:
+//      - null-terminated strings, for names
+//      - arrays of null-terminated strings, for alias lists
+//      - spv_operand_type_t
+//      - spv::Capability
+//      - spvtools::Extension
+//
+// Note: Currently alias lists never have more than one element.
+// The data structures and code do not assume this.
+
+// TODO(dneto): convert the tables for extended instructions
+// Currently (as defined in table.h):
+//    An spv_ext_inst_group_t has:
+//      - array of spv_ext_inst_desc_t
+//
+//    An spv_ext_inst_desc_t has:
+//      - a name string
+//      - array of spv::Capability
+//      - array of spv_operand_type_t
+
+namespace spvtools {
+
+#include "core_tables_header.inc"
+
+using IndexRange = utils::IndexRange<uint32_t, uint32_t>;
+
+// Describes a SPIR-V operand.
+struct OperandDesc {
+  const uint32_t value;
+
+  const IndexRange operands_range;      // Indexes kOperandSpans
+  const IndexRange name_range;          // Indexes kStrings
+  const IndexRange aliases_range;       // Indexes kAliasSpans
+  const IndexRange capabilities_range;  // Indexes kCapabilitySpans
+  // A set of extensions that enable this feature. If empty then this operand
+  // value is in core and its availability is subject to minVersion. The
+  // assembler, binary parser, and disassembler ignore this rule, so you can
+  // freely process invalid modules.
+  const IndexRange extensions_range;  // Indexes kExtensionSpans
+  // Minimal core SPIR-V version required for this feature, if without
+  // extensions. ~0u means reserved for future use. ~0u and non-empty
+  // extension lists means only available in extensions.
+  const uint32_t minVersion = 0xFFFFFFFFu;
+  const uint32_t lastVersion = 0xFFFFFFFFu;
+  utils::Span<const spv_operand_type_t> operands() const;
+  utils::Span<const char> name() const;
+  utils::Span<const IndexRange> aliases() const;
+  utils::Span<const spv::Capability> capabilities() const;
+  utils::Span<const spvtools::Extension> extensions() const;
+
+  constexpr OperandDesc(uint32_t v, IndexRange o, IndexRange n, IndexRange a,
+                        IndexRange c, IndexRange e, uint32_t mv, uint32_t lv)
+      : value(v),
+        operands_range(o),
+        name_range(n),
+        aliases_range(a),
+        capabilities_range(c),
+        extensions_range(e),
+        minVersion(mv),
+        lastVersion(lv) {}
+
+  constexpr OperandDesc(uint32_t v) : value(v) {}
+
+  OperandDesc(const OperandDesc&) = delete;
+  OperandDesc(OperandDesc&&) = delete;
+};
+
+// Describes an Instruction
+struct InstructionDesc {
+  const spv::Op opcode;
+  const bool hasResult = false;
+  const bool hasType = false;
+
+  const IndexRange operands_range;      // Indexes kOperandSpans
+  const IndexRange name_range;          // Indexes kStrings
+  const IndexRange aliases_range;       // Indexes kAliasSpans
+  const IndexRange capabilities_range;  // Indexes kCapbilitySpans
+  // A set of extensions that enable this feature. If empty then this operand
+  // value is in core and its availability is subject to minVersion. The
+  // assembler, binary parser, and disassembler ignore this rule, so you can
+  // freely process invalid modules.
+  const IndexRange extensions_range;  // Indexes kExtensionSpans
+  // Minimal core SPIR-V version required for this feature, if without
+  // extensions. ~0u means reserved for future use. ~0u and non-empty
+  // extension lists means only available in extensions.
+  const uint32_t minVersion = 0xFFFFFFFFu;
+  const uint32_t lastVersion = 0xFFFFFFFFu;
+  // The printing class specifies what kind of instruction it is, e.g. what
+  // section of the SPIR-V spec. E.g. kImage, kComposite
+  const PrintingClass printingClass = PrintingClass::kReserved;
+  // Returns the span of elements in the global grammar tables corresponding
+  // to the privately-stored index ranges
+  utils::Span<const spv_operand_type_t> operands() const;
+  utils::Span<const char> name() const;
+  utils::Span<const IndexRange> aliases() const;
+  utils::Span<const spv::Capability> capabilities() const;
+  utils::Span<const spvtools::Extension> extensions() const;
+
+  constexpr InstructionDesc(spv::Op oc, bool hr, bool ht, IndexRange o,
+                            IndexRange n, IndexRange a, IndexRange c,
+                            IndexRange e, uint32_t mv, uint32_t lv,
+                            PrintingClass pc)
+      : opcode(oc),
+        hasResult(hr),
+        hasType(ht),
+        operands_range(o),
+        name_range(n),
+        aliases_range(a),
+        capabilities_range(c),
+        extensions_range(e),
+        minVersion(mv),
+        lastVersion(lv),
+        printingClass(pc) {}
+
+  constexpr InstructionDesc(spv::Op oc) : opcode(oc) {}
+
+  InstructionDesc(const InstructionDesc&) = delete;
+  InstructionDesc(InstructionDesc&&) = delete;
+};
+
+// Describes an extended instruction
+struct ExtInstDesc {
+  const uint32_t value;
+  const IndexRange operands_range;      // Indexes kOperandSpans
+  const IndexRange name_range;          // Indexes kStrings
+  const IndexRange capabilities_range;  // Indexes kCapbilitySpans
+  // Returns the span of elements in the global grammar tables corresponding
+  // to the privately-stored index ranges
+  utils::Span<const spv_operand_type_t> operands() const;
+  utils::Span<const char> name() const;
+  utils::Span<const spv::Capability> capabilities() const;
+
+  constexpr ExtInstDesc(uint32_t v, IndexRange o, IndexRange n, IndexRange c)
+      : value(v), operands_range(o), name_range(n), capabilities_range(c) {}
+
+  constexpr ExtInstDesc(uint32_t v) : value(v) {}
+
+  ExtInstDesc(const ExtInstDesc&) = delete;
+  ExtInstDesc(ExtInstDesc&&) = delete;
+};
+
+// Finds the instruction description by opcode name. The name should not
+// have the "Op" prefix. On success, returns SPV_SUCCESS and updates *desc.
+spv_result_t LookupOpcode(const char* name, const InstructionDesc** desc);
+// Finds the instruction description by opcode value.
+// On success, returns SPV_SUCCESS and updates *desc.
+spv_result_t LookupOpcode(spv::Op opcode, const InstructionDesc** desc);
+
+// Finds the instruction description by opcode name, without the "Op" prefix.
+// A lookup will succeed if:
+// - The instruction exists, and
+// - Either the target environment supports the SPIR-V version of the
+// instruction,
+//   or the instruction is enabled by at least one extension,
+//   or the instruction is enabled by at least one capability.,
+// On success, returns SPV_SUCCESS and updates *desc.
+spv_result_t LookupOpcodeForEnv(spv_target_env env, const char* name,
+                                const InstructionDesc** desc);
+
+// Finds the instruction description by opcode value.
+// A lookup will succeed if:
+// - The instruction exists, and
+// - Either the target environment supports the SPIR-V version of the
+// instruction,
+//   or the instruction is enabled by at least one extension,
+//   or the instruction is enabled by at least one capability.,
+// On success, returns SPV_SUCCESS and updates *desc.
+spv_result_t LookupOpcodeForEnv(spv_target_env env, spv::Op,
+                                const InstructionDesc** desc);
+
+spv_result_t LookupOperand(spv_operand_type_t type, const char* name,
+                           size_t name_len, const OperandDesc** desc);
+spv_result_t LookupOperand(spv_operand_type_t type, uint32_t operand,
+                           const OperandDesc** desc);
+
+// Finds the extended instruction description by opcode name.
+// On success, returns SPV_SUCCESS and updates *desc.
+spv_result_t LookupExtInst(spv_ext_inst_type_t type, const char* name,
+                           const ExtInstDesc** desc);
+// Finds the extended instruction description by opcode value.
+// On success, returns SPV_SUCCESS and updates *desc.
+spv_result_t LookupExtInst(spv_ext_inst_type_t type, uint32_t value,
+                           const ExtInstDesc** desc);
+
+// Finds Extension enum corresponding to |str|. Returns false if not found.
+bool GetExtensionFromString(const char* str, Extension* extension);
+
+// Returns text string corresponding to |extension|.
+const char* ExtensionToString(Extension extension);
+
+/// Used to provide better error message
+const char* StorageClassToString(spv::StorageClass value);
+
+}  // namespace spvtools
+#endif  // SOURCE_TABLE2_H_
diff --git a/source/text.cpp b/source/text.cpp
index 2154e85..0978d81 100644
--- a/source/text.cpp
+++ b/source/text.cpp
@@ -38,6 +38,7 @@
 #include "source/spirv_constant.h"
 #include "source/spirv_target_env.h"
 #include "source/table.h"
+#include "source/table2.h"
 #include "source/text_handler.h"
 #include "source/util/bitutils.h"
 #include "source/util/parse_number.h"
@@ -241,14 +242,20 @@
     case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: {
       // The assembler accepts the symbolic name for an extended instruction,
       // and emits its corresponding number.
-      spv_ext_inst_desc extInst;
-      if (grammar.lookupExtInst(pInst->extInstType, textValue, &extInst) ==
+      const spvtools::ExtInstDesc* desc = nullptr;
+      if (spvtools::LookupExtInst(pInst->extInstType, textValue, &desc) ==
           SPV_SUCCESS) {
         // if we know about this extended instruction, push the numeric value
-        spvInstructionAddWord(pInst, extInst->ext_inst);
+        spvInstructionAddWord(pInst, desc->value);
+
+        // Push VARIABLE_ID so extra trailing operands from future NSDI
+        // versions are silently absorbed after the instruction-specific ones.
+        if (spvExtInstIsNonSemantic(pInst->extInstType)) {
+          pExpectedOperands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID);
+        }
 
         // Prepare to parse the operands for the extended instructions.
-        spvPushOperandTypes(extInst->operandTypes, pExpectedOperands);
+        spvPushOperandTypes(desc->operands(), pExpectedOperands);
       } else {
         // if we don't know this extended instruction and the set isn't
         // non-semantic, we cannot process further
@@ -284,8 +291,8 @@
         return context->diagnostic() << "Invalid " << spvOperandTypeStr(type)
                                      << " '" << textValue << "'.";
       }
-      spv_opcode_desc opcodeEntry = nullptr;
-      if (grammar.lookupOpcode(opcode, &opcodeEntry)) {
+      const spvtools::InstructionDesc* opcodeEntry = nullptr;
+      if (LookupOpcodeForEnv(grammar.target_env(), opcode, &opcodeEntry)) {
         return context->diagnostic(SPV_ERROR_INTERNAL)
                << "OpSpecConstant opcode table out of sync";
       }
@@ -295,8 +302,9 @@
       // type Id and result Id, since they've already been processed.
       assert(opcodeEntry->hasType);
       assert(opcodeEntry->hasResult);
-      assert(opcodeEntry->numTypes >= 2);
-      spvPushOperandTypes(opcodeEntry->operandTypes + 2, pExpectedOperands);
+      assert(opcodeEntry->operands().size() >= 2);
+      spvPushOperandTypes(opcodeEntry->operands().subspan(2),
+                          pExpectedOperands);
     } break;
 
     case SPV_OPERAND_TYPE_LITERAL_INTEGER:
@@ -346,10 +354,11 @@
             context->getTypeOfTypeGeneratingValue(pInst->resultTypeId);
         if (!spvtools::isScalarFloating(expected_type) &&
             !spvtools::isScalarIntegral(expected_type)) {
-          spv_opcode_desc d;
+          const spvtools::InstructionDesc* opcodeEntry = nullptr;
           const char* opcode_name = "opcode";
-          if (SPV_SUCCESS == grammar.lookupOpcode(pInst->opcode, &d)) {
-            opcode_name = d->name;
+          if (SPV_SUCCESS == LookupOpcode(pInst->opcode, &opcodeEntry)) {
+            opcode_name =
+                opcodeEntry->name().data();  // assumes it's null-terminated
           }
           return context->diagnostic()
                  << "Type for " << opcode_name
@@ -409,6 +418,8 @@
     case SPV_OPERAND_TYPE_LOOP_CONTROL:
     case SPV_OPERAND_TYPE_IMAGE:
     case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
+    case SPV_OPERAND_TYPE_TENSOR_OPERANDS:
+    case SPV_OPERAND_TYPE_OPTIONAL_TENSOR_OPERANDS:
     case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
     case SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS:
     case SPV_OPERAND_TYPE_SELECTION_CONTROL:
@@ -416,7 +427,8 @@
     case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
     case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS:
     case SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS:
-    case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_REDUCE: {
+    case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_REDUCE:
+    case SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS: {
       uint32_t value;
       if (auto error = grammar.parseMaskOperand(type, textValue, &value)) {
         return context->diagnostic(error)
@@ -454,8 +466,8 @@
     default: {
       // NOTE: All non literal operands are handled here using the operand
       // table.
-      spv_operand_desc entry;
-      if (grammar.lookupOperand(type, textValue, strlen(textValue), &entry)) {
+      const spvtools::OperandDesc* entry = nullptr;
+      if (spvtools::LookupOperand(type, textValue, strlen(textValue), &entry)) {
         return context->diagnostic() << "Invalid " << spvOperandTypeStr(type)
                                      << " '" << textValue << "'.";
       }
@@ -465,7 +477,7 @@
       }
 
       // Prepare to parse the operands for this logical operand.
-      spvPushOperandTypes(entry->operandTypes, pExpectedOperands);
+      spvPushOperandTypes(entry->operands(), pExpectedOperands);
     } break;
   }
   return SPV_SUCCESS;
@@ -502,7 +514,7 @@
     if (operandValue == "=")
       return context->diagnostic() << firstWord << " not allowed before =.";
 
-    // Needed to pass to spvTextEncodeOpcode(), but it shouldn't ever be
+    // Needed to pass to spvTextEncodeOperand(), but it shouldn't ever be
     // expanded.
     spv_operand_pattern_t dummyExpectedOperands;
     error = spvTextEncodeOperand(
@@ -514,6 +526,124 @@
   return SPV_SUCCESS;
 }
 
+/// @brief Translate an instruction started by OpUnknown and the following
+/// operands to binary form
+///
+/// @param[in] grammar the grammar to use for compilation
+/// @param[in, out] context the dynamic compilation info
+/// @param[out] pInst returned binary Opcode
+///
+/// @return result code
+spv_result_t encodeInstructionStartingWithOpUnknown(
+    const spvtools::AssemblyGrammar& grammar,
+    spvtools::AssemblyContext* context, spv_instruction_t* pInst) {
+  spv_position_t nextPosition = {};
+
+  uint16_t opcode;
+  uint16_t wordCount;
+
+  // The '(' character.
+  if (context->advance())
+    return context->diagnostic() << "Expected '(', found end of stream.";
+  if ('(' != context->peek()) {
+    return context->diagnostic() << "'(' expected after OpUnknown but found '"
+                                 << context->peek() << "'.";
+  }
+  context->seekForward(1);
+
+  // The opcode enumerant.
+  if (context->advance())
+    return context->diagnostic()
+           << "Expected opcode enumerant, found end of stream.";
+  std::string opcodeString;
+  spv_result_t error = context->getWord(&opcodeString, &nextPosition);
+  if (error) return context->diagnostic(error) << "Internal Error";
+
+  if (!spvtools::utils::ParseNumber(opcodeString.c_str(), &opcode)) {
+    return context->diagnostic()
+           << "Invalid opcode enumerant: \"" << opcodeString << "\".";
+  }
+
+  context->setPosition(nextPosition);
+
+  // The ',' character.
+  if (context->advance())
+    return context->diagnostic() << "Expected ',', found end of stream.";
+  if (',' != context->peek()) {
+    return context->diagnostic()
+           << "',' expected after opcode enumerant but found '"
+           << context->peek() << "'.";
+  }
+  context->seekForward(1);
+
+  // The number of words.
+  if (context->advance())
+    return context->diagnostic()
+           << "Expected number of words, found end of stream.";
+  std::string wordCountString;
+  error = context->getWord(&wordCountString, &nextPosition);
+  if (error) return context->diagnostic(error) << "Internal Error";
+
+  if (!spvtools::utils::ParseNumber(wordCountString.c_str(), &wordCount)) {
+    return context->diagnostic()
+           << "Invalid number of words: \"" << wordCountString << "\".";
+  }
+
+  if (wordCount == 0) {
+    return context->diagnostic() << "Number of words (which includes the "
+                                    "opcode) must be greater than zero.";
+  }
+
+  context->setPosition(nextPosition);
+
+  // The ')' character.
+  if (context->advance())
+    return context->diagnostic() << "Expected ')', found end of stream.";
+  if (')' != context->peek()) {
+    return context->diagnostic()
+           << "')' expected after number of words but found '"
+           << context->peek() << "'.";
+  }
+  context->seekForward(1);
+
+  pInst->opcode = static_cast<spv::Op>(opcode);
+  context->binaryEncodeU32(spvOpcodeMake(wordCount, pInst->opcode), pInst);
+
+  wordCount--;  // Subtract the opcode from the number of words left to read.
+
+  while (wordCount-- > 0) {
+    if (context->advance() == SPV_END_OF_STREAM) {
+      return context->diagnostic() << "Expected " << wordCount + 1
+                                   << " more operands, found end of stream.";
+    }
+    if (context->isStartOfNewInst()) {
+      std::string invalid;
+      context->getWord(&invalid, &nextPosition);
+      return context->diagnostic()
+             << "Unexpected start of new instruction: \"" << invalid
+             << "\". Expected " << wordCount + 1 << " more operands";
+    }
+
+    std::string operandValue;
+    if ((error = context->getWord(&operandValue, &nextPosition)))
+      return context->diagnostic(error) << "Internal Error";
+
+    if (operandValue == "=")
+      return context->diagnostic() << "OpUnknown not allowed before =.";
+
+    // Needed to pass to spvTextEncodeOperand(), but it shouldn't ever be
+    // expanded.
+    spv_operand_pattern_t dummyExpectedOperands;
+    error = spvTextEncodeOperand(
+        grammar, context, SPV_OPERAND_TYPE_OPTIONAL_CIV, operandValue.c_str(),
+        pInst, &dummyExpectedOperands);
+    if (error) return error;
+    context->setPosition(nextPosition);
+  }
+
+  return SPV_SUCCESS;
+}
+
 /// @brief Translate single Opcode and operands to binary form
 ///
 /// @param[in] grammar the grammar to use for compilation
@@ -573,11 +703,21 @@
     }
   }
 
+  if (opcodeName == "OpUnknown") {
+    if (!result_id.empty()) {
+      return context->diagnostic()
+             << "OpUnknown not allowed in assignment. Use an explicit result "
+                "id operand instead.";
+    }
+    context->setPosition(nextPosition);
+    return encodeInstructionStartingWithOpUnknown(grammar, context, pInst);
+  }
+
   // NOTE: The table contains Opcode names without the "Op" prefix.
   const char* pInstName = opcodeName.data() + 2;
 
-  spv_opcode_desc opcodeEntry;
-  error = grammar.lookupOpcode(pInstName, &opcodeEntry);
+  const spvtools::InstructionDesc* opcodeEntry = nullptr;
+  error = LookupOpcodeForEnv(grammar.target_env(), pInstName, &opcodeEntry);
   if (error) {
     return context->diagnostic(error)
            << "Invalid Opcode name '" << opcodeName << "'";
@@ -605,10 +745,15 @@
   // ExecutionMode), or for extended instructions that may have their
   // own operands depending on the selected extended instruction.
   spv_operand_pattern_t expectedOperands;
-  expectedOperands.reserve(opcodeEntry->numTypes);
-  for (auto i = 0; i < opcodeEntry->numTypes; i++)
-    expectedOperands.push_back(
-        opcodeEntry->operandTypes[opcodeEntry->numTypes - i - 1]);
+  {
+    const auto operands = opcodeEntry->operands();
+    const auto n = operands.size();
+    expectedOperands.reserve(n);
+    for (auto i = 0u; i < n; i++) {
+      auto ty = operands[n - i - 1];
+      expectedOperands.push_back(ty);
+    }
+  }
 
   while (!expectedOperands.empty()) {
     const spv_operand_type_t type = expectedOperands.back();
@@ -724,10 +869,6 @@
 
   if (!text->str) return context.diagnostic() << "Missing assembly text.";
 
-  if (!grammar.isValid()) {
-    return SPV_ERROR_INVALID_TABLE;
-  }
-
   // Skip past whitespace and comments.
   context.advance();
 
@@ -773,10 +914,6 @@
   spvtools::AssemblyContext context(text, consumer, std::move(ids_to_preserve));
 
   if (!text->str) return context.diagnostic() << "Missing assembly text.";
-
-  if (!grammar.isValid()) {
-    return SPV_ERROR_INVALID_TABLE;
-  }
   if (!pBinary) return SPV_ERROR_INVALID_POINTER;
 
   std::vector<spv_instruction_t> instructions;
diff --git a/source/text_handler.cpp b/source/text_handler.cpp
index a778c2c..22401e9 100644
--- a/source/text_handler.cpp
+++ b/source/text_handler.cpp
@@ -18,6 +18,7 @@
 #include <cassert>
 #include <cstdlib>
 #include <cstring>
+#include <string_view>
 #include <tuple>
 
 #include "source/assembly_grammar.h"
@@ -118,6 +119,9 @@
           break;
         case ' ':
         case ';':
+        case ',':
+        case '(':
+        case ')':
         case '\t':
         case '\n':
         case '\r':
@@ -149,6 +153,31 @@
   return ('O' == ch0 && 'p' == ch1 && ('A' <= ch2 && ch2 <= 'Z'));
 }
 
+// Returns false if the the floating point encoding requires a bit width
+// different from the given width. Write the expected bit width via *expected.
+bool validBitWidthForFPEncoding(spv_fp_encoding_t enc, uint32_t width,
+                                uint32_t* expected) {
+  switch (enc) {
+    case SPV_FP_ENCODING_IEEE754_BINARY16:
+    case SPV_FP_ENCODING_BFLOAT16:
+      *expected = 16;
+      break;
+    case SPV_FP_ENCODING_IEEE754_BINARY32:
+      *expected = 32;
+      break;
+    case SPV_FP_ENCODING_IEEE754_BINARY64:
+      *expected = 64;
+      break;
+    case SPV_FP_ENCODING_FLOAT8_E5M2:
+    case SPV_FP_ENCODING_FLOAT8_E4M3:
+      *expected = 8;
+      break;
+    default:
+      return true;
+  }
+  return width == *expected;
+}
+
 }  // namespace
 
 const IdType kUnknownType = {0, false, IdTypeClass::kBottom};
@@ -251,13 +280,13 @@
              << "Unexpected numeric literal type";
     case IdTypeClass::kScalarIntegerType:
       if (type.isSigned) {
-        number_type = {type.bitwidth, SPV_NUMBER_SIGNED_INT};
+        number_type = {type.bitwidth, SPV_NUMBER_SIGNED_INT, type.encoding};
       } else {
-        number_type = {type.bitwidth, SPV_NUMBER_UNSIGNED_INT};
+        number_type = {type.bitwidth, SPV_NUMBER_UNSIGNED_INT, type.encoding};
       }
       break;
     case IdTypeClass::kScalarFloatType:
-      number_type = {type.bitwidth, SPV_NUMBER_FLOATING};
+      number_type = {type.bitwidth, SPV_NUMBER_FLOATING, type.encoding};
       break;
     case IdTypeClass::kBottom:
       // kBottom means the type is unknown and we need to infer the type before
@@ -267,11 +296,11 @@
       // signed integer, otherwise an unsigned integer.
       uint32_t bitwidth = static_cast<uint32_t>(assumedBitWidth(type));
       if (strchr(val, '.')) {
-        number_type = {bitwidth, SPV_NUMBER_FLOATING};
+        number_type = {bitwidth, SPV_NUMBER_FLOATING, type.encoding};
       } else if (type.isSigned || val[0] == '-') {
-        number_type = {bitwidth, SPV_NUMBER_SIGNED_INT};
+        number_type = {bitwidth, SPV_NUMBER_SIGNED_INT, type.encoding};
       } else {
-        number_type = {bitwidth, SPV_NUMBER_UNSIGNED_INT};
+        number_type = {bitwidth, SPV_NUMBER_UNSIGNED_INT, type.encoding};
       }
       break;
   }
@@ -327,14 +356,36 @@
     if (pInst->words.size() != 4)
       return diagnostic() << "Invalid OpTypeInt instruction";
     types_[value] = {pInst->words[2], pInst->words[3] != 0,
-                     IdTypeClass::kScalarIntegerType};
+                     IdTypeClass::kScalarIntegerType, SPV_FP_ENCODING_UNKNOWN};
   } else if (pInst->opcode == spv::Op::OpTypeFloat) {
     if ((pInst->words.size() != 3) && (pInst->words.size() != 4))
       return diagnostic() << "Invalid OpTypeFloat instruction";
-    // TODO(kpet) Do we need to record the FP Encoding here?
-    types_[value] = {pInst->words[2], false, IdTypeClass::kScalarFloatType};
+    spv_fp_encoding_t enc = SPV_FP_ENCODING_UNKNOWN;
+    if (pInst->words.size() >= 4) {
+      const spvtools::OperandDesc* desc = nullptr;
+      spv_result_t status = spvtools::LookupOperand(SPV_OPERAND_TYPE_FPENCODING,
+                                                    pInst->words[3], &desc);
+      if (status == SPV_SUCCESS) {
+        enc = spvFPEncodingFromOperandFPEncoding(
+            static_cast<spv::FPEncoding>(desc->value));
+        uint32_t expected_width;
+        if (!validBitWidthForFPEncoding(enc, pInst->words[2],
+                                        &expected_width)) {
+          const auto& name_span = desc->name();
+          const std::string_view name(name_span.data(), name_span.size() - 1);
+          return diagnostic() << "Invalid bit width " << pInst->words[2]
+                              << " for floating point encoding " << name
+                              << "; expected " << expected_width;
+        }
+      } else {
+        return diagnostic() << "Invalid OpTypeFloat encoding";
+      }
+    }
+    types_[value] = {pInst->words[2], false, IdTypeClass::kScalarFloatType,
+                     enc};
   } else {
-    types_[value] = {0, false, IdTypeClass::kOtherType};
+    types_[value] = {0, false, IdTypeClass::kOtherType,
+                     SPV_FP_ENCODING_UNKNOWN};
   }
   return SPV_SUCCESS;
 }
diff --git a/source/text_handler.h b/source/text_handler.h
index 19972e9..54ffe61 100644
--- a/source/text_handler.h
+++ b/source/text_handler.h
@@ -47,6 +47,7 @@
   uint32_t bitwidth;  // Safe to assume that we will not have > 2^32 bits.
   bool isSigned;      // This is only significant if type_class is integral.
   IdTypeClass type_class;
+  spv_fp_encoding_t encoding;
 };
 
 // Default equality operator for IdType. Tests if all members are the same.
@@ -141,7 +142,14 @@
   spv_result_t advance();
 
   // Sets word to the next word in the input text. Fills next_position with
-  // the next location past the end of the word.
+  // the next location past the end of the word.  Returns an error if the
+  // context is invalid or has no more text. Otherwise returns SPV_SUCCESS.
+  // Assumes the next part of the input is not whitespace.
+  //
+  // A word ends at the next comment or whitespace.  However, double-quoted
+  // strings remain intact, and a backslash always escapes the next character.
+  // The input stream may end before a matching double-quote, or immediately
+  // after a backslash. Both such cases still count as success.
   spv_result_t getWord(std::string* word, spv_position next_position);
 
   // Returns true if the next word in the input is the start of a new Opcode.
diff --git a/source/util/bitutils.h b/source/util/bitutils.h
index 2763bc2..dbf6a9c 100644
--- a/source/util/bitutils.h
+++ b/source/util/bitutils.h
@@ -206,6 +206,23 @@
   return utils::ClearHighBits(value, bit_width - number_of_bits);
 }
 
+// Returns the the least significant bit from |value|.
+template <typename T>
+constexpr T LSB(T value) {
+  static_assert(std::is_integral<T>::value, "LSB requires integer type");
+  if constexpr (std::is_unsigned_v<T>) {
+    // Prevent warnings about doing a -x on unsigned values.
+    return value & (~value + 1);
+  } else {
+    return value & -value;
+  }
+}
+
+static_assert(LSB<uint32_t>(UINT32_MAX) == uint32_t(0x00000001), "LSB failed");
+static_assert(LSB<uint32_t>(0x10001000) == uint32_t(0x00001000), "LSB failed");
+static_assert(LSB<uint32_t>(0x10000000) == uint32_t(0x10000000), "LSB failed");
+static_assert(LSB<int32_t>(-1) == int32_t(0x00000001), "LSB failed");
+
 }  // namespace utils
 }  // namespace spvtools
 
diff --git a/source/util/hex_float.h b/source/util/hex_float.h
index 98353a4..03efb14 100644
--- a/source/util/hex_float.h
+++ b/source/util/hex_float.h
@@ -36,6 +36,50 @@
 namespace spvtools {
 namespace utils {
 
+class Float8_E4M3 {
+ public:
+  Float8_E4M3(uint8_t v) : val(v) {}
+  Float8_E4M3() = default;
+  static bool isNan(const Float8_E4M3& val) { return (val.val & 0x7f) == 0x7f; }
+  // Returns true if the given value is any kind of infinity.
+  static bool isInfinity(const Float8_E4M3&) {
+    return false;  // E4M3 has no infinity representation
+  }
+  Float8_E4M3(const Float8_E4M3& other) { val = other.val; }
+  uint8_t get_value() const { return val; }
+
+  // Returns the maximum normal value.
+  static Float8_E4M3 max() { return Float8_E4M3(0x7e); }
+  // Returns the lowest normal value.
+  static Float8_E4M3 lowest() { return Float8_E4M3(0x8); }
+
+ private:
+  uint8_t val;
+};
+
+class Float8_E5M2 {
+ public:
+  Float8_E5M2(uint8_t v) : val(v) {}
+  Float8_E5M2() = default;
+  static bool isNan(const Float8_E5M2& val) {
+    return ((val.val & 0x7c) == 0x7c) && ((val.val & 0x3) != 0);
+  }
+  // Returns true if the given value is any kind of infinity.
+  static bool isInfinity(const Float8_E5M2& val) {
+    return (val.val & 0x7f) == 0x7c;
+  }
+  Float8_E5M2(const Float8_E5M2& other) { val = other.val; }
+  uint8_t get_value() const { return val; }
+
+  // Returns the maximum normal value.
+  static Float8_E5M2 max() { return Float8_E5M2(0x7b); }
+  // Returns the lowest normal value.
+  static Float8_E5M2 lowest() { return Float8_E5M2(0x4); }
+
+ private:
+  uint8_t val;
+};
+
 class Float16 {
  public:
   Float16(uint16_t v) : val(v) {}
@@ -59,6 +103,34 @@
   uint16_t val;
 };
 
+class BFloat16 {
+ public:
+  BFloat16(uint16_t v) : val(v) {}
+  BFloat16() = default;
+  BFloat16(const BFloat16& other) { val = other.val; }
+
+  // Exponent mask: 0x7F80, Mantissa mask: 0x007F
+  static bool isNan(const BFloat16& val) {
+    return ((val.val & 0x7F80) == 0x7F80) && ((val.val & 0x007F) != 0);
+  }
+  static bool isInfinity(const BFloat16& val) {
+    return ((val.val & 0x7F80) == 0x7F80) && ((val.val & 0x007F) == 0);
+  }
+
+  uint16_t get_value() const { return val; }
+
+  // a sign bit of 0, and an all 1 mantissa.
+  static BFloat16 max() { return BFloat16(0x7F7F); }
+  // a sign bit of 1, and an all 1 mantissa.
+  static BFloat16 lowest() { return BFloat16(0xFF7F); }
+
+ private:
+  // 15: Sign
+  // 14-7: Exponent
+  // 6-0: Mantissa
+  uint16_t val;
+};
+
 // To specialize this type, you must override uint_type to define
 // an unsigned integer that can fit your floating point type.
 // You must also add a isNan function that returns true if
@@ -111,6 +183,46 @@
 };
 
 template <>
+struct FloatProxyTraits<Float8_E4M3> {
+  using uint_type = uint8_t;
+  static bool isNan(Float8_E4M3 f) { return Float8_E4M3::isNan(f); }
+  // Returns true if the given value is any kind of infinity.
+  static bool isInfinity(Float8_E4M3 f) { return Float8_E4M3::isInfinity(f); }
+  // Returns the maximum normal value.
+  static Float8_E4M3 max() { return Float8_E4M3::max(); }
+  // Returns the lowest normal value.
+  static Float8_E4M3 lowest() { return Float8_E4M3::lowest(); }
+  // Returns the value as the native floating point format.
+  static Float8_E4M3 getAsFloat(const uint_type& t) { return Float8_E4M3(t); }
+  // Returns the bits from the given floating pointer number.
+  static uint_type getBitsFromFloat(const Float8_E4M3& t) {
+    return t.get_value();
+  }
+  // Returns the bitwidth.
+  static uint32_t width() { return 8u; }
+};
+
+template <>
+struct FloatProxyTraits<Float8_E5M2> {
+  using uint_type = uint8_t;
+  static bool isNan(Float8_E5M2 f) { return Float8_E5M2::isNan(f); }
+  // Returns true if the given value is any kind of infinity.
+  static bool isInfinity(Float8_E5M2 f) { return Float8_E5M2::isInfinity(f); }
+  // Returns the maximum normal value.
+  static Float8_E5M2 max() { return Float8_E5M2::max(); }
+  // Returns the lowest normal value.
+  static Float8_E5M2 lowest() { return Float8_E5M2::lowest(); }
+  // Returns the value as the native floating point format.
+  static Float8_E5M2 getAsFloat(const uint_type& t) { return Float8_E5M2(t); }
+  // Returns the bits from the given floating pointer number.
+  static uint_type getBitsFromFloat(const Float8_E5M2& t) {
+    return t.get_value();
+  }
+  // Returns the bitwidth.
+  static uint32_t width() { return 8u; }
+};
+
+template <>
 struct FloatProxyTraits<Float16> {
   using uint_type = uint16_t;
   static bool isNan(Float16 f) { return Float16::isNan(f); }
@@ -128,6 +240,24 @@
   static uint32_t width() { return 16u; }
 };
 
+template <>
+struct FloatProxyTraits<BFloat16> {
+  using uint_type = uint16_t;
+  static bool isNan(BFloat16 f) { return BFloat16::isNan(f); }
+  // Returns true if the given value is any kind of infinity.
+  static bool isInfinity(BFloat16 f) { return BFloat16::isInfinity(f); }
+  // Returns the maximum normal value.
+  static BFloat16 max() { return BFloat16::max(); }
+  // Returns the lowest normal value.
+  static BFloat16 lowest() { return BFloat16::lowest(); }
+  // Returns the value as the native floating point format.
+  static BFloat16 getAsFloat(const uint_type& t) { return BFloat16(t); }
+  // Returns the bits from the given floating pointer number.
+  static uint_type getBitsFromFloat(const BFloat16& t) { return t.get_value(); }
+  // Returns the bitwidth.
+  static uint32_t width() { return 16u; }
+};
+
 // Since copying a floating point number (especially if it is NaN)
 // does not guarantee that bits are preserved, this class lets us
 // store the type and use it as a float when necessary.
@@ -216,6 +346,7 @@
   using int_type = void;
   // The numerical type that this HexFloat represents.
   using underlying_type = void;
+  using underlying_typetraits = void;
   // The type needed to construct the underlying type.
   using native_type = void;
   // The number of bits that are actually relevant in the uint_type.
@@ -229,6 +360,8 @@
   // The bias of the exponent. (How much we need to subtract from the stored
   // value to get the correct value.)
   static const uint32_t exponent_bias = 0;
+  static const bool has_infinity = true;
+  static const uint32_t NaN_pattern = 0;
 };
 
 // Traits for IEEE float.
@@ -238,11 +371,14 @@
   using uint_type = uint32_t;
   using int_type = int32_t;
   using underlying_type = FloatProxy<float>;
+  using underlying_typetraits = FloatProxyTraits<float>;
   using native_type = float;
   static const uint_type num_used_bits = 32;
   static const uint_type num_exponent_bits = 8;
   static const uint_type num_fraction_bits = 23;
   static const uint_type exponent_bias = 127;
+  static const bool has_infinity = true;
+  static const uint_type NaN_pattern = 0x7f80000;
 };
 
 // Traits for IEEE double.
@@ -252,11 +388,48 @@
   using uint_type = uint64_t;
   using int_type = int64_t;
   using underlying_type = FloatProxy<double>;
+  using underlying_typetraits = FloatProxyTraits<double>;
   using native_type = double;
   static const uint_type num_used_bits = 64;
   static const uint_type num_exponent_bits = 11;
   static const uint_type num_fraction_bits = 52;
   static const uint_type exponent_bias = 1023;
+  static const bool has_infinity = true;
+  static const uint_type NaN_pattern = 0x7FF0000000000000;
+};
+
+// Traits for FP8 E4M3.
+// 1 sign bit, 4 exponent bits, 3 fractional bits.
+template <>
+struct HexFloatTraits<FloatProxy<Float8_E4M3>> {
+  using uint_type = uint8_t;
+  using int_type = int8_t;
+  using underlying_type = FloatProxy<Float8_E4M3>;
+  using underlying_typetraits = FloatProxyTraits<Float8_E4M3>;
+  using native_type = uint8_t;
+  static const uint_type num_used_bits = 8;
+  static const uint_type num_exponent_bits = 4;
+  static const uint_type num_fraction_bits = 3;
+  static const uint_type exponent_bias = 7;
+  static const bool has_infinity = false;
+  static const uint_type NaN_pattern = 0x7F;
+};
+
+// Traits for FP8 E5M2.
+// 1 sign bit, 4 exponent bits, 3 fractional bits.
+template <>
+struct HexFloatTraits<FloatProxy<Float8_E5M2>> {
+  using uint_type = uint8_t;
+  using int_type = int8_t;
+  using underlying_type = FloatProxy<Float8_E5M2>;
+  using underlying_typetraits = FloatProxyTraits<Float8_E5M2>;
+  using native_type = uint8_t;
+  static const uint_type num_used_bits = 8;
+  static const uint_type num_exponent_bits = 5;
+  static const uint_type num_fraction_bits = 2;
+  static const uint_type exponent_bias = 15;
+  static const bool has_infinity = true;
+  static const uint_type NaN_pattern = 0x7c;
 };
 
 // Traits for IEEE half.
@@ -265,12 +438,32 @@
 struct HexFloatTraits<FloatProxy<Float16>> {
   using uint_type = uint16_t;
   using int_type = int16_t;
-  using underlying_type = uint16_t;
+  using underlying_type = FloatProxy<Float16>;
+  using underlying_typetraits = FloatProxyTraits<Float16>;
   using native_type = uint16_t;
   static const uint_type num_used_bits = 16;
   static const uint_type num_exponent_bits = 5;
   static const uint_type num_fraction_bits = 10;
   static const uint_type exponent_bias = 15;
+  static const bool has_infinity = true;
+  static const uint_type NaN_pattern = 0x7c00;
+};
+
+// Traits for BFloat16.
+// 1 sign bit, 7 exponent bits, 8 fractional bits.
+template <>
+struct HexFloatTraits<FloatProxy<BFloat16>> {
+  using uint_type = uint16_t;
+  using int_type = int16_t;
+  using underlying_type = FloatProxy<BFloat16>;
+  using underlying_typetraits = FloatProxyTraits<BFloat16>;
+  using native_type = uint16_t;
+  static const uint_type num_used_bits = 16;
+  static const uint_type num_exponent_bits = 8;
+  static const uint_type num_fraction_bits = 7;
+  static const uint_type exponent_bias = 127;
+  static const bool has_infinity = true;
+  static const uint_type NaN_pattern = 0x7F80;
 };
 
 enum class round_direction {
@@ -291,6 +484,7 @@
   using int_type = typename Traits::int_type;
   using underlying_type = typename Traits::underlying_type;
   using native_type = typename Traits::native_type;
+  using traits = Traits;
 
   explicit HexFloat(T f) : value_(f) {}
 
@@ -493,9 +687,9 @@
   struct negatable_left_shift {
     static uint_type val(uint_type val) {
       if (N > 0) {
-        return static_cast<uint_type>(val << N);
+        return static_cast<uint_type>(static_cast<uint64_t>(val) << N);
       } else {
-        return static_cast<uint_type>(val >> N);
+        return static_cast<uint_type>(static_cast<uint64_t>(val) >> N);
       }
     }
   };
@@ -519,28 +713,28 @@
   template <int_type N, typename enable = void>
   struct negatable_left_shift {
     static uint_type val(uint_type val) {
-      return static_cast<uint_type>(val >> -N);
+      return static_cast<uint_type>(static_cast<uint64_t>(val) >> -N);
     }
   };
 
   template <int_type N>
   struct negatable_left_shift<N, typename std::enable_if<N >= 0>::type> {
     static uint_type val(uint_type val) {
-      return static_cast<uint_type>(val << N);
+      return static_cast<uint_type>(static_cast<uint64_t>(val) << N);
     }
   };
 
   template <int_type N, typename enable = void>
   struct negatable_right_shift {
     static uint_type val(uint_type val) {
-      return static_cast<uint_type>(val << -N);
+      return static_cast<uint_type>(static_cast<uint64_t>(val) << -N);
     }
   };
 
   template <int_type N>
   struct negatable_right_shift<N, typename std::enable_if<N >= 0>::type> {
     static uint_type val(uint_type val) {
-      return static_cast<uint_type>(val >> N);
+      return static_cast<uint_type>(static_cast<uint64_t>(val) >> N);
     }
   };
 #endif
@@ -639,6 +833,9 @@
   // underflow to (0 or min depending on rounding) if the number underflows.
   template <typename other_T>
   void castTo(other_T& other, round_direction round_dir) {
+    using other_traits = typename other_T::traits;
+    using other_underlyingtraits = typename other_traits::underlying_typetraits;
+
     other = other_T(static_cast<typename other_T::native_type>(0));
     bool negate = isNegative();
     if (getUnsignedBits() == 0) {
@@ -664,18 +861,24 @@
       }
     }
 
-    bool is_nan =
-        (getBits() & exponent_mask) == exponent_mask && significand != 0;
+    bool is_nan = T(getBits()).isNan();
     bool is_inf =
         !is_nan &&
         ((exponent + carried) > static_cast<int_type>(other_T::exponent_bias) ||
-         (significand == 0 && (getBits() & exponent_mask) == exponent_mask));
+         T(getBits()).isInfinity());
 
     // If we are Nan or Inf we should pass that through.
     if (is_inf) {
-      other.set_value(typename other_T::underlying_type(
-          static_cast<typename other_T::uint_type>(
-              (negate ? other_T::sign_mask : 0) | other_T::exponent_mask)));
+      if (other_traits::has_infinity)
+        other.set_value(typename other_T::underlying_type(
+            static_cast<typename other_T::uint_type>(
+                (negate ? other_T::sign_mask : 0) | other_T::exponent_mask)));
+      else  // if the type doesnt use infinity, set it to max value (E4M3)
+        other.set_value(typename other_T::underlying_type(
+            static_cast<typename other_T::uint_type>(
+                (negate ? other_T::sign_mask : 0) |
+                other_underlyingtraits::getBitsFromFloat(
+                    other_underlyingtraits::max()))));
       return;
     }
     if (is_nan) {
@@ -690,7 +893,8 @@
       // just set the last bit.
       other.set_value(typename other_T::underlying_type(
           static_cast<typename other_T::uint_type>(
-              (negate ? other_T::sign_mask : 0) | other_T::exponent_mask |
+              other_traits::NaN_pattern | (negate ? other_T::sign_mask : 0) |
+              other_T::exponent_mask |
               (shifted_significand == 0 ? 0x1 : shifted_significand))));
       return;
     }
@@ -738,8 +942,8 @@
 template <typename T, typename Traits>
 std::ostream& operator<<(std::ostream& os, const HexFloat<T, Traits>& value) {
   using HF = HexFloat<T, Traits>;
-  using uint_type = typename HF::uint_type;
-  using int_type = typename HF::int_type;
+  using uint_type = uint64_t;
+  using int_type = int64_t;
 
   static_assert(HF::num_used_bits != 0,
                 "num_used_bits must be non-zero for a valid float");
@@ -807,13 +1011,17 @@
   return os;
 }
 
-// Returns true if negate_value is true and the next character on the
-// input stream is a plus or minus sign.  In that case we also set the fail bit
-// on the stream and set the value to the zero value for its type.
+// Encodes whether a leading sign has been seen, and if so which one.
+enum class LeadingSign { None, Plus, Minus };
+
+// Returns true if leading_sign is either Plus or Minus, and the next character
+// on the input stream is a plus or minus sign.  In that case we also set the
+// fail bit on the stream and set the value to the zero value for its type.
 template <typename T, typename Traits>
-inline bool RejectParseDueToLeadingSign(std::istream& is, bool negate_value,
+inline bool RejectParseDueToLeadingSign(std::istream& is,
+                                        LeadingSign leading_sign,
                                         HexFloat<T, Traits>& value) {
-  if (negate_value) {
+  if (leading_sign != LeadingSign::None) {
     auto next_char = is.peek();
     if (next_char == '-' || next_char == '+') {
       // Fail the parse.  Emulate standard behaviour by setting the value to
@@ -828,22 +1036,24 @@
 
 // Parses a floating point number from the given stream and stores it into the
 // value parameter.
-// If negate_value is true then the number may not have a leading minus or
-// plus, and if it successfully parses, then the number is negated before
-// being stored into the value parameter.
+// If leading_sign is Plus or Minus, then the number may not have a leading
+// minus or plus. If it successfully parses, and the leading sign was Minus,
+// then the number is negated before being stored into the value parameter.
 // If the value cannot be correctly parsed or overflows the target floating
 // point type, then set the fail bit on the stream.
 // TODO(dneto): Promise C++11 standard behavior in how the value is set in
 // the error case, but only after all target platforms implement it correctly.
 // In particular, the Microsoft C++ runtime appears to be out of spec.
 template <typename T, typename Traits>
-inline std::istream& ParseNormalFloat(std::istream& is, bool negate_value,
+inline std::istream& ParseNormalFloat(std::istream& is,
+                                      LeadingSign leading_sign,
                                       HexFloat<T, Traits>& value) {
-  if (RejectParseDueToLeadingSign(is, negate_value, value)) {
+  if (RejectParseDueToLeadingSign(is, leading_sign, value)) {
     return is;
   }
   T val;
   is >> val;
+  const bool negate_value = leading_sign == LeadingSign::Minus;
   if (negate_value) {
     val = -val;
   }
@@ -866,8 +1076,9 @@
 // This will parse the float as it were a 32-bit floating point number,
 // and then round it down to fit into a Float16 value.
 // The number is rounded towards zero.
-// If negate_value is true then the number may not have a leading minus or
-// plus, and if it successfully parses, then the number is negated before
+// If leading_sign is Plus or Minus, then the number may not have a leading
+// minus or plus. If it successfully parses, and the leading sign was Minus,
+// then the number is negated before being stored into the value parameter.
 // being stored into the value parameter.
 // If the value cannot be correctly parsed or overflows the target floating
 // point type, then set the fail bit on the stream.
@@ -877,11 +1088,11 @@
 template <>
 inline std::istream&
 ParseNormalFloat<FloatProxy<Float16>, HexFloatTraits<FloatProxy<Float16>>>(
-    std::istream& is, bool negate_value,
+    std::istream& is, LeadingSign leading_sign,
     HexFloat<FloatProxy<Float16>, HexFloatTraits<FloatProxy<Float16>>>& value) {
   // First parse as a 32-bit float.
   HexFloat<FloatProxy<float>> float_val(0.0f);
-  ParseNormalFloat(is, negate_value, float_val);
+  ParseNormalFloat(is, leading_sign, float_val);
 
   // Then convert to 16-bit float, saturating at infinities, and
   // rounding toward zero.
@@ -889,6 +1100,8 @@
 
   // Overflow on 16-bit behaves the same as for 32- and 64-bit: set the
   // fail bit and set the lowest or highest value.
+  // /!\ We get an error if there is no overflow but the value is infinity.
+  // Is it what we want?
   if (Float16::isInfinity(value.value().getAsFloat())) {
     value.set_value(value.isNegative() ? Float16::lowest() : Float16::max());
     is.setstate(std::ios_base::failbit);
@@ -896,6 +1109,98 @@
   return is;
 }
 
+// Same flow as Float16
+template <>
+inline std::istream&
+ParseNormalFloat<FloatProxy<BFloat16>, HexFloatTraits<FloatProxy<BFloat16>>>(
+    std::istream& is, LeadingSign leading_sign,
+    HexFloat<FloatProxy<BFloat16>, HexFloatTraits<FloatProxy<BFloat16>>>&
+        value) {
+  HexFloat<FloatProxy<float>> float_val(0.0f);
+  ParseNormalFloat(is, leading_sign, float_val);
+
+  float_val.castTo(value, round_direction::kToZero);
+
+  if (BFloat16::isInfinity(value.value().getAsFloat())) {
+    value.set_value(value.isNegative() ? BFloat16::lowest() : BFloat16::max());
+    is.setstate(std::ios_base::failbit);
+  }
+  return is;
+}
+
+// Specialization of ParseNormalFloat for FloatProxy<Float8_E4M3> values.
+// This will parse the float as it were a 32-bit floating point number,
+// and then round it down to fit into a Float8_E4M3 value.
+// The number is rounded towards zero.
+// If leading_sign is Plus or Minus, then the number may not have a leading
+// minus or plus. If it successfully parses, and the leading sign was Minus,
+// being stored into the value parameter.
+// If the value cannot be correctly parsed or overflows the target floating
+// point type, then set the fail bit on the stream.
+// TODO(dneto): Promise C++11 standard behavior in how the value is set in
+// the error case, but only after all target platforms implement it correctly.
+// In particular, the Microsoft C++ runtime appears to be out of spec.
+template <>
+inline std::istream& ParseNormalFloat<FloatProxy<Float8_E4M3>,
+                                      HexFloatTraits<FloatProxy<Float8_E4M3>>>(
+    std::istream& is, LeadingSign leading_sign,
+    HexFloat<FloatProxy<Float8_E4M3>, HexFloatTraits<FloatProxy<Float8_E4M3>>>&
+        value) {
+  // First parse as a 32-bit float.
+  HexFloat<FloatProxy<float>> float_val(0.0f);
+  ParseNormalFloat(is, leading_sign, float_val);
+
+  if (float_val.value().getAsFloat() > 448.0f) {
+    is.setstate(std::ios_base::failbit);
+    value.set_value(Float8_E4M3::max());
+    return is;
+  } else if (float_val.value().getAsFloat() < -448.0f) {
+    is.setstate(std::ios_base::failbit);
+    value.set_value(0x80 | Float8_E4M3::max().get_value());
+    return is;
+  }
+  // Then convert to E4M3 float, saturating at infinities, and
+  // rounding toward zero.
+  float_val.castTo(value, round_direction::kToZero);
+
+  return is;
+}
+// Specialization of ParseNormalFloat for FloatProxy<Float8_E5M2> values.
+// This will parse the float as it were a Float8_E5M2 floating point number,
+// and then round it down to fit into a Float16 value.
+// The number is rounded towards zero.
+// If leading_sign is Plus or Minus, then the number may not have a leading
+// minus or plus. If it successfully parses, and the leading sign was Minus,
+// being stored into the value parameter.
+// If the value cannot be correctly parsed or overflows the target floating
+// point type, then set the fail bit on the stream.
+// TODO(dneto): Promise C++11 standard behavior in how the value is set in
+// the error case, but only after all target platforms implement it correctly.
+// In particular, the Microsoft C++ runtime appears to be out of spec.
+template <>
+inline std::istream& ParseNormalFloat<FloatProxy<Float8_E5M2>,
+                                      HexFloatTraits<FloatProxy<Float8_E5M2>>>(
+    std::istream& is, LeadingSign leading_sign,
+    HexFloat<FloatProxy<Float8_E5M2>, HexFloatTraits<FloatProxy<Float8_E5M2>>>&
+        value) {
+  // First parse as a 32-bit float.
+  HexFloat<FloatProxy<float>> float_val(0.0f);
+  ParseNormalFloat(is, leading_sign, float_val);
+
+  // Then convert to Float8_E5M2 float, saturating at infinities, and
+  // rounding toward zero.
+  float_val.castTo(value, round_direction::kToZero);
+
+  // Overflow on Float8_E5M2 behaves the same as for 32- and 64-bit: set the
+  // fail bit and set the lowest or highest value.
+  if (Float8_E5M2::isInfinity(value.value().getAsFloat())) {
+    value.set_value(value.isNegative() ? Float8_E5M2::lowest()
+                                       : Float8_E5M2::max());
+    is.setstate(std::ios_base::failbit);
+  }
+  return is;
+}
+
 namespace detail {
 
 // Returns a new value formed from 'value' by setting 'bit' that is the
@@ -972,14 +1277,19 @@
   }
 
   auto next_char = is.peek();
-  bool negate_value = false;
 
-  if (next_char != '-' && next_char != '0') {
-    return ParseNormalFloat(is, negate_value, value);
+  auto leading_sign = LeadingSign::None;
+
+  if (next_char != '-' && next_char != '0' && next_char != '+') {
+    return ParseNormalFloat(is, LeadingSign::None, value);
   }
 
   if (next_char == '-') {
-    negate_value = true;
+    leading_sign = LeadingSign::Minus;
+    is.get();
+    next_char = is.peek();
+  } else if (next_char == '+') {
+    leading_sign = LeadingSign::Plus;
     is.get();
     next_char = is.peek();
   }
@@ -989,12 +1299,12 @@
     auto maybe_hex_start = is.peek();
     if (maybe_hex_start != 'x' && maybe_hex_start != 'X') {
       is.unget();
-      return ParseNormalFloat(is, negate_value, value);
+      return ParseNormalFloat(is, leading_sign, value);
     } else {
       is.get();  // Throw away the 'x';
     }
   } else {
-    return ParseNormalFloat(is, negate_value, value);
+    return ParseNormalFloat(is, leading_sign, value);
   }
 
   // This "looks" like a hex-float so treat it as one.
@@ -1210,7 +1520,8 @@
   }
 
   uint_type output_bits = static_cast<uint_type>(
-      static_cast<uint_type>(negate_value ? 1 : 0) << HF::top_bit_left_shift);
+      static_cast<uint_type>(leading_sign == LeadingSign::Minus ? 1 : 0)
+      << HF::top_bit_left_shift);
   output_bits |= fraction;
 
   uint_type shifted_exponent = static_cast<uint_type>(
@@ -1253,6 +1564,27 @@
   return os;
 }
 
+template <>
+inline std::ostream& operator<< <BFloat16>(std::ostream& os,
+                                           const FloatProxy<BFloat16>& value) {
+  os << HexFloat<FloatProxy<BFloat16>>(value);
+  return os;
+}
+
+template <>
+inline std::ostream& operator<< <Float8_E4M3>(
+    std::ostream& os, const FloatProxy<Float8_E4M3>& value) {
+  os << HexFloat<FloatProxy<Float8_E4M3>>(value);
+  return os;
+}
+
+template <>
+inline std::ostream& operator<< <Float8_E5M2>(
+    std::ostream& os, const FloatProxy<Float8_E5M2>& value) {
+  os << HexFloat<FloatProxy<Float8_E5M2>>(value);
+  return os;
+}
+
 }  // namespace utils
 }  // namespace spvtools
 
diff --git a/source/util/index_range.h b/source/util/index_range.h
new file mode 100644
index 0000000..d256bde
--- /dev/null
+++ b/source/util/index_range.h
@@ -0,0 +1,71 @@
+// Copyright 2025 Google LLC
+//
+// 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_UTIL_INDEX_RANGE_H_
+#define SOURCE_UTIL_INDEX_RANGE_H_
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include "source/util/span.h"
+
+namespace spvtools {
+namespace utils {
+
+// Implement a range of indicies, to index over an array of values of type T,
+// but whose base pointer is supplied externally.  Think of this as a span
+// but without the base pointer, which is to be applied later.  Parameterization
+// by T makes usage more readable and less error-prone.
+template <typename T, class IndexType = uint32_t, class CountType = IndexType>
+class IndexRange {
+ public:
+  static_assert(std::is_integral<IndexType>::value);
+  static_assert(std::is_unsigned<IndexType>::value);
+  static_assert(std::is_integral<CountType>::value);
+  static_assert(std::is_unsigned<CountType>::value);
+  using value_type = T;
+  using index_type = IndexType;
+  using size_type = CountType;
+
+  constexpr IndexRange() {}
+  constexpr IndexRange(index_type first, size_type count)
+      : first_(first), count_(count) {}
+
+  size_type count() const { return count_; }
+  bool empty() const { return count() == size_type(0); }
+
+  IndexType first() const { return first_; }
+
+  // Returns the span of indexed elements using the given base pointer.
+  template <typename E>
+  spvtools::utils::Span<E> apply(E* base) const {
+    using span_type = spvtools::utils::Span<E>;
+    return base ? span_type(base + first_, count_) : span_type();
+  }
+  template <typename E = int>
+  spvtools::utils::Span<E> apply(std::nullptr_t) const {
+    using span_type = spvtools::utils::Span<E>;
+    return span_type();
+  }
+
+ private:
+  index_type first_ = 0;
+  size_type count_ = 0;
+};
+
+}  // namespace utils
+}  // namespace spvtools
+
+#endif  // SOURCE_UTIL_INDEX_RANGE_H_
diff --git a/source/util/parse_number.cpp b/source/util/parse_number.cpp
index c3351c2..117e6f2 100644
--- a/source/util/parse_number.cpp
+++ b/source/util/parse_number.cpp
@@ -131,6 +131,19 @@
   return EncodeNumberStatus::kSuccess;
 }
 
+spv_fp_encoding_t DeduceEncoding(const NumberType& type) {
+  if (type.encoding != SPV_FP_ENCODING_UNKNOWN) return type.encoding;
+  switch (type.bitwidth) {
+    case 16:
+      return SPV_FP_ENCODING_IEEE754_BINARY16;
+    case 32:
+      return SPV_FP_ENCODING_IEEE754_BINARY32;
+    case 64:
+      return SPV_FP_ENCODING_IEEE754_BINARY64;
+    default:
+      return SPV_FP_ENCODING_UNKNOWN;
+  }
+}
 EncodeNumberStatus ParseAndEncodeFloatingPointNumber(
     const char* text, const NumberType& type,
     std::function<void(uint32_t)> emit, std::string* error_msg) {
@@ -145,8 +158,43 @@
   }
 
   const auto bit_width = AssumedBitWidth(type);
-  switch (bit_width) {
-    case 16: {
+  switch (DeduceEncoding(type)) {
+    case SPV_FP_ENCODING_FLOAT8_E4M3: {
+      HexFloat<FloatProxy<Float8_E4M3>> hVal(0);
+      if (!ParseNumber(text, &hVal)) {
+        ErrorMsgStream(error_msg) << "Invalid E4M3 float literal: " << text;
+        return EncodeNumberStatus::kInvalidText;
+      }
+      // getAsFloat will return the Float16 value, and get_value
+      // will return a uint16_t representing the bits of the float.
+      // The encoding is therefore correct from the perspective of the SPIR-V
+      // spec since the top 16 bits will be 0.
+      emit(static_cast<uint32_t>(hVal.value().getAsFloat().get_value()));
+      return EncodeNumberStatus::kSuccess;
+    } break;
+    case SPV_FP_ENCODING_FLOAT8_E5M2: {
+      HexFloat<FloatProxy<Float8_E5M2>> hVal(0);
+      if (!ParseNumber(text, &hVal)) {
+        ErrorMsgStream(error_msg) << "Invalid E5M2 float literal: " << text;
+        return EncodeNumberStatus::kInvalidText;
+      }
+      // getAsFloat will return the Float16 value, and get_value
+      // will return a uint16_t representing the bits of the float.
+      // The encoding is therefore correct from the perspective of the SPIR-V
+      // spec since the top 16 bits will be 0.
+      emit(static_cast<uint32_t>(hVal.value().getAsFloat().get_value()));
+      return EncodeNumberStatus::kSuccess;
+    } break;
+    case SPV_FP_ENCODING_BFLOAT16: {
+      HexFloat<FloatProxy<BFloat16>> hVal(0);
+      if (!ParseNumber(text, &hVal)) {
+        ErrorMsgStream(error_msg) << "Invalid bfloat16 literal: " << text;
+        return EncodeNumberStatus::kInvalidText;
+      }
+      emit(static_cast<uint32_t>(hVal.value().getAsFloat().get_value()));
+      return EncodeNumberStatus::kSuccess;
+    } break;
+    case SPV_FP_ENCODING_IEEE754_BINARY16: {
       HexFloat<FloatProxy<Float16>> hVal(0);
       if (!ParseNumber(text, &hVal)) {
         ErrorMsgStream(error_msg) << "Invalid 16-bit float literal: " << text;
@@ -159,7 +207,7 @@
       emit(static_cast<uint32_t>(hVal.value().getAsFloat().get_value()));
       return EncodeNumberStatus::kSuccess;
     } break;
-    case 32: {
+    case SPV_FP_ENCODING_IEEE754_BINARY32: {
       HexFloat<FloatProxy<float>> fVal(0.0f);
       if (!ParseNumber(text, &fVal)) {
         ErrorMsgStream(error_msg) << "Invalid 32-bit float literal: " << text;
@@ -168,7 +216,7 @@
       emit(BitwiseCast<uint32_t>(fVal));
       return EncodeNumberStatus::kSuccess;
     } break;
-    case 64: {
+    case SPV_FP_ENCODING_IEEE754_BINARY64: {
       HexFloat<FloatProxy<double>> dVal(0.0);
       if (!ParseNumber(text, &dVal)) {
         ErrorMsgStream(error_msg) << "Invalid 64-bit float literal: " << text;
diff --git a/source/util/parse_number.h b/source/util/parse_number.h
index d0f2a09..27a0327 100644
--- a/source/util/parse_number.h
+++ b/source/util/parse_number.h
@@ -32,6 +32,7 @@
   // SPV_NUMBER_NONE means the type is unknown and is invalid to be used with
   // ParseAndEncode{|Integer|Floating}Number().
   spv_number_kind_t kind;
+  spv_fp_encoding_t encoding;
 };
 
 // Returns true if the type is a scalar integer type.
@@ -160,6 +161,14 @@
   return true;
 }
 
+template <typename T>
+struct IsHexFloat {
+  static const bool value = false;
+};
+template <typename T>
+struct IsHexFloat<HexFloat<T>> {
+  static const bool value = true;
+};
 // Parses a numeric value of a given type from the given text.  The number
 // should take up the entire string, and should be within bounds for the target
 // type. On success, returns true and populates the object referenced by
@@ -169,8 +178,10 @@
   // C++11 doesn't define std::istringstream(int8_t&), so calling this method
   // with a single-byte type leads to implementation-defined behaviour.
   // Similarly for uint8_t.
-  static_assert(sizeof(T) > 1,
-                "Single-byte types are not supported in this parse method");
+  // HexFloat<T> overloads the operator
+  static_assert(sizeof(T) > 1 || IsHexFloat<T>::value,
+                "Single-byte types other than HexFloat<> are not supported in "
+                "this parse method");
 
   if (!text) return false;
   std::istringstream text_stream(text);
diff --git a/source/util/small_vector.h b/source/util/small_vector.h
index 1351475..c15ede1 100644
--- a/source/util/small_vector.h
+++ b/source/util/small_vector.h
@@ -43,6 +43,7 @@
 template <class T, size_t small_size>
 class SmallVector {
  public:
+  using value_type = T;
   using iterator = T*;
   using const_iterator = const T*;
 
@@ -183,7 +184,8 @@
   }
 
 // Avoid infinite recursion from rewritten operators in C++20
-#if __cplusplus <= 201703L
+#if (defined(_MSVC_LANG) && _MSVC_LANG <= 201703L) || \
+    (!defined(_MSVC_LANG) && __cplusplus <= 201703L)
   friend bool operator==(const std::vector<T>& lhs, const SmallVector& rhs) {
     return rhs == lhs;
   }
diff --git a/source/util/span.h b/source/util/span.h
new file mode 100644
index 0000000..1e29017
--- /dev/null
+++ b/source/util/span.h
@@ -0,0 +1,72 @@
+// Copyright 2025 Google LLC
+//
+// 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_UTIL_SPAN_H_
+#define SOURCE_UTIL_SPAN_H_
+
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+
+namespace spvtools {
+namespace utils {
+
+// Implement a subset of the C++20 std::span, using at most C++17 functionality.
+// Replace this when SPIRV-Tools can use C++20.
+template <class T>
+class Span {
+ public:
+  using element_type = T;
+  using value_type = std::remove_cv_t<T>;
+  using size_type = std::size_t;
+  using difference_type = std::ptrdiff_t;
+  using pointer = T*;
+  using const_pointer = const T*;
+  using reference = T&;
+  using const_reference = const T&;
+  using iterator = T*;
+  using const_iterator = const T*;
+
+  Span() {}
+  Span(iterator first, size_type count) : first_(first), count_(count) {}
+
+  iterator begin() const { return first_; }
+  iterator end() const { return first_ ? first_ + count_ : nullptr; }
+  const_iterator cbegin() const { return first_; }
+  const_iterator cend() const { return first_ ? first_ + count_ : nullptr; }
+
+  size_type size() const { return count_; }
+  size_type size_bytes() const { return count_ * sizeof(T); }
+  bool empty() const { return first_ == nullptr || count_ == 0; }
+
+  reference front() const { return *first_; }
+  reference back() const { return *(first_ + count_ - 1); }
+  pointer data() const { return first_; }
+  reference operator[](size_type idx) const { return first_[idx]; }
+  Span<T> subspan(size_type offset) const {
+    if (count_ > offset) {
+      return Span(first_ + offset, count_ - offset);
+    }
+    return Span<T>();
+  }
+
+ private:
+  T* first_ = nullptr;
+  size_type count_ = 0;
+};
+
+}  // namespace utils
+}  // namespace spvtools
+
+#endif  // SOURCE_UTIL_SPAN_H_
diff --git a/source/enum_string_mapping.cpp b/source/util/status.h
similarity index 66%
rename from source/enum_string_mapping.cpp
rename to source/util/status.h
index 32361a0..3799d38 100644
--- a/source/enum_string_mapping.cpp
+++ b/source/util/status.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 Google Inc.
+// Copyright (c) 2025 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,18 +12,20 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "source/enum_string_mapping.h"
-
-#include <algorithm>
-#include <cassert>
-#include <cstring>
-#include <string>
-#include <unordered_map>
-
-#include "source/extensions.h"
+#ifndef SOURCE_UTIL_STATUS_H_
+#define SOURCE_UTIL_STATUS_H_
 
 namespace spvtools {
+namespace utils {
 
-#include "enum_string_mapping.inc"
+// The result of processing a module.
+enum class Status {
+  Failure = 0x0,
+  SuccessWithChange = 0x10,
+  SuccessWithoutChange = 0x11
+};
 
+}  // namespace utils
 }  // namespace spvtools
+
+#endif  // SOURCE_UTIL_STATUS_H_
diff --git a/source/util/string_utils.h b/source/util/string_utils.h
index 03e20b3..590f8cf 100644
--- a/source/util/string_utils.h
+++ b/source/util/string_utils.h
@@ -17,13 +17,12 @@
 
 #include <assert.h>
 
+#include <cstdint>
 #include <cstring>
 #include <sstream>
 #include <string>
 #include <vector>
 
-#include "source/util/string_utils.h"
-
 namespace spvtools {
 namespace utils {
 
@@ -48,8 +47,9 @@
 
 // Encodes a string as a sequence of words, using the SPIR-V encoding, appending
 // to an existing vector.
-inline void AppendToVector(const std::string& input,
-                           std::vector<uint32_t>* result) {
+template <class VectorType = std::vector<uint32_t>>
+inline void AppendToVector(const std::string& input, VectorType* result) {
+  static_assert(std::is_same<uint32_t, typename VectorType::value_type>::value);
   uint32_t word = 0;
   size_t num_bytes = input.size();
   // SPIR-V strings are null-terminated.  The byte_index == num_bytes
@@ -70,8 +70,10 @@
 }
 
 // Encodes a string as a sequence of words, using the SPIR-V encoding.
-inline std::vector<uint32_t> MakeVector(const std::string& input) {
-  std::vector<uint32_t> result;
+template <class VectorType = std::vector<uint32_t>>
+inline VectorType MakeVector(const std::string& input) {
+  static_assert(std::is_same<uint32_t, typename VectorType::value_type>::value);
+  VectorType result;
   AppendToVector(input, &result);
   return result;
 }
diff --git a/source/val/function.h b/source/val/function.h
index c4cd5a4..d3dd639 100644
--- a/source/val/function.h
+++ b/source/val/function.h
@@ -20,6 +20,7 @@
 #include <map>
 #include <set>
 #include <string>
+#include <type_traits>
 #include <unordered_map>
 #include <unordered_set>
 #include <utility>
diff --git a/source/val/instruction.h b/source/val/instruction.h
index 59e8af1..6b42489 100644
--- a/source/val/instruction.h
+++ b/source/val/instruction.h
@@ -24,6 +24,7 @@
 #include "source/ext_inst.h"
 #include "source/opcode.h"
 #include "source/table.h"
+#include "source/table2.h"
 #include "spirv-tools/libspirv.h"
 
 namespace spvtools {
@@ -83,9 +84,7 @@
   const spv_parsed_instruction_t& c_inst() const { return inst_; }
 
   /// Provides direct access to instructions spv_ext_inst_type_t object.
-  const spv_ext_inst_type_t& ext_inst_type() const {
-    return inst_.ext_inst_type;
-  }
+  spv_ext_inst_type_t ext_inst_type() const { return inst_.ext_inst_type; }
 
   bool IsNonSemantic() const {
     return spvIsExtendedInstruction(opcode()) &&
@@ -113,7 +112,7 @@
  private:
   const std::vector<uint32_t> words_;
   const std::vector<spv_parsed_operand_t> operands_;
-  spv_parsed_instruction_t inst_;
+  const spv_parsed_instruction_t inst_;
   size_t line_num_ = 0;
 
   /// The function in which this instruction was declared
diff --git a/source/val/validate.cpp b/source/val/validate.cpp
index 2d10347..41c7a91 100644
--- a/source/val/validate.cpp
+++ b/source/val/validate.cpp
@@ -22,12 +22,12 @@
 
 #include "source/binary.h"
 #include "source/diagnostic.h"
-#include "source/enum_string_mapping.h"
 #include "source/extensions.h"
 #include "source/opcode.h"
 #include "source/spirv_constant.h"
 #include "source/spirv_endian.h"
 #include "source/spirv_target_env.h"
+#include "source/table2.h"
 #include "source/val/construct.h"
 #include "source/val/instruction.h"
 #include "source/val/validation_state.h"
@@ -64,9 +64,12 @@
 spv_result_t ProcessExtensions(void* user_data,
                                const spv_parsed_instruction_t* inst) {
   const spv::Op opcode = static_cast<spv::Op>(inst->opcode);
-  if (opcode == spv::Op::OpCapability) return SPV_SUCCESS;
+  if (opcode == spv::Op::OpCapability ||
+      opcode == spv::Op::OpConditionalCapabilityINTEL)
+    return SPV_SUCCESS;
 
-  if (opcode == spv::Op::OpExtension) {
+  if (opcode == spv::Op::OpExtension ||
+      opcode == spv::Op::OpConditionalExtensionINTEL) {
     ValidationState_t& _ = *(reinterpret_cast<ValidationState_t*>(user_data));
     RegisterExtension(_, inst);
     return SPV_SUCCESS;
@@ -115,10 +118,11 @@
   _.ComputeFunctionToEntryPointMapping();
   _.ComputeRecursiveEntryPoints();
 
-  if (_.entry_points().empty() && !_.HasCapability(spv::Capability::Linkage)) {
+  if (_.entry_points().empty() && !_.HasCapability(spv::Capability::Linkage) &&
+      !_.HasCapability(spv::Capability::GraphARM)) {
     return _.diag(SPV_ERROR_INVALID_BINARY, nullptr)
            << "No OpEntryPoint instruction was found. This is only allowed if "
-              "the Linkage capability is being used.";
+              "the Linkage or GraphARM capability is being used.";
   }
 
   for (const auto& entry_point : _.entry_points()) {
@@ -151,6 +155,16 @@
   return SPV_SUCCESS;
 }
 
+spv_result_t ValidateGraphEntryPoints(ValidationState_t& _) {
+  if (_.graph_entry_points().empty() &&
+      _.HasCapability(spv::Capability::GraphARM)) {
+    return _.diag(SPV_ERROR_INVALID_BINARY, nullptr)
+           << "No OpGraphEntryPointARM instruction was found but the GraphARM "
+              "capability is declared.";
+  }
+  return SPV_SUCCESS;
+}
+
 spv_result_t ValidateBinaryUsingContextAndValidationState(
     const spv_context_t& context, const uint32_t* words, const size_t num_words,
     spv_diagnostic* pDiagnostic, ValidationState_t* vstate) {
@@ -217,43 +231,59 @@
       // able to, briefly, de-const the instruction.
       Instruction* inst = const_cast<Instruction*>(&instruction);
 
-      if (inst->opcode() == spv::Op::OpEntryPoint) {
-        const auto entry_point = inst->GetOperandAs<uint32_t>(1);
-        const auto execution_model = inst->GetOperandAs<spv::ExecutionModel>(0);
-        const std::string desc_name = inst->GetOperandAs<std::string>(2);
+      if ((inst->opcode() == spv::Op::OpEntryPoint) ||
+          (inst->opcode() == spv::Op::OpConditionalEntryPointINTEL)) {
+        const int i_model = inst->opcode() == spv::Op::OpEntryPoint ? 0 : 1;
+        const int i_point = inst->opcode() == spv::Op::OpEntryPoint ? 1 : 2;
+        const int i_name = inst->opcode() == spv::Op::OpEntryPoint ? 2 : 3;
+        const int min_num_operands =
+            inst->opcode() == spv::Op::OpEntryPoint ? 3 : 4;
+
+        const auto entry_point = inst->GetOperandAs<uint32_t>(i_point);
+        const auto execution_model =
+            inst->GetOperandAs<spv::ExecutionModel>(i_model);
+        const std::string desc_name = inst->GetOperandAs<std::string>(i_name);
 
         ValidationState_t::EntryPointDescription desc;
         desc.name = desc_name;
 
         std::vector<uint32_t> interfaces;
-        for (size_t j = 3; j < inst->operands().size(); ++j)
+        for (size_t j = min_num_operands; j < inst->operands().size(); ++j)
           desc.interfaces.push_back(inst->word(inst->operand(j).offset));
 
         vstate->RegisterEntryPoint(entry_point, execution_model,
                                    std::move(desc));
 
-        if (visited_entry_points.size() > 0) {
-          for (const Instruction* check_inst : visited_entry_points) {
-            const auto check_execution_model =
-                check_inst->GetOperandAs<spv::ExecutionModel>(0);
-            const std::string check_name =
-                check_inst->GetOperandAs<std::string>(2);
+        if (inst->opcode() == spv::Op::OpEntryPoint) {
+          // conditional entry points are allowed to share the same name and
+          // exec mode
+          if (visited_entry_points.size() > 0) {
+            for (const Instruction* check_inst : visited_entry_points) {
+              const auto check_execution_model =
+                  check_inst->GetOperandAs<spv::ExecutionModel>(i_model);
+              const std::string check_name =
+                  check_inst->GetOperandAs<std::string>(i_name);
 
-            if (desc_name == check_name &&
-                execution_model == check_execution_model) {
-              return vstate->diag(SPV_ERROR_INVALID_DATA, inst)
-                     << "2 Entry points cannot share the same name and "
-                        "ExecutionMode.";
+              if (desc_name == check_name &&
+                  execution_model == check_execution_model) {
+                return vstate->diag(SPV_ERROR_INVALID_DATA, inst)
+                       << "2 Entry points cannot share the same name and "
+                          "ExecutionMode.";
+              }
             }
           }
+          visited_entry_points.push_back(inst);
         }
-        visited_entry_points.push_back(inst);
 
         has_mask_task_nv |= (execution_model == spv::ExecutionModel::TaskNV ||
                              execution_model == spv::ExecutionModel::MeshNV);
         has_mask_task_ext |= (execution_model == spv::ExecutionModel::TaskEXT ||
                               execution_model == spv::ExecutionModel::MeshEXT);
       }
+      if (inst->opcode() == spv::Op::OpGraphEntryPointARM) {
+        const auto graph = inst->GetOperandAs<uint32_t>(1);
+        vstate->RegisterGraphEntryPoint(graph);
+      }
       if (inst->opcode() == spv::Op::OpFunctionCall) {
         if (!vstate->in_function_body()) {
           return vstate->diag(SPV_ERROR_INVALID_LAYOUT, &instruction)
@@ -299,6 +329,10 @@
     return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr)
            << "Missing OpFunctionEnd at end of module.";
 
+  if (vstate->graph_definition_region() != kGraphDefinitionOutside)
+    return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr)
+           << "Missing OpGraphEndARM at end of module.";
+
   if (vstate->HasCapability(spv::Capability::BindlessTextureNV) &&
       !vstate->has_samplerimage_variable_address_mode_specified())
     return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr)
@@ -314,7 +348,7 @@
   if (auto error = ValidateForwardDecls(*vstate)) return error;
 
   // Calculate reachability after all the blocks are parsed, but early that it
-  // can be relied on in subsequent pases.
+  // can be relied on in subsequent passes.
   ReachabilityPass(*vstate);
 
   // ID usage needs be handled in its own iteration of the instructions,
@@ -356,17 +390,22 @@
     if (auto error = AtomicsPass(*vstate, &instruction)) return error;
     if (auto error = PrimitivesPass(*vstate, &instruction)) return error;
     if (auto error = BarriersPass(*vstate, &instruction)) return error;
-    // Group
+    if (auto error = DotProductPass(*vstate, &instruction)) return error;
+    if (auto error = GroupPass(*vstate, &instruction)) return error;
     // Device-Side Enqueue
-    // Pipe
+    if (auto error = PipePass(*vstate, &instruction)) return error;
     if (auto error = NonUniformPass(*vstate, &instruction)) return error;
 
     if (auto error = LiteralsPass(*vstate, &instruction)) return error;
     if (auto error = RayQueryPass(*vstate, &instruction)) return error;
     if (auto error = RayTracingPass(*vstate, &instruction)) return error;
     if (auto error = RayReorderNVPass(*vstate, &instruction)) return error;
+    if (auto error = RayReorderEXTPass(*vstate, &instruction)) return error;
     if (auto error = MeshShadingPass(*vstate, &instruction)) return error;
     if (auto error = TensorLayoutPass(*vstate, &instruction)) return error;
+    if (auto error = TensorPass(*vstate, &instruction)) return error;
+    if (auto error = GraphPass(*vstate, &instruction)) return error;
+    if (auto error = InvalidTypePass(*vstate, &instruction)) return error;
   }
 
   // Validate the preconditions involving adjacent instructions. e.g.
@@ -375,6 +414,7 @@
   if (auto error = ValidateAdjacency(*vstate)) return error;
 
   if (auto error = ValidateEntryPoints(*vstate)) return error;
+  if (auto error = ValidateGraphEntryPoints(*vstate)) return error;
   // CFG checks are performed after the binary has been parsed
   // and the CFGPass has collected information about the control flow
   if (auto error = PerformCfgChecks(*vstate)) return error;
@@ -392,6 +432,7 @@
     if (auto error = ValidateQCOMImageProcessingTextureUsages(*vstate, &inst))
       return error;
   }
+  if (auto error = ValidateLogicalPointers(*vstate)) return error;
 
   return SPV_SUCCESS;
 }
diff --git a/source/val/validate.h b/source/val/validate.h
index 5514ff7..717fb34 100644
--- a/source/val/validate.h
+++ b/source/val/validate.h
@@ -180,6 +180,12 @@
 /// Validates correctness of barrier instructions.
 spv_result_t BarriersPass(ValidationState_t& _, const Instruction* inst);
 
+/// Validates correctness of DotProduct instructions.
+spv_result_t DotProductPass(ValidationState_t& _, const Instruction* inst);
+
+/// Validates correctness of Group (Kernel) instructions.
+spv_result_t GroupPass(ValidationState_t& _, const Instruction* inst);
+
 /// Validates correctness of literal numbers.
 spv_result_t LiteralsPass(ValidationState_t& _, const Instruction* inst);
 
@@ -189,14 +195,17 @@
 /// Validates correctness of annotation instructions.
 spv_result_t AnnotationPass(ValidationState_t& _, const Instruction* inst);
 
+/// Validates correctness of pipe instructions.
+spv_result_t PipePass(ValidationState_t& _, const Instruction* inst);
+
 /// Validates correctness of non-uniform group instructions.
 spv_result_t NonUniformPass(ValidationState_t& _, const Instruction* inst);
 
 /// Validates correctness of debug instructions.
 spv_result_t DebugPass(ValidationState_t& _, const Instruction* inst);
 
-// Validates that capability declarations use operands allowed in the current
-// context.
+/// Validates that capability declarations use operands allowed in the current
+/// context.
 spv_result_t CapabilityPass(ValidationState_t& _, const Instruction* inst);
 
 /// Validates correctness of primitive instructions.
@@ -220,9 +229,21 @@
 /// Validates correctness of shader execution reorder instructions.
 spv_result_t RayReorderNVPass(ValidationState_t& _, const Instruction* inst);
 
+/// Validates correctness of shader execution reorder EXT instructions.
+spv_result_t RayReorderEXTPass(ValidationState_t& _, const Instruction* inst);
+
 /// Validates correctness of mesh shading instructions.
 spv_result_t MeshShadingPass(ValidationState_t& _, const Instruction* inst);
 
+/// Validates correctness of tensor instructions.
+spv_result_t TensorPass(ValidationState_t& _, const Instruction* inst);
+
+/// Validates correctness of graph instructions.
+spv_result_t GraphPass(ValidationState_t& _, const Instruction* inst);
+
+/// Validates correctness of certain special type instructions.
+spv_result_t InvalidTypePass(ValidationState_t& _, const Instruction* inst);
+
 /// Calculates the reachability of basic blocks.
 void ReachabilityPass(ValidationState_t& _);
 
@@ -250,6 +271,9 @@
 spv_result_t ValidateQCOMImageProcessingTextureUsages(ValidationState_t& _,
                                                       const Instruction* inst);
 
+/// Validates logical pointer restrictions.
+spv_result_t ValidateLogicalPointers(ValidationState_t& _);
+
 /// @brief Validate the ID's within a SPIR-V binary
 ///
 /// @param[in] pInstructions array of instructions
diff --git a/source/val/validate_annotation.cpp b/source/val/validate_annotation.cpp
index cf6f96b..b8ac6cb 100644
--- a/source/val/validate_annotation.cpp
+++ b/source/val/validate_annotation.cpp
@@ -1,4 +1,6 @@
 // Copyright (c) 2018 Google LLC.
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -30,6 +32,15 @@
     case spv::Decoration::AlignmentId:
     case spv::Decoration::MaxByteOffsetId:
     case spv::Decoration::HlslCounterBufferGOOGLE:
+    case spv::Decoration::NodeMaxPayloadsAMDX:
+    case spv::Decoration::NodeSharesPayloadLimitsWithAMDX:
+    case spv::Decoration::PayloadNodeArraySizeAMDX:
+    case spv::Decoration::PayloadNodeNameAMDX:
+    case spv::Decoration::PayloadNodeBaseIndexAMDX:
+    case spv::Decoration::ArrayStrideIdEXT:
+    case spv::Decoration::OffsetIdEXT:
+    case spv::Decoration::AliasScopeINTEL:
+    case spv::Decoration::NoAliasINTEL:
       return true;
     default:
       break;
@@ -58,6 +69,7 @@
     case spv::Decoration::Block:
     case spv::Decoration::BufferBlock:
     case spv::Decoration::ArrayStride:
+    case spv::Decoration::ArrayStrideIdEXT:
     case spv::Decoration::GLSLShared:
     case spv::Decoration::GLSLPacked:
     case spv::Decoration::CPacked:
@@ -107,8 +119,12 @@
   };
   switch (dec) {
     case spv::Decoration::SpecId:
-      if (!spvOpcodeIsScalarSpecConstant(target->opcode())) {
-        return fail(0) << "must be a scalar specialization constant";
+      if (target->opcode() != spv::Op::OpSpecConstantTrue &&
+          target->opcode() != spv::Op::OpSpecConstantFalse &&
+          target->opcode() != spv::Op::OpSpecConstant &&
+          target->opcode() != spv::Op::OpSpecConstantDataKHR) {
+        return fail(0) << "must be OpSpecConstantTrue, OpSpecConstantFalse, "
+                          "OpSpecConstant, or OpSpecConstantDataKHR";
       }
       break;
     case spv::Decoration::Block:
@@ -163,10 +179,12 @@
     case spv::Decoration::Stream:
     case spv::Decoration::RestrictPointer:
     case spv::Decoration::AliasedPointer:
+    case spv::Decoration::PerPrimitiveEXT:
       if (target->opcode() != spv::Op::OpVariable &&
           target->opcode() != spv::Op::OpUntypedVariableKHR &&
           target->opcode() != spv::Op::OpFunctionParameter &&
-          target->opcode() != spv::Op::OpRawAccessChainNV) {
+          target->opcode() != spv::Op::OpRawAccessChainNV &&
+          target->opcode() != spv::Op::OpBufferPointerEXT) {
         return fail(0) << "must be a memory object declaration";
       }
       if (!_.IsPointerType(target->type_id())) {
@@ -209,6 +227,7 @@
             sc != spv::StorageClass::IncomingCallableDataKHR &&
             sc != spv::StorageClass::ShaderRecordBufferKHR &&
             sc != spv::StorageClass::HitObjectAttributeNV &&
+            sc != spv::StorageClass::HitObjectAttributeEXT &&
             sc != spv::StorageClass::TileImageEXT) {
           return _.diag(SPV_ERROR_INVALID_ID, target)
                  << _.VkErrorID(6672) << _.SpvDecorationString(dec)
@@ -225,7 +244,8 @@
       case spv::Decoration::DescriptorSet:
         if (sc != spv::StorageClass::StorageBuffer &&
             sc != spv::StorageClass::Uniform &&
-            sc != spv::StorageClass::UniformConstant) {
+            sc != spv::StorageClass::UniformConstant &&
+            sc != spv::StorageClass::TileAttachmentQCOM) {
           return fail(6491) << "must be in the StorageBuffer, Uniform, or "
                                "UniformConstant storage class";
         }
@@ -324,6 +344,14 @@
 }
 
 spv_result_t ValidateDecorateId(ValidationState_t& _, const Instruction* inst) {
+  const auto target_id = inst->GetOperandAs<uint32_t>(0);
+  const auto target = _.FindDef(target_id);
+  if (target && spv::Op::OpDecorationGroup == target->opcode()) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "OpMemberDecorate Target <id> " << _.getIdName(target_id)
+           << " must not be an OpDecorationGroup instruction.";
+  }
+
   const auto decoration = inst->GetOperandAs<spv::Decoration>(1);
   if (!DecorationTakesIdParameters(decoration)) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
@@ -331,6 +359,56 @@
               "OpDecorateId";
   }
 
+  if (decoration == spv::Decoration::ArrayStrideIdEXT) {
+    if (target->opcode() != spv::Op::OpTypeArray &&
+        target->opcode() != spv::Op::OpTypeRuntimeArray) {
+      // ArrayStrideIdEXT is suppose to identical to ArrayStride, which would
+      // allow it to be a OpTypePointer/OpTypeUntypedPointerKHR
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "ArrayStrideIdEXT decoration must only be applied to array "
+                "types.";
+    } else {
+      const uint32_t operand_id = inst->GetOperandAs<uint32_t>(2);
+      if (!_.IsIntScalarType(_.GetTypeId(operand_id), 32)) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "ArrayStrideIdEXT extra operand must be a 32-bit int "
+                  "scalar type.";
+      }
+
+      // Even if spec constant, validation layers will test when frozen
+      uint64_t stride_value = 0;
+      if (_.EvalConstantValUint64(operand_id, &stride_value)) {
+        if (stride_value == 0) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "ArrayStrideIdEXT contains a stride of zero.";
+        }
+      }
+
+      // Strip array and should be the descriptor type
+      const uint32_t element_type =
+          _.FindDef(target_id)->GetOperandAs<uint32_t>(1);
+      if (!_.IsDescriptorType(element_type)) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "ArrayStrideIdEXT decoration must only be applied to"
+               << " array type containing a Descriptor type.";
+      }
+    }
+  }
+
+  for (uint32_t i = 2; i < inst->operands().size(); ++i) {
+    const auto param_id = inst->GetOperandAs<uint32_t>(i);
+    const auto param = _.FindDef(param_id);
+
+    // Both target and param are elements of ordered_instructions we can
+    // determine their relative positions in the SPIR-V module by comparing
+    // pointers.
+    if (target <= param) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "Parameter <ID> " << _.getIdName(param_id)
+             << " must appear earlier in the binary than the target";
+    }
+  }
+
   // No member decorations take id parameters, so we don't bother checking if
   // we are using a member only decoration here.
 
@@ -343,24 +421,70 @@
                                     const Instruction* inst) {
   const auto struct_type_id = inst->GetOperandAs<uint32_t>(0);
   const auto struct_type = _.FindDef(struct_type_id);
+  const bool is_mem_dec_id_inst =
+      (inst->opcode() == spv::Op::OpMemberDecorateIdEXT);
   if (!struct_type || spv::Op::OpTypeStruct != struct_type->opcode()) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "OpMemberDecorate Structure type <id> "
-           << _.getIdName(struct_type_id) << " is not a struct type.";
+           << (is_mem_dec_id_inst ? "OpMemberDecorateIdEXT"
+                                  : "OpMemberDecorate")
+           << " Structure type <id> " << _.getIdName(struct_type_id)
+           << " is not a struct type.";
   }
   const auto member = inst->GetOperandAs<uint32_t>(1);
   const auto member_count =
       static_cast<uint32_t>(struct_type->words().size() - 2);
   if (member_count <= member) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "Index " << member
-           << " provided in OpMemberDecorate for struct <id> "
-           << _.getIdName(struct_type_id)
+           << "Index " << member << " provided in "
+           << (is_mem_dec_id_inst ? "OpMemberDecorateIdEXT"
+                                  : "OpMemberDecorate")
+           << " for struct <id> " << _.getIdName(struct_type_id)
            << " is out of bounds. The structure has " << member_count
            << " members. Largest valid index is " << member_count - 1 << ".";
   }
 
   const auto decoration = inst->GetOperandAs<spv::Decoration>(2);
+  if (is_mem_dec_id_inst) {
+    if (decoration != spv::Decoration::OffsetIdEXT) {
+      if (decoration == spv::Decoration::ArrayStrideIdEXT) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "ArrayStrideIdEXT could only be directly applied"
+               << " to array type using OpDecorateId.";
+      } else {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "Decoration operand could only be OffsetIdEXT.";
+      }
+    }
+
+    const auto is_descriptor_type = [&_](const Instruction* type_inst) {
+      return _.IsDescriptorType(type_inst->opcode());
+    };
+
+    // recursively scans the struct to find if anything has a descriptor type,
+    // must be at least 1
+    if (decoration == spv::Decoration::OffsetIdEXT) {
+      const uint32_t operand_id = inst->GetOperandAs<uint32_t>(3);
+      if (!_.IsIntScalarType(_.GetTypeId(operand_id), 32)) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "OffsetIdEXT extra operand must be a 32-bit int scalar type.";
+      }
+      if (!_.ContainsType(struct_type_id, is_descriptor_type, true)) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "OffsetIdEXT decoration in MemberDecorateIdEXT must only be "
+                  "applied to members of structs where the struct contains "
+                  "descriptor types.";
+      }
+    }
+
+    for (uint32_t elem_idx = 3; elem_idx < inst->operands().size();
+         elem_idx++) {
+      if (_.FindDef(inst->GetOperandAs<uint32_t>(elem_idx)) > struct_type) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "All <id> Extra Operands must appear before Structure Type.";
+      }
+    }
+  }
+
   if (IsNotMemberDecoration(decoration)) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << _.SpvDecorationString(decoration)
@@ -379,8 +503,7 @@
     if (use->opcode() != spv::Op::OpDecorate &&
         use->opcode() != spv::Op::OpGroupDecorate &&
         use->opcode() != spv::Op::OpGroupMemberDecorate &&
-        use->opcode() != spv::Op::OpName &&
-        use->opcode() != spv::Op::OpDecorateId && !use->IsNonSemantic()) {
+        use->opcode() != spv::Op::OpName && !use->IsNonSemantic()) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "Result id of OpDecorationGroup can only "
              << "be targeted by OpName, OpGroupDecorate, "
@@ -466,7 +589,8 @@
       _.RegisterDecorationForId(target_id, Decoration(dec_type, dec_params));
       break;
     }
-    case spv::Op::OpMemberDecorate: {
+    case spv::Op::OpMemberDecorate:
+    case spv::Op::OpMemberDecorateIdEXT: {
       const uint32_t struct_id = inst->word(1);
       const uint32_t index = inst->word(2);
       const spv::Decoration dec_type =
@@ -537,6 +661,7 @@
     // TODO(dneto): spv::Op::OpDecorateStringGOOGLE
     // See https://github.com/KhronosGroup/SPIRV-Tools/issues/2253
     case spv::Op::OpMemberDecorate:
+    case spv::Op::OpMemberDecorateIdEXT:
       if (auto error = ValidateMemberDecorate(_, inst)) return error;
       break;
     case spv::Op::OpDecorationGroup:
diff --git a/source/val/validate_arithmetics.cpp b/source/val/validate_arithmetics.cpp
index 8b0049c..03697c6 100644
--- a/source/val/validate_arithmetics.cpp
+++ b/source/val/validate_arithmetics.cpp
@@ -24,776 +24,895 @@
 namespace spvtools {
 namespace val {
 
-// Validates correctness of arithmetic instructions.
-spv_result_t ArithmeticsPass(ValidationState_t& _, const Instruction* inst) {
+spv_result_t ValidateFloat(ValidationState_t& _, const Instruction* inst,
+                           uint32_t starting_index = 2) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  bool supportsCoopMat =
+      (opcode != spv::Op::OpFMul && opcode != spv::Op::OpFRem &&
+       opcode != spv::Op::OpFMod);
+  bool supportsCoopVec =
+      (opcode != spv::Op::OpFRem && opcode != spv::Op::OpFMod);
+  if (!_.IsFloatScalarType(result_type) && !_.IsFloatVectorType(result_type) &&
+      !(supportsCoopMat && _.IsFloatCooperativeMatrixType(result_type)) &&
+      !(opcode == spv::Op::OpFMul &&
+        _.IsCooperativeMatrixKHRType(result_type) &&
+        _.IsFloatCooperativeMatrixType(result_type)) &&
+      !(supportsCoopVec && _.IsFloatCooperativeVectorNVType(result_type)))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected floating scalar or vector type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  for (size_t operand_index = starting_index;
+       operand_index < inst->operands().size(); ++operand_index) {
+    if (supportsCoopVec && _.IsCooperativeVectorNVType(result_type)) {
+      const uint32_t type_id = _.GetOperandTypeId(inst, operand_index);
+      if (!_.IsCooperativeVectorNVType(type_id)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected arithmetic operands to be of Result Type: "
+               << spvOpcodeString(opcode) << " operand index " << operand_index;
+      }
+      spv_result_t ret =
+          _.CooperativeVectorDimensionsMatch(inst, type_id, result_type);
+      if (ret != SPV_SUCCESS) return ret;
+    } else if (supportsCoopMat && _.IsCooperativeMatrixKHRType(result_type)) {
+      const uint32_t type_id = _.GetOperandTypeId(inst, operand_index);
+      if (!_.IsCooperativeMatrixKHRType(type_id) ||
+          !_.IsFloatCooperativeMatrixType(type_id)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected arithmetic operands to be of Result Type: "
+               << spvOpcodeString(opcode) << " operand index " << operand_index;
+      }
+      spv_result_t ret =
+          _.CooperativeMatrixShapesMatch(inst, result_type, type_id, false);
+      if (ret != SPV_SUCCESS) return ret;
+    } else if (_.GetOperandTypeId(inst, operand_index) != result_type)
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected arithmetic operands to be of Result Type: "
+             << spvOpcodeString(opcode) << " operand index " << operand_index;
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateUnsignedInt(ValidationState_t& _, const Instruction* inst,
+                                 uint32_t starting_index = 2) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  bool supportsCoopMat = (opcode == spv::Op::OpUDiv);
+  bool supportsCoopVec = (opcode == spv::Op::OpUDiv);
+  if (!_.IsUnsignedIntScalarType(result_type) &&
+      !_.IsUnsignedIntVectorType(result_type) &&
+      !(supportsCoopMat && _.IsUnsignedIntCooperativeMatrixType(result_type)) &&
+      !(supportsCoopVec && _.IsUnsignedIntCooperativeVectorNVType(result_type)))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected unsigned int scalar or vector type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  for (size_t operand_index = starting_index;
+       operand_index < inst->operands().size(); ++operand_index) {
+    if (supportsCoopVec && _.IsCooperativeVectorNVType(result_type)) {
+      const uint32_t type_id = _.GetOperandTypeId(inst, operand_index);
+      if (!_.IsCooperativeVectorNVType(type_id)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected arithmetic operands to be of Result Type: "
+               << spvOpcodeString(opcode) << " operand index " << operand_index;
+      }
+      spv_result_t ret =
+          _.CooperativeVectorDimensionsMatch(inst, type_id, result_type);
+      if (ret != SPV_SUCCESS) return ret;
+    } else if (supportsCoopMat && _.IsCooperativeMatrixKHRType(result_type)) {
+      const uint32_t type_id = _.GetOperandTypeId(inst, operand_index);
+      if (!_.IsCooperativeMatrixKHRType(type_id) ||
+          !_.IsUnsignedIntCooperativeMatrixType(type_id)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected arithmetic operands to be of Result Type: "
+               << spvOpcodeString(opcode) << " operand index " << operand_index;
+      }
+      spv_result_t ret =
+          _.CooperativeMatrixShapesMatch(inst, result_type, type_id, false);
+      if (ret != SPV_SUCCESS) return ret;
+    } else if (_.GetOperandTypeId(inst, operand_index) != result_type)
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected arithmetic operands to be of Result Type: "
+             << spvOpcodeString(opcode) << " operand index " << operand_index;
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateSignedInt(ValidationState_t& _, const Instruction* inst,
+                               uint32_t starting_index = 2) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  bool supportsCoopMat =
+      (opcode != spv::Op::OpIMul && opcode != spv::Op::OpSRem &&
+       opcode != spv::Op::OpSMod);
+  bool supportsCoopVec =
+      (opcode != spv::Op::OpSRem && opcode != spv::Op::OpSMod);
+  if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) &&
+      !(supportsCoopMat && _.IsIntCooperativeMatrixType(result_type)) &&
+      !(opcode == spv::Op::OpIMul &&
+        _.IsCooperativeMatrixKHRType(result_type) &&
+        _.IsIntCooperativeMatrixType(result_type)) &&
+      !(supportsCoopVec && _.IsIntCooperativeVectorNVType(result_type)))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected int scalar or vector type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t dimension = _.GetDimension(result_type);
+  const uint32_t bit_width = _.GetBitWidth(result_type);
+
+  for (size_t operand_index = starting_index;
+       operand_index < inst->operands().size(); ++operand_index) {
+    const uint32_t type_id = _.GetOperandTypeId(inst, operand_index);
+
+    if (supportsCoopVec && _.IsCooperativeVectorNVType(result_type)) {
+      if (!_.IsCooperativeVectorNVType(type_id)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected arithmetic operands to be of Result Type: "
+               << spvOpcodeString(opcode) << " operand index " << operand_index;
+      }
+      spv_result_t ret =
+          _.CooperativeVectorDimensionsMatch(inst, type_id, result_type);
+      if (ret != SPV_SUCCESS) return ret;
+    }
+
+    if (supportsCoopMat && _.IsCooperativeMatrixKHRType(result_type)) {
+      if (!_.IsCooperativeMatrixKHRType(type_id) ||
+          !_.IsIntCooperativeMatrixType(type_id)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected arithmetic operands to be of Result Type: "
+               << spvOpcodeString(opcode) << " operand index " << operand_index;
+      }
+      spv_result_t ret =
+          _.CooperativeMatrixShapesMatch(inst, result_type, type_id, false);
+      if (ret != SPV_SUCCESS) return ret;
+    }
+
+    if (!type_id ||
+        (!_.IsIntScalarType(type_id) && !_.IsIntVectorType(type_id) &&
+         !(supportsCoopMat && _.IsIntCooperativeMatrixType(result_type)) &&
+         !(opcode == spv::Op::OpIMul &&
+           _.IsCooperativeMatrixKHRType(result_type) &&
+           _.IsIntCooperativeMatrixType(result_type)) &&
+         !(supportsCoopVec && _.IsIntCooperativeVectorNVType(result_type))))
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected int scalar or vector type as operand: "
+             << spvOpcodeString(opcode) << " operand index " << operand_index;
+
+    if (_.GetDimension(type_id) != dimension)
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected arithmetic operands to have the same dimension "
+             << "as Result Type: " << spvOpcodeString(opcode)
+             << " operand index " << operand_index;
+
+    if (_.GetBitWidth(type_id) != bit_width)
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected arithmetic operands to have the same bit width "
+             << "as Result Type: " << spvOpcodeString(opcode)
+             << " operand index " << operand_index;
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateDot(ValidationState_t& _, const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsFloatScalarType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected float scalar type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  if (_.IsBfloat16ScalarType(result_type)) {
+    if (!_.HasCapability(spv::Capability::BFloat16DotProductKHR)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "OpDot Result Type <id> " << _.getIdName(result_type)
+             << "requires BFloat16DotProductKHR be declared.";
+    }
+  }
+
+  uint32_t first_vector_num_components = 0;
+
+  for (size_t operand_index = 2; operand_index < inst->operands().size();
+       ++operand_index) {
+    const uint32_t type_id = _.GetOperandTypeId(inst, operand_index);
+
+    if (!type_id || !_.IsFloatVectorType(type_id))
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected float vector as operand: " << spvOpcodeString(opcode)
+             << " operand index " << operand_index;
+
+    const uint32_t component_type = _.GetComponentType(type_id);
+    if (component_type != result_type)
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected component type to be equal to Result Type: "
+             << spvOpcodeString(opcode) << " operand index " << operand_index;
+
+    const uint32_t num_components = _.GetDimension(type_id);
+    if (operand_index == 2) {
+      first_vector_num_components = num_components;
+    } else if (num_components != first_vector_num_components) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected operands to have the same number of components: "
+             << spvOpcodeString(opcode);
+    }
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateVectorTimesScalar(ValidationState_t& _,
+                                       const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsFloatVectorType(result_type) &&
+      !_.IsFloatCooperativeVectorNVType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected float vector type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t vector_type_id = _.GetOperandTypeId(inst, 2);
+  if (result_type != vector_type_id)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected vector operand type to be equal to Result Type: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t component_type = _.GetComponentType(vector_type_id);
+
+  const uint32_t scalar_type_id = _.GetOperandTypeId(inst, 3);
+  if (component_type != scalar_type_id)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected scalar operand type to be equal to the component "
+           << "type of the vector operand: " << spvOpcodeString(opcode);
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateMatrixTimesScalar(ValidationState_t& _,
+                                       const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsFloatMatrixType(result_type) &&
+      !(_.IsCooperativeMatrixType(result_type)))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected float matrix type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 2);
+  if (result_type != matrix_type_id)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected matrix operand type to be equal to Result Type: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t component_type = _.GetComponentType(matrix_type_id);
+
+  const uint32_t scalar_type_id = _.GetOperandTypeId(inst, 3);
+  if (component_type != scalar_type_id)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected scalar operand type to be equal to the component "
+           << "type of the matrix operand: " << spvOpcodeString(opcode);
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateVectorTimesMatrix(ValidationState_t& _,
+                                       const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  const uint32_t vector_type_id = _.GetOperandTypeId(inst, 2);
+  const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 3);
+
+  if (!_.IsFloatVectorType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected float vector type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t res_component_type = _.GetComponentType(result_type);
+
+  if (!vector_type_id || !_.IsFloatVectorType(vector_type_id))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected float vector type as left operand: "
+           << spvOpcodeString(opcode);
+
+  if (res_component_type != _.GetComponentType(vector_type_id))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected component types of Result Type and vector to be "
+           << "equal: " << spvOpcodeString(opcode);
+
+  uint32_t matrix_num_rows = 0;
+  uint32_t matrix_num_cols = 0;
+  uint32_t matrix_col_type = 0;
+  uint32_t matrix_component_type = 0;
+  if (!_.GetMatrixTypeInfo(matrix_type_id, &matrix_num_rows, &matrix_num_cols,
+                           &matrix_col_type, &matrix_component_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected float matrix type as right operand: "
+           << spvOpcodeString(opcode);
+
+  if (res_component_type != matrix_component_type)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected component types of Result Type and matrix to be "
+           << "equal: " << spvOpcodeString(opcode);
+
+  if (matrix_num_cols != _.GetDimension(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected number of columns of the matrix to be equal to "
+           << "Result Type vector size: " << spvOpcodeString(opcode);
+
+  if (matrix_num_rows != _.GetDimension(vector_type_id))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected number of rows of the matrix to be equal to the "
+           << "vector operand size: " << spvOpcodeString(opcode);
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateMatrixTimesVector(ValidationState_t& _,
+                                       const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 2);
+  const uint32_t vector_type_id = _.GetOperandTypeId(inst, 3);
+
+  if (!_.IsFloatVectorType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected float vector type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  uint32_t matrix_num_rows = 0;
+  uint32_t matrix_num_cols = 0;
+  uint32_t matrix_col_type = 0;
+  uint32_t matrix_component_type = 0;
+  if (!_.GetMatrixTypeInfo(matrix_type_id, &matrix_num_rows, &matrix_num_cols,
+                           &matrix_col_type, &matrix_component_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected float matrix type as left operand: "
+           << spvOpcodeString(opcode);
+
+  if (result_type != matrix_col_type)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected column type of the matrix to be equal to Result "
+              "Type: "
+           << spvOpcodeString(opcode);
+
+  if (!vector_type_id || !_.IsFloatVectorType(vector_type_id))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected float vector type as right operand: "
+           << spvOpcodeString(opcode);
+
+  if (matrix_component_type != _.GetComponentType(vector_type_id))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected component types of the operands to be equal: "
+           << spvOpcodeString(opcode);
+
+  if (matrix_num_cols != _.GetDimension(vector_type_id))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected number of columns of the matrix to be equal to the "
+           << "vector size: " << spvOpcodeString(opcode);
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateMatrixTimesMatrix(ValidationState_t& _,
+                                       const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  const uint32_t left_type_id = _.GetOperandTypeId(inst, 2);
+  const uint32_t right_type_id = _.GetOperandTypeId(inst, 3);
+
+  uint32_t res_num_rows = 0;
+  uint32_t res_num_cols = 0;
+  uint32_t res_col_type = 0;
+  uint32_t res_component_type = 0;
+  if (!_.GetMatrixTypeInfo(result_type, &res_num_rows, &res_num_cols,
+                           &res_col_type, &res_component_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected float matrix type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  uint32_t left_num_rows = 0;
+  uint32_t left_num_cols = 0;
+  uint32_t left_col_type = 0;
+  uint32_t left_component_type = 0;
+  if (!_.GetMatrixTypeInfo(left_type_id, &left_num_rows, &left_num_cols,
+                           &left_col_type, &left_component_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected float matrix type as left operand: "
+           << spvOpcodeString(opcode);
+
+  uint32_t right_num_rows = 0;
+  uint32_t right_num_cols = 0;
+  uint32_t right_col_type = 0;
+  uint32_t right_component_type = 0;
+  if (!_.GetMatrixTypeInfo(right_type_id, &right_num_rows, &right_num_cols,
+                           &right_col_type, &right_component_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected float matrix type as right operand: "
+           << spvOpcodeString(opcode);
+
+  if (!_.IsFloatScalarType(res_component_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected float matrix type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  if (res_col_type != left_col_type)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected column types of Result Type and left matrix to be "
+           << "equal: " << spvOpcodeString(opcode);
+
+  if (res_component_type != right_component_type)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected component types of Result Type and right matrix to "
+              "be "
+           << "equal: " << spvOpcodeString(opcode);
+
+  if (res_num_cols != right_num_cols)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected number of columns of Result Type and right matrix "
+              "to "
+           << "be equal: " << spvOpcodeString(opcode);
+
+  if (left_num_cols != right_num_rows)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected number of columns of left matrix and number of "
+              "rows "
+           << "of right matrix to be equal: " << spvOpcodeString(opcode);
+
+  assert(left_num_rows == res_num_rows);
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateOuterProduct(ValidationState_t& _,
+                                  const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  const uint32_t left_type_id = _.GetOperandTypeId(inst, 2);
+  const uint32_t right_type_id = _.GetOperandTypeId(inst, 3);
+
+  uint32_t res_num_rows = 0;
+  uint32_t res_num_cols = 0;
+  uint32_t res_col_type = 0;
+  uint32_t res_component_type = 0;
+  if (!_.GetMatrixTypeInfo(result_type, &res_num_rows, &res_num_cols,
+                           &res_col_type, &res_component_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected float matrix type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  if (left_type_id != res_col_type)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected column type of Result Type to be equal to the type "
+           << "of the left operand: " << spvOpcodeString(opcode);
+
+  if (!right_type_id || !_.IsFloatVectorType(right_type_id))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected float vector type as right operand: "
+           << spvOpcodeString(opcode);
+
+  if (res_component_type != _.GetComponentType(right_type_id))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected component types of the operands to be equal: "
+           << spvOpcodeString(opcode);
+
+  if (res_num_cols != _.GetDimension(right_type_id))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected number of columns of the matrix to be equal to the "
+           << "vector size of the right operand: " << spvOpcodeString(opcode);
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateExtendedCarry(ValidationState_t& _,
+                                   const Instruction* inst) {
   const spv::Op opcode = inst->opcode();
   const uint32_t result_type = inst->type_id();
 
-  switch (opcode) {
+  std::vector<uint32_t> result_types;
+  if (!_.GetStructMemberTypes(result_type, &result_types))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected a struct as Result Type: " << spvOpcodeString(opcode);
+
+  if (result_types.size() != 2)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Result Type struct to have two members: "
+           << spvOpcodeString(opcode);
+
+  if (opcode == spv::Op::OpSMulExtended) {
+    if (!_.IsIntScalarType(result_types[0]) &&
+        !_.IsIntVectorType(result_types[0]))
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected Result Type struct member types to be integer "
+                "scalar "
+             << "or vector: " << spvOpcodeString(opcode);
+  } else {
+    if (!_.IsUnsignedIntScalarType(result_types[0]) &&
+        !_.IsUnsignedIntVectorType(result_types[0]))
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected Result Type struct member types to be unsigned "
+             << "integer scalar or vector: " << spvOpcodeString(opcode);
+  }
+
+  if (result_types[0] != result_types[1])
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Result Type struct member types to be identical: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t left_type_id = _.GetOperandTypeId(inst, 2);
+  const uint32_t right_type_id = _.GetOperandTypeId(inst, 3);
+
+  if (left_type_id != result_types[0] || right_type_id != result_types[0])
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected both operands to be of Result Type member type: "
+           << spvOpcodeString(opcode);
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateCooperativeMatrixMulAddNV(ValidationState_t& _,
+                                               const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t D_type_id = _.GetOperandTypeId(inst, 1);
+  const uint32_t A_type_id = _.GetOperandTypeId(inst, 2);
+  const uint32_t B_type_id = _.GetOperandTypeId(inst, 3);
+  const uint32_t C_type_id = _.GetOperandTypeId(inst, 4);
+
+  if (!_.IsCooperativeMatrixNVType(A_type_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected cooperative matrix type as A Type: "
+           << spvOpcodeString(opcode);
+  }
+  if (!_.IsCooperativeMatrixNVType(B_type_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected cooperative matrix type as B Type: "
+           << spvOpcodeString(opcode);
+  }
+  if (!_.IsCooperativeMatrixNVType(C_type_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected cooperative matrix type as C Type: "
+           << spvOpcodeString(opcode);
+  }
+  if (!_.IsCooperativeMatrixNVType(D_type_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected cooperative matrix type as Result Type: "
+           << spvOpcodeString(opcode);
+  }
+
+  const auto A = _.FindDef(A_type_id);
+  const auto B = _.FindDef(B_type_id);
+  const auto C = _.FindDef(C_type_id);
+  const auto D = _.FindDef(D_type_id);
+
+  std::tuple<bool, bool, uint32_t> A_scope, B_scope, C_scope, D_scope, A_rows,
+      B_rows, C_rows, D_rows, A_cols, B_cols, C_cols, D_cols;
+
+  A_scope = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(2));
+  B_scope = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(2));
+  C_scope = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(2));
+  D_scope = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(2));
+
+  A_rows = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(3));
+  B_rows = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(3));
+  C_rows = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(3));
+  D_rows = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(3));
+
+  A_cols = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(4));
+  B_cols = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(4));
+  C_cols = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(4));
+  D_cols = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(4));
+
+  const auto notEqual = [](std::tuple<bool, bool, uint32_t> X,
+                           std::tuple<bool, bool, uint32_t> Y) {
+    return (std::get<1>(X) && std::get<1>(Y) &&
+            std::get<2>(X) != std::get<2>(Y));
+  };
+
+  if (notEqual(A_scope, B_scope) || notEqual(A_scope, C_scope) ||
+      notEqual(A_scope, D_scope) || notEqual(B_scope, C_scope) ||
+      notEqual(B_scope, D_scope) || notEqual(C_scope, D_scope)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Cooperative matrix scopes must match: "
+           << spvOpcodeString(opcode);
+  }
+
+  if (notEqual(A_rows, C_rows) || notEqual(A_rows, D_rows) ||
+      notEqual(C_rows, D_rows)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Cooperative matrix 'M' mismatch: " << spvOpcodeString(opcode);
+  }
+
+  if (notEqual(B_cols, C_cols) || notEqual(B_cols, D_cols) ||
+      notEqual(C_cols, D_cols)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Cooperative matrix 'N' mismatch: " << spvOpcodeString(opcode);
+  }
+
+  if (notEqual(A_cols, B_rows)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Cooperative matrix 'K' mismatch: " << spvOpcodeString(opcode);
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateCooperativeMatrixMulAddKHR(ValidationState_t& _,
+                                                const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t D_type_id = _.GetOperandTypeId(inst, 1);
+  const uint32_t A_type_id = _.GetOperandTypeId(inst, 2);
+  const uint32_t B_type_id = _.GetOperandTypeId(inst, 3);
+  const uint32_t C_type_id = _.GetOperandTypeId(inst, 4);
+
+  if (!_.IsCooperativeMatrixAType(A_type_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Cooperative matrix type must be A Type: "
+           << spvOpcodeString(opcode);
+  }
+  if (!_.IsCooperativeMatrixBType(B_type_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Cooperative matrix type must be B Type: "
+           << spvOpcodeString(opcode);
+  }
+  if (!_.IsCooperativeMatrixAccType(C_type_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Cooperative matrix type must be Accumulator Type: "
+           << spvOpcodeString(opcode);
+  }
+  if (!_.IsCooperativeMatrixKHRType(D_type_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected cooperative matrix type as Result Type: "
+           << spvOpcodeString(opcode);
+  }
+
+  const auto A = _.FindDef(A_type_id);
+  const auto B = _.FindDef(B_type_id);
+  const auto C = _.FindDef(C_type_id);
+  const auto D = _.FindDef(D_type_id);
+
+  std::tuple<bool, bool, uint32_t> A_scope, B_scope, C_scope, D_scope, A_rows,
+      B_rows, C_rows, D_rows, A_cols, B_cols, C_cols, D_cols;
+
+  A_scope = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(2));
+  B_scope = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(2));
+  C_scope = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(2));
+  D_scope = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(2));
+
+  A_rows = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(3));
+  B_rows = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(3));
+  C_rows = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(3));
+  D_rows = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(3));
+
+  A_cols = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(4));
+  B_cols = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(4));
+  C_cols = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(4));
+  D_cols = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(4));
+
+  const auto notEqual = [](std::tuple<bool, bool, uint32_t> X,
+                           std::tuple<bool, bool, uint32_t> Y) {
+    return (std::get<1>(X) && std::get<1>(Y) &&
+            std::get<2>(X) != std::get<2>(Y));
+  };
+
+  if (notEqual(A_scope, B_scope) || notEqual(A_scope, C_scope) ||
+      notEqual(A_scope, D_scope) || notEqual(B_scope, C_scope) ||
+      notEqual(B_scope, D_scope) || notEqual(C_scope, D_scope)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Cooperative matrix scopes must match: "
+           << spvOpcodeString(opcode);
+  }
+
+  if (notEqual(A_rows, C_rows) || notEqual(A_rows, D_rows) ||
+      notEqual(C_rows, D_rows)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Cooperative matrix 'M' mismatch: " << spvOpcodeString(opcode);
+  }
+
+  if (notEqual(B_cols, C_cols) || notEqual(B_cols, D_cols) ||
+      notEqual(C_cols, D_cols)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Cooperative matrix 'N' mismatch: " << spvOpcodeString(opcode);
+  }
+
+  if (notEqual(A_cols, B_rows)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Cooperative matrix 'K' mismatch: " << spvOpcodeString(opcode);
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateCooperativeMatrixReduceNV(ValidationState_t& _,
+                                               const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsCooperativeMatrixKHRType(result_type)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result Type must be a cooperative matrix type: "
+           << spvOpcodeString(opcode);
+  }
+
+  const auto result_comp_type_id =
+      _.FindDef(result_type)->GetOperandAs<uint32_t>(1);
+
+  const auto matrix_id = inst->GetOperandAs<uint32_t>(2);
+  const auto matrix = _.FindDef(matrix_id);
+  const auto matrix_type_id = matrix->type_id();
+  if (!_.IsCooperativeMatrixKHRType(matrix_type_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Matrix must have a cooperative matrix type: "
+           << spvOpcodeString(opcode);
+  }
+  const auto matrix_type = _.FindDef(matrix_type_id);
+  const auto matrix_comp_type_id = matrix_type->GetOperandAs<uint32_t>(1);
+  if (matrix_comp_type_id != result_comp_type_id) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result Type and Matrix type must have the same component "
+              "type: "
+           << spvOpcodeString(opcode);
+  }
+  if (_.FindDef(result_type)->GetOperandAs<uint32_t>(2) !=
+      matrix_type->GetOperandAs<uint32_t>(2)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result Type and Matrix type must have the same scope: "
+           << spvOpcodeString(opcode);
+  }
+
+  if (!_.IsCooperativeMatrixAccType(result_type)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result Type must have UseAccumulator: "
+           << spvOpcodeString(opcode);
+  }
+  if (!_.IsCooperativeMatrixAccType(matrix_type_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Matrix type must have UseAccumulator: "
+           << spvOpcodeString(opcode);
+  }
+
+  const auto reduce_value = inst->GetOperandAs<uint32_t>(3);
+
+  if ((reduce_value &
+       uint32_t(
+           spv::CooperativeMatrixReduceMask::CooperativeMatrixReduce2x2)) &&
+      (reduce_value & uint32_t(spv::CooperativeMatrixReduceMask::Row |
+                               spv::CooperativeMatrixReduceMask::Column))) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Reduce 2x2 must not be used with Row/Column: "
+           << spvOpcodeString(opcode);
+  }
+
+  std::tuple<bool, bool, uint32_t> result_rows, result_cols, matrix_rows,
+      matrix_cols;
+  result_rows =
+      _.EvalInt32IfConst(_.FindDef(result_type)->GetOperandAs<uint32_t>(3));
+  result_cols =
+      _.EvalInt32IfConst(_.FindDef(result_type)->GetOperandAs<uint32_t>(4));
+  matrix_rows = _.EvalInt32IfConst(matrix_type->GetOperandAs<uint32_t>(3));
+  matrix_cols = _.EvalInt32IfConst(matrix_type->GetOperandAs<uint32_t>(4));
+
+  if (reduce_value &
+      uint32_t(spv::CooperativeMatrixReduceMask::CooperativeMatrixReduce2x2)) {
+    if (std::get<1>(result_rows) && std::get<1>(result_cols) &&
+        std::get<1>(matrix_rows) && std::get<1>(matrix_cols) &&
+        (std::get<2>(result_rows) != std::get<2>(matrix_rows) / 2 ||
+         std::get<2>(result_cols) != std::get<2>(matrix_cols) / 2)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "For Reduce2x2, result rows/cols must be half of matrix "
+                "rows/cols: "
+             << spvOpcodeString(opcode);
+    }
+  }
+  if (reduce_value == uint32_t(spv::CooperativeMatrixReduceMask::Row)) {
+    if (std::get<1>(result_rows) && std::get<1>(matrix_rows) &&
+        std::get<2>(result_rows) != std::get<2>(matrix_rows)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "For ReduceRow, result rows must match matrix rows: "
+             << spvOpcodeString(opcode);
+    }
+  }
+  if (reduce_value == uint32_t(spv::CooperativeMatrixReduceMask::Column)) {
+    if (std::get<1>(result_cols) && std::get<1>(matrix_cols) &&
+        std::get<2>(result_cols) != std::get<2>(matrix_cols)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "For ReduceColumn, result cols must match matrix cols: "
+             << spvOpcodeString(opcode);
+    }
+  }
+
+  const auto combine_func_id = inst->GetOperandAs<uint32_t>(4);
+  const auto combine_func = _.FindDef(combine_func_id);
+  if (!combine_func || combine_func->opcode() != spv::Op::OpFunction) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "CombineFunc must be a function: " << spvOpcodeString(opcode);
+  }
+  const auto function_type_id = combine_func->GetOperandAs<uint32_t>(3);
+  const auto function_type = _.FindDef(function_type_id);
+  if (function_type->operands().size() != 4) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "CombineFunc must have two parameters: "
+           << spvOpcodeString(opcode);
+  }
+  for (uint32_t i = 0; i < 3; ++i) {
+    // checks return type and two params
+    const auto param_type_id = function_type->GetOperandAs<uint32_t>(i + 1);
+    if (param_type_id != matrix_comp_type_id) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "CombineFunc return type and parameters must match matrix "
+                "component type: "
+             << spvOpcodeString(opcode);
+    }
+  }
+  return SPV_SUCCESS;
+}
+
+// Validates correctness of arithmetic instructions.
+spv_result_t ArithmeticsPass(ValidationState_t& _, const Instruction* inst) {
+  switch (inst->opcode()) {
     case spv::Op::OpFAdd:
     case spv::Op::OpFSub:
     case spv::Op::OpFMul:
     case spv::Op::OpFDiv:
     case spv::Op::OpFRem:
     case spv::Op::OpFMod:
-    case spv::Op::OpFNegate: {
-      bool supportsCoopMat =
-          (opcode != spv::Op::OpFMul && opcode != spv::Op::OpFRem &&
-           opcode != spv::Op::OpFMod);
-      if (!_.IsFloatScalarType(result_type) &&
-          !_.IsFloatVectorType(result_type) &&
-          !(supportsCoopMat && _.IsFloatCooperativeMatrixType(result_type)) &&
-          !(opcode == spv::Op::OpFMul &&
-            _.IsCooperativeMatrixKHRType(result_type) &&
-            _.IsFloatCooperativeMatrixType(result_type)))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected floating scalar or vector type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      for (size_t operand_index = 2; operand_index < inst->operands().size();
-           ++operand_index) {
-        if (supportsCoopMat && _.IsCooperativeMatrixKHRType(result_type)) {
-          const uint32_t type_id = _.GetOperandTypeId(inst, operand_index);
-          if (!_.IsCooperativeMatrixKHRType(type_id) ||
-              !_.IsFloatCooperativeMatrixType(type_id)) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << "Expected arithmetic operands to be of Result Type: "
-                   << spvOpcodeString(opcode) << " operand index "
-                   << operand_index;
-          }
-          spv_result_t ret =
-              _.CooperativeMatrixShapesMatch(inst, result_type, type_id, false);
-          if (ret != SPV_SUCCESS) return ret;
-        } else if (_.GetOperandTypeId(inst, operand_index) != result_type)
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected arithmetic operands to be of Result Type: "
-                 << spvOpcodeString(opcode) << " operand index "
-                 << operand_index;
-      }
-      break;
-    }
-
+    case spv::Op::OpFNegate:
+    case spv::Op::OpFmaKHR:
+      return ValidateFloat(_, inst);
     case spv::Op::OpUDiv:
-    case spv::Op::OpUMod: {
-      bool supportsCoopMat = (opcode == spv::Op::OpUDiv);
-      if (!_.IsUnsignedIntScalarType(result_type) &&
-          !_.IsUnsignedIntVectorType(result_type) &&
-          !(supportsCoopMat &&
-            _.IsUnsignedIntCooperativeMatrixType(result_type)))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected unsigned int scalar or vector type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      for (size_t operand_index = 2; operand_index < inst->operands().size();
-           ++operand_index) {
-        if (supportsCoopMat && _.IsCooperativeMatrixKHRType(result_type)) {
-          const uint32_t type_id = _.GetOperandTypeId(inst, operand_index);
-          if (!_.IsCooperativeMatrixKHRType(type_id) ||
-              !_.IsUnsignedIntCooperativeMatrixType(type_id)) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << "Expected arithmetic operands to be of Result Type: "
-                   << spvOpcodeString(opcode) << " operand index "
-                   << operand_index;
-          }
-          spv_result_t ret =
-              _.CooperativeMatrixShapesMatch(inst, result_type, type_id, false);
-          if (ret != SPV_SUCCESS) return ret;
-        } else if (_.GetOperandTypeId(inst, operand_index) != result_type)
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected arithmetic operands to be of Result Type: "
-                 << spvOpcodeString(opcode) << " operand index "
-                 << operand_index;
-      }
-      break;
-    }
-
+    case spv::Op::OpUMod:
+      return ValidateUnsignedInt(_, inst);
     case spv::Op::OpISub:
     case spv::Op::OpIAdd:
     case spv::Op::OpIMul:
     case spv::Op::OpSDiv:
     case spv::Op::OpSMod:
     case spv::Op::OpSRem:
-    case spv::Op::OpSNegate: {
-      bool supportsCoopMat =
-          (opcode != spv::Op::OpIMul && opcode != spv::Op::OpSRem &&
-           opcode != spv::Op::OpSMod);
-      if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) &&
-          !(supportsCoopMat && _.IsIntCooperativeMatrixType(result_type)) &&
-          !(opcode == spv::Op::OpIMul &&
-            _.IsCooperativeMatrixKHRType(result_type) &&
-            _.IsIntCooperativeMatrixType(result_type)))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected int scalar or vector type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t dimension = _.GetDimension(result_type);
-      const uint32_t bit_width = _.GetBitWidth(result_type);
-
-      for (size_t operand_index = 2; operand_index < inst->operands().size();
-           ++operand_index) {
-        const uint32_t type_id = _.GetOperandTypeId(inst, operand_index);
-
-        if (supportsCoopMat && _.IsCooperativeMatrixKHRType(result_type)) {
-          if (!_.IsCooperativeMatrixKHRType(type_id) ||
-              !_.IsIntCooperativeMatrixType(type_id)) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << "Expected arithmetic operands to be of Result Type: "
-                   << spvOpcodeString(opcode) << " operand index "
-                   << operand_index;
-          }
-          spv_result_t ret =
-              _.CooperativeMatrixShapesMatch(inst, result_type, type_id, false);
-          if (ret != SPV_SUCCESS) return ret;
-        }
-
-        if (!type_id ||
-            (!_.IsIntScalarType(type_id) && !_.IsIntVectorType(type_id) &&
-             !(supportsCoopMat && _.IsIntCooperativeMatrixType(result_type)) &&
-             !(opcode == spv::Op::OpIMul &&
-               _.IsCooperativeMatrixKHRType(result_type) &&
-               _.IsIntCooperativeMatrixType(result_type))))
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected int scalar or vector type as operand: "
-                 << spvOpcodeString(opcode) << " operand index "
-                 << operand_index;
-
-        if (_.GetDimension(type_id) != dimension)
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected arithmetic operands to have the same dimension "
-                 << "as Result Type: " << spvOpcodeString(opcode)
-                 << " operand index " << operand_index;
-
-        if (_.GetBitWidth(type_id) != bit_width)
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected arithmetic operands to have the same bit width "
-                 << "as Result Type: " << spvOpcodeString(opcode)
-                 << " operand index " << operand_index;
-      }
-      break;
-    }
-
-    case spv::Op::OpDot: {
-      if (!_.IsFloatScalarType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected float scalar type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      uint32_t first_vector_num_components = 0;
-
-      for (size_t operand_index = 2; operand_index < inst->operands().size();
-           ++operand_index) {
-        const uint32_t type_id = _.GetOperandTypeId(inst, operand_index);
-
-        if (!type_id || !_.IsFloatVectorType(type_id))
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected float vector as operand: "
-                 << spvOpcodeString(opcode) << " operand index "
-                 << operand_index;
-
-        const uint32_t component_type = _.GetComponentType(type_id);
-        if (component_type != result_type)
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected component type to be equal to Result Type: "
-                 << spvOpcodeString(opcode) << " operand index "
-                 << operand_index;
-
-        const uint32_t num_components = _.GetDimension(type_id);
-        if (operand_index == 2) {
-          first_vector_num_components = num_components;
-        } else if (num_components != first_vector_num_components) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected operands to have the same number of components: "
-                 << spvOpcodeString(opcode);
-        }
-      }
-      break;
-    }
-
-    case spv::Op::OpVectorTimesScalar: {
-      if (!_.IsFloatVectorType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected float vector type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t vector_type_id = _.GetOperandTypeId(inst, 2);
-      if (result_type != vector_type_id)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected vector operand type to be equal to Result Type: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t component_type = _.GetComponentType(vector_type_id);
-
-      const uint32_t scalar_type_id = _.GetOperandTypeId(inst, 3);
-      if (component_type != scalar_type_id)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected scalar operand type to be equal to the component "
-               << "type of the vector operand: " << spvOpcodeString(opcode);
-
-      break;
-    }
-
-    case spv::Op::OpMatrixTimesScalar: {
-      if (!_.IsFloatMatrixType(result_type) &&
-          !(_.IsCooperativeMatrixType(result_type)))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected float matrix type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 2);
-      if (result_type != matrix_type_id)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected matrix operand type to be equal to Result Type: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t component_type = _.GetComponentType(matrix_type_id);
-
-      const uint32_t scalar_type_id = _.GetOperandTypeId(inst, 3);
-      if (component_type != scalar_type_id)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected scalar operand type to be equal to the component "
-               << "type of the matrix operand: " << spvOpcodeString(opcode);
-
-      break;
-    }
-
-    case spv::Op::OpVectorTimesMatrix: {
-      const uint32_t vector_type_id = _.GetOperandTypeId(inst, 2);
-      const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 3);
-
-      if (!_.IsFloatVectorType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected float vector type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t res_component_type = _.GetComponentType(result_type);
-
-      if (!vector_type_id || !_.IsFloatVectorType(vector_type_id))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected float vector type as left operand: "
-               << spvOpcodeString(opcode);
-
-      if (res_component_type != _.GetComponentType(vector_type_id))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected component types of Result Type and vector to be "
-               << "equal: " << spvOpcodeString(opcode);
-
-      uint32_t matrix_num_rows = 0;
-      uint32_t matrix_num_cols = 0;
-      uint32_t matrix_col_type = 0;
-      uint32_t matrix_component_type = 0;
-      if (!_.GetMatrixTypeInfo(matrix_type_id, &matrix_num_rows,
-                               &matrix_num_cols, &matrix_col_type,
-                               &matrix_component_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected float matrix type as right operand: "
-               << spvOpcodeString(opcode);
-
-      if (res_component_type != matrix_component_type)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected component types of Result Type and matrix to be "
-               << "equal: " << spvOpcodeString(opcode);
-
-      if (matrix_num_cols != _.GetDimension(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected number of columns of the matrix to be equal to "
-               << "Result Type vector size: " << spvOpcodeString(opcode);
-
-      if (matrix_num_rows != _.GetDimension(vector_type_id))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected number of rows of the matrix to be equal to the "
-               << "vector operand size: " << spvOpcodeString(opcode);
-
-      break;
-    }
-
-    case spv::Op::OpMatrixTimesVector: {
-      const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 2);
-      const uint32_t vector_type_id = _.GetOperandTypeId(inst, 3);
-
-      if (!_.IsFloatVectorType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected float vector type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      uint32_t matrix_num_rows = 0;
-      uint32_t matrix_num_cols = 0;
-      uint32_t matrix_col_type = 0;
-      uint32_t matrix_component_type = 0;
-      if (!_.GetMatrixTypeInfo(matrix_type_id, &matrix_num_rows,
-                               &matrix_num_cols, &matrix_col_type,
-                               &matrix_component_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected float matrix type as left operand: "
-               << spvOpcodeString(opcode);
-
-      if (result_type != matrix_col_type)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected column type of the matrix to be equal to Result "
-                  "Type: "
-               << spvOpcodeString(opcode);
-
-      if (!vector_type_id || !_.IsFloatVectorType(vector_type_id))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected float vector type as right operand: "
-               << spvOpcodeString(opcode);
-
-      if (matrix_component_type != _.GetComponentType(vector_type_id))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected component types of the operands to be equal: "
-               << spvOpcodeString(opcode);
-
-      if (matrix_num_cols != _.GetDimension(vector_type_id))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected number of columns of the matrix to be equal to the "
-               << "vector size: " << spvOpcodeString(opcode);
-
-      break;
-    }
-
-    case spv::Op::OpMatrixTimesMatrix: {
-      const uint32_t left_type_id = _.GetOperandTypeId(inst, 2);
-      const uint32_t right_type_id = _.GetOperandTypeId(inst, 3);
-
-      uint32_t res_num_rows = 0;
-      uint32_t res_num_cols = 0;
-      uint32_t res_col_type = 0;
-      uint32_t res_component_type = 0;
-      if (!_.GetMatrixTypeInfo(result_type, &res_num_rows, &res_num_cols,
-                               &res_col_type, &res_component_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected float matrix type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      uint32_t left_num_rows = 0;
-      uint32_t left_num_cols = 0;
-      uint32_t left_col_type = 0;
-      uint32_t left_component_type = 0;
-      if (!_.GetMatrixTypeInfo(left_type_id, &left_num_rows, &left_num_cols,
-                               &left_col_type, &left_component_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected float matrix type as left operand: "
-               << spvOpcodeString(opcode);
-
-      uint32_t right_num_rows = 0;
-      uint32_t right_num_cols = 0;
-      uint32_t right_col_type = 0;
-      uint32_t right_component_type = 0;
-      if (!_.GetMatrixTypeInfo(right_type_id, &right_num_rows, &right_num_cols,
-                               &right_col_type, &right_component_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected float matrix type as right operand: "
-               << spvOpcodeString(opcode);
-
-      if (!_.IsFloatScalarType(res_component_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected float matrix type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      if (res_col_type != left_col_type)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected column types of Result Type and left matrix to be "
-               << "equal: " << spvOpcodeString(opcode);
-
-      if (res_component_type != right_component_type)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected component types of Result Type and right matrix to "
-                  "be "
-               << "equal: " << spvOpcodeString(opcode);
-
-      if (res_num_cols != right_num_cols)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected number of columns of Result Type and right matrix "
-                  "to "
-               << "be equal: " << spvOpcodeString(opcode);
-
-      if (left_num_cols != right_num_rows)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected number of columns of left matrix and number of "
-                  "rows "
-               << "of right matrix to be equal: " << spvOpcodeString(opcode);
-
-      assert(left_num_rows == res_num_rows);
-      break;
-    }
-
-    case spv::Op::OpOuterProduct: {
-      const uint32_t left_type_id = _.GetOperandTypeId(inst, 2);
-      const uint32_t right_type_id = _.GetOperandTypeId(inst, 3);
-
-      uint32_t res_num_rows = 0;
-      uint32_t res_num_cols = 0;
-      uint32_t res_col_type = 0;
-      uint32_t res_component_type = 0;
-      if (!_.GetMatrixTypeInfo(result_type, &res_num_rows, &res_num_cols,
-                               &res_col_type, &res_component_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected float matrix type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      if (left_type_id != res_col_type)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected column type of Result Type to be equal to the type "
-               << "of the left operand: " << spvOpcodeString(opcode);
-
-      if (!right_type_id || !_.IsFloatVectorType(right_type_id))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected float vector type as right operand: "
-               << spvOpcodeString(opcode);
-
-      if (res_component_type != _.GetComponentType(right_type_id))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected component types of the operands to be equal: "
-               << spvOpcodeString(opcode);
-
-      if (res_num_cols != _.GetDimension(right_type_id))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected number of columns of the matrix to be equal to the "
-               << "vector size of the right operand: "
-               << spvOpcodeString(opcode);
-
-      break;
-    }
-
+    case spv::Op::OpSNegate:
+      return ValidateSignedInt(_, inst);
+    case spv::Op::OpDot:
+      return ValidateDot(_, inst);
+    case spv::Op::OpVectorTimesScalar:
+      return ValidateVectorTimesScalar(_, inst);
+    case spv::Op::OpMatrixTimesScalar:
+      return ValidateMatrixTimesScalar(_, inst);
+    case spv::Op::OpVectorTimesMatrix:
+      return ValidateVectorTimesMatrix(_, inst);
+    case spv::Op::OpMatrixTimesVector:
+      return ValidateMatrixTimesVector(_, inst);
+    case spv::Op::OpMatrixTimesMatrix:
+      return ValidateMatrixTimesMatrix(_, inst);
+    case spv::Op::OpOuterProduct:
+      return ValidateOuterProduct(_, inst);
     case spv::Op::OpIAddCarry:
     case spv::Op::OpISubBorrow:
     case spv::Op::OpUMulExtended:
-    case spv::Op::OpSMulExtended: {
-      std::vector<uint32_t> result_types;
-      if (!_.GetStructMemberTypes(result_type, &result_types))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected a struct as Result Type: "
-               << spvOpcodeString(opcode);
+    case spv::Op::OpSMulExtended:
+      return ValidateExtendedCarry(_, inst);
+    case spv::Op::OpCooperativeMatrixMulAddNV:
+      return ValidateCooperativeMatrixMulAddNV(_, inst);
+    case spv::Op::OpCooperativeMatrixMulAddKHR:
+      return ValidateCooperativeMatrixMulAddKHR(_, inst);
+    case spv::Op::OpCooperativeMatrixReduceNV:
+      return ValidateCooperativeMatrixReduceNV(_, inst);
 
-      if (result_types.size() != 2)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Result Type struct to have two members: "
-               << spvOpcodeString(opcode);
-
-      if (opcode == spv::Op::OpSMulExtended) {
-        if (!_.IsIntScalarType(result_types[0]) &&
-            !_.IsIntVectorType(result_types[0]))
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected Result Type struct member types to be integer "
-                    "scalar "
-                 << "or vector: " << spvOpcodeString(opcode);
-      } else {
-        if (!_.IsUnsignedIntScalarType(result_types[0]) &&
-            !_.IsUnsignedIntVectorType(result_types[0]))
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected Result Type struct member types to be unsigned "
-                 << "integer scalar or vector: " << spvOpcodeString(opcode);
-      }
-
-      if (result_types[0] != result_types[1])
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Result Type struct member types to be identical: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t left_type_id = _.GetOperandTypeId(inst, 2);
-      const uint32_t right_type_id = _.GetOperandTypeId(inst, 3);
-
-      if (left_type_id != result_types[0] || right_type_id != result_types[0])
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected both operands to be of Result Type member type: "
-               << spvOpcodeString(opcode);
-
-      break;
-    }
-
-    case spv::Op::OpCooperativeMatrixMulAddNV: {
-      const uint32_t D_type_id = _.GetOperandTypeId(inst, 1);
-      const uint32_t A_type_id = _.GetOperandTypeId(inst, 2);
-      const uint32_t B_type_id = _.GetOperandTypeId(inst, 3);
-      const uint32_t C_type_id = _.GetOperandTypeId(inst, 4);
-
-      if (!_.IsCooperativeMatrixNVType(A_type_id)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected cooperative matrix type as A Type: "
-               << spvOpcodeString(opcode);
-      }
-      if (!_.IsCooperativeMatrixNVType(B_type_id)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected cooperative matrix type as B Type: "
-               << spvOpcodeString(opcode);
-      }
-      if (!_.IsCooperativeMatrixNVType(C_type_id)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected cooperative matrix type as C Type: "
-               << spvOpcodeString(opcode);
-      }
-      if (!_.IsCooperativeMatrixNVType(D_type_id)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected cooperative matrix type as Result Type: "
-               << spvOpcodeString(opcode);
-      }
-
-      const auto A = _.FindDef(A_type_id);
-      const auto B = _.FindDef(B_type_id);
-      const auto C = _.FindDef(C_type_id);
-      const auto D = _.FindDef(D_type_id);
-
-      std::tuple<bool, bool, uint32_t> A_scope, B_scope, C_scope, D_scope,
-          A_rows, B_rows, C_rows, D_rows, A_cols, B_cols, C_cols, D_cols;
-
-      A_scope = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(2));
-      B_scope = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(2));
-      C_scope = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(2));
-      D_scope = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(2));
-
-      A_rows = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(3));
-      B_rows = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(3));
-      C_rows = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(3));
-      D_rows = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(3));
-
-      A_cols = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(4));
-      B_cols = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(4));
-      C_cols = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(4));
-      D_cols = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(4));
-
-      const auto notEqual = [](std::tuple<bool, bool, uint32_t> X,
-                               std::tuple<bool, bool, uint32_t> Y) {
-        return (std::get<1>(X) && std::get<1>(Y) &&
-                std::get<2>(X) != std::get<2>(Y));
-      };
-
-      if (notEqual(A_scope, B_scope) || notEqual(A_scope, C_scope) ||
-          notEqual(A_scope, D_scope) || notEqual(B_scope, C_scope) ||
-          notEqual(B_scope, D_scope) || notEqual(C_scope, D_scope)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Cooperative matrix scopes must match: "
-               << spvOpcodeString(opcode);
-      }
-
-      if (notEqual(A_rows, C_rows) || notEqual(A_rows, D_rows) ||
-          notEqual(C_rows, D_rows)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Cooperative matrix 'M' mismatch: "
-               << spvOpcodeString(opcode);
-      }
-
-      if (notEqual(B_cols, C_cols) || notEqual(B_cols, D_cols) ||
-          notEqual(C_cols, D_cols)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Cooperative matrix 'N' mismatch: "
-               << spvOpcodeString(opcode);
-      }
-
-      if (notEqual(A_cols, B_rows)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Cooperative matrix 'K' mismatch: "
-               << spvOpcodeString(opcode);
+    case spv::Op::OpSpecConstantOp: {
+      switch (inst->GetOperandAs<spv::Op>(2u)) {
+        case spv::Op::OpFAdd:
+        case spv::Op::OpFSub:
+        case spv::Op::OpFMul:
+        case spv::Op::OpFDiv:
+        case spv::Op::OpFRem:
+        case spv::Op::OpFMod:
+        case spv::Op::OpFNegate:
+          return ValidateFloat(_, inst, 3);
+        case spv::Op::OpUDiv:
+        case spv::Op::OpUMod:
+          return ValidateUnsignedInt(_, inst, 3);
+        case spv::Op::OpISub:
+        case spv::Op::OpIAdd:
+        case spv::Op::OpIMul:
+        case spv::Op::OpSDiv:
+        case spv::Op::OpSMod:
+        case spv::Op::OpSRem:
+        case spv::Op::OpSNegate:
+          return ValidateSignedInt(_, inst, 3);
+        default:
+          break;
       }
       break;
     }
-
-    case spv::Op::OpCooperativeMatrixMulAddKHR: {
-      const uint32_t D_type_id = _.GetOperandTypeId(inst, 1);
-      const uint32_t A_type_id = _.GetOperandTypeId(inst, 2);
-      const uint32_t B_type_id = _.GetOperandTypeId(inst, 3);
-      const uint32_t C_type_id = _.GetOperandTypeId(inst, 4);
-
-      if (!_.IsCooperativeMatrixAType(A_type_id)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Cooperative matrix type must be A Type: "
-               << spvOpcodeString(opcode);
-      }
-      if (!_.IsCooperativeMatrixBType(B_type_id)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Cooperative matrix type must be B Type: "
-               << spvOpcodeString(opcode);
-      }
-      if (!_.IsCooperativeMatrixAccType(C_type_id)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Cooperative matrix type must be Accumulator Type: "
-               << spvOpcodeString(opcode);
-      }
-      if (!_.IsCooperativeMatrixKHRType(D_type_id)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected cooperative matrix type as Result Type: "
-               << spvOpcodeString(opcode);
-      }
-
-      const auto A = _.FindDef(A_type_id);
-      const auto B = _.FindDef(B_type_id);
-      const auto C = _.FindDef(C_type_id);
-      const auto D = _.FindDef(D_type_id);
-
-      std::tuple<bool, bool, uint32_t> A_scope, B_scope, C_scope, D_scope,
-          A_rows, B_rows, C_rows, D_rows, A_cols, B_cols, C_cols, D_cols;
-
-      A_scope = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(2));
-      B_scope = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(2));
-      C_scope = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(2));
-      D_scope = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(2));
-
-      A_rows = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(3));
-      B_rows = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(3));
-      C_rows = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(3));
-      D_rows = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(3));
-
-      A_cols = _.EvalInt32IfConst(A->GetOperandAs<uint32_t>(4));
-      B_cols = _.EvalInt32IfConst(B->GetOperandAs<uint32_t>(4));
-      C_cols = _.EvalInt32IfConst(C->GetOperandAs<uint32_t>(4));
-      D_cols = _.EvalInt32IfConst(D->GetOperandAs<uint32_t>(4));
-
-      const auto notEqual = [](std::tuple<bool, bool, uint32_t> X,
-                               std::tuple<bool, bool, uint32_t> Y) {
-        return (std::get<1>(X) && std::get<1>(Y) &&
-                std::get<2>(X) != std::get<2>(Y));
-      };
-
-      if (notEqual(A_scope, B_scope) || notEqual(A_scope, C_scope) ||
-          notEqual(A_scope, D_scope) || notEqual(B_scope, C_scope) ||
-          notEqual(B_scope, D_scope) || notEqual(C_scope, D_scope)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Cooperative matrix scopes must match: "
-               << spvOpcodeString(opcode);
-      }
-
-      if (notEqual(A_rows, C_rows) || notEqual(A_rows, D_rows) ||
-          notEqual(C_rows, D_rows)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Cooperative matrix 'M' mismatch: "
-               << spvOpcodeString(opcode);
-      }
-
-      if (notEqual(B_cols, C_cols) || notEqual(B_cols, D_cols) ||
-          notEqual(C_cols, D_cols)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Cooperative matrix 'N' mismatch: "
-               << spvOpcodeString(opcode);
-      }
-
-      if (notEqual(A_cols, B_rows)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Cooperative matrix 'K' mismatch: "
-               << spvOpcodeString(opcode);
-      }
-      break;
-    }
-
-    case spv::Op::OpCooperativeMatrixReduceNV: {
-      if (!_.IsCooperativeMatrixKHRType(result_type)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Result Type must be a cooperative matrix type: "
-               << spvOpcodeString(opcode);
-      }
-
-      const auto result_comp_type_id =
-          _.FindDef(result_type)->GetOperandAs<uint32_t>(1);
-
-      const auto matrix_id = inst->GetOperandAs<uint32_t>(2);
-      const auto matrix = _.FindDef(matrix_id);
-      const auto matrix_type_id = matrix->type_id();
-      if (!_.IsCooperativeMatrixKHRType(matrix_type_id)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Matrix must have a cooperative matrix type: "
-               << spvOpcodeString(opcode);
-      }
-      const auto matrix_type = _.FindDef(matrix_type_id);
-      const auto matrix_comp_type_id = matrix_type->GetOperandAs<uint32_t>(1);
-      if (matrix_comp_type_id != result_comp_type_id) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Result Type and Matrix type must have the same component "
-                  "type: "
-               << spvOpcodeString(opcode);
-      }
-      if (_.FindDef(result_type)->GetOperandAs<uint32_t>(2) !=
-          matrix_type->GetOperandAs<uint32_t>(2)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Result Type and Matrix type must have the same scope: "
-               << spvOpcodeString(opcode);
-      }
-
-      if (!_.IsCooperativeMatrixAccType(result_type)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Result Type must have UseAccumulator: "
-               << spvOpcodeString(opcode);
-      }
-      if (!_.IsCooperativeMatrixAccType(matrix_type_id)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Matrix type must have UseAccumulator: "
-               << spvOpcodeString(opcode);
-      }
-
-      const auto reduce_value = inst->GetOperandAs<uint32_t>(3);
-
-      if ((reduce_value &
-           uint32_t(
-               spv::CooperativeMatrixReduceMask::CooperativeMatrixReduce2x2)) &&
-          (reduce_value & uint32_t(spv::CooperativeMatrixReduceMask::Row |
-                                   spv::CooperativeMatrixReduceMask::Column))) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Reduce 2x2 must not be used with Row/Column: "
-               << spvOpcodeString(opcode);
-      }
-
-      std::tuple<bool, bool, uint32_t> result_rows, result_cols, matrix_rows,
-          matrix_cols;
-      result_rows =
-          _.EvalInt32IfConst(_.FindDef(result_type)->GetOperandAs<uint32_t>(3));
-      result_cols =
-          _.EvalInt32IfConst(_.FindDef(result_type)->GetOperandAs<uint32_t>(4));
-      matrix_rows = _.EvalInt32IfConst(matrix_type->GetOperandAs<uint32_t>(3));
-      matrix_cols = _.EvalInt32IfConst(matrix_type->GetOperandAs<uint32_t>(4));
-
-      if (reduce_value &
-          uint32_t(
-              spv::CooperativeMatrixReduceMask::CooperativeMatrixReduce2x2)) {
-        if (std::get<1>(result_rows) && std::get<1>(result_cols) &&
-            std::get<1>(matrix_rows) && std::get<1>(matrix_cols) &&
-            (std::get<2>(result_rows) != std::get<2>(matrix_rows) / 2 ||
-             std::get<2>(result_cols) != std::get<2>(matrix_cols) / 2)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "For Reduce2x2, result rows/cols must be half of matrix "
-                    "rows/cols: "
-                 << spvOpcodeString(opcode);
-        }
-      }
-      if (reduce_value == uint32_t(spv::CooperativeMatrixReduceMask::Row)) {
-        if (std::get<1>(result_rows) && std::get<1>(matrix_rows) &&
-            std::get<2>(result_rows) != std::get<2>(matrix_rows)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "For ReduceRow, result rows must match matrix rows: "
-                 << spvOpcodeString(opcode);
-        }
-      }
-      if (reduce_value == uint32_t(spv::CooperativeMatrixReduceMask::Column)) {
-        if (std::get<1>(result_cols) && std::get<1>(matrix_cols) &&
-            std::get<2>(result_cols) != std::get<2>(matrix_cols)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "For ReduceColumn, result cols must match matrix cols: "
-                 << spvOpcodeString(opcode);
-        }
-      }
-
-      const auto combine_func_id = inst->GetOperandAs<uint32_t>(4);
-      const auto combine_func = _.FindDef(combine_func_id);
-      if (!combine_func || combine_func->opcode() != spv::Op::OpFunction) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "CombineFunc must be a function: " << spvOpcodeString(opcode);
-      }
-      const auto function_type_id = combine_func->GetOperandAs<uint32_t>(3);
-      const auto function_type = _.FindDef(function_type_id);
-      if (function_type->operands().size() != 4) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "CombineFunc must have two parameters: "
-               << spvOpcodeString(opcode);
-      }
-      for (uint32_t i = 0; i < 3; ++i) {
-        // checks return type and two params
-        const auto param_type_id = function_type->GetOperandAs<uint32_t>(i + 1);
-        if (param_type_id != matrix_comp_type_id) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "CombineFunc return type and parameters must match matrix "
-                    "component type: "
-                 << spvOpcodeString(opcode);
-        }
-      }
-
-      break;
-    }
-
     default:
       break;
   }
diff --git a/source/val/validate_atomics.cpp b/source/val/validate_atomics.cpp
index 990ed31..443e970 100644
--- a/source/val/validate_atomics.cpp
+++ b/source/val/validate_atomics.cpp
@@ -224,7 +224,7 @@
       }
 
       // Can't use result_type because OpAtomicStore doesn't have a result
-      if (_.IsIntScalarType(data_type) && _.GetBitWidth(data_type) == 64 &&
+      if (_.IsIntScalarType(data_type, 64) &&
           !_.HasCapability(spv::Capability::Int64Atomics)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << spvOpcodeString(opcode)
@@ -235,7 +235,9 @@
       if (!IsStorageClassAllowedByUniversalRules(storage_class)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << spvOpcodeString(opcode)
-               << ": storage class forbidden by universal validation rules.";
+               << ": Can not be used with storage class "
+               << spvtools::StorageClassToString(storage_class)
+               << " by universal validation rules";
       }
 
       // Then Shader rules
@@ -249,8 +251,10 @@
               (storage_class != spv::StorageClass::PhysicalStorageBuffer) &&
               (storage_class != spv::StorageClass::TaskPayloadWorkgroupEXT)) {
             return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << _.VkErrorID(4686) << spvOpcodeString(opcode)
-                   << ": Vulkan spec only allows storage classes for atomic to "
+                   << _.VkErrorID(4686) << spvOpcodeString(opcode) << ": "
+                   << spvtools::StorageClassToString(storage_class)
+                   << " is not allowed, the Vulkan spec only allows storage "
+                      "classes for atomic to "
                       "be: Uniform, Workgroup, Image, StorageBuffer, "
                       "PhysicalStorageBuffer or TaskPayloadWorkgroupEXT.";
           }
@@ -335,8 +339,9 @@
             (storage_class != spv::StorageClass::CrossWorkgroup) &&
             (storage_class != spv::StorageClass::Generic)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << spvOpcodeString(opcode)
-                 << ": storage class must be Function, Workgroup, "
+                 << spvOpcodeString(opcode) << ": storage class is "
+                 << spvtools::StorageClassToString(storage_class)
+                 << ", but must be Function, Workgroup, "
                     "CrossWorkGroup or Generic in the OpenCL environment.";
         }
 
@@ -352,7 +357,7 @@
       // If result and pointer type are different, need to do special check here
       if (opcode == spv::Op::OpAtomicFlagTestAndSet ||
           opcode == spv::Op::OpAtomicFlagClear) {
-        if (!_.IsIntScalarType(data_type) || _.GetBitWidth(data_type) != 32) {
+        if (!_.IsIntScalarType(data_type, 32)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << spvOpcodeString(opcode)
                  << ": expected Pointer to point to a value of 32-bit integer "
@@ -388,27 +393,6 @@
         if (auto error = ValidateMemorySemantics(
                 _, inst, unequal_semantics_index, memory_scope))
           return error;
-
-        // Volatile bits must match for equal and unequal semantics. Previous
-        // checks guarantee they are 32-bit constants, but we need to recheck
-        // whether they are evaluatable constants.
-        bool is_int32 = false;
-        bool is_equal_const = false;
-        bool is_unequal_const = false;
-        uint32_t equal_value = 0;
-        uint32_t unequal_value = 0;
-        std::tie(is_int32, is_equal_const, equal_value) = _.EvalInt32IfConst(
-            inst->GetOperandAs<uint32_t>(equal_semantics_index));
-        std::tie(is_int32, is_unequal_const, unequal_value) =
-            _.EvalInt32IfConst(
-                inst->GetOperandAs<uint32_t>(unequal_semantics_index));
-        if (is_equal_const && is_unequal_const &&
-            ((equal_value & uint32_t(spv::MemorySemanticsMask::Volatile)) ^
-             (unequal_value & uint32_t(spv::MemorySemanticsMask::Volatile)))) {
-          return _.diag(SPV_ERROR_INVALID_ID, inst)
-                 << "Volatile mask setting must match for Equal and Unequal "
-                    "memory semantics";
-        }
       }
 
       if (opcode == spv::Op::OpAtomicStore) {
diff --git a/source/val/validate_barriers.cpp b/source/val/validate_barriers.cpp
index 0abd5c8..43a2e86 100644
--- a/source/val/validate_barriers.cpp
+++ b/source/val/validate_barriers.cpp
@@ -45,10 +45,10 @@
                       model != spv::ExecutionModel::MeshNV) {
                     if (message) {
                       *message =
-                          "OpControlBarrier requires one of the following "
-                          "Execution "
-                          "Models: TessellationControl, GLCompute, Kernel, "
-                          "MeshNV or TaskNV";
+                          "In SPIR-V 1.2 or earlier, OpControlBarrier requires "
+                          "one of the following "
+                          "Execution Models: TessellationControl, GLCompute, "
+                          "Kernel, MeshNV or TaskNV";
                     }
                     return false;
                   }
@@ -94,8 +94,7 @@
       }
 
       const uint32_t subgroup_count_type = _.GetOperandTypeId(inst, 2);
-      if (!_.IsIntScalarType(subgroup_count_type) ||
-          _.GetBitWidth(subgroup_count_type) != 32) {
+      if (!_.IsIntScalarType(subgroup_count_type, 32)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << spvOpcodeString(opcode)
                << ": expected Subgroup Count to be a 32-bit int";
diff --git a/source/val/validate_bitwise.cpp b/source/val/validate_bitwise.cpp
index d8d9958..f038617 100644
--- a/source/val/validate_bitwise.cpp
+++ b/source/val/validate_bitwise.cpp
@@ -30,18 +30,20 @@
 
   if (!_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << _.VkErrorID(4781)
            << "Expected int scalar or vector type for Base operand: "
            << spvOpcodeString(opcode);
   }
 
   // Vulkan has a restriction to 32 bit for base
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    if (_.GetBitWidth(base_type) != 32) {
+    if (_.GetBitWidth(base_type) != 32 &&
+        !_.options()->allow_vulkan_32_bit_bitwise) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << _.VkErrorID(4781)
+             << _.VkErrorID(10824)
              << "Expected 32-bit int type for Base operand: "
-             << spvOpcodeString(opcode);
+             << spvOpcodeString(opcode)
+             << _.MissingFeature("maintenance9 feature",
+                                 "--allow-vulkan-32-bit-bitwise", false);
     }
   }
 
@@ -55,169 +57,211 @@
   return SPV_SUCCESS;
 }
 
-// Validates correctness of bitwise instructions.
-spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) {
+spv_result_t ValidateShift(ValidationState_t& _, const Instruction* inst,
+                           uint32_t starting_index = 2) {
   const spv::Op opcode = inst->opcode();
   const uint32_t result_type = inst->type_id();
+  if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) &&
+      !_.IsIntCooperativeVectorNVType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected int scalar or vector type as Result Type: "
+           << spvOpcodeString(opcode);
 
-  switch (opcode) {
+  const uint32_t result_dimension = _.GetDimension(result_type);
+  const uint32_t base_type = _.GetOperandTypeId(inst, starting_index);
+  const uint32_t shift_type = _.GetOperandTypeId(inst, starting_index + 1);
+
+  if (!base_type ||
+      (!_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type) &&
+       !_.IsIntCooperativeVectorNVType(base_type)))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Base to be int scalar or vector: "
+           << spvOpcodeString(opcode);
+
+  if (_.GetDimension(base_type) != result_dimension)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Base to have the same dimension "
+           << "as Result Type: " << spvOpcodeString(opcode);
+
+  if (_.GetBitWidth(base_type) != _.GetBitWidth(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Base to have the same bit width "
+           << "as Result Type: " << spvOpcodeString(opcode);
+
+  if (!shift_type ||
+      (!_.IsIntScalarType(shift_type) && !_.IsIntVectorType(shift_type) &&
+       !_.IsIntCooperativeVectorNVType(shift_type)))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Shift to be int scalar or vector: "
+           << spvOpcodeString(opcode);
+
+  if (_.GetDimension(shift_type) != result_dimension)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Shift to have the same dimension "
+           << "as Result Type: " << spvOpcodeString(opcode);
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateBitwise(ValidationState_t& _, const Instruction* inst,
+                             uint32_t starting_index = 2) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) &&
+      !_.IsIntCooperativeVectorNVType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected int scalar or vector type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t result_dimension = _.GetDimension(result_type);
+  const uint32_t result_bit_width = _.GetBitWidth(result_type);
+
+  for (size_t operand_index = starting_index;
+       operand_index < inst->operands().size(); ++operand_index) {
+    const uint32_t type_id = _.GetOperandTypeId(inst, operand_index);
+    if (!type_id ||
+        (!_.IsIntScalarType(type_id) && !_.IsIntVectorType(type_id) &&
+         !_.IsIntCooperativeVectorNVType(type_id)))
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected int scalar or vector as operand: "
+             << spvOpcodeString(opcode) << " operand index " << operand_index;
+
+    if (_.GetDimension(type_id) != result_dimension)
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected operands to have the same dimension "
+             << "as Result Type: " << spvOpcodeString(opcode)
+             << " operand index " << operand_index;
+
+    if (_.GetBitWidth(type_id) != result_bit_width)
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected operands to have the same bit width "
+             << "as Result Type: " << spvOpcodeString(opcode)
+             << " operand index " << operand_index;
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateBitFieldInsert(ValidationState_t& _,
+                                    const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  const uint32_t base_type = _.GetOperandTypeId(inst, 2);
+  const uint32_t insert_type = _.GetOperandTypeId(inst, 3);
+  const uint32_t offset_type = _.GetOperandTypeId(inst, 4);
+  const uint32_t count_type = _.GetOperandTypeId(inst, 5);
+
+  if (spv_result_t error = ValidateBaseType(_, inst, base_type)) {
+    return error;
+  }
+
+  if (insert_type != result_type)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Insert Type to be equal to Result Type: "
+           << spvOpcodeString(opcode);
+
+  if (!offset_type || !_.IsIntScalarType(offset_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Offset Type to be int scalar: "
+           << spvOpcodeString(opcode);
+
+  if (!count_type || !_.IsIntScalarType(count_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Count Type to be int scalar: "
+           << spvOpcodeString(opcode);
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateBitFieldExtract(ValidationState_t& _,
+                                     const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t base_type = _.GetOperandTypeId(inst, 2);
+  const uint32_t offset_type = _.GetOperandTypeId(inst, 3);
+  const uint32_t count_type = _.GetOperandTypeId(inst, 4);
+
+  if (spv_result_t error = ValidateBaseType(_, inst, base_type)) {
+    return error;
+  }
+
+  if (!offset_type || !_.IsIntScalarType(offset_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Offset Type to be int scalar: "
+           << spvOpcodeString(opcode);
+
+  if (!count_type || !_.IsIntScalarType(count_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Count Type to be int scalar: "
+           << spvOpcodeString(opcode);
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateBitReverse(ValidationState_t& _, const Instruction* inst) {
+  const uint32_t base_type = _.GetOperandTypeId(inst, 2);
+  if (spv_result_t error = ValidateBaseType(_, inst, base_type)) {
+    return error;
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateBitCount(ValidationState_t& _, const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected int scalar or vector type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t base_type = _.GetOperandTypeId(inst, 2);
+
+  if (spv_result_t error = ValidateBaseType(_, inst, base_type)) {
+    return error;
+  }
+
+  const uint32_t base_dimension = _.GetDimension(base_type);
+  const uint32_t result_dimension = _.GetDimension(result_type);
+
+  if (base_dimension != result_dimension)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Base dimension to be equal to Result Type "
+              "dimension: "
+           << spvOpcodeString(opcode);
+  return SPV_SUCCESS;
+}
+
+// Validates correctness of bitwise instructions.
+spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) {
+  switch (inst->opcode()) {
     case spv::Op::OpShiftRightLogical:
     case spv::Op::OpShiftRightArithmetic:
-    case spv::Op::OpShiftLeftLogical: {
-      if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected int scalar or vector type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t result_dimension = _.GetDimension(result_type);
-      const uint32_t base_type = _.GetOperandTypeId(inst, 2);
-      const uint32_t shift_type = _.GetOperandTypeId(inst, 3);
-
-      if (!base_type ||
-          (!_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type)))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Base to be int scalar or vector: "
-               << spvOpcodeString(opcode);
-
-      if (_.GetDimension(base_type) != result_dimension)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Base to have the same dimension "
-               << "as Result Type: " << spvOpcodeString(opcode);
-
-      if (_.GetBitWidth(base_type) != _.GetBitWidth(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Base to have the same bit width "
-               << "as Result Type: " << spvOpcodeString(opcode);
-
-      if (!shift_type ||
-          (!_.IsIntScalarType(shift_type) && !_.IsIntVectorType(shift_type)))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Shift to be int scalar or vector: "
-               << spvOpcodeString(opcode);
-
-      if (_.GetDimension(shift_type) != result_dimension)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Shift to have the same dimension "
-               << "as Result Type: " << spvOpcodeString(opcode);
-      break;
-    }
-
+    case spv::Op::OpShiftLeftLogical:
+      return ValidateShift(_, inst);
     case spv::Op::OpBitwiseOr:
     case spv::Op::OpBitwiseXor:
     case spv::Op::OpBitwiseAnd:
-    case spv::Op::OpNot: {
-      if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected int scalar or vector type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t result_dimension = _.GetDimension(result_type);
-      const uint32_t result_bit_width = _.GetBitWidth(result_type);
-
-      for (size_t operand_index = 2; operand_index < inst->operands().size();
-           ++operand_index) {
-        const uint32_t type_id = _.GetOperandTypeId(inst, operand_index);
-        if (!type_id ||
-            (!_.IsIntScalarType(type_id) && !_.IsIntVectorType(type_id)))
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected int scalar or vector as operand: "
-                 << spvOpcodeString(opcode) << " operand index "
-                 << operand_index;
-
-        if (_.GetDimension(type_id) != result_dimension)
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected operands to have the same dimension "
-                 << "as Result Type: " << spvOpcodeString(opcode)
-                 << " operand index " << operand_index;
-
-        if (_.GetBitWidth(type_id) != result_bit_width)
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected operands to have the same bit width "
-                 << "as Result Type: " << spvOpcodeString(opcode)
-                 << " operand index " << operand_index;
-      }
-      break;
-    }
-
-    case spv::Op::OpBitFieldInsert: {
-      const uint32_t base_type = _.GetOperandTypeId(inst, 2);
-      const uint32_t insert_type = _.GetOperandTypeId(inst, 3);
-      const uint32_t offset_type = _.GetOperandTypeId(inst, 4);
-      const uint32_t count_type = _.GetOperandTypeId(inst, 5);
-
-      if (spv_result_t error = ValidateBaseType(_, inst, base_type)) {
-        return error;
-      }
-
-      if (insert_type != result_type)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Insert Type to be equal to Result Type: "
-               << spvOpcodeString(opcode);
-
-      if (!offset_type || !_.IsIntScalarType(offset_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Offset Type to be int scalar: "
-               << spvOpcodeString(opcode);
-
-      if (!count_type || !_.IsIntScalarType(count_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Count Type to be int scalar: "
-               << spvOpcodeString(opcode);
-      break;
-    }
-
+    case spv::Op::OpNot:
+      return ValidateBitwise(_, inst);
+    case spv::Op::OpBitFieldInsert:
+      return ValidateBitFieldInsert(_, inst);
     case spv::Op::OpBitFieldSExtract:
-    case spv::Op::OpBitFieldUExtract: {
-      const uint32_t base_type = _.GetOperandTypeId(inst, 2);
-      const uint32_t offset_type = _.GetOperandTypeId(inst, 3);
-      const uint32_t count_type = _.GetOperandTypeId(inst, 4);
+    case spv::Op::OpBitFieldUExtract:
+      return ValidateBitFieldExtract(_, inst);
+    case spv::Op::OpBitReverse:
+      return ValidateBitReverse(_, inst);
+    case spv::Op::OpBitCount:
+      return ValidateBitCount(_, inst);
 
-      if (spv_result_t error = ValidateBaseType(_, inst, base_type)) {
-        return error;
+    case spv::Op::OpSpecConstantOp: {
+      switch (inst->GetOperandAs<spv::Op>(2u)) {
+        case spv::Op::OpShiftRightLogical:
+        case spv::Op::OpShiftRightArithmetic:
+        case spv::Op::OpShiftLeftLogical:
+          return ValidateShift(_, inst, 3);
+        case spv::Op::OpBitwiseOr:
+        case spv::Op::OpBitwiseXor:
+        case spv::Op::OpBitwiseAnd:
+        case spv::Op::OpNot:
+          return ValidateBitwise(_, inst, 3);
+        default:
+          break;
       }
-
-      if (!offset_type || !_.IsIntScalarType(offset_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Offset Type to be int scalar: "
-               << spvOpcodeString(opcode);
-
-      if (!count_type || !_.IsIntScalarType(count_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Count Type to be int scalar: "
-               << spvOpcodeString(opcode);
-      break;
-    }
-
-    case spv::Op::OpBitReverse: {
-      const uint32_t base_type = _.GetOperandTypeId(inst, 2);
-
-      if (spv_result_t error = ValidateBaseType(_, inst, base_type)) {
-        return error;
-      }
-
-      break;
-    }
-
-    case spv::Op::OpBitCount: {
-      if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected int scalar or vector type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t base_type = _.GetOperandTypeId(inst, 2);
-
-      if (spv_result_t error = ValidateBaseType(_, inst, base_type)) {
-        return error;
-      }
-
-      const uint32_t base_dimension = _.GetDimension(base_type);
-      const uint32_t result_dimension = _.GetDimension(result_type);
-
-      if (base_dimension != result_dimension)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Base dimension to be equal to Result Type "
-                  "dimension: "
-               << spvOpcodeString(opcode);
       break;
     }
 
diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp
index 1305dc1..5e913d4 100644
--- a/source/val/validate_builtins.cpp
+++ b/source/val/validate_builtins.cpp
@@ -17,21 +17,22 @@
 // Validates correctness of built-in variables.
 
 #include <array>
+#include <cstdint>
 #include <functional>
 #include <list>
 #include <map>
 #include <set>
 #include <sstream>
-#include <stack>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "source/opcode.h"
 #include "source/spirv_target_env.h"
-#include "source/util/bitutils.h"
 #include "source/val/instruction.h"
 #include "source/val/validate.h"
 #include "source/val/validation_state.h"
+#include "spirv/unified1/spirv.hpp11"
 
 namespace spvtools {
 namespace val {
@@ -122,7 +123,7 @@
   VUIDErrorMax,
 } VUIDError;
 
-const static uint32_t NumVUIDBuiltins = 39;
+const static uint32_t NumVUIDBuiltins = 42;
 
 typedef struct {
   spv::BuiltIn builtIn;
@@ -169,9 +170,13 @@
     {spv::BuiltIn::CullMaskKHR,               {6735, 6736, 6737}},
     {spv::BuiltIn::BaryCoordKHR,              {4154, 4155, 4156}},
     {spv::BuiltIn::BaryCoordNoPerspKHR,       {4160, 4161, 4162}},
+    {spv::BuiltIn::LocalInvocationIndex,      {4284, 4285, 4286}},
     {spv::BuiltIn::PrimitivePointIndicesEXT,  {7041, 7043, 7044}},
     {spv::BuiltIn::PrimitiveLineIndicesEXT,   {7047, 7049, 7050}},
     {spv::BuiltIn::PrimitiveTriangleIndicesEXT, {7053, 7055, 7056}},
+    {spv::BuiltIn::CullPrimitiveEXT,          {7034, 7035, 7036}},
+    {spv::BuiltIn::HitTriangleVertexPositionsKHR, {8747, 8748, 8749}},
+
     // clang-format on
 }};
 
@@ -192,6 +197,7 @@
   switch (builtin) {
     case spv::BuiltIn::HitKindKHR:
     case spv::BuiltIn::HitTNV:
+    case spv::BuiltIn::HitTriangleVertexPositionsKHR:
       if (stage == spv::ExecutionModel::AnyHitKHR ||
           stage == spv::ExecutionModel::ClosestHitKHR) {
         return true;
@@ -269,6 +275,9 @@
   // specified. Seeds id_to_at_reference_checks_ with decorated ids if needed.
   spv_result_t ValidateSingleBuiltInAtDefinition(const Decoration& decoration,
                                                  const Instruction& inst);
+  spv_result_t ValidateSingleBuiltInAtDefinitionVulkan(
+      const Decoration& decoration, const Instruction& inst,
+      const spv::BuiltIn label);
 
   // The following section contains functions which are called when id defined
   // by |inst| is decorated with BuiltIn |decoration|.
@@ -362,12 +371,27 @@
   spv_result_t ValidateShadingRateAtDefinition(const Decoration& decoration,
                                                const Instruction& inst);
 
+  spv_result_t ValidateDescriptorHeapAtDefinition(const Decoration& decoration,
+                                                  const Instruction& inst);
+
   spv_result_t ValidateRayTracingBuiltinsAtDefinition(
       const Decoration& decoration, const Instruction& inst);
 
   spv_result_t ValidateMeshShadingEXTBuiltinsAtDefinition(
       const Decoration& decoration, const Instruction& inst);
 
+  // Used as a common method for validating MeshEXT builtins
+  spv_result_t ValidateMeshBuiltinInterfaceRules(
+      const Decoration& decoration, const Instruction& inst,
+      spv::Op scalar_type, const Instruction& referenced_from_inst);
+  spv_result_t ValidatePrimitiveShadingRateInterfaceRules(
+      const Decoration& decoration, const Instruction& inst,
+      const Instruction& referenced_from_inst);
+  // Builtin that needs check incase **not** used with MeshEXT
+  spv_result_t ValidateNonMeshInterfaceRules(
+      const Decoration& decoration, const Instruction& inst,
+      const Instruction& referenced_from_inst);
+
   // The following section contains functions which are called when id defined
   // by |referenced_inst| is
   // 1. referenced by |referenced_from_inst|
@@ -585,6 +609,10 @@
   spv_result_t ValidateBool(
       const Decoration& decoration, const Instruction& inst,
       const std::function<spv_result_t(const std::string& message)>& diag);
+  spv_result_t ValidateBlockTypeOrArrayedType(
+      const Decoration& decoration, const Instruction& inst,
+      bool& present_in_block, spv::Op expected_scalar_type,
+      const std::function<spv_result_t(const std::string& message)>& diag);
   spv_result_t ValidateI(
       const Decoration& decoration, const Instruction& inst,
       const std::function<spv_result_t(const std::string& message)>& diag);
@@ -627,6 +655,10 @@
       const Decoration& decoration, const Instruction& inst,
       uint32_t num_components,
       const std::function<spv_result_t(const std::string& message)>& diag);
+  spv_result_t ValidateArrayedF32Vec(
+      const Decoration& decoration, const Instruction& inst,
+      uint32_t num_components, uint32_t array_length,
+      const std::function<spv_result_t(const std::string& message)>& diag);
   spv_result_t ValidateF32VecHelper(
       const Decoration& decoration, const Instruction& inst,
       uint32_t num_components,
@@ -667,10 +699,75 @@
   // UniformConstant".
   std::string GetStorageClassDesc(const Instruction& inst) const;
 
+  uint64_t GetArrayLength(uint32_t interface_var_id);
+
   // Updates inner working of the class. Is called sequentially for every
   // instruction.
   void Update(const Instruction& inst);
 
+  bool IsBulitinInEntryPoint(const Instruction& inst, uint32_t entry_point) {
+    auto get_underlying_type_id = [&](const Instruction* ifx_var) {
+      auto pointer_type_inst = _.FindDef(ifx_var->type_id());
+      auto type_inst = _.FindDef(pointer_type_inst->GetOperandAs<uint32_t>(2));
+      while (type_inst->opcode() == spv::Op::OpTypeArray) {
+        type_inst = _.FindDef(type_inst->GetOperandAs<uint32_t>(1));
+      };
+      return type_inst->id();
+    };
+
+    for (const auto& desc : _.entry_point_descriptions(entry_point)) {
+      for (auto interface : desc.interfaces) {
+        if (inst.opcode() == spv::Op::OpTypeStruct) {
+          auto varInst = _.FindDef(interface);
+          if (inst.id() == get_underlying_type_id(varInst)) {
+            return true;
+          }
+        } else if (inst.id() == interface) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  // Check if "inst" is an interface variable or type of a interface varibale
+  // of any mesh entry point. Populate entry_point_interface_id with all
+  // entry points and interface variables that refer to the "inst"
+  bool IsMeshInterfaceVar(
+      const Instruction& inst,
+      std::map<uint32_t, uint32_t>& entry_point_interface_id) {
+    auto get_underlying_type_id = [&](const Instruction* ifx_var) {
+      auto pointer_type_inst = _.FindDef(ifx_var->type_id());
+      auto type_inst = _.FindDef(pointer_type_inst->GetOperandAs<uint32_t>(2));
+      while (type_inst->opcode() == spv::Op::OpTypeArray) {
+        type_inst = _.FindDef(type_inst->GetOperandAs<uint32_t>(1));
+      };
+      return type_inst->id();
+    };
+
+    for (const uint32_t entry_point : _.entry_points()) {
+      const auto* models = _.GetExecutionModels(entry_point);
+      if (models->find(spv::ExecutionModel::MeshEXT) != models->end() ||
+          models->find(spv::ExecutionModel::MeshNV) != models->end()) {
+        for (const auto& desc : _.entry_point_descriptions(entry_point)) {
+          for (auto interface : desc.interfaces) {
+            if (inst.opcode() == spv::Op::OpTypeStruct) {
+              auto varInst = _.FindDef(interface);
+              if (inst.id() == get_underlying_type_id(varInst)) {
+                entry_point_interface_id[entry_point] = interface;
+                break;
+              }
+            } else if (inst.id() == interface) {
+              entry_point_interface_id[entry_point] = interface;
+              break;
+            }
+          }
+        }
+      }
+    }
+    return !entry_point_interface_id.empty();
+  }
+
   ValidationState_t& _;
 
   // Mapping id -> list of rules which validate instruction referencing the
@@ -691,6 +788,10 @@
 
   // Execution models with which the current function can be called.
   std::set<spv::ExecutionModel> execution_models_;
+
+  // For Builtin that can only be declared once in an entry point, keep track if
+  // the entry point has it already
+  std::set<uint32_t> cull_primitive_entry_points_;
 };
 
 void BuiltInsValidator::Update(const Instruction& inst) {
@@ -768,6 +869,29 @@
   return ss.str();
 }
 
+uint64_t BuiltInsValidator::GetArrayLength(uint32_t interface_var_id) {
+  uint32_t underlying_type;
+  spv::StorageClass storage_class;
+  uint64_t array_len = -1;
+  const Instruction* inst = _.FindDef(interface_var_id);
+  if (inst->opcode() != spv::Op::OpVariable) {
+    return -1;
+  }
+
+  if (!_.GetPointerTypeInfo(inst->type_id(), &underlying_type,
+                            &storage_class)) {
+    return 0;
+  }
+  if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) {
+    // Get the array length
+    const auto length_id = _.FindDef(underlying_type)->word(3u);
+    if (!_.EvalConstantValUint64(length_id, &array_len)) {
+      return 0;
+    }
+  }
+  return array_len;
+}
+
 spv_result_t BuiltInsValidator::ValidateBool(
     const Decoration& decoration, const Instruction& inst,
     const std::function<spv_result_t(const std::string& message)>& diag) {
@@ -784,6 +908,55 @@
   return SPV_SUCCESS;
 }
 
+spv_result_t BuiltInsValidator::ValidateBlockTypeOrArrayedType(
+    const Decoration& decoration, const Instruction& inst, bool& isBlock,
+    spv::Op expected_scalar_type,
+    const std::function<spv_result_t(const std::string& message)>& diag) {
+  uint32_t underlying_type = 0;
+  int64_t array_len = -1;
+  isBlock = true;
+  if (spv_result_t error =
+          GetUnderlyingType(_, decoration, inst, &underlying_type)) {
+    return error;
+  }
+  // Strip the array, if present.
+  if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) {
+    // Get the array length
+    const auto length_id = _.FindDef(underlying_type)->word(3u);
+    if (!_.EvalConstantValInt64(length_id, &array_len)) {
+      return diag(GetDefinitionDesc(decoration, inst) +
+                  " Failed to find the array length.");
+    }
+    underlying_type = _.FindDef(underlying_type)->word(2u);
+    isBlock = false;
+  } else if (!_.HasDecoration(inst.id(), spv::Decoration::Block)) {
+    // If not in array, and bool is in a struct, must be in a Block struct
+    return diag(GetDefinitionDesc(decoration, inst) +
+                " Scalar boolean must be in a Block.");
+  }
+
+  switch (expected_scalar_type) {
+    case spv::Op::OpTypeBool:
+      if (!_.IsBoolScalarType(underlying_type)) {
+        return diag(GetDefinitionDesc(decoration, inst) +
+                    " is not a bool scalar.");
+      }
+      break;
+    case spv::Op::OpTypeInt:
+      if (!_.IsIntScalarType(underlying_type)) {
+        return diag(GetDefinitionDesc(decoration, inst) +
+                    " is not an integer scalar.");
+      }
+      break;
+    default:
+      assert(0 && "Unhandled scalar type");
+      return diag(GetDefinitionDesc(decoration, inst) +
+                  " is not a recognized scalar type.");
+  }
+
+  return SPV_SUCCESS;
+}
+
 spv_result_t BuiltInsValidator::ValidateI(
     const Decoration& decoration, const Instruction& inst,
     const std::function<spv_result_t(const std::string& message)>& diag) {
@@ -988,6 +1161,38 @@
                               underlying_type);
 }
 
+spv_result_t BuiltInsValidator::ValidateArrayedF32Vec(
+    const Decoration& decoration, const Instruction& inst,
+    uint32_t num_components, uint32_t array_length,
+    const std::function<spv_result_t(const std::string& message)>& diag) {
+  uint32_t underlying_type = 0;
+  if (spv_result_t error =
+          GetUnderlyingType(_, decoration, inst, &underlying_type)) {
+    return error;
+  }
+
+  if (_.GetIdOpcode(underlying_type) != spv::Op::OpTypeArray) {
+    return diag(GetDefinitionDesc(decoration, inst) + " is not an array.");
+  }
+
+  const uint32_t length_id = _.FindDef(underlying_type)->word(3u);
+  uint64_t found_length = 0;
+  if (!_.EvalConstantValUint64(length_id, &found_length)) {
+    return diag(GetDefinitionDesc(decoration, inst) +
+                " array has a non constant length.");
+  }
+
+  if (array_length != found_length) {
+    return diag(GetDefinitionDesc(decoration, inst) + " array length must be " +
+                std::to_string(array_length));
+  }
+
+  underlying_type = _.FindDef(underlying_type)->word(2u);
+
+  return ValidateF32VecHelper(decoration, inst, num_components, diag,
+                              underlying_type);
+}
+
 spv_result_t BuiltInsValidator::ValidateF32Vec(
     const Decoration& decoration, const Instruction& inst,
     uint32_t num_components,
@@ -2125,38 +2330,6 @@
 
 spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtDefinition(
     const Decoration& decoration, const Instruction& inst) {
-  if (spvIsVulkanEnv(_.context()->target_env)) {
-    // PrimitiveId can be a per-primitive variable for mesh shader stage.
-    // In such cases variable will have an array of 32-bit integers.
-    if (decoration.struct_member_index() != Decoration::kInvalidMember) {
-      // This must be a 32-bit int scalar.
-      if (spv_result_t error = ValidateI32(
-              decoration, inst,
-              [this, &inst](const std::string& message) -> spv_result_t {
-                return _.diag(SPV_ERROR_INVALID_DATA, &inst)
-                       << _.VkErrorID(4337)
-                       << "According to the Vulkan spec BuiltIn PrimitiveId "
-                          "variable needs to be a 32-bit int scalar. "
-                       << message;
-              })) {
-        return error;
-      }
-    } else {
-      if (spv_result_t error = ValidateOptionalArrayedI32(
-              decoration, inst,
-              [this, &inst](const std::string& message) -> spv_result_t {
-                return _.diag(SPV_ERROR_INVALID_DATA, &inst)
-                       << _.VkErrorID(4337)
-                       << "According to the Vulkan spec BuiltIn PrimitiveId "
-                          "variable needs to be a 32-bit int scalar. "
-                       << message;
-              })) {
-        return error;
-      }
-    }
-  }
-
-  // Seed at reference checks with this built-in.
   return ValidatePrimitiveIdAtReference(decoration, inst, inst, inst);
 }
 
@@ -2223,6 +2396,27 @@
           referenced_from_inst, std::placeholders::_1));
     }
 
+    if (!_.HasCapability(spv::Capability::MeshShadingEXT) &&
+        !_.HasCapability(spv::Capability::MeshShadingNV) &&
+        !_.HasCapability(spv::Capability::Geometry) &&
+        !_.HasCapability(spv::Capability::Tessellation)) {
+      id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
+          &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, 4333,
+          "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for "
+          "variables in the Fragment execution model unless it declares "
+          "Geometry, Tessellation, or MeshShader capabilities.",
+          spv::ExecutionModel::Fragment, decoration, built_in_inst,
+          referenced_from_inst, std::placeholders::_1));
+    }
+
+    id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
+        &BuiltInsValidator::ValidateMeshBuiltinInterfaceRules, this, decoration,
+        built_in_inst, spv::Op::OpTypeInt, std::placeholders::_1));
+
+    id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
+        std::bind(&BuiltInsValidator::ValidateNonMeshInterfaceRules, this,
+                  decoration, built_in_inst, std::placeholders::_1));
+
     for (const spv::ExecutionModel execution_model : execution_models_) {
       switch (execution_model) {
         case spv::ExecutionModel::Fragment:
@@ -2519,6 +2713,13 @@
             })) {
       return error;
     }
+
+    if (!_.HasDecoration(inst.id(), spv::Decoration::Patch)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+             << _.VkErrorID(10880)
+             << "BuiltIn TessLevelOuter variable needs to also have a Patch "
+                "decoration.";
+    }
   }
 
   // Seed at reference checks with this built-in.
@@ -2533,13 +2734,20 @@
             [this, &inst](const std::string& message) -> spv_result_t {
               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
                      << _.VkErrorID(4397)
-                     << "According to the Vulkan spec BuiltIn TessLevelOuter "
+                     << "According to the Vulkan spec BuiltIn TessLevelInner "
                         "variable needs to be a 2-component 32-bit float "
                         "array. "
                      << message;
             })) {
       return error;
     }
+
+    if (!_.HasDecoration(inst.id(), spv::Decoration::Patch)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+             << _.VkErrorID(10880)
+             << "BuiltIn TessLevelInner variable needs to also have a Patch "
+                "decoration.";
+    }
   }
 
   // Seed at reference checks with this built-in.
@@ -2663,14 +2871,69 @@
 
 spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtDefinition(
     const Decoration& decoration, const Instruction& inst) {
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    if (spv_result_t error = ValidateI32(
+            decoration, inst,
+            [this, &inst](const std::string& message) -> spv_result_t {
+              uint32_t vuid = GetVUIDForBuiltin(
+                  spv::BuiltIn::LocalInvocationIndex, VUIDErrorType);
+              return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                     << _.VkErrorID(vuid)
+                     << "According to the Vulkan spec BuiltIn "
+                        "LocalInvocationIndex variable needs to be a 32-bit "
+                        "int scalar. "
+                     << message;
+            })) {
+      return error;
+    }
+  }
+
   // Seed at reference checks with this built-in.
   return ValidateLocalInvocationIndexAtReference(decoration, inst, inst, inst);
 }
 
 spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtReference(
     const Decoration& decoration, const Instruction& built_in_inst,
-    const Instruction&,
+    const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    const spv::StorageClass storage_class =
+        GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
+      uint32_t vuid = GetVUIDForBuiltin(spv::BuiltIn::LocalInvocationIndex,
+                                        VUIDErrorStorageClass);
+      return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
+             << _.VkErrorID(vuid)
+             << "Vulkan spec allows BuiltIn LocalInvocationIndex to be only "
+                "used for variables with Input storage class. "
+             << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
+                                 referenced_from_inst)
+             << " " << GetStorageClassDesc(referenced_from_inst);
+    }
+
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      bool has_vulkan_model =
+          execution_model == spv::ExecutionModel::GLCompute ||
+          execution_model == spv::ExecutionModel::TaskNV ||
+          execution_model == spv::ExecutionModel::MeshNV ||
+          execution_model == spv::ExecutionModel::TaskEXT ||
+          execution_model == spv::ExecutionModel::MeshEXT;
+
+      if (spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) {
+        uint32_t vuid = GetVUIDForBuiltin(spv::BuiltIn::LocalInvocationIndex,
+                                          VUIDErrorExecutionModel);
+        return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
+               << _.VkErrorID(vuid)
+               << "Vulkan spec allows BuiltIn LocalInvocationIndex to be used "
+                  "only with GLCompute, MeshNV, TaskNV, MeshEXT or"
+               << " TaskEXT execution model. "
+               << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
+                                   referenced_from_inst, execution_model);
+      }
+    }
+  }
+
   if (function_id_ == 0) {
     // Propagate this rule to all dependant ids in the global scope.
     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
@@ -2722,52 +2985,270 @@
   return SPV_SUCCESS;
 }
 
-spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtDefinition(
-    const Decoration& decoration, const Instruction& inst) {
-  if (spvIsVulkanEnv(_.context()->target_env)) {
-    // This can be a per-primitive variable for mesh shader stage.
-    // In such cases variable will have an array of 32-bit integers.
-    if (decoration.struct_member_index() != Decoration::kInvalidMember) {
-      // This must be a 32-bit int scalar.
-      if (spv_result_t error = ValidateI32(
-              decoration, inst,
-              [this, &decoration,
-               &inst](const std::string& message) -> spv_result_t {
-                uint32_t vuid =
-                    (decoration.builtin() == spv::BuiltIn::Layer) ? 4276 : 4408;
+typedef struct {
+  uint32_t array_type;
+  uint32_t array_size;
+  uint32_t block_array_size;
+  uint32_t perprim_deco;
+} MeshBuiltinVUIDs;
+
+spv_result_t BuiltInsValidator::ValidateMeshBuiltinInterfaceRules(
+    const Decoration& decoration, const Instruction& inst, spv::Op scalar_type,
+    const Instruction& referenced_from_inst) {
+  if (function_id_) {
+    if (!execution_models_.count(spv::ExecutionModel::MeshEXT)) {
+      return SPV_SUCCESS;
+    }
+
+    const spv::BuiltIn builtin = decoration.builtin();
+    const bool is_topology =
+        builtin == spv::BuiltIn::PrimitiveTriangleIndicesEXT ||
+        builtin == spv::BuiltIn::PrimitiveLineIndicesEXT ||
+        builtin == spv::BuiltIn::PrimitivePointIndicesEXT;
+
+    // These builtin have the ability to be an array with MeshEXT
+    // When an array, we need to make sure the array size lines up
+    std::map<uint32_t, uint32_t> entry_interface_id_map;
+    const bool is_interface_var =
+        IsMeshInterfaceVar(inst, entry_interface_id_map);
+
+    if (!is_topology) {
+      bool is_block = false;
+
+      static const std::unordered_map<spv::BuiltIn, MeshBuiltinVUIDs>
+          mesh_vuid_map = {{
+              {spv::BuiltIn::CullPrimitiveEXT, {7036, 10589, 10590, 7038}},
+              {spv::BuiltIn::PrimitiveId, {10595, 10596, 10597, 7040}},
+              {spv::BuiltIn::Layer, {10592, 10593, 10594, 7039}},
+              {spv::BuiltIn::ViewportIndex, {10601, 10602, 10603, 7060}},
+              {spv::BuiltIn::PrimitiveShadingRateKHR,
+               {10598, 10599, 10600, 7059}},
+          }};
+      const MeshBuiltinVUIDs& vuids = mesh_vuid_map.at(builtin);
+      if (spv_result_t error = ValidateBlockTypeOrArrayedType(
+              decoration, inst, is_block, scalar_type,
+              [this, &inst, &builtin, &scalar_type,
+               &vuids](const std::string& message) -> spv_result_t {
                 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
-                       << _.VkErrorID(vuid)
-                       << "According to the Vulkan spec BuiltIn "
+                       << _.VkErrorID(vuids.array_type)
+                       << "According to the Vulkan specspec BuiltIn "
                        << _.grammar().lookupOperandName(
-                              SPV_OPERAND_TYPE_BUILT_IN,
-                              (uint32_t)decoration.builtin())
-                       << "variable needs to be a 32-bit int scalar. "
-                       << message;
+                              SPV_OPERAND_TYPE_BUILT_IN, (uint32_t)builtin)
+                       << " variable needs to be a either a "
+                       << spvOpcodeString(scalar_type)
+                       << " or an "
+                          "array of "
+                       << spvOpcodeString(scalar_type) << ". " << message;
               })) {
         return error;
       }
-    } else {
-      if (spv_result_t error = ValidateOptionalArrayedI32(
+
+      if (!_.HasDecoration(inst.id(), spv::Decoration::PerPrimitiveEXT)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+               << _.VkErrorID(vuids.perprim_deco)
+               << "According to the Vulkan spec the variable decorated with "
+                  "Builtin "
+               << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
+                                                (uint32_t)builtin)
+               << " within the MeshEXT Execution Model must also be "
+               << "decorated with the PerPrimitiveEXT decoration. ";
+      }
+      if (is_interface_var) {
+        for (const auto& id : entry_interface_id_map) {
+          uint32_t entry_point_id = id.first;
+          uint32_t interface_var_id = id.second;
+
+          const uint64_t interface_size = GetArrayLength(interface_var_id);
+          const uint32_t output_prim_size =
+              _.GetOutputPrimitivesEXT(entry_point_id);
+          if (interface_size != output_prim_size) {
+            return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                   << _.VkErrorID(is_block ? vuids.block_array_size
+                                           : vuids.array_size)
+                   << " The size of the array decorated with "
+                   << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
+                                                    (uint32_t)builtin)
+                   << " (" << interface_size
+                   << ") must match the value specified by OutputPrimitivesEXT "
+                      "("
+                   << output_prim_size << "). ";
+          }
+        }
+      }
+    }
+
+    if (is_interface_var && is_topology) {
+      for (const auto& id : entry_interface_id_map) {
+        uint32_t entry_point_id = id.first;
+
+        uint64_t max_output_primitives =
+            _.GetOutputPrimitivesEXT(entry_point_id);
+        uint32_t underlying_type = 0;
+        if (spv_result_t error =
+                GetUnderlyingType(_, decoration, inst, &underlying_type)) {
+          return error;
+        }
+
+        uint64_t primitive_array_dim = 0;
+        if (_.GetIdOpcode(underlying_type) == spv::Op::OpTypeArray) {
+          underlying_type = _.FindDef(underlying_type)->word(3u);
+          if (!_.EvalConstantValUint64(underlying_type, &primitive_array_dim)) {
+            assert(0 && "Array type definition is corrupt");
+          }
+        }
+
+        const auto* modes = _.GetExecutionModes(entry_point_id);
+        if (builtin == spv::BuiltIn::PrimitiveTriangleIndicesEXT) {
+          if (!modes || !modes->count(spv::ExecutionMode::OutputTrianglesEXT)) {
+            return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                   << _.VkErrorID(7054)
+                   << "The PrimitiveTriangleIndicesEXT decoration must be used "
+                      "with the OutputTrianglesEXT Execution Mode. ";
+          }
+          if (primitive_array_dim &&
+              primitive_array_dim != max_output_primitives) {
+            return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                   << _.VkErrorID(7058)
+                   << "The size of the array decorated with "
+                      "PrimitiveTriangleIndicesEXT ("
+                   << primitive_array_dim
+                   << ") must match the value specified "
+                      "by OutputPrimitivesEXT ("
+                   << max_output_primitives << "). ";
+          }
+        } else if (builtin == spv::BuiltIn::PrimitiveLineIndicesEXT) {
+          if (!modes || !modes->count(spv::ExecutionMode::OutputLinesEXT)) {
+            return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                   << _.VkErrorID(7048)
+                   << "The PrimitiveLineIndicesEXT decoration must be used "
+                      "with the OutputLinesEXT Execution Mode. ";
+          }
+          if (primitive_array_dim &&
+              primitive_array_dim != max_output_primitives) {
+            return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                   << _.VkErrorID(7052)
+                   << "The size of the array decorated with "
+                      "PrimitiveLineIndicesEXT ("
+                   << primitive_array_dim
+                   << ") must match the value specified "
+                      "by OutputPrimitivesEXT ("
+                   << max_output_primitives << "). ";
+          }
+
+        } else if (builtin == spv::BuiltIn::PrimitivePointIndicesEXT) {
+          if (!modes || !modes->count(spv::ExecutionMode::OutputPoints)) {
+            return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                   << _.VkErrorID(7042)
+                   << "The PrimitivePointIndicesEXT decoration must be used "
+                      "with the OutputPoints Execution Mode. ";
+          }
+          if (primitive_array_dim &&
+              primitive_array_dim != max_output_primitives) {
+            return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                   << _.VkErrorID(7046)
+                   << "The size of the array decorated with "
+                      "PrimitivePointIndicesEXT ("
+                   << primitive_array_dim
+                   << ") must match the value specified "
+                      "by OutputPrimitivesEXT ("
+                   << max_output_primitives << "). ";
+          }
+        }
+      }
+    }
+  } else {
+    // Propagate this rule to all dependant ids in the global scope.
+    id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
+        std::bind(&BuiltInsValidator::ValidateMeshBuiltinInterfaceRules, this,
+                  decoration, inst, scalar_type, std::placeholders::_1));
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t BuiltInsValidator::ValidatePrimitiveShadingRateInterfaceRules(
+    const Decoration& decoration, const Instruction& inst,
+    const Instruction& referenced_from_inst) {
+  if (function_id_) {
+    if (!execution_models_.count(spv::ExecutionModel::MeshEXT)) {
+      if (spv_result_t error = ValidateI32(
               decoration, inst,
-              [this, &decoration,
-               &inst](const std::string& message) -> spv_result_t {
-                uint32_t vuid =
-                    (decoration.builtin() == spv::BuiltIn::Layer) ? 4276 : 4408;
+              [this, &inst,
+               &decoration](const std::string& message) -> spv_result_t {
                 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
-                       << _.VkErrorID(vuid)
+                       << _.VkErrorID(4486)
                        << "According to the Vulkan spec BuiltIn "
                        << _.grammar().lookupOperandName(
                               SPV_OPERAND_TYPE_BUILT_IN,
                               (uint32_t)decoration.builtin())
-                       << "variable needs to be a 32-bit int scalar. "
+                       << " variable needs to be a 32-bit int scalar. "
                        << message;
               })) {
         return error;
       }
     }
+  } else {
+    // Propagate this rule to all dependant ids in the global scope.
+    id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
+        &BuiltInsValidator::ValidatePrimitiveShadingRateInterfaceRules, this,
+        decoration, inst, std::placeholders::_1));
   }
+  return SPV_SUCCESS;
+}
 
-  // Seed at reference checks with this built-in.
+// For Layer, ViewportIndex, and PrimitiveId
+spv_result_t BuiltInsValidator::ValidateNonMeshInterfaceRules(
+    const Decoration& decoration, const Instruction& inst,
+    const Instruction& referenced_from_inst) {
+  if (function_id_) {
+    // This can be a per-primitive variable for NV mesh shader stage.
+    // In such cases variable will have an array of 32-bit integers.
+    if (!execution_models_.count(spv::ExecutionModel::MeshEXT)) {
+      const spv::BuiltIn builtin = decoration.builtin();
+      const uint32_t vuid = (builtin == spv::BuiltIn::Layer)           ? 4276
+                            : (builtin == spv::BuiltIn::ViewportIndex) ? 4408
+                                                                       : 4337;
+      if (decoration.struct_member_index() != Decoration::kInvalidMember) {
+        if (spv_result_t error = ValidateI32(
+                decoration, inst,
+                [this, &vuid, builtin,
+                 &inst](const std::string& message) -> spv_result_t {
+                  return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                         << _.VkErrorID(vuid)
+                         << "According to the Vulkan spec BuiltIn "
+                         << _.grammar().lookupOperandName(
+                                SPV_OPERAND_TYPE_BUILT_IN, (uint32_t)builtin)
+                         << "variable needs to be a 32-bit int scalar. "
+                         << message;
+                })) {
+          return error;
+        }
+      } else if (spv_result_t error = ValidateOptionalArrayedI32(
+                     decoration, inst,
+                     [this, &vuid, builtin,
+                      &inst](const std::string& message) -> spv_result_t {
+                       return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                              << _.VkErrorID(vuid)
+                              << "According to the Vulkan spec BuiltIn "
+                              << _.grammar().lookupOperandName(
+                                     SPV_OPERAND_TYPE_BUILT_IN,
+                                     (uint32_t)builtin)
+                              << "variable needs to be a 32-bit int scalar. "
+                              << message;
+                     })) {
+        return error;
+      }
+    }
+  } else {
+    // Propagate this rule to all dependant ids in the global scope.
+    id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
+        std::bind(&BuiltInsValidator::ValidateNonMeshInterfaceRules, this,
+                  decoration, inst, std::placeholders::_1));
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtDefinition(
+    const Decoration& decoration, const Instruction& inst) {
   return ValidateLayerOrViewportIndexAtReference(decoration, inst, inst, inst);
 }
 
@@ -2825,6 +3306,14 @@
                     referenced_from_inst, std::placeholders::_1));
     }
 
+    id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
+        &BuiltInsValidator::ValidateMeshBuiltinInterfaceRules, this, decoration,
+        built_in_inst, spv::Op::OpTypeInt, std::placeholders::_1));
+
+    id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
+        std::bind(&BuiltInsValidator::ValidateNonMeshInterfaceRules, this,
+                  decoration, built_in_inst, std::placeholders::_1));
+
     for (const spv::ExecutionModel execution_model : execution_models_) {
       switch (execution_model) {
         case spv::ExecutionModel::Geometry:
@@ -3215,16 +3704,8 @@
 
 spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtDefinition(
     const Decoration& decoration, const Instruction& inst) {
+  // Vulkan requires 32-bit int, but Universal has no restrictions
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    if (spvIsVulkanEnv(_.context()->target_env) &&
-        !spvOpcodeIsConstant(inst.opcode())) {
-      return _.diag(SPV_ERROR_INVALID_DATA, &inst)
-             << _.VkErrorID(4426)
-             << "Vulkan spec requires BuiltIn WorkgroupSize to be a "
-                "constant. "
-             << GetIdDesc(inst) << " is not a constant.";
-    }
-
     if (spv_result_t error = ValidateI32Vec(
             decoration, inst, 3,
             [this, &inst](const std::string& message) -> spv_result_t {
@@ -3239,6 +3720,69 @@
     }
   }
 
+  if (!spvOpcodeIsConstant(inst.opcode())) {
+    if (spvIsVulkanEnv(_.context()->target_env)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+             << _.VkErrorID(4426)
+             << "Vulkan spec requires BuiltIn WorkgroupSize to be a "
+                "constant. "
+             << GetIdDesc(inst) << " is not a constant.";
+    }
+  } else if (inst.opcode() == spv::Op::OpConstantComposite) {
+    // can only validate product if static and not spec constant
+    if (_.FindDef(inst.word(3))->opcode() == spv::Op::OpConstant &&
+        _.FindDef(inst.word(4))->opcode() == spv::Op::OpConstant &&
+        _.FindDef(inst.word(5))->opcode() == spv::Op::OpConstant) {
+      uint64_t x_size, y_size, z_size;
+      // ValidateI32Vec above confirms there will be 3 words to read
+      bool static_x = _.EvalConstantValUint64(inst.word(3), &x_size);
+      bool static_y = _.EvalConstantValUint64(inst.word(4), &y_size);
+      bool static_z = _.EvalConstantValUint64(inst.word(5), &z_size);
+      if (static_x && static_y && static_z) {
+        const uint64_t product_size = x_size * y_size * z_size;
+        if (product_size == 0) {
+          return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                 << "WorkgroupSize decorations must not have a static "
+                    "product of zero (X = "
+                 << x_size << ", Y = " << y_size << ", Z = " << z_size << ").";
+        }
+
+        // If there is a known static workgroup size, all entrypoints with
+        // explicit derivative execution modes can be validated. These are only
+        // found in execution models that support explicit workgroup sizes
+        for (const uint32_t entry_point : _.entry_points()) {
+          const auto* modes = _.GetExecutionModes(entry_point);
+          if (!modes) continue;
+          if (modes->count(spv::ExecutionMode::DerivativeGroupQuadsKHR)) {
+            if (x_size % 2 != 0 || y_size % 2 != 0) {
+              return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                     << _.VkErrorID(10151)
+                     << "WorkgroupSize decorations has a static dimensions of "
+                        "(X = "
+                     << x_size << ", Y = " << y_size << ") but Entry Point id "
+                     << entry_point
+                     << " has an DerivativeGroupQuadsKHR execution mode, so "
+                        "both dimensions must be a multiple of 2";
+            }
+          }
+          if (modes->count(spv::ExecutionMode::DerivativeGroupLinearKHR)) {
+            if (product_size % 4 != 0) {
+              return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                     << _.VkErrorID(10152)
+                     << "WorkgroupSize decorations has a static dimensions of "
+                        "(X = "
+                     << x_size << ", Y = " << y_size << ", Z = " << z_size
+                     << ") but Entry Point id " << entry_point
+                     << " has an DerivativeGroupLinearKHR execution mode, so "
+                        "the product ("
+                     << product_size << ") must be a multiple of 4";
+            }
+          }
+        }
+      }
+    }
+  }
+
   // Seed at reference checks with this built-in.
   return ValidateWorkgroupSizeAtReference(decoration, inst, inst, inst);
 }
@@ -3877,25 +4421,6 @@
 
 spv_result_t BuiltInsValidator::ValidatePrimitiveShadingRateAtDefinition(
     const Decoration& decoration, const Instruction& inst) {
-  if (spvIsVulkanEnv(_.context()->target_env)) {
-    if (spv_result_t error = ValidateI32(
-            decoration, inst,
-            [this, &inst,
-             &decoration](const std::string& message) -> spv_result_t {
-              return _.diag(SPV_ERROR_INVALID_DATA, &inst)
-                     << _.VkErrorID(4486)
-                     << "According to the Vulkan spec BuiltIn "
-                     << _.grammar().lookupOperandName(
-                            SPV_OPERAND_TYPE_BUILT_IN,
-                            (uint32_t)decoration.builtin())
-                     << " variable needs to be a 32-bit int scalar. "
-                     << message;
-            })) {
-      return error;
-    }
-  }
-
-  // Seed at reference checks with this built-in.
   return ValidatePrimitiveShadingRateAtReference(decoration, inst, inst, inst);
 }
 
@@ -3917,6 +4442,14 @@
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
 
+    id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
+        &BuiltInsValidator::ValidateMeshBuiltinInterfaceRules, this, decoration,
+        built_in_inst, spv::Op::OpTypeInt, std::placeholders::_1));
+
+    id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
+        &BuiltInsValidator::ValidatePrimitiveShadingRateInterfaceRules, this,
+        decoration, built_in_inst, std::placeholders::_1));
+
     for (const spv::ExecutionModel execution_model : execution_models_) {
       switch (execution_model) {
         case spv::ExecutionModel::Vertex:
@@ -3930,7 +4463,7 @@
                  << _.grammar().lookupOperandName(
                         SPV_OPERAND_TYPE_BUILT_IN,
                         (uint32_t)decoration.builtin())
-                 << " to be used only with Vertex, Geometry, or MeshNV "
+                 << " to be used only with Vertex, Geometry, MeshNV or MeshEXT "
                     "execution models. "
                  << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
                                      referenced_from_inst, execution_model);
@@ -4015,6 +4548,18 @@
   return SPV_SUCCESS;
 }
 
+spv_result_t BuiltInsValidator::ValidateDescriptorHeapAtDefinition(
+    const Decoration& decoration, const Instruction& inst) {
+  if (decoration.struct_member_index() != Decoration::kInvalidMember) {
+    return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+           << "BuiltIn "
+           << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
+                                            (uint32_t)decoration.builtin())
+           << " cannot be used as a member decoration ";
+  }
+  return SPV_SUCCESS;
+}
+
 spv_result_t BuiltInsValidator::ValidateRayTracingBuiltinsAtDefinition(
     const Decoration& decoration, const Instruction& inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
@@ -4126,6 +4671,25 @@
           return error;
         }
         break;
+      case spv::BuiltIn::HitTriangleVertexPositionsKHR:
+        // array[3] of f32 vec3
+        if (spv_result_t error = ValidateArrayedF32Vec(
+                decoration, inst, 3, 3,
+                [this, &inst,
+                 builtin](const std::string& message) -> spv_result_t {
+                  uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
+                  return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                         << _.VkErrorID(vuid)
+                         << "According to the Vulkan spec BuiltIn "
+                         << _.grammar().lookupOperandName(
+                                SPV_OPERAND_TYPE_BUILT_IN, uint32_t(builtin))
+                         << " variable needs to be an array of 3, 32-bit float "
+                            "3-component vectors. "
+                         << message;
+                })) {
+          return error;
+        }
+        break;
       default:
         assert(0 && "Unexpected ray tracing builtin");
         break;
@@ -4190,63 +4754,104 @@
   if (spvIsVulkanEnv(_.context()->target_env)) {
     const spv::BuiltIn builtin = decoration.builtin();
     uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
-    if (builtin == spv::BuiltIn::PrimitivePointIndicesEXT) {
-      if (spv_result_t error = ValidateI32Arr(
-              decoration, inst,
-              [this, &inst, &decoration,
-               &vuid](const std::string& message) -> spv_result_t {
-                return _.diag(SPV_ERROR_INVALID_DATA, &inst)
-                       << _.VkErrorID(vuid) << "According to the "
-                       << spvLogStringForEnv(_.context()->target_env)
-                       << " spec BuiltIn "
-                       << _.grammar().lookupOperandName(
-                              SPV_OPERAND_TYPE_BUILT_IN,
-                              (uint32_t)decoration.builtin())
-                       << " variable needs to be a 32-bit int array."
-                       << message;
-              })) {
-        return error;
+    switch (builtin) {
+      case spv::BuiltIn::PrimitivePointIndicesEXT:
+        if (spv_result_t error = ValidateI32Arr(
+                decoration, inst,
+                [this, &inst, &decoration,
+                 &vuid](const std::string& message) -> spv_result_t {
+                  return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                         << _.VkErrorID(vuid) << "According to the "
+                         << spvLogStringForEnv(_.context()->target_env)
+                         << " spec BuiltIn "
+                         << _.grammar().lookupOperandName(
+                                SPV_OPERAND_TYPE_BUILT_IN,
+                                (uint32_t)decoration.builtin())
+                         << " variable needs to be a 32-bit int array."
+                         << message;
+                })) {
+          return error;
+        }
+        break;
+      case spv::BuiltIn::PrimitiveLineIndicesEXT:
+        if (spv_result_t error = ValidateArrayedI32Vec(
+                decoration, inst, 2,
+                [this, &inst, &decoration,
+                 &vuid](const std::string& message) -> spv_result_t {
+                  return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                         << _.VkErrorID(vuid) << "According to the "
+                         << spvLogStringForEnv(_.context()->target_env)
+                         << " spec BuiltIn "
+                         << _.grammar().lookupOperandName(
+                                SPV_OPERAND_TYPE_BUILT_IN,
+                                (uint32_t)decoration.builtin())
+                         << " variable needs to be a 2-component 32-bit int "
+                            "array."
+                         << message;
+                })) {
+          return error;
+        }
+        break;
+      case spv::BuiltIn::PrimitiveTriangleIndicesEXT:
+        if (spv_result_t error = ValidateArrayedI32Vec(
+                decoration, inst, 3,
+                [this, &inst, &decoration,
+                 &vuid](const std::string& message) -> spv_result_t {
+                  return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                         << _.VkErrorID(vuid) << "According to the "
+                         << spvLogStringForEnv(_.context()->target_env)
+                         << " spec BuiltIn "
+                         << _.grammar().lookupOperandName(
+                                SPV_OPERAND_TYPE_BUILT_IN,
+                                (uint32_t)decoration.builtin())
+                         << " variable needs to be a 3-component 32-bit int "
+                            "array."
+                         << message;
+                })) {
+          return error;
+        }
+        break;
+      case spv::BuiltIn::CullPrimitiveEXT: {
+        for (const uint32_t entry_point : _.entry_points()) {
+          auto* models = _.GetExecutionModels(entry_point);
+          if (models->find(spv::ExecutionModel::MeshEXT) == models->end() &&
+              models->find(spv::ExecutionModel::MeshNV) == models->end()) {
+            continue;
+          }
+
+          if (IsBulitinInEntryPoint(inst, entry_point)) {
+            if (cull_primitive_entry_points_.find(entry_point) !=
+                cull_primitive_entry_points_.end()) {
+              return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                     << _.VkErrorID(10591)
+                     << "There must be only one declaration of the "
+                        "CullPrimitiveEXT associated in entry point's "
+                        "interface. "
+                     << GetIdDesc(*_.FindDef(entry_point));
+            } else {
+              cull_primitive_entry_points_.insert(entry_point);
+            }
+          }
+        }
+
+        break;
       }
+      default:
+        assert(0 && "Unexpected mesh EXT builtin");
     }
-    if (builtin == spv::BuiltIn::PrimitiveLineIndicesEXT) {
-      if (spv_result_t error = ValidateArrayedI32Vec(
-              decoration, inst, 2,
-              [this, &inst, &decoration,
-               &vuid](const std::string& message) -> spv_result_t {
-                return _.diag(SPV_ERROR_INVALID_DATA, &inst)
-                       << _.VkErrorID(vuid) << "According to the "
-                       << spvLogStringForEnv(_.context()->target_env)
-                       << " spec BuiltIn "
-                       << _.grammar().lookupOperandName(
-                              SPV_OPERAND_TYPE_BUILT_IN,
-                              (uint32_t)decoration.builtin())
-                       << " variable needs to be a 2-component 32-bit int "
-                          "array."
-                       << message;
-              })) {
-        return error;
-      }
-    }
-    if (builtin == spv::BuiltIn::PrimitiveTriangleIndicesEXT) {
-      if (spv_result_t error = ValidateArrayedI32Vec(
-              decoration, inst, 3,
-              [this, &inst, &decoration,
-               &vuid](const std::string& message) -> spv_result_t {
-                return _.diag(SPV_ERROR_INVALID_DATA, &inst)
-                       << _.VkErrorID(vuid) << "According to the "
-                       << spvLogStringForEnv(_.context()->target_env)
-                       << " spec BuiltIn "
-                       << _.grammar().lookupOperandName(
-                              SPV_OPERAND_TYPE_BUILT_IN,
-                              (uint32_t)decoration.builtin())
-                       << " variable needs to be a 3-component 32-bit int "
-                          "array."
-                       << message;
-              })) {
-        return error;
-      }
+
+    // - We know this only allowed for Mesh Execution Model.
+    // - The Scalar type is is boolean for CullPrimitiveEXT, the other 3 builtin
+    // (topology) don't need this type.
+    // - It is possible to have multiple mesh
+    // shaders (https://github.com/KhronosGroup/SPIRV-Tools/issues/6320) and we
+    // need to validate these at reference time.
+    if (spv_result_t error = ValidateMeshBuiltinInterfaceRules(
+            decoration, inst, spv::Op::OpTypeBool, inst)) {
+      return error;
     }
   }
+
   // Seed at reference checks with this built-in.
   return ValidateMeshShadingEXTBuiltinsAtReference(decoration, inst, inst,
                                                    inst);
@@ -4273,7 +4878,6 @@
                                  referenced_from_inst)
              << " " << GetStorageClassDesc(referenced_from_inst);
     }
-
     for (const spv::ExecutionModel execution_model : execution_models_) {
       if (execution_model != spv::ExecutionModel::MeshEXT) {
         uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
@@ -4304,18 +4908,20 @@
 spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
     const Decoration& decoration, const Instruction& inst) {
   const spv::BuiltIn label = decoration.builtin();
-
-  if (!spvIsVulkanEnv(_.context()->target_env)) {
-    // Early return. All currently implemented rules are based on Vulkan spec.
-    //
-    // TODO: If you are adding validation rules for environments other than
-    // Vulkan (or general rules which are not environment independent), then
-    // you need to modify or remove this condition. Consider also adding early
-    // returns into BuiltIn-specific rules, so that the system doesn't spawn new
-    // rules which don't do anything.
-    return SPV_SUCCESS;
+  // Universial checks
+  if (label == spv::BuiltIn::WorkgroupSize) {
+    return ValidateWorkgroupSizeAtDefinition(decoration, inst);
   }
 
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    return ValidateSingleBuiltInAtDefinitionVulkan(decoration, inst, label);
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinitionVulkan(
+    const Decoration& decoration, const Instruction& inst,
+    const spv::BuiltIn label) {
   // If you are adding a new BuiltIn enum, please register it here.
   // If the newly added enum has validation rules associated with it
   // consider leaving a TODO and/or creating an issue.
@@ -4407,9 +5013,6 @@
     case spv::BuiltIn::VertexIndex: {
       return ValidateVertexIndexAtDefinition(decoration, inst);
     }
-    case spv::BuiltIn::WorkgroupSize: {
-      return ValidateWorkgroupSizeAtDefinition(decoration, inst);
-    }
     case spv::BuiltIn::VertexId: {
       return ValidateVertexIdAtDefinition(decoration, inst);
     }
@@ -4455,27 +5058,28 @@
       return ValidateFullyCoveredAtDefinition(decoration, inst);
     }
     // Ray tracing builtins
-    case spv::BuiltIn::HitKindKHR:  // alias spv::BuiltIn::HitKindNV
+    case spv::BuiltIn::HitKindKHR:  // alias HitKindNV
     case spv::BuiltIn::HitTNV:      // NOT present in KHR
     case spv::BuiltIn::InstanceId:
-    case spv::BuiltIn::LaunchIdKHR:           // alias spv::BuiltIn::LaunchIdNV
-    case spv::BuiltIn::LaunchSizeKHR:         // alias spv::BuiltIn::LaunchSizeNV
-    case spv::BuiltIn::WorldRayOriginKHR:     // alias spv::BuiltIn::WorldRayOriginNV
-    case spv::BuiltIn::WorldRayDirectionKHR:  // alias spv::BuiltIn::WorldRayDirectionNV
-    case spv::BuiltIn::ObjectRayOriginKHR:    // alias spv::BuiltIn::ObjectRayOriginNV
-    case spv::BuiltIn::ObjectRayDirectionKHR:   // alias
-                                            // spv::BuiltIn::ObjectRayDirectionNV
-    case spv::BuiltIn::RayTminKHR:              // alias spv::BuiltIn::RayTminNV
-    case spv::BuiltIn::RayTmaxKHR:              // alias spv::BuiltIn::RayTmaxNV
-    case spv::BuiltIn::InstanceCustomIndexKHR:  // alias
-                                            // spv::BuiltIn::InstanceCustomIndexNV
-    case spv::BuiltIn::ObjectToWorldKHR:        // alias spv::BuiltIn::ObjectToWorldNV
-    case spv::BuiltIn::WorldToObjectKHR:        // alias spv::BuiltIn::WorldToObjectNV
-    case spv::BuiltIn::IncomingRayFlagsKHR:    // alias spv::BuiltIn::IncomingRayFlagsNV
+    case spv::BuiltIn::LaunchIdKHR:             // alias LaunchIdNV
+    case spv::BuiltIn::LaunchSizeKHR:           // alias LaunchSizeNV
+    case spv::BuiltIn::WorldRayOriginKHR:       // alias WorldRayOriginNV
+    case spv::BuiltIn::WorldRayDirectionKHR:    // alias WorldRayDirectionNV
+    case spv::BuiltIn::ObjectRayOriginKHR:      // alias ObjectRayOriginNV
+    case spv::BuiltIn::ObjectRayDirectionKHR:   // alias ObjectRayDirectionNV
+    case spv::BuiltIn::RayTminKHR:              // alias RayTminNV
+    case spv::BuiltIn::RayTmaxKHR:              // alias RayTmaxNV
+    case spv::BuiltIn::InstanceCustomIndexKHR:  // alias InstanceCustomIndexNV
+    case spv::BuiltIn::ObjectToWorldKHR:        // alias ObjectToWorldNV
+    case spv::BuiltIn::WorldToObjectKHR:        // alias WorldToObjectNV
+    case spv::BuiltIn::IncomingRayFlagsKHR:     // alias IncomingRayFlagsNV
     case spv::BuiltIn::RayGeometryIndexKHR:    // NOT present in NV
+    case spv::BuiltIn::HitTriangleVertexPositionsKHR:
     case spv::BuiltIn::CullMaskKHR: {
       return ValidateRayTracingBuiltinsAtDefinition(decoration, inst);
     }
+    // These are only for Mesh, not Task execution model
+    case spv::BuiltIn::CullPrimitiveEXT:
     case spv::BuiltIn::PrimitivePointIndicesEXT:
     case spv::BuiltIn::PrimitiveLineIndicesEXT:
     case spv::BuiltIn::PrimitiveTriangleIndicesEXT: {
@@ -4487,6 +5091,10 @@
     case spv::BuiltIn::ShadingRateKHR: {
       return ValidateShadingRateAtDefinition(decoration, inst);
     }
+    case spv::BuiltIn::SamplerHeapEXT:
+    case spv::BuiltIn::ResourceHeapEXT: {
+      return ValidateDescriptorHeapAtDefinition(decoration, inst);
+    }
     default:
       // No validation rules (for the moment).
       break;
diff --git a/source/val/validate_capability.cpp b/source/val/validate_capability.cpp
index 81d2ad5..aae9dd9 100644
--- a/source/val/validate_capability.cpp
+++ b/source/val/validate_capability.cpp
@@ -18,6 +18,7 @@
 #include <string>
 
 #include "source/opcode.h"
+#include "source/table2.h"
 #include "source/val/instruction.h"
 #include "source/val/validate.h"
 #include "source/val/validation_state.h"
@@ -67,6 +68,50 @@
   return false;
 }
 
+bool IsSupportGuaranteedVulkan_1_3(uint32_t capability) {
+  if (IsSupportGuaranteedVulkan_1_2(capability)) return true;
+  switch (spv::Capability(capability)) {
+    case spv::Capability::DotProduct:
+    case spv::Capability::DotProductInputAll:
+    case spv::Capability::DotProductInput4x8Bit:
+    case spv::Capability::DotProductInput4x8BitPacked:
+    case spv::Capability::VulkanMemoryModel:
+    case spv::Capability::VulkanMemoryModelDeviceScope:
+    case spv::Capability::PhysicalStorageBufferAddresses:
+    case spv::Capability::DemoteToHelperInvocation:
+      return true;
+    default:
+      break;
+  }
+  return false;
+}
+
+bool IsSupportGuaranteedVulkan_1_4(uint32_t capability) {
+  if (IsSupportGuaranteedVulkan_1_3(capability)) return true;
+  switch (spv::Capability(capability)) {
+    case spv::Capability::UniformBufferArrayDynamicIndexing:
+    case spv::Capability::SampledImageArrayDynamicIndexing:
+    case spv::Capability::StorageBufferArrayDynamicIndexing:
+    case spv::Capability::StorageImageArrayDynamicIndexing:
+    case spv::Capability::Int16:
+    case spv::Capability::StorageBuffer16BitAccess:
+    case spv::Capability::VariablePointers:
+    case spv::Capability::VariablePointersStorageBuffer:
+    case spv::Capability::UniformTexelBufferArrayDynamicIndexing:
+    case spv::Capability::StorageTexelBufferArrayDynamicIndexing:
+    case spv::Capability::Int8:
+    case spv::Capability::StorageBuffer8BitAccess:
+    case spv::Capability::FloatControls2:
+    case spv::Capability::SampleRateShading:
+    case spv::Capability::StorageImageExtendedFormats:
+    case spv::Capability::ImageGatherExtended:
+      return true;
+    default:
+      break;
+  }
+  return false;
+}
+
 bool IsSupportOptionalVulkan_1_0(uint32_t capability) {
   switch (spv::Capability(capability)) {
     case spv::Capability::Geometry:
@@ -100,6 +145,9 @@
     case spv::Capability::GeometryStreams:
     case spv::Capability::Float16:
     case spv::Capability::Int8:
+    case spv::Capability::BFloat16TypeKHR:
+    case spv::Capability::Float8EXT:
+    case spv::Capability::PushConstantBanksNV:
       return true;
     default:
       break;
@@ -164,6 +212,8 @@
     case spv::Capability::InputAttachmentArrayNonUniformIndexing:
     case spv::Capability::UniformTexelBufferArrayNonUniformIndexing:
     case spv::Capability::StorageTexelBufferArrayNonUniformIndexing:
+    case spv::Capability::InputAttachmentArrayDynamicIndexing:
+    case spv::Capability::ShaderNonUniform:
       return true;
     default:
       break;
@@ -171,6 +221,16 @@
   return false;
 }
 
+// Vulkan 1.3 only added required features.
+bool IsSupportOptionalVulkan_1_3(uint32_t capability) {
+  return IsSupportOptionalVulkan_1_2(capability);
+}
+
+// Vulkan 1.4 only added required features.
+bool IsSupportOptionalVulkan_1_4(uint32_t capability) {
+  return IsSupportOptionalVulkan_1_3(capability);
+}
+
 bool IsSupportGuaranteedOpenCL_1_2(uint32_t capability, bool embedded_profile) {
   switch (spv::Capability(capability)) {
     case spv::Capability::Addresses:
@@ -221,6 +281,7 @@
   switch (spv::Capability(capability)) {
     case spv::Capability::ImageBasic:
     case spv::Capability::Float64:
+    case spv::Capability::Float16:
       return true;
     default:
       break;
@@ -230,16 +291,16 @@
 
 // Checks if |capability| was enabled by extension.
 bool IsEnabledByExtension(ValidationState_t& _, uint32_t capability) {
-  spv_operand_desc operand_desc = nullptr;
-  _.grammar().lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability,
-                            &operand_desc);
+  const spvtools::OperandDesc* operand_desc = nullptr;
+  spvtools::LookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability,
+                          &operand_desc);
 
   // operand_desc is expected to be not null, otherwise validator would have
   // failed at an earlier stage. This 'assert' is 'just in case'.
   assert(operand_desc);
 
-  ExtensionSet operand_exts(operand_desc->numExtensions,
-                            operand_desc->extensions);
+  ExtensionSet operand_exts(operand_desc->extensions_range.count(),
+                            operand_desc->extensions().data());
   if (operand_exts.empty()) return false;
 
   return _.HasAnyOfExtensions(operand_exts);
@@ -287,24 +348,31 @@
 // Validates that capability declarations use operands allowed in the current
 // context.
 spv_result_t CapabilityPass(ValidationState_t& _, const Instruction* inst) {
-  if (inst->opcode() != spv::Op::OpCapability) return SPV_SUCCESS;
+  if (inst->opcode() != spv::Op::OpCapability &&
+      inst->opcode() != spv::Op::OpConditionalCapabilityINTEL)
+    return SPV_SUCCESS;
 
-  assert(inst->operands().size() == 1);
+  assert(!((inst->opcode() == spv::Op::OpCapability) ^
+           (inst->operands().size() == 1)));
+  assert(!((inst->opcode() == spv::Op::OpConditionalCapabilityINTEL) ^
+           (inst->operands().size() == 2)));
 
-  const spv_parsed_operand_t& operand = inst->operand(0);
+  const uint32_t i_cap =
+      inst->opcode() == spv::Op::OpConditionalCapabilityINTEL ? 1 : 0;
+  const spv_parsed_operand_t& operand = inst->operand(i_cap);
 
   assert(operand.num_words == 1);
   assert(operand.offset < inst->words().size());
 
   const uint32_t capability = inst->word(operand.offset);
-  const auto capability_str = [&_, capability]() {
-    spv_operand_desc desc = nullptr;
-    if (_.grammar().lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability,
-                                  &desc) != SPV_SUCCESS ||
+  const auto capability_str = [capability]() {
+    const spvtools::OperandDesc* desc = nullptr;
+    if (spvtools::LookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability,
+                                &desc) != SPV_SUCCESS ||
         !desc) {
       return std::string("Unknown");
     }
-    return std::string(desc->name);
+    return std::string(desc->name().data());
   };
 
   const auto env = _.context()->target_env;
@@ -340,6 +408,24 @@
              << " is not allowed by Vulkan 1.2 specification"
              << " (or requires extension)";
     }
+  } else if (env == SPV_ENV_VULKAN_1_3) {
+    if (!IsSupportGuaranteedVulkan_1_3(capability) &&
+        !IsSupportOptionalVulkan_1_3(capability) &&
+        !IsEnabledByExtension(_, capability)) {
+      return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
+             << "Capability " << capability_str()
+             << " is not allowed by Vulkan 1.3 specification"
+             << " (or requires extension)";
+    }
+  } else if (env == SPV_ENV_VULKAN_1_4) {
+    if (!IsSupportGuaranteedVulkan_1_4(capability) &&
+        !IsSupportOptionalVulkan_1_4(capability) &&
+        !IsEnabledByExtension(_, capability)) {
+      return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
+             << "Capability " << capability_str()
+             << " is not allowed by Vulkan 1.4 specification"
+             << " (or requires extension)";
+    }
   } else if (env == SPV_ENV_OPENCL_1_2 || env == SPV_ENV_OPENCL_EMBEDDED_1_2) {
     if (!IsSupportGuaranteedOpenCL_1_2(capability, opencl_embedded) &&
         !IsSupportOptionalOpenCL_1_2(capability) &&
diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp
index df127f9..57681c2 100644
--- a/source/val/validate_cfg.cpp
+++ b/source/val/validate_cfg.cpp
@@ -353,6 +353,34 @@
   return SPV_SUCCESS;
 }
 
+spv_result_t ValidateLifetime(ValidationState_t& _, const Instruction* inst) {
+  const uint32_t pointer_id = _.GetOperandTypeId(inst, 0);
+  const Instruction* pointer_inst = _.FindDef(pointer_id);
+  if (pointer_inst->opcode() != spv::Op::OpTypePointer) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Op" << spvOpcodeString(inst->opcode())
+           << " pointer operand type must be a OpTypePointer.";
+  } else if (pointer_inst->GetOperandAs<spv::StorageClass>(1) !=
+             spv::StorageClass::Function) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Op" << spvOpcodeString(inst->opcode())
+           << " pointer operand must be in the Function storage class.";
+  }
+
+  const uint32_t size = inst->GetOperandAs<uint32_t>(1);
+  if (size != 0) {
+    if (!_.HasCapability(spv::Capability::Addresses)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Op" << spvOpcodeString(inst->opcode())
+             << " size is non-zero, but the Addresses Capability is not "
+                "declared.";
+    }
+    // TODO - "Size must be 0 if Pointer is a pointer to a non-void type"
+  }
+
+  return SPV_SUCCESS;
+}
+
 }  // namespace
 
 void printDominatorList(const BasicBlock& b) {
@@ -1178,6 +1206,7 @@
     case spv::Op::OpIgnoreIntersectionKHR:
     case spv::Op::OpTerminateRayKHR:
     case spv::Op::OpEmitMeshTasksEXT:
+    case spv::Op::OpAbortKHR:
       _.current_function().RegisterBlockEnd(std::vector<uint32_t>());
       // Ops with dedicated passes check for the Execution Model there
       if (opcode == spv::Op::OpKill) {
@@ -1269,6 +1298,10 @@
     case spv::Op::OpLoopMerge:
       if (auto error = ValidateLoopMerge(_, inst)) return error;
       break;
+    case spv::Op::OpLifetimeStart:
+    case spv::Op::OpLifetimeStop:
+      if (auto error = ValidateLifetime(_, inst)) return error;
+      break;
     default:
       break;
   }
diff --git a/source/val/validate_composites.cpp b/source/val/validate_composites.cpp
index 26486da..d656d61 100644
--- a/source/val/validate_composites.cpp
+++ b/source/val/validate_composites.cpp
@@ -1,4 +1,6 @@
 // Copyright (c) 2017 Google Inc.
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -14,6 +16,9 @@
 
 // Validates correctness of composite SPIR-V instructions.
 
+#include <climits>
+#include <cstdint>
+
 #include "source/opcode.h"
 #include "source/spirv_target_env.h"
 #include "source/val/instruction.h"
@@ -32,14 +37,11 @@
 // deep).
 spv_result_t GetExtractInsertValueType(ValidationState_t& _,
                                        const Instruction* inst,
-                                       uint32_t* member_type) {
-  const spv::Op opcode = inst->opcode();
-  assert(opcode == spv::Op::OpCompositeExtract ||
-         opcode == spv::Op::OpCompositeInsert);
-  uint32_t word_index = opcode == spv::Op::OpCompositeExtract ? 4 : 5;
-  const uint32_t num_words = static_cast<uint32_t>(inst->words().size());
-  const uint32_t composite_id_index = word_index - 1;
-  const uint32_t num_indices = num_words - word_index;
+                                       uint32_t* member_type,
+                                       uint32_t composite_id_index) {
+  const uint32_t num_operands = static_cast<uint32_t>(inst->operands().size());
+  const uint32_t first_literal_index = composite_id_index + 1;
+  const uint32_t num_indices = num_operands - first_literal_index;
   const uint32_t kCompositeExtractInsertMaxNumIndices = 255;
 
   if (num_indices == 0) {
@@ -49,19 +51,21 @@
 
   } else if (num_indices > kCompositeExtractInsertMaxNumIndices) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "The number of indexes in Op" << spvOpcodeString(opcode)
+           << "The number of indexes in Op" << spvOpcodeString(inst->opcode())
            << " may not exceed " << kCompositeExtractInsertMaxNumIndices
            << ". Found " << num_indices << " indexes.";
   }
 
-  *member_type = _.GetTypeId(inst->word(composite_id_index));
+  *member_type = _.GetOperandTypeId(inst, composite_id_index);
   if (*member_type == 0) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Composite to be an object of composite type";
   }
 
-  for (; word_index < num_words; ++word_index) {
-    const uint32_t component_index = inst->word(word_index);
+  for (uint32_t operand_index = first_literal_index;
+       operand_index < num_operands; ++operand_index) {
+    const uint32_t component_index =
+        inst->GetOperandAs<uint32_t>(operand_index);
     const Instruction* const type_inst = _.FindDef(*member_type);
     assert(type_inst);
     switch (type_inst->opcode()) {
@@ -104,7 +108,8 @@
         }
         break;
       }
-      case spv::Op::OpTypeRuntimeArray: {
+      case spv::Op::OpTypeRuntimeArray:
+      case spv::Op::OpTypeNodePayloadArrayAMDX: {
         *member_type = type_inst->word(2);
         // Array size is unknown.
         break;
@@ -122,6 +127,7 @@
         *member_type = type_inst->word(component_index + 2);
         break;
       }
+      case spv::Op::OpTypeVectorIdEXT:
       case spv::Op::OpTypeCooperativeMatrixKHR:
       case spv::Op::OpTypeCooperativeMatrixNV: {
         *member_type = type_inst->word(2);
@@ -148,7 +154,8 @@
 
   const uint32_t vector_type = _.GetOperandTypeId(inst, 2);
   const spv::Op vector_opcode = _.GetIdOpcode(vector_type);
-  if (vector_opcode != spv::Op::OpTypeVector) {
+  if (vector_opcode != spv::Op::OpTypeVector &&
+      vector_opcode != spv::Op::OpTypeVectorIdEXT) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Vector type to be OpTypeVector";
   }
@@ -176,7 +183,8 @@
                                          const Instruction* inst) {
   const uint32_t result_type = inst->type_id();
   const spv::Op result_opcode = _.GetIdOpcode(result_type);
-  if (result_opcode != spv::Op::OpTypeVector) {
+  if (result_opcode != spv::Op::OpTypeVector &&
+      result_opcode != spv::Op::OpTypeVectorIdEXT) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Result Type to be OpTypeVector";
   }
@@ -214,14 +222,25 @@
   const uint32_t result_type = inst->type_id();
   const spv::Op result_opcode = _.GetIdOpcode(result_type);
   switch (result_opcode) {
-    case spv::Op::OpTypeVector: {
-      const uint32_t num_result_components = _.GetDimension(result_type);
+    case spv::Op::OpTypeVector:
+    case spv::Op::OpTypeVectorIdEXT: {
+      uint32_t num_result_components = _.GetDimension(result_type);
       const uint32_t result_component_type = _.GetComponentType(result_type);
       uint32_t given_component_count = 0;
 
-      if (num_operands <= 3) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected number of constituents to be at least 2";
+      bool comp_is_int32 = true, comp_is_const_int32 = true;
+
+      if (result_opcode == spv::Op::OpTypeVector) {
+        if (num_operands <= 3 &&
+            !_.HasCapability(spv::Capability::LongVectorEXT)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Expected number of constituents to be at least 2";
+        }
+      } else {
+        uint32_t comp_count_id =
+            _.FindDef(result_type)->GetOperandAs<uint32_t>(2);
+        std::tie(comp_is_int32, comp_is_const_int32, num_result_components) =
+            _.EvalInt32IfConst(comp_count_id);
       }
 
       for (uint32_t operand_index = 2; operand_index < num_operands;
@@ -241,7 +260,8 @@
         }
       }
 
-      if (num_result_components != given_component_count) {
+      if (comp_is_const_int32 &&
+          num_result_components != given_component_count) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected total number of given components to be equal "
                << "to the size of Result Type vector";
@@ -389,10 +409,74 @@
   return SPV_SUCCESS;
 }
 
+spv_result_t ValidateCompositeConstructReplicate(ValidationState_t& _,
+                                                 const Instruction* inst) {
+  const auto result_type = _.FindDef(inst->type_id());
+  const uint32_t operand_type = _.GetOperandTypeId(inst, 2);
+
+  switch (result_type->opcode()) {
+    case spv::Op::OpTypeVector:
+    case spv::Op::OpTypeVectorIdEXT:
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeArray:
+    case spv::Op::OpTypeCooperativeMatrixKHR:
+    case spv::Op::OpTypeCooperativeMatrixNV: {
+      const auto element_type = result_type->GetOperandAs<uint32_t>(1);
+      if (operand_type != element_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected Value type to be equal to the "
+               << "result's element type";
+      }
+      break;
+    }
+    case spv::Op::OpTypeStruct: {
+      for (uint32_t operand_index = 1;
+           operand_index < result_type->operands().size(); ++operand_index) {
+        const uint32_t member_type =
+            result_type->GetOperandAs<uint32_t>(operand_index);
+        if (operand_type != member_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Expected Value type to be equal to the "
+                 << "corresponding member type of the result";
+        }
+      }
+      break;
+    }
+    case spv::Op::OpTypeTensorARM: {
+      const uint32_t component_type = result_type->GetOperandAs<uint32_t>(1);
+      if (operand_type != component_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected Value type to be equal to the result's element "
+                  "type";
+      }
+      if (result_type->operands().size() <= 3) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Result tensor type is not a composite type because it lacks "
+                  "a shape operand";
+      }
+      break;
+    }
+    default: {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected Result Type to be a composite type";
+    }
+  }
+
+  if (_.HasCapability(spv::Capability::Shader) &&
+      _.ContainsLimitedUseIntOrFloatType(inst->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Cannot create a composite containing 8- or 16-bit types";
+  }
+  return SPV_SUCCESS;
+}
+
 spv_result_t ValidateCompositeExtract(ValidationState_t& _,
-                                      const Instruction* inst) {
+                                      const Instruction* inst,
+                                      uint32_t operand_index = 2) {
   uint32_t member_type = 0;
-  if (spv_result_t error = GetExtractInsertValueType(_, inst, &member_type)) {
+
+  if (spv_result_t error =
+          GetExtractInsertValueType(_, inst, &member_type, operand_index)) {
     return error;
   }
 
@@ -415,9 +499,10 @@
 }
 
 spv_result_t ValidateCompositeInsert(ValidationState_t& _,
-                                     const Instruction* inst) {
-  const uint32_t object_type = _.GetOperandTypeId(inst, 2);
-  const uint32_t composite_type = _.GetOperandTypeId(inst, 3);
+                                     const Instruction* inst,
+                                     uint32_t operand_index = 2) {
+  const uint32_t object_type = _.GetOperandTypeId(inst, operand_index);
+  const uint32_t composite_type = _.GetOperandTypeId(inst, operand_index + 1);
   const uint32_t result_type = inst->type_id();
   if (result_type != composite_type) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -427,7 +512,8 @@
   }
 
   uint32_t member_type = 0;
-  if (spv_result_t error = GetExtractInsertValueType(_, inst, &member_type)) {
+  if (spv_result_t error =
+          GetExtractInsertValueType(_, inst, &member_type, operand_index + 1)) {
     return error;
   }
 
@@ -508,59 +594,58 @@
 }
 
 spv_result_t ValidateVectorShuffle(ValidationState_t& _,
-                                   const Instruction* inst) {
-  auto resultType = _.FindDef(inst->type_id());
-  if (!resultType || resultType->opcode() != spv::Op::OpTypeVector) {
+                                   const Instruction* inst,
+                                   uint32_t operand_index = 2) {
+  auto result_type = _.FindDef(inst->type_id());
+  if (!_.IsVectorType(result_type->id())) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "The Result Type of OpVectorShuffle must be"
-           << " OpTypeVector. Found Op"
-           << spvOpcodeString(static_cast<spv::Op>(resultType->opcode()))
-           << ".";
+           << " a vector type. Found Op"
+           << spvOpcodeString(result_type->opcode()) << ".";
   }
 
   // The number of components in Result Type must be the same as the number of
   // Component operands.
-  auto componentCount = inst->operands().size() - 4;
-  auto resultVectorDimension = resultType->GetOperandAs<uint32_t>(2);
-  if (componentCount != resultVectorDimension) {
+  uint32_t first_literal_index = operand_index + 2;
+  uint32_t component_count =
+      static_cast<uint32_t>(inst->operands().size()) - first_literal_index;
+  auto result_vec_dimension = _.GetDimension(result_type->id());
+  if (result_vec_dimension > 0 && component_count != result_vec_dimension) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpVectorShuffle component literals count does not match "
               "Result Type <id> "
-           << _.getIdName(resultType->id()) << "s vector component count.";
+           << _.getIdName(result_type->id()) << "s vector component count.";
   }
 
   // Vector 1 and Vector 2 must both have vector types, with the same Component
   // Type as Result Type.
-  auto vector1Object = _.FindDef(inst->GetOperandAs<uint32_t>(2));
-  auto vector1Type = _.FindDef(vector1Object->type_id());
-  auto vector2Object = _.FindDef(inst->GetOperandAs<uint32_t>(3));
-  auto vector2Type = _.FindDef(vector2Object->type_id());
-  if (!vector1Type || vector1Type->opcode() != spv::Op::OpTypeVector) {
+  auto vec1_type = _.FindDef(_.GetOperandTypeId(inst, operand_index));
+  auto vec2_type = _.FindDef(_.GetOperandTypeId(inst, operand_index + 1));
+  if (!vec1_type || !_.IsVectorType(vec1_type->id())) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The type of Vector 1 must be OpTypeVector.";
+           << "The type of Vector 1 must be a vector type.";
   }
-  if (!vector2Type || vector2Type->opcode() != spv::Op::OpTypeVector) {
+  if (!vec2_type || !_.IsVectorType(vec2_type->id())) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The type of Vector 2 must be OpTypeVector.";
+           << "The type of Vector 2 must be a vector type.";
   }
 
-  auto resultComponentType = resultType->GetOperandAs<uint32_t>(1);
-  if (vector1Type->GetOperandAs<uint32_t>(1) != resultComponentType) {
+  uint32_t result_component_type = result_type->GetOperandAs<uint32_t>(1);
+  if (vec1_type->GetOperandAs<uint32_t>(1) != result_component_type) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "The Component Type of Vector 1 must be the same as ResultType.";
   }
-  if (vector2Type->GetOperandAs<uint32_t>(1) != resultComponentType) {
+  if (vec2_type->GetOperandAs<uint32_t>(1) != result_component_type) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "The Component Type of Vector 2 must be the same as ResultType.";
   }
 
   // All Component literals must either be FFFFFFFF or in [0, N - 1].
-  auto vector1ComponentCount = vector1Type->GetOperandAs<uint32_t>(2);
-  auto vector2ComponentCount = vector2Type->GetOperandAs<uint32_t>(2);
-  auto N = vector1ComponentCount + vector2ComponentCount;
-  auto firstLiteralIndex = 4;
-  for (size_t i = firstLiteralIndex; i < inst->operands().size(); ++i) {
-    auto literal = inst->GetOperandAs<uint32_t>(i);
+  uint32_t vec1_component_count = vec1_type->GetOperandAs<uint32_t>(2);
+  uint32_t vec2_component_count = vec2_type->GetOperandAs<uint32_t>(2);
+  uint32_t N = vec1_component_count + vec2_component_count;
+  for (size_t i = first_literal_index; i < inst->operands().size(); ++i) {
+    uint32_t literal = inst->GetOperandAs<uint32_t>(i);
     if (literal != 0xFFFFFFFF && literal >= N) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "Component index " << literal << " is out of bounds for "
@@ -601,8 +686,464 @@
   return SPV_SUCCESS;
 }
 
-}  // anonymous namespace
+spv_result_t ValidateCompositeConstructCoopMatQCOM(ValidationState_t& _,
+                                                   const Instruction* inst) {
+  // Is the result of coop mat ?
+  const auto result_type_inst = _.FindDef(inst->type_id());
+  if (!result_type_inst ||
+      result_type_inst->opcode() != spv::Op::OpTypeCooperativeMatrixKHR) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Opcode " << spvOpcodeString(inst->opcode())
+           << " requires the result type be OpTypeCooperativeMatrixKHR";
+  }
 
+  const auto source = _.FindDef(inst->GetOperandAs<uint32_t>(2u));
+  const auto source_type_inst = _.FindDef(source->type_id());
+
+  if (!source_type_inst || source_type_inst->opcode() != spv::Op::OpTypeArray) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Opcode " << spvOpcodeString(inst->opcode())
+           << " requires the input operand be an OpTypeArray.";
+  }
+
+  // Is the scope Subgrouop ?
+  {
+    unsigned scope = UINT_MAX;
+    unsigned scope_id = result_type_inst->GetOperandAs<unsigned>(2u);
+    bool status = _.GetConstantValueAs<unsigned>(scope_id, scope);
+    bool is_scope_spec_const =
+        spvOpcodeIsSpecConstant(_.FindDef(scope_id)->opcode());
+    if (!is_scope_spec_const &&
+        (!status || scope != static_cast<uint64_t>(spv::Scope::Subgroup))) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Opcode " << spvOpcodeString(inst->opcode())
+             << " requires the result type's scope be Subgroup.";
+    }
+  }
+
+  unsigned ar_len = UINT_MAX;
+  unsigned src_arr_len_id = source_type_inst->GetOperandAs<unsigned>(2u);
+  bool ar_len_status = _.GetConstantValueAs<unsigned>(src_arr_len_id, ar_len);
+  bool is_src_arr_len_spec_const =
+      spvOpcodeIsSpecConstant(_.FindDef(src_arr_len_id)->opcode());
+
+  const auto source_elt_type = _.GetComponentType(source_type_inst->id());
+  const auto result_elt_type = result_type_inst->GetOperandAs<uint32_t>(1u);
+
+  if ((source_elt_type != result_elt_type) &&
+      !(_.ContainsSizedIntOrFloatType(source_elt_type, spv::Op::OpTypeInt,
+                                      32) &&
+        _.IsUnsignedIntScalarType(source_elt_type))) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Opcode " << spvOpcodeString(inst->opcode())
+           << " requires ether the input element type is equal to the result "
+              "element type or it is the unsigned 32-bit integer.";
+  }
+
+  unsigned res_row_id = result_type_inst->GetOperandAs<unsigned>(3u);
+  unsigned res_col_id = result_type_inst->GetOperandAs<unsigned>(4u);
+  unsigned res_use_id = result_type_inst->GetOperandAs<unsigned>(5u);
+
+  unsigned cm_use = UINT_MAX;
+  bool cm_use_status = _.GetConstantValueAs<unsigned>(res_use_id, cm_use);
+
+  switch (static_cast<spv::CooperativeMatrixUse>(cm_use)) {
+    case spv::CooperativeMatrixUse::MatrixAKHR: {
+      // result coopmat component type check
+      if (!_.IsIntNOrFP32OrFP16<8>(result_elt_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Opcode " << spvOpcodeString(inst->opcode())
+               << " requires the result element type is one of 8-bit OpTypeInt "
+                  "signed/unsigned, 16- or 32-bit OpTypeFloat"
+               << " when result coopmat's use is MatrixAKHR";
+      }
+
+      // result coopmat column length check
+      unsigned n_cols = UINT_MAX;
+      bool status = _.GetConstantValueAs<unsigned>(res_col_id, n_cols);
+      bool is_res_col_spec_const =
+          spvOpcodeIsSpecConstant(_.FindDef(res_col_id)->opcode());
+      if (!is_res_col_spec_const &&
+          (!status || (!(_.ContainsSizedIntOrFloatType(result_elt_type,
+                                                       spv::Op::OpTypeInt, 8) &&
+                         n_cols == 32) &&
+                       !(_.ContainsSizedIntOrFloatType(
+                             result_elt_type, spv::Op::OpTypeFloat, 16) &&
+                         n_cols == 16) &&
+                       !(_.ContainsSizedIntOrFloatType(
+                             result_elt_type, spv::Op::OpTypeFloat, 32) &&
+                         n_cols == 8)))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Opcode " << spvOpcodeString(inst->opcode())
+               << " requires the columns of the result coopmat have the bit "
+                  "length of 256"
+               << " when result coopmat's use is MatrixAKHR";
+      }
+      // source array length check
+      if (!is_src_arr_len_spec_const &&
+          (!ar_len_status ||
+           (!(_.ContainsSizedIntOrFloatType(source_elt_type, spv::Op::OpTypeInt,
+                                            32) &&
+              _.IsUnsignedIntScalarType(source_elt_type) && (ar_len == 8)) &&
+            !(n_cols == ar_len)))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Opcode " << spvOpcodeString(inst->opcode())
+               << " requires the source array length be 8 if its elt type is "
+                  "32-bit unsigned OpTypeInt and be the result's number of "
+                  "columns, otherwise"
+               << " when result coopmat's use is MatrixAKHR";
+      }
+      break;
+    }
+    case spv::CooperativeMatrixUse::MatrixBKHR: {
+      // result coopmat component type check
+      if (!_.IsIntNOrFP32OrFP16<8>(result_elt_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Opcode " << spvOpcodeString(inst->opcode())
+               << " requires the result element type is one of 8-bit OpTypeInt "
+                  "signed/unsigned, 16- or 32-bit OpTypeFloat"
+               << " when result coopmat's use is MatrixBKHR";
+      }
+
+      // result coopmat row length check
+      unsigned n_rows = UINT_MAX;
+      bool status = _.GetConstantValueAs<unsigned>(res_row_id, n_rows);
+      bool is_res_row_spec_const =
+          spvOpcodeIsSpecConstant(_.FindDef(res_row_id)->opcode());
+      if (!is_res_row_spec_const &&
+          (!status || (!(_.ContainsSizedIntOrFloatType(result_elt_type,
+                                                       spv::Op::OpTypeInt, 8) &&
+                         n_rows == 32) &&
+                       !(_.ContainsSizedIntOrFloatType(
+                             result_elt_type, spv::Op::OpTypeFloat, 16) &&
+                         n_rows == 16) &&
+                       !(_.ContainsSizedIntOrFloatType(
+                             result_elt_type, spv::Op::OpTypeFloat, 32) &&
+                         n_rows == 8)))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Opcode " << spvOpcodeString(inst->opcode())
+               << " requires the rows of the result operand have the bit "
+                  "length of 256"
+               << " when result coopmat's use is MatrixBKHR";
+      }
+      // source array length check
+      if (!is_src_arr_len_spec_const &&
+          (!ar_len_status ||
+           (!(_.ContainsSizedIntOrFloatType(source_elt_type, spv::Op::OpTypeInt,
+                                            32) &&
+              _.IsUnsignedIntScalarType(source_elt_type) && (ar_len == 8)) &&
+            !(n_rows == ar_len)))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Opcode " << spvOpcodeString(inst->opcode())
+               << " requires the source array length be 8 if its elt type is "
+                  "32-bit unsigned OpTypeInt and be the result's number of "
+                  "rows, otherwise"
+               << " when result coopmat's use is MatrixBKHR";
+      }
+      break;
+    }
+    case spv::CooperativeMatrixUse::MatrixAccumulatorKHR: {
+      // result coopmat component type check
+      if (!_.IsIntNOrFP32OrFP16<32>(result_elt_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Opcode " << spvOpcodeString(inst->opcode())
+               << " requires the result element type is one of 32-bit "
+                  "OpTypeInt signed/unsigned, 16- or 32-bit OpTypeFloat"
+               << " when result coopmat's use is MatrixAccumulatorKHR";
+      }
+
+      // source array length check
+      unsigned n_cols = UINT_MAX;
+      bool status = _.GetConstantValueAs<unsigned>(res_col_id, n_cols);
+      bool is_res_col_spec_const =
+          spvOpcodeIsSpecConstant(_.FindDef(res_col_id)->opcode());
+      if (!is_res_col_spec_const && !is_src_arr_len_spec_const &&
+          (!status || !ar_len_status ||
+           (!(_.ContainsSizedIntOrFloatType(source_elt_type, spv::Op::OpTypeInt,
+                                            32) &&
+              _.IsUnsignedIntScalarType(source_elt_type) &&
+              (_.ContainsSizedIntOrFloatType(result_elt_type,
+                                             spv::Op::OpTypeFloat, 16)
+                   ? (n_cols / 2 == ar_len)
+                   : n_cols == ar_len)) &&
+            (n_cols != ar_len)))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Opcode " << spvOpcodeString(inst->opcode())
+               << " requires the source array length be a half of the number "
+                  "of columns of the resulting cooerative matrix if the "
+                  "matrix's componet type is 16-bit OpTypeFloat and be equal "
+                  "to the number of columns, otherwise,"
+               << " when result coopmat's use is MatrixAccumulatorKHR";
+      }
+      break;
+    }
+    default: {
+      bool is_cm_use_spec_const =
+          spvOpcodeIsSpecConstant(_.FindDef(res_use_id)->opcode());
+      if (!is_cm_use_spec_const || !cm_use_status) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Opcode " << spvOpcodeString(inst->opcode())
+               << " requires the the resulting cooerative matrix's use be "
+               << " one of MatrixAKHR (== 0), MatrixBKHR (== 1), and "
+                  "MatrixAccumulatorKHR (== 2)";
+      }
+      break;
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateCompositeExtractCoopMatQCOM(ValidationState_t& _,
+                                                 const Instruction* inst) {
+  const auto result_type_inst = _.FindDef(inst->type_id());
+  if (!result_type_inst || result_type_inst->opcode() != spv::Op::OpTypeArray) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Opcode " << spvOpcodeString(inst->opcode())
+           << " requires the input operand be an OpTypeArray.";
+  }
+
+  const auto source = _.FindDef(inst->GetOperandAs<uint32_t>(2u));
+  const auto source_type_inst = _.FindDef(source->type_id());
+
+  // Is the source of coop mat ?
+  if (!source_type_inst ||
+      source_type_inst->opcode() != spv::Op::OpTypeCooperativeMatrixKHR) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Opcode " << spvOpcodeString(inst->opcode())
+           << " requires the source type be OpTypeCooperativeMatrixKHR";
+  }
+
+  // Is the scope Subgrouop ?
+  {
+    unsigned scope = UINT_MAX;
+    unsigned scope_id = source_type_inst->GetOperandAs<unsigned>(2u);
+    bool status = _.GetConstantValueAs<unsigned>(scope_id, scope);
+    bool is_scope_spec_const =
+        spvOpcodeIsSpecConstant(_.FindDef(scope_id)->opcode());
+    if (!is_scope_spec_const &&
+        (!status || scope != static_cast<uint64_t>(spv::Scope::Subgroup))) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Opcode " << spvOpcodeString(inst->opcode())
+             << " requires the source type's scope be Subgroup.";
+    }
+  }
+
+  unsigned ar_len = UINT_MAX;
+  unsigned res_arr_len_id = result_type_inst->GetOperandAs<unsigned>(2u);
+  bool ar_len_status = _.GetConstantValueAs<unsigned>(res_arr_len_id, ar_len);
+  bool is_res_arr_len_spec_const =
+      spvOpcodeIsSpecConstant(_.FindDef(res_arr_len_id)->opcode());
+
+  const auto source_elt_type = _.GetComponentType(source_type_inst->id());
+  const auto result_elt_type = result_type_inst->GetOperandAs<uint32_t>(1u);
+
+  unsigned src_row_id = source_type_inst->GetOperandAs<unsigned>(3u);
+  unsigned src_col_id = source_type_inst->GetOperandAs<unsigned>(4u);
+  unsigned src_use_id = source_type_inst->GetOperandAs<unsigned>(5u);
+
+  unsigned cm_use = UINT_MAX;
+  bool cm_use_status = _.GetConstantValueAs<unsigned>(src_use_id, cm_use);
+
+  switch (static_cast<spv::CooperativeMatrixUse>(cm_use)) {
+    case spv::CooperativeMatrixUse::MatrixAKHR: {
+      // source coopmat component type check
+      if (!_.IsIntNOrFP32OrFP16<8>(source_elt_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Opcode " << spvOpcodeString(inst->opcode())
+               << " requires the source element type be one of 8-bit OpTypeInt "
+                  "signed/unsigned, 16- or 32-bit OpTypeFloat"
+               << " when source coopmat's use is MatrixAKHR";
+      }
+
+      // source coopmat column length check
+      unsigned n_cols = UINT_MAX;
+      bool status = _.GetConstantValueAs<unsigned>(src_col_id, n_cols);
+      bool is_src_col_spec_const =
+          spvOpcodeIsSpecConstant(_.FindDef(src_col_id)->opcode());
+      if (!is_src_col_spec_const &&
+          (!status || (!(_.ContainsSizedIntOrFloatType(source_elt_type,
+                                                       spv::Op::OpTypeInt, 8) &&
+                         n_cols == 32) &&
+                       !(_.ContainsSizedIntOrFloatType(
+                             source_elt_type, spv::Op::OpTypeFloat, 16) &&
+                         n_cols == 16) &&
+                       !(_.ContainsSizedIntOrFloatType(
+                             source_elt_type, spv::Op::OpTypeFloat, 32) &&
+                         n_cols == 8)))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Opcode " << spvOpcodeString(inst->opcode())
+               << " requires the columns of the source coopmat have the bit "
+                  "length of 256"
+               << " when source coopmat's use is MatrixAKHR";
+      }
+      // result type check
+      if (!is_res_arr_len_spec_const &&
+          !(source_elt_type == result_elt_type && (n_cols == ar_len)) &&
+          !(_.ContainsSizedIntOrFloatType(result_elt_type, spv::Op::OpTypeInt,
+                                          32) &&
+            _.IsUnsignedIntScalarType(result_elt_type) && (ar_len == 8))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Opcode " << spvOpcodeString(inst->opcode())
+               << " requires either the result element type be the same as the "
+                  "source cooperative matrix's component type"
+               << " and its length be the same as the number of columns of the "
+                  "matrix or the result element type be"
+               << " unsigned 32-bit OpTypeInt and the length be 8"
+               << " when source coopmat's use is MatrixAKHR";
+      }
+      break;
+    }
+    case spv::CooperativeMatrixUse::MatrixBKHR: {
+      // source coopmat component type check
+      if (!_.IsIntNOrFP32OrFP16<8>(source_elt_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Opcode " << spvOpcodeString(inst->opcode())
+               << " requires the source element type be one of 8-bit OpTypeInt "
+                  "signed/unsigned, 16- or 32-bit OpTypeFloat"
+               << " when source coopmat's use is MatrixBKHR";
+      }
+
+      // source coopmat row length check
+      unsigned n_rows = UINT_MAX;
+      bool status = _.GetConstantValueAs<unsigned>(src_row_id, n_rows);
+      bool is_src_row_spec_const =
+          spvOpcodeIsSpecConstant(_.FindDef(src_row_id)->opcode());
+      if (!is_src_row_spec_const &&
+          (!status || (!(_.ContainsSizedIntOrFloatType(source_elt_type,
+                                                       spv::Op::OpTypeInt, 8) &&
+                         n_rows == 32) &&
+                       !(_.ContainsSizedIntOrFloatType(
+                             source_elt_type, spv::Op::OpTypeFloat, 16) &&
+                         n_rows == 16) &&
+                       !(_.ContainsSizedIntOrFloatType(
+                             source_elt_type, spv::Op::OpTypeFloat, 32) &&
+                         n_rows == 8)))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Opcode " << spvOpcodeString(inst->opcode())
+               << " requires the rows of the source coopmat have the bit "
+                  "length of 256"
+               << " when source coopmat's use is MatrixBKHR";
+      }
+      // result type check
+      if (!is_res_arr_len_spec_const &&
+          !(source_elt_type == result_elt_type && (n_rows == ar_len)) &&
+          !(_.ContainsSizedIntOrFloatType(result_elt_type, spv::Op::OpTypeInt,
+                                          32) &&
+            _.IsUnsignedIntScalarType(result_elt_type) && (ar_len == 8))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Opcode " << spvOpcodeString(inst->opcode())
+               << " requires either the result element type be the same as the "
+                  "source cooperative matrix's component type"
+               << " and its length be the same as the number of rows of the "
+                  "matrix or the result element type be"
+               << " unsigned 32-bit OpTypeInt and the length be 8"
+               << " when source coopmat's use is MatrixBKHR";
+      }
+      break;
+    }
+    case spv::CooperativeMatrixUse::MatrixAccumulatorKHR: {
+      // source coopmat component type check
+      if (!_.IsIntNOrFP32OrFP16<32>(source_elt_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Opcode " << spvOpcodeString(inst->opcode())
+               << " requires the source element type be one of 32-bit "
+                  "OpTypeInt signed/unsigned, 16- or 32-bit OpTypeFloat"
+               << " when source coopmat's use is MatrixAccumulatorKHR";
+      }
+
+      // result type check
+      unsigned n_cols = UINT_MAX;
+      bool status = _.GetConstantValueAs<unsigned>(src_col_id, n_cols);
+      bool is_src_col_spec_const =
+          spvOpcodeIsSpecConstant(_.FindDef(src_col_id)->opcode());
+      if (!is_src_col_spec_const && !is_res_arr_len_spec_const &&
+          (!status || !ar_len_status ||
+           (!(source_elt_type == result_elt_type && (n_cols == ar_len)) &&
+            !(_.ContainsSizedIntOrFloatType(result_elt_type, spv::Op::OpTypeInt,
+                                            32) &&
+              _.IsUnsignedIntScalarType(result_elt_type) &&
+              (_.ContainsSizedIntOrFloatType(source_elt_type,
+                                             spv::Op::OpTypeFloat, 16)
+                   ? (n_cols / 2 == ar_len)
+                   : (n_cols == ar_len)))))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Opcode " << spvOpcodeString(inst->opcode())
+               << " requires either the result element type be the same as the "
+                  "source cooperative matrix's component type"
+               << " and its length be the same as the number of columns of the "
+                  "matrix or the result element type be"
+               << " unsigned 32-bit OpTypeInt and the length be the number of "
+                  "the columns of the matrix if its component"
+               << " type is 32-bit OpTypeFloat and be a half of the number of "
+                  "the columns of the matrix if its component"
+               << " type is 16-bit OpTypeFloat"
+               << " when source coopmat's use is MatrixAccumulatorKHR";
+      }
+      break;
+    }
+    default: {
+      bool is_cm_use_spec_const =
+          spvOpcodeIsSpecConstant(_.FindDef(src_use_id)->opcode());
+      if (!is_cm_use_spec_const || !cm_use_status) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Opcode " << spvOpcodeString(inst->opcode())
+               << " requires the the source cooerative matrix's use be "
+               << " one of MatrixAKHR (== 0), MatrixBKHR (== 1), and "
+                  "MatrixAccumulatorKHR (== 2)";
+      }
+      break;
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateExtractSubArrayQCOM(ValidationState_t& _,
+                                         const Instruction* inst) {
+  const auto result_type_inst = _.FindDef(inst->type_id());
+  const auto source = _.FindDef(inst->GetOperandAs<uint32_t>(2u));
+  const auto source_type_inst = _.FindDef(source->type_id());
+
+  // Are the input and the result arrays?
+  if (result_type_inst->opcode() != spv::Op::OpTypeArray ||
+      source_type_inst->opcode() != spv::Op::OpTypeArray) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Opcode " << spvOpcodeString(inst->opcode())
+           << " requires OpTypeArray operands for the input and the result.";
+  }
+
+  const auto source_elt_type = _.GetComponentType(source_type_inst->id());
+  const auto result_elt_type = _.GetComponentType(result_type_inst->id());
+
+  // Do the input and result element types match?
+  if (source_elt_type != result_elt_type) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Opcode " << spvOpcodeString(inst->opcode())
+           << " requires the input and result element types match.";
+  }
+
+  // Elt type must be one of int32_t/uint32_t/float32/float16
+  if (!_.IsIntNOrFP32OrFP16<32>(source_elt_type)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Opcode " << spvOpcodeString(inst->opcode())
+           << " requires the element type be one of 32-bit OpTypeInt "
+              "(signed/unsigned), 32-bit OpTypeFloat and 16-bit OpTypeFloat";
+  }
+
+  const auto start_index = _.FindDef(inst->GetOperandAs<uint32_t>(3u));
+  if (!start_index || !_.ContainsSizedIntOrFloatType(start_index->type_id(),
+                                                     spv::Op::OpTypeInt, 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Opcode " << spvOpcodeString(inst->opcode())
+           << " requires the type of the start index operand be 32-bit "
+              "OpTypeInt";
+  }
+
+  return SPV_SUCCESS;
+}
+
+}  // anonymous namespace
 // Validates correctness of composite instructions.
 spv_result_t CompositesPass(ValidationState_t& _, const Instruction* inst) {
   switch (inst->opcode()) {
@@ -614,6 +1155,8 @@
       return ValidateVectorShuffle(_, inst);
     case spv::Op::OpCompositeConstruct:
       return ValidateCompositeConstruct(_, inst);
+    case spv::Op::OpCompositeConstructReplicateEXT:
+      return ValidateCompositeConstructReplicate(_, inst);
     case spv::Op::OpCompositeExtract:
       return ValidateCompositeExtract(_, inst);
     case spv::Op::OpCompositeInsert:
@@ -624,6 +1167,26 @@
       return ValidateTranspose(_, inst);
     case spv::Op::OpCopyLogical:
       return ValidateCopyLogical(_, inst);
+    case spv::Op::OpCompositeConstructCoopMatQCOM:
+      return ValidateCompositeConstructCoopMatQCOM(_, inst);
+    case spv::Op::OpCompositeExtractCoopMatQCOM:
+      return ValidateCompositeExtractCoopMatQCOM(_, inst);
+    case spv::Op::OpExtractSubArrayQCOM:
+      return ValidateExtractSubArrayQCOM(_, inst);
+
+    case spv::Op::OpSpecConstantOp: {
+      switch (inst->GetOperandAs<spv::Op>(2u)) {
+        case spv::Op::OpVectorShuffle:
+          return ValidateVectorShuffle(_, inst, 3);
+        case spv::Op::OpCompositeExtract:
+          return ValidateCompositeExtract(_, inst, 3);
+        case spv::Op::OpCompositeInsert:
+          return ValidateCompositeInsert(_, inst, 3);
+        default:
+          break;
+      }
+    }
+
     default:
       break;
   }
diff --git a/source/val/validate_constants.cpp b/source/val/validate_constants.cpp
index 1d40eed..54941e2 100644
--- a/source/val/validate_constants.cpp
+++ b/source/val/validate_constants.cpp
@@ -33,22 +33,62 @@
   return SPV_SUCCESS;
 }
 
+bool isCompositeType(const Instruction* inst) {
+  bool is_tensor = inst->opcode() == spv::Op::OpTypeTensorARM;
+  bool tensor_is_shaped = inst->words().size() == 5;
+  return spvOpcodeIsComposite(inst->opcode()) ||
+         (is_tensor && tensor_is_shaped);
+}
+
+spv_result_t ValidateConstantOperand(ValidationState_t& _,
+                                     const Instruction* inst, size_t operand) {
+  std::string opcode_name = std::string("Op") + spvOpcodeString(inst->opcode());
+
+  const auto operand_id = inst->GetOperandAs<uint32_t>(operand);
+  const bool inst_is_spec_constant = spvOpcodeIsSpecConstant(inst->opcode());
+  const auto operand_opcode = _.GetIdOpcode(operand_id);
+  const bool is_constant = spvOpcodeIsConstantOrUndef(operand_opcode);
+  const bool is_spec_constant = spvOpcodeIsSpecConstant(operand_opcode);
+  if (!is_constant) {
+    // All operands must be constant or undef.
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << opcode_name << " must only have constant or undef operands: <id> "
+           << _.getIdName(operand_id);
+  } else if (!inst_is_spec_constant && is_spec_constant) {
+    // Spec constants are only allowed for spec constant opcodes.
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << opcode_name << " must not have spec constant operands: <id> "
+           << _.getIdName(operand_id);
+  }
+
+  return SPV_SUCCESS;
+}
+
 spv_result_t ValidateConstantComposite(ValidationState_t& _,
                                        const Instruction* inst) {
   std::string opcode_name = std::string("Op") + spvOpcodeString(inst->opcode());
 
   const auto result_type = _.FindDef(inst->type_id());
-  if (!result_type || !spvOpcodeIsComposite(result_type->opcode())) {
+  if (!result_type || !isCompositeType(result_type)) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << opcode_name << " Result Type <id> "
            << _.getIdName(inst->type_id()) << " is not a composite type.";
   }
 
-  const auto constituent_count = inst->words().size() - 3;
+  const auto constituent_count = inst->operands().size() - 2;
   switch (result_type->opcode()) {
-    case spv::Op::OpTypeVector: {
-      const auto component_count = result_type->GetOperandAs<uint32_t>(2);
-      if (component_count != constituent_count) {
+    case spv::Op::OpTypeVector:
+    case spv::Op::OpTypeVectorIdEXT: {
+      uint32_t num_result_components = _.GetDimension(result_type->id());
+      bool comp_is_int32 = true, comp_is_const_int32 = true;
+
+      if (result_type->opcode() == spv::Op::OpTypeVectorIdEXT) {
+        uint32_t comp_count_id = result_type->GetOperandAs<uint32_t>(2);
+        std::tie(comp_is_int32, comp_is_const_int32, num_result_components) =
+            _.EvalInt32IfConst(comp_count_id);
+      }
+
+      if (comp_is_const_int32 && num_result_components != constituent_count) {
         // TODO: Output ID's on diagnostic
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << opcode_name
@@ -67,13 +107,6 @@
         const auto constituent_id =
             inst->GetOperandAs<uint32_t>(constituent_index);
         const auto constituent = _.FindDef(constituent_id);
-        if (!constituent ||
-            !spvOpcodeIsConstantOrUndef(constituent->opcode())) {
-          return _.diag(SPV_ERROR_INVALID_ID, inst)
-                 << opcode_name << " Constituent <id> "
-                 << _.getIdName(constituent_id)
-                 << " is not a constant or undef.";
-        }
         const auto constituent_result_type = _.FindDef(constituent->type_id());
         if (!constituent_result_type ||
             component_type->id() != constituent_result_type->id()) {
@@ -96,7 +129,8 @@
                << _.getIdName(result_type->id()) << "s matrix column count.";
       }
 
-      const auto column_type = _.FindDef(result_type->words()[2]);
+      const auto column_type =
+          _.FindDef(result_type->GetOperandAs<uint32_t>(1));
       if (!column_type) {
         return _.diag(SPV_ERROR_INVALID_ID, result_type)
                << "Column type is not defined.";
@@ -114,15 +148,6 @@
         const auto constituent_id =
             inst->GetOperandAs<uint32_t>(constituent_index);
         const auto constituent = _.FindDef(constituent_id);
-        if (!constituent ||
-            !spvOpcodeIsConstantOrUndef(constituent->opcode())) {
-          // The message says "... or undef" because the spec does not say
-          // undef is a constant.
-          return _.diag(SPV_ERROR_INVALID_ID, inst)
-                 << opcode_name << " Constituent <id> "
-                 << _.getIdName(constituent_id)
-                 << " is not a constant or undef.";
-        }
         const auto vector = _.FindDef(constituent->type_id());
         if (!vector) {
           return _.diag(SPV_ERROR_INVALID_ID, constituent)
@@ -145,7 +170,7 @@
                  << _.getIdName(result_type->id())
                  << "s matrix column component type.";
         }
-        if (component_count != vector->words()[3]) {
+        if (component_count != vector->GetOperandAs<uint32_t>(2)) {
           return _.diag(SPV_ERROR_INVALID_ID, inst)
                  << opcode_name << " Constituent <id> "
                  << _.getIdName(constituent_id)
@@ -182,13 +207,6 @@
         const auto constituent_id =
             inst->GetOperandAs<uint32_t>(constituent_index);
         const auto constituent = _.FindDef(constituent_id);
-        if (!constituent ||
-            !spvOpcodeIsConstantOrUndef(constituent->opcode())) {
-          return _.diag(SPV_ERROR_INVALID_ID, inst)
-                 << opcode_name << " Constituent <id> "
-                 << _.getIdName(constituent_id)
-                 << " is not a constant or undef.";
-        }
         const auto constituent_type = _.FindDef(constituent->type_id());
         if (!constituent_type) {
           return _.diag(SPV_ERROR_INVALID_ID, constituent)
@@ -204,7 +222,7 @@
       }
     } break;
     case spv::Op::OpTypeStruct: {
-      const auto member_count = result_type->words().size() - 2;
+      const auto member_count = result_type->operands().size() - 1;
       if (member_count != constituent_count) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << opcode_name << " Constituent <id> "
@@ -218,13 +236,6 @@
         const auto constituent_id =
             inst->GetOperandAs<uint32_t>(constituent_index);
         const auto constituent = _.FindDef(constituent_id);
-        if (!constituent ||
-            !spvOpcodeIsConstantOrUndef(constituent->opcode())) {
-          return _.diag(SPV_ERROR_INVALID_ID, inst)
-                 << opcode_name << " Constituent <id> "
-                 << _.getIdName(constituent_id)
-                 << " is not a constant or undef.";
-        }
         const auto constituent_type = _.FindDef(constituent->type_id());
         if (!constituent_type) {
           return _.diag(SPV_ERROR_INVALID_ID, constituent)
@@ -252,11 +263,6 @@
       }
       const auto constituent_id = inst->GetOperandAs<uint32_t>(2);
       const auto constituent = _.FindDef(constituent_id);
-      if (!constituent || !spvOpcodeIsConstantOrUndef(constituent->opcode())) {
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << opcode_name << " Constituent <id> "
-               << _.getIdName(constituent_id) << " is not a constant or undef.";
-      }
       const auto constituent_type = _.FindDef(constituent->type_id());
       if (!constituent_type) {
         return _.diag(SPV_ERROR_INVALID_ID, constituent)
@@ -273,12 +279,201 @@
                << _.getIdName(result_type->id()) << "s component type.";
       }
     } break;
+    case spv::Op::OpTypeTensorARM: {
+      auto inst_element_type =
+          _.FindDef(result_type->GetOperandAs<uint32_t>(1));
+      if (!inst_element_type) {
+        return _.diag(SPV_ERROR_INVALID_ID, result_type)
+               << "Element type is not defined.";
+      }
+      const auto inst_rank = _.FindDef(result_type->GetOperandAs<uint32_t>(2));
+      if (!inst_rank) {
+        return _.diag(SPV_ERROR_INVALID_ID, result_type)
+               << "Rank is not defined.";
+      }
+      const auto inst_shape = _.FindDef(result_type->GetOperandAs<uint32_t>(3));
+      if (!inst_shape) {
+        return _.diag(SPV_ERROR_INVALID_ID, result_type)
+               << "Shape is not defined.";
+      }
+
+      uint64_t rank = 0;
+      _.EvalConstantValUint64(inst_rank->id(), &rank);
+
+      uint64_t outermost_shape = 0;
+      if (_.EvalConstantValUint64(inst_shape->GetOperandAs<uint32_t>(2),
+                                  &outermost_shape) &&
+          (outermost_shape != constituent_count)) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << opcode_name
+               << " Constituent count does not match "
+                  "the shape of Result Type <id> "
+               << _.getIdName(result_type->id())
+               << " along its outermost dimension, " << "expected "
+               << outermost_shape << " but got " << constituent_count << ".";
+      }
+
+      for (size_t constituent_index = 2;
+           constituent_index < inst->operands().size(); constituent_index++) {
+        const auto constituent_id =
+            inst->GetOperandAs<uint32_t>(constituent_index);
+        const auto constituent = _.FindDef(constituent_id);
+        const auto constituent_type = _.FindDef(constituent->type_id());
+        if (!constituent_type) {
+          return _.diag(SPV_ERROR_INVALID_ID, constituent)
+                 << "Type of Constituent " << constituent_index - 2
+                 << " is not defined.";
+        }
+
+        if (rank == 0) {
+          // The rank of the returned tensor constant is not known.
+          // Skip rank-dependent validation.
+          continue;
+        }
+
+        if (rank == 1) {
+          if (inst_element_type->id() != constituent_type->id()) {
+            return _.diag(SPV_ERROR_INVALID_ID, inst)
+                   << opcode_name << " Constituent <id> "
+                   << _.getIdName(constituent_id)
+                   << " type does not match the element type of the tensor ("
+                   << _.getIdName(result_type->id()) << ").";
+          }
+        } else {
+          if (constituent_type->opcode() != spv::Op::OpTypeTensorARM) {
+            return _.diag(SPV_ERROR_INVALID_ID, inst)
+                   << opcode_name << " Constituent <id> "
+                   << _.getIdName(constituent_id)
+                   << " must be an OpTypeTensorARM.";
+          }
+          auto inst_constituent_element_type =
+              _.FindDef(constituent_type->GetOperandAs<uint32_t>(1));
+          if (!inst_constituent_element_type ||
+              inst_constituent_element_type->id() != inst_element_type->id()) {
+            return _.diag(SPV_ERROR_INVALID_ID, inst)
+                   << opcode_name << " Constituent <id> "
+                   << _.getIdName(constituent_id)
+                   << " must have the same Element Type as Result Type <id> "
+                   << _.getIdName(result_type->id()) << ".";
+          }
+          auto inst_constituent_rank =
+              _.FindDef(constituent_type->GetOperandAs<uint32_t>(2));
+          uint64_t constituent_rank;
+          if (inst_constituent_rank &&
+              _.EvalConstantValUint64(inst_constituent_rank->id(),
+                                      &constituent_rank) &&
+              (constituent_rank != rank - 1)) {
+            return _.diag(SPV_ERROR_INVALID_ID, inst)
+                   << opcode_name << " Constituent <id> "
+                   << _.getIdName(constituent_id)
+                   << " must have a Rank that is 1 less than the Rank of "
+                      "Result Type <id> "
+                   << _.getIdName(result_type->id()) << ", expected "
+                   << rank - 1 << " but got " << constituent_rank << ".";
+          }
+
+          auto inst_constituent_shape =
+              _.FindDef(constituent_type->GetOperandAs<uint32_t>(3));
+          if (!inst_constituent_shape) {
+            return _.diag(SPV_ERROR_INVALID_ID, result_type)
+                   << "Shape of Constituent " << constituent_index - 2
+                   << " is not defined.";
+          }
+          for (size_t constituent_shape_index = 2;
+               constituent_shape_index <
+               inst_constituent_shape->operands().size();
+               constituent_shape_index++) {
+            size_t shape_index = constituent_shape_index + 1;
+            uint64_t constituent_shape = 0, shape = 1;
+            if (_.EvalConstantValUint64(
+                    inst_constituent_shape->GetOperandAs<uint32_t>(
+                        constituent_shape_index),
+                    &constituent_shape) &&
+                _.EvalConstantValUint64(
+                    inst_shape->GetOperandAs<uint32_t>(shape_index), &shape) &&
+                (constituent_shape != shape)) {
+              return _.diag(SPV_ERROR_INVALID_ID, inst)
+                     << opcode_name << " Constituent <id> "
+                     << _.getIdName(constituent_id)
+                     << " must have a Shape that matches that of Result Type "
+                        "<id> "
+                     << _.getIdName(result_type->id())
+                     << " along all inner dimensions of Result Type, expected "
+                     << shape << " for dimension "
+                     << constituent_shape_index - 2
+                     << " of Constituent but got " << constituent_shape << ".";
+            }
+          }
+        }
+      }
+    } break;
     default:
       break;
   }
+
+  for (size_t i = 2; i < inst->operands().size(); i++) {
+    if (auto error = ValidateConstantOperand(_, inst, i)) {
+      return error;
+    }
+  }
+
   return SPV_SUCCESS;
 }
 
+spv_result_t ValidateConstantCompositeReplicate(ValidationState_t& _,
+                                                const Instruction* inst) {
+  std::string opcode_name = std::string("Op") + spvOpcodeString(inst->opcode());
+
+  const auto result_type = _.FindDef(inst->type_id());
+  if (!result_type || !isCompositeType(result_type)) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << opcode_name << " Result Type <id> "
+           << _.getIdName(inst->type_id()) << " is not a composite type.";
+  }
+
+  const auto constituent_id = inst->GetOperandAs<uint32_t>(2);
+  const auto constituent = _.FindDef(constituent_id);
+  switch (result_type->opcode()) {
+    case spv::Op::OpTypeVector:
+    case spv::Op::OpTypeVectorIdEXT:
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeArray:
+    case spv::Op::OpTypeCooperativeMatrixKHR:
+    case spv::Op::OpTypeCooperativeMatrixNV:
+    case spv::Op::OpTypeTensorARM: {
+      const auto component_type = result_type->GetOperandAs<uint32_t>(1);
+      if (component_type != constituent->type_id()) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << opcode_name << " Constituent <id> "
+               << _.getIdName(constituent_id)
+               << "s type does not match Result Type <id> "
+               << _.getIdName(result_type->id()) << "s element type.";
+      }
+      break;
+    }
+    case spv::Op::OpTypeStruct: {
+      const auto member_count = result_type->operands().size() - 1;
+      for (uint32_t member_index = 1; member_index <= member_count;
+           member_index++) {
+        const auto member_type_id =
+            result_type->GetOperandAs<uint32_t>(member_index);
+        if (member_type_id != constituent->type_id()) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << opcode_name << " Constituent <id> "
+                 << _.getIdName(constituent_id)
+                 << " type does not match the Result Type <id> "
+                 << _.getIdName(result_type->id()) << "s member type.";
+        }
+      }
+      break;
+    }
+    default:
+      break;
+  }
+
+  return ValidateConstantOperand(_, inst, 2);
+}
+
 spv_result_t ValidateConstantSampler(ValidationState_t& _,
                                      const Instruction* inst) {
   const auto result_type = _.FindDef(inst->type_id());
@@ -312,6 +507,7 @@
     case spv::Op::OpTypeMatrix:
     case spv::Op::OpTypeCooperativeMatrixNV:
     case spv::Op::OpTypeCooperativeMatrixKHR:
+    case spv::Op::OpTypeVectorIdEXT:
     case spv::Op::OpTypeVector: {
       auto base_type = _.FindDef(instruction[2]);
       return base_type && IsTypeNullable(base_type->words(), _);
@@ -331,6 +527,11 @@
         return false;
       }
       return true;
+    case spv::Op::OpTypeTensorARM: {
+      auto elem_type = _.FindDef(instruction[2]);
+      return (instruction.size() > 4) && elem_type &&
+             IsTypeNullable(elem_type->words(), _);
+    }
     default:
       return false;
   }
@@ -348,6 +549,28 @@
   return SPV_SUCCESS;
 }
 
+spv_result_t ValidateConstantSizeOfEXT(ValidationState_t& _,
+                                       const Instruction* inst) {
+  const Instruction* result_type = _.FindDef(inst->type_id());
+  const uint32_t bit_width = result_type->GetOperandAs<uint32_t>(1);
+  // VVL will validate the SPV_EXT_shader_64bit_indexing interaction
+  if (result_type->opcode() != spv::Op::OpTypeInt ||
+      (bit_width != 64 && bit_width != 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "For OpConstantSizeOfEXT instruction, its result type "
+           << "must be a 32-bit or 64-bit integer type scalar."
+           << " (OpCapability Int64 is required for 64-bit)";
+  }
+
+  const uint32_t type_operand = inst->GetOperandAs<uint32_t>(2);
+  if (!_.IsDescriptorType(type_operand)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "For OpConstantSizeOfEXT instruction, its Type operand <Id> "
+           << _.getIdName(type_operand) << " must be a Descriptor type.";
+  }
+  return SPV_SUCCESS;
+}
+
 // Validates that OpSpecConstant specializes to either int or float type.
 spv_result_t ValidateSpecConstant(ValidationState_t& _,
                                   const Instruction* inst) {
@@ -424,6 +647,95 @@
   return SPV_SUCCESS;
 }
 
+spv_result_t ValidateConstantFunctionPointerINTEL(ValidationState_t& _,
+                                                  const Instruction* inst) {
+  const auto result_type = _.FindDef(inst->type_id());
+  // Result Type must be a pointer type
+  if (result_type->opcode() != spv::Op::OpTypePointer &&
+      result_type->opcode() != spv::Op::OpTypeUntypedPointerKHR) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "OpConstantFunctionPointerINTEL Result Type <id> "
+           << _.getIdName(inst->type_id()) << " is not a pointer type";
+  }
+
+  // For typed pointers, check that pointee is a function type
+  const Instruction* pointee_type = nullptr;
+  if (result_type->opcode() == spv::Op::OpTypePointer) {
+    pointee_type = _.FindDef(result_type->GetOperandAs<uint32_t>(2));
+    if (pointee_type->opcode() != spv::Op::OpTypeFunction) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "OpConstantFunctionPointerINTEL Result Type <id> "
+             << _.getIdName(inst->type_id())
+             << " must be a pointer to function type";
+    }
+  }
+
+  // Validate that the function operand refers to an OpFunction
+  const uint32_t function_id = inst->GetOperandAs<uint32_t>(2);
+  const auto function_inst = _.FindDef(function_id);
+  if (function_inst->opcode() != spv::Op::OpFunction) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "OpConstantFunctionPointerINTEL Function operand <id> "
+           << _.getIdName(function_id) << " is not an OpFunction";
+  }
+
+  // For typed pointers, validate that function type matches pointee type
+  if (pointee_type) {
+    const uint32_t function_type_id = function_inst->GetOperandAs<uint32_t>(3);
+    if (function_type_id != pointee_type->id()) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "OpConstantFunctionPointerINTEL Function operand <id> "
+             << _.getIdName(function_id)
+             << " type does not match the pointer's function type";
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateConstantData(ValidationState_t& _,
+                                  const Instruction* inst) {
+  const auto array_inst = _.FindDef(inst->type_id());
+  if (array_inst->opcode() != spv::Op::OpTypeArray) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Result type must be an array.";
+  }
+
+  const auto element_type_inst =
+      _.FindDef(array_inst->GetOperandAs<uint32_t>(1));
+  if (!_.IsIntScalarType(element_type_inst->id())) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Result type must be an array of integer scalar type.";
+  }
+
+  const uint32_t int_width = element_type_inst->word(2);
+  const uint32_t data_words = static_cast<uint32_t>(inst->words().size() - 3);
+
+  if (data_words == 0) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "There must be at least 1 literal integer (because an array of "
+              "zero is not allowed).";
+  }
+
+  uint64_t array_length = 0;
+  if (!_.EvalConstantValUint64(array_inst->GetOperandAs<uint32_t>(2),
+                               &array_length)) {
+    // The length could be a SpecConstant, will need to be frozen to validate
+    return SPV_SUCCESS;
+  }
+
+  const uint32_t words_needed =
+      (((int_width / 8) * static_cast<uint32_t>(array_length) + 3) & ~3) / 4;
+  if (data_words != words_needed) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "contains " << data_words << " words of data, but needs to have "
+           << words_needed << " words to match the array of " << array_length
+           << " of " << int_width << "-bit ints.";
+  }
+
+  return SPV_SUCCESS;
+}
+
 }  // namespace
 
 spv_result_t ConstantPass(ValidationState_t& _, const Instruction* inst) {
@@ -438,6 +750,11 @@
     case spv::Op::OpSpecConstantComposite:
       if (auto error = ValidateConstantComposite(_, inst)) return error;
       break;
+    case spv::Op::OpConstantCompositeReplicateEXT:
+    case spv::Op::OpSpecConstantCompositeReplicateEXT:
+      if (auto error = ValidateConstantCompositeReplicate(_, inst))
+        return error;
+      break;
     case spv::Op::OpConstantSampler:
       if (auto error = ValidateConstantSampler(_, inst)) return error;
       break;
@@ -450,6 +767,16 @@
     case spv::Op::OpSpecConstantOp:
       if (auto error = ValidateSpecConstantOp(_, inst)) return error;
       break;
+    case spv::Op::OpConstantSizeOfEXT:
+      if (auto error = ValidateConstantSizeOfEXT(_, inst)) return error;
+      break;
+    case spv::Op::OpConstantFunctionPointerINTEL:
+      if (auto error = ValidateConstantFunctionPointerINTEL(_, inst))
+        return error;
+      break;
+    case spv::Op::OpConstantDataKHR:
+      if (auto error = ValidateConstantData(_, inst)) return error;
+      break;
     default:
       break;
   }
diff --git a/source/val/validate_conversion.cpp b/source/val/validate_conversion.cpp
index 770b8e2..639be6c 100644
--- a/source/val/validate_conversion.cpp
+++ b/source/val/validate_conversion.cpp
@@ -14,6 +14,8 @@
 
 // Validates correctness of conversion instructions.
 
+#include <climits>
+
 #include "source/opcode.h"
 #include "source/spirv_constant.h"
 #include "source/spirv_target_env.h"
@@ -24,610 +26,944 @@
 namespace spvtools {
 namespace val {
 
-// Validates correctness of conversion instructions.
-spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) {
+spv_result_t ValidateShaderBitWidth(ValidationState_t& _,
+                                    const Instruction* inst) {
+  if (_.HasCapability(spv::Capability::Shader)) {
+    if (_.ContainsLimitedUseIntOrFloatType(inst->type_id()) ||
+        _.ContainsLimitedUseIntOrFloatType(_.GetOperandTypeId(inst, 2u))) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "8- or 16-bit types can only be used with width-only "
+                "conversions";
+    }
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateConvertFToU(ValidationState_t& _, const Instruction* inst,
+                                 uint32_t operand_index = 2) {
   const spv::Op opcode = inst->opcode();
   const uint32_t result_type = inst->type_id();
+  if (!_.IsUnsignedIntScalarType(result_type) &&
+      !_.IsUnsignedIntVectorType(result_type) &&
+      !_.IsUnsignedIntCooperativeMatrixType(result_type) &&
+      !_.IsUnsignedIntCooperativeVectorNVType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected unsigned int scalar or vector type as Result Type: "
+           << spvOpcodeString(opcode);
 
-  switch (opcode) {
-    case spv::Op::OpConvertFToU: {
-      if (!_.IsUnsignedIntScalarType(result_type) &&
-          !_.IsUnsignedIntVectorType(result_type) &&
-          !_.IsUnsignedIntCooperativeMatrixType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected unsigned int scalar or vector type as Result Type: "
-               << spvOpcodeString(opcode);
+  const uint32_t input_type = _.GetOperandTypeId(inst, operand_index);
+  if (!input_type ||
+      (!_.IsFloatScalarType(input_type) && !_.IsFloatVectorType(input_type) &&
+       !_.IsFloatCooperativeMatrixType(input_type) &&
+       !_.IsFloatCooperativeVectorNVType(input_type)))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input to be float scalar or vector: "
+           << spvOpcodeString(opcode);
 
-      const uint32_t input_type = _.GetOperandTypeId(inst, 2);
-      if (!input_type || (!_.IsFloatScalarType(input_type) &&
-                          !_.IsFloatVectorType(input_type) &&
-                          !_.IsFloatCooperativeMatrixType(input_type)))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input to be float scalar or vector: "
-               << spvOpcodeString(opcode);
-
-      if (_.IsCooperativeMatrixType(result_type) ||
-          _.IsCooperativeMatrixType(input_type)) {
-        spv_result_t ret =
-            _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
-        if (ret != SPV_SUCCESS) return ret;
-      } else {
-        if (_.GetDimension(result_type) != _.GetDimension(input_type))
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected input to have the same dimension as Result Type: "
-                 << spvOpcodeString(opcode);
-      }
-
-      break;
-    }
-
-    case spv::Op::OpConvertFToS: {
-      if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) &&
-          !_.IsIntCooperativeMatrixType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected int scalar or vector type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t input_type = _.GetOperandTypeId(inst, 2);
-      if (!input_type || (!_.IsFloatScalarType(input_type) &&
-                          !_.IsFloatVectorType(input_type) &&
-                          !_.IsFloatCooperativeMatrixType(input_type)))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input to be float scalar or vector: "
-               << spvOpcodeString(opcode);
-
-      if (_.IsCooperativeMatrixType(result_type) ||
-          _.IsCooperativeMatrixType(input_type)) {
-        spv_result_t ret =
-            _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
-        if (ret != SPV_SUCCESS) return ret;
-      } else {
-        if (_.GetDimension(result_type) != _.GetDimension(input_type))
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected input to have the same dimension as Result Type: "
-                 << spvOpcodeString(opcode);
-      }
-
-      break;
-    }
-
-    case spv::Op::OpConvertSToF:
-    case spv::Op::OpConvertUToF: {
-      if (!_.IsFloatScalarType(result_type) &&
-          !_.IsFloatVectorType(result_type) &&
-          !_.IsFloatCooperativeMatrixType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected float scalar or vector type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t input_type = _.GetOperandTypeId(inst, 2);
-      if (!input_type ||
-          (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) &&
-           !_.IsIntCooperativeMatrixType(input_type)))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input to be int scalar or vector: "
-               << spvOpcodeString(opcode);
-
-      if (_.IsCooperativeMatrixType(result_type) ||
-          _.IsCooperativeMatrixType(input_type)) {
-        spv_result_t ret =
-            _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
-        if (ret != SPV_SUCCESS) return ret;
-      } else {
-        if (_.GetDimension(result_type) != _.GetDimension(input_type))
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected input to have the same dimension as Result Type: "
-                 << spvOpcodeString(opcode);
-      }
-
-      break;
-    }
-
-    case spv::Op::OpUConvert: {
-      if (!_.IsUnsignedIntScalarType(result_type) &&
-          !_.IsUnsignedIntVectorType(result_type) &&
-          !_.IsUnsignedIntCooperativeMatrixType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected unsigned int scalar or vector type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t input_type = _.GetOperandTypeId(inst, 2);
-      if (!input_type ||
-          (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) &&
-           !_.IsIntCooperativeMatrixType(input_type)))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input to be int scalar or vector: "
-               << spvOpcodeString(opcode);
-
-      if (_.IsCooperativeMatrixType(result_type) ||
-          _.IsCooperativeMatrixType(input_type)) {
-        spv_result_t ret =
-            _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
-        if (ret != SPV_SUCCESS) return ret;
-      } else {
-        if (_.GetDimension(result_type) != _.GetDimension(input_type))
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected input to have the same dimension as Result Type: "
-                 << spvOpcodeString(opcode);
-      }
-
-      if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input to have different bit width from Result "
-                  "Type: "
-               << spvOpcodeString(opcode);
-      break;
-    }
-
-    case spv::Op::OpSConvert: {
-      if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) &&
-          !_.IsIntCooperativeMatrixType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected int scalar or vector type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t input_type = _.GetOperandTypeId(inst, 2);
-      if (!input_type ||
-          (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) &&
-           !_.IsIntCooperativeMatrixType(input_type)))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input to be int scalar or vector: "
-               << spvOpcodeString(opcode);
-
-      if (_.IsCooperativeMatrixType(result_type) ||
-          _.IsCooperativeMatrixType(input_type)) {
-        spv_result_t ret =
-            _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
-        if (ret != SPV_SUCCESS) return ret;
-      } else {
-        if (_.GetDimension(result_type) != _.GetDimension(input_type))
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected input to have the same dimension as Result Type: "
-                 << spvOpcodeString(opcode);
-      }
-
-      if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input to have different bit width from Result "
-                  "Type: "
-               << spvOpcodeString(opcode);
-      break;
-    }
-
-    case spv::Op::OpFConvert: {
-      if (!_.IsFloatScalarType(result_type) &&
-          !_.IsFloatVectorType(result_type) &&
-          !_.IsFloatCooperativeMatrixType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected float scalar or vector type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t input_type = _.GetOperandTypeId(inst, 2);
-      if (!input_type || (!_.IsFloatScalarType(input_type) &&
-                          !_.IsFloatVectorType(input_type) &&
-                          !_.IsFloatCooperativeMatrixType(input_type)))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input to be float scalar or vector: "
-               << spvOpcodeString(opcode);
-
-      if (_.IsCooperativeMatrixType(result_type) ||
-          _.IsCooperativeMatrixType(input_type)) {
-        spv_result_t ret =
-            _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
-        if (ret != SPV_SUCCESS) return ret;
-      } else {
-        if (_.GetDimension(result_type) != _.GetDimension(input_type))
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected input to have the same dimension as Result Type: "
-                 << spvOpcodeString(opcode);
-      }
-
-      if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input to have different bit width from Result "
-                  "Type: "
-               << spvOpcodeString(opcode);
-      break;
-    }
-
-    case spv::Op::OpQuantizeToF16: {
-      if ((!_.IsFloatScalarType(result_type) &&
-           !_.IsFloatVectorType(result_type)) ||
-          _.GetBitWidth(result_type) != 32)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected 32-bit float scalar or vector type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t input_type = _.GetOperandTypeId(inst, 2);
-      if (input_type != result_type)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input type to be equal to Result Type: "
-               << spvOpcodeString(opcode);
-      break;
-    }
-
-    case spv::Op::OpConvertPtrToU: {
-      if (!_.IsUnsignedIntScalarType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected unsigned int scalar type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t input_type = _.GetOperandTypeId(inst, 2);
-      if (!_.IsPointerType(input_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input to be a pointer: " << spvOpcodeString(opcode);
-
-      if (_.addressing_model() == spv::AddressingModel::Logical)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Logical addressing not supported: "
-               << spvOpcodeString(opcode);
-
-      if (_.addressing_model() ==
-          spv::AddressingModel::PhysicalStorageBuffer64) {
-        spv::StorageClass input_storage_class;
-        uint32_t input_data_type = 0;
-        _.GetPointerTypeInfo(input_type, &input_data_type,
-                             &input_storage_class);
-        if (input_storage_class != spv::StorageClass::PhysicalStorageBuffer)
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Pointer storage class must be PhysicalStorageBuffer: "
-                 << spvOpcodeString(opcode);
-
-        if (spvIsVulkanEnv(_.context()->target_env)) {
-          if (_.GetBitWidth(result_type) != 64) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << _.VkErrorID(4710)
-                   << "PhysicalStorageBuffer64 addressing mode requires the "
-                      "result integer type to have a 64-bit width for Vulkan "
-                      "environment.";
-          }
-        }
-      }
-      break;
-    }
-
-    case spv::Op::OpSatConvertSToU:
-    case spv::Op::OpSatConvertUToS: {
-      if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected int scalar or vector type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t input_type = _.GetOperandTypeId(inst, 2);
-      if (!input_type ||
-          (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type)))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected int scalar or vector as input: "
-               << spvOpcodeString(opcode);
-
-      if (_.GetDimension(result_type) != _.GetDimension(input_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input to have the same dimension as Result Type: "
-               << spvOpcodeString(opcode);
-      break;
-    }
-
-    case spv::Op::OpConvertUToPtr: {
-      if (!_.IsPointerType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Result Type to be a pointer: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t input_type = _.GetOperandTypeId(inst, 2);
-      if (!input_type || !_.IsIntScalarType(input_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected int scalar as input: " << spvOpcodeString(opcode);
-
-      if (_.addressing_model() == spv::AddressingModel::Logical)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Logical addressing not supported: "
-               << spvOpcodeString(opcode);
-
-      if (_.addressing_model() ==
-          spv::AddressingModel::PhysicalStorageBuffer64) {
-        spv::StorageClass result_storage_class;
-        uint32_t result_data_type = 0;
-        _.GetPointerTypeInfo(result_type, &result_data_type,
-                             &result_storage_class);
-        if (result_storage_class != spv::StorageClass::PhysicalStorageBuffer)
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Pointer storage class must be PhysicalStorageBuffer: "
-                 << spvOpcodeString(opcode);
-
-        if (spvIsVulkanEnv(_.context()->target_env)) {
-          if (_.GetBitWidth(input_type) != 64) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << _.VkErrorID(4710)
-                   << "PhysicalStorageBuffer64 addressing mode requires the "
-                      "input integer to have a 64-bit width for Vulkan "
-                      "environment.";
-          }
-        }
-      }
-      break;
-    }
-
-    case spv::Op::OpPtrCastToGeneric: {
-      spv::StorageClass result_storage_class;
-      uint32_t result_data_type = 0;
-      if (!_.GetPointerTypeInfo(result_type, &result_data_type,
-                                &result_storage_class))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Result Type to be a pointer: "
-               << spvOpcodeString(opcode);
-
-      if (result_storage_class != spv::StorageClass::Generic)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Result Type to have storage class Generic: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t input_type = _.GetOperandTypeId(inst, 2);
-      spv::StorageClass input_storage_class;
-      uint32_t input_data_type = 0;
-      if (!_.GetPointerTypeInfo(input_type, &input_data_type,
-                                &input_storage_class))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input to be a pointer: " << spvOpcodeString(opcode);
-
-      if (input_storage_class != spv::StorageClass::Workgroup &&
-          input_storage_class != spv::StorageClass::CrossWorkgroup &&
-          input_storage_class != spv::StorageClass::Function)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input to have storage class Workgroup, "
-               << "CrossWorkgroup or Function: " << spvOpcodeString(opcode);
-
-      if (result_data_type != input_data_type)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input and Result Type to point to the same type: "
-               << spvOpcodeString(opcode);
-      break;
-    }
-
-    case spv::Op::OpGenericCastToPtr: {
-      spv::StorageClass result_storage_class;
-      uint32_t result_data_type = 0;
-      if (!_.GetPointerTypeInfo(result_type, &result_data_type,
-                                &result_storage_class))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Result Type to be a pointer: "
-               << spvOpcodeString(opcode);
-
-      if (result_storage_class != spv::StorageClass::Workgroup &&
-          result_storage_class != spv::StorageClass::CrossWorkgroup &&
-          result_storage_class != spv::StorageClass::Function)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Result Type to have storage class Workgroup, "
-               << "CrossWorkgroup or Function: " << spvOpcodeString(opcode);
-
-      const uint32_t input_type = _.GetOperandTypeId(inst, 2);
-      spv::StorageClass input_storage_class;
-      uint32_t input_data_type = 0;
-      if (!_.GetPointerTypeInfo(input_type, &input_data_type,
-                                &input_storage_class))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input to be a pointer: " << spvOpcodeString(opcode);
-
-      if (input_storage_class != spv::StorageClass::Generic)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input to have storage class Generic: "
-               << spvOpcodeString(opcode);
-
-      if (result_data_type != input_data_type)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input and Result Type to point to the same type: "
-               << spvOpcodeString(opcode);
-      break;
-    }
-
-    case spv::Op::OpGenericCastToPtrExplicit: {
-      spv::StorageClass result_storage_class;
-      uint32_t result_data_type = 0;
-      if (!_.GetPointerTypeInfo(result_type, &result_data_type,
-                                &result_storage_class))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Result Type to be a pointer: "
-               << spvOpcodeString(opcode);
-
-      const auto target_storage_class =
-          inst->GetOperandAs<spv::StorageClass>(3);
-      if (result_storage_class != target_storage_class)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Result Type to be of target storage class: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t input_type = _.GetOperandTypeId(inst, 2);
-      spv::StorageClass input_storage_class;
-      uint32_t input_data_type = 0;
-      if (!_.GetPointerTypeInfo(input_type, &input_data_type,
-                                &input_storage_class))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input to be a pointer: " << spvOpcodeString(opcode);
-
-      if (input_storage_class != spv::StorageClass::Generic)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input to have storage class Generic: "
-               << spvOpcodeString(opcode);
-
-      if (result_data_type != input_data_type)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input and Result Type to point to the same type: "
-               << spvOpcodeString(opcode);
-
-      if (target_storage_class != spv::StorageClass::Workgroup &&
-          target_storage_class != spv::StorageClass::CrossWorkgroup &&
-          target_storage_class != spv::StorageClass::Function)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected target storage class to be Workgroup, "
-               << "CrossWorkgroup or Function: " << spvOpcodeString(opcode);
-      break;
-    }
-
-    case spv::Op::OpBitcast: {
-      const uint32_t input_type = _.GetOperandTypeId(inst, 2);
-      if (!input_type)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input to have a type: " << spvOpcodeString(opcode);
-
-      const bool result_is_pointer = _.IsPointerType(result_type);
-      const bool result_is_int_scalar = _.IsIntScalarType(result_type);
-      const bool input_is_pointer = _.IsPointerType(input_type);
-      const bool input_is_int_scalar = _.IsIntScalarType(input_type);
-
-      const bool result_is_coopmat = _.IsCooperativeMatrixType(result_type);
-      const bool input_is_coopmat = _.IsCooperativeMatrixType(input_type);
-
-      if (!result_is_pointer && !result_is_int_scalar && !result_is_coopmat &&
-          !_.IsIntVectorType(result_type) &&
-          !_.IsFloatScalarType(result_type) &&
-          !_.IsFloatVectorType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Result Type to be a pointer or int or float vector "
-               << "or scalar type: " << spvOpcodeString(opcode);
-
-      if (!input_is_pointer && !input_is_int_scalar && !input_is_coopmat &&
-          !_.IsIntVectorType(input_type) && !_.IsFloatScalarType(input_type) &&
-          !_.IsFloatVectorType(input_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected input to be a pointer or int or float vector "
-               << "or scalar: " << spvOpcodeString(opcode);
-
-      if (result_is_coopmat != input_is_coopmat)
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Cooperative matrix can only be cast to another cooperative "
-               << "matrix: " << spvOpcodeString(opcode);
-
-      if (result_is_coopmat) {
-        spv_result_t ret = _.CooperativeMatrixShapesMatch(inst, result_type,
-                                                          input_type, false);
-        if (ret != SPV_SUCCESS) return ret;
-      }
-
-      if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 5) ||
-          _.HasExtension(kSPV_KHR_physical_storage_buffer)) {
-        const bool result_is_int_vector = _.IsIntVectorType(result_type);
-        const bool result_has_int32 =
-            _.ContainsSizedIntOrFloatType(result_type, spv::Op::OpTypeInt, 32);
-        const bool input_is_int_vector = _.IsIntVectorType(input_type);
-        const bool input_has_int32 =
-            _.ContainsSizedIntOrFloatType(input_type, spv::Op::OpTypeInt, 32);
-        if (result_is_pointer && !input_is_pointer && !input_is_int_scalar &&
-            !(input_is_int_vector && input_has_int32))
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected input to be a pointer, int scalar or 32-bit int "
-                    "vector if Result Type is pointer: "
-                 << spvOpcodeString(opcode);
-
-        if (input_is_pointer && !result_is_pointer && !result_is_int_scalar &&
-            !(result_is_int_vector && result_has_int32))
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Pointer can only be converted to another pointer, int "
-                    "scalar or 32-bit int vector: "
-                 << spvOpcodeString(opcode);
-      } else {
-        if (result_is_pointer && !input_is_pointer && !input_is_int_scalar)
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected input to be a pointer or int scalar if Result "
-                    "Type is pointer: "
-                 << spvOpcodeString(opcode);
-
-        if (input_is_pointer && !result_is_pointer && !result_is_int_scalar)
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Pointer can only be converted to another pointer or int "
-                    "scalar: "
-                 << spvOpcodeString(opcode);
-      }
-
-      if (!result_is_pointer && !input_is_pointer) {
-        const uint32_t result_size =
-            _.GetBitWidth(result_type) * _.GetDimension(result_type);
-        const uint32_t input_size =
-            _.GetBitWidth(input_type) * _.GetDimension(input_type);
-        if (result_size != input_size)
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected input to have the same total bit width as "
-                 << "Result Type: " << spvOpcodeString(opcode);
-      }
-      break;
-    }
-
-    case spv::Op::OpConvertUToAccelerationStructureKHR: {
-      if (!_.IsAccelerationStructureType(result_type)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Result Type to be a Acceleration Structure: "
-               << spvOpcodeString(opcode);
-      }
-
-      const uint32_t input_type = _.GetOperandTypeId(inst, 2);
-      if (!input_type || !_.IsUnsigned64BitHandle(input_type)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected 64-bit uint scalar or 2-component 32-bit uint "
-                  "vector as input: "
-               << spvOpcodeString(opcode);
-      }
-
-      break;
-    }
-
-    case spv::Op::OpCooperativeMatrixConvertNV:
-    case spv::Op::OpCooperativeMatrixTransposeNV: {
-      if (!_.IsCooperativeMatrixType(result_type)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected cooperative matrix Result Type: "
-               << spvOpcodeString(opcode);
-      }
-      const uint32_t input_type = _.GetOperandTypeId(inst, 2);
-      if (!_.IsCooperativeMatrixType(input_type)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected cooperative matrix type for Matrix input: "
-               << spvOpcodeString(opcode);
-      }
-
-      bool swap_row_col = (opcode == spv::Op::OpCooperativeMatrixTransposeNV);
-      if (auto error = _.CooperativeMatrixShapesMatch(
-              inst, result_type, input_type, true, swap_row_col))
-        return error;
-
-      if (opcode == spv::Op::OpCooperativeMatrixConvertNV) {
-        if (_.FindDef(result_type)->GetOperandAs<uint32_t>(1) !=
-            _.FindDef(input_type)->GetOperandAs<uint32_t>(1)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Result Type and Matrix component types mismatch: "
-                 << spvOpcodeString(opcode);
-        }
-      }
-
-      if (opcode == spv::Op::OpCooperativeMatrixTransposeNV) {
-        if (!_.IsCooperativeMatrixBType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Result Type must have UseB: " << spvOpcodeString(opcode);
-        }
-      }
-      break;
-    }
-
-    default:
-      break;
+  if (_.IsCooperativeVectorNVType(result_type) ||
+      _.IsCooperativeVectorNVType(input_type)) {
+    spv_result_t ret =
+        _.CooperativeVectorDimensionsMatch(inst, result_type, input_type);
+    if (ret != SPV_SUCCESS) return ret;
+  } else if (_.IsCooperativeMatrixType(result_type) ||
+             _.IsCooperativeMatrixType(input_type)) {
+    spv_result_t ret =
+        _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
+    if (ret != SPV_SUCCESS) return ret;
+  } else {
+    if (_.GetDimension(result_type) != _.GetDimension(input_type))
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected input to have the same dimension as Result Type: "
+             << spvOpcodeString(opcode);
   }
 
-  if (_.HasCapability(spv::Capability::Shader)) {
-    switch (inst->opcode()) {
-      case spv::Op::OpConvertFToU:
-      case spv::Op::OpConvertFToS:
-      case spv::Op::OpConvertSToF:
-      case spv::Op::OpConvertUToF:
-      case spv::Op::OpBitcast:
-        if (_.ContainsLimitedUseIntOrFloatType(inst->type_id()) ||
-            _.ContainsLimitedUseIntOrFloatType(_.GetOperandTypeId(inst, 2u))) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "8- or 16-bit types can only be used with width-only "
-                    "conversions";
-        }
-        break;
-      default:
-        break;
+  if (auto error = ValidateShaderBitWidth(_, inst)) return error;
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateConvertFToS(ValidationState_t& _, const Instruction* inst,
+                                 uint32_t operand_index = 2) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) &&
+      !_.IsIntCooperativeMatrixType(result_type) &&
+      !_.IsIntCooperativeVectorNVType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected int scalar or vector type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t input_type = _.GetOperandTypeId(inst, operand_index);
+  if (!input_type ||
+      (!_.IsFloatScalarType(input_type) && !_.IsFloatVectorType(input_type) &&
+       !_.IsFloatCooperativeMatrixType(input_type) &&
+       !_.IsFloatCooperativeVectorNVType(input_type)))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input to be float scalar or vector: "
+           << spvOpcodeString(opcode);
+
+  if (_.IsCooperativeVectorNVType(result_type) ||
+      _.IsCooperativeVectorNVType(input_type)) {
+    spv_result_t ret =
+        _.CooperativeVectorDimensionsMatch(inst, result_type, input_type);
+    if (ret != SPV_SUCCESS) return ret;
+  } else if (_.IsCooperativeMatrixType(result_type) ||
+             _.IsCooperativeMatrixType(input_type)) {
+    spv_result_t ret =
+        _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
+    if (ret != SPV_SUCCESS) return ret;
+  } else {
+    if (_.GetDimension(result_type) != _.GetDimension(input_type))
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected input to have the same dimension as Result Type: "
+             << spvOpcodeString(opcode);
+  }
+
+  if (auto error = ValidateShaderBitWidth(_, inst)) return error;
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateConvertIntToF(ValidationState_t& _,
+                                   const Instruction* inst,
+                                   uint32_t operand_index = 2) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsFloatScalarType(result_type) && !_.IsFloatVectorType(result_type) &&
+      !_.IsFloatCooperativeMatrixType(result_type) &&
+      !_.IsFloatCooperativeVectorNVType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected float scalar or vector type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t input_type = _.GetOperandTypeId(inst, operand_index);
+  if (!input_type ||
+      (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) &&
+       !_.IsIntCooperativeMatrixType(input_type) &&
+       !_.IsIntCooperativeVectorNVType(input_type)))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input to be int scalar or vector: "
+           << spvOpcodeString(opcode);
+
+  if (_.IsCooperativeVectorNVType(result_type) ||
+      _.IsCooperativeVectorNVType(input_type)) {
+    spv_result_t ret =
+        _.CooperativeVectorDimensionsMatch(inst, result_type, input_type);
+    if (ret != SPV_SUCCESS) return ret;
+  } else if (_.IsCooperativeMatrixType(result_type) ||
+             _.IsCooperativeMatrixType(input_type)) {
+    spv_result_t ret =
+        _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
+    if (ret != SPV_SUCCESS) return ret;
+  } else {
+    if (_.GetDimension(result_type) != _.GetDimension(input_type))
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected input to have the same dimension as Result Type: "
+             << spvOpcodeString(opcode);
+  }
+
+  if (auto error = ValidateShaderBitWidth(_, inst)) return error;
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateUConvert(ValidationState_t& _, const Instruction* inst,
+                              uint32_t operand_index = 2) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsUnsignedIntScalarType(result_type) &&
+      !_.IsUnsignedIntVectorType(result_type) &&
+      !_.IsUnsignedIntCooperativeMatrixType(result_type) &&
+      !_.IsUnsignedIntCooperativeVectorNVType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected unsigned int scalar or vector type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t input_type = _.GetOperandTypeId(inst, operand_index);
+  if (!input_type ||
+      (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) &&
+       !_.IsIntCooperativeMatrixType(input_type) &&
+       !_.IsIntCooperativeVectorNVType(input_type)))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input to be int scalar or vector: "
+           << spvOpcodeString(opcode);
+
+  if (_.IsCooperativeVectorNVType(result_type) ||
+      _.IsCooperativeVectorNVType(input_type)) {
+    spv_result_t ret =
+        _.CooperativeVectorDimensionsMatch(inst, result_type, input_type);
+    if (ret != SPV_SUCCESS) return ret;
+  } else if (_.IsCooperativeMatrixType(result_type) ||
+             _.IsCooperativeMatrixType(input_type)) {
+    spv_result_t ret =
+        _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
+    if (ret != SPV_SUCCESS) return ret;
+  } else {
+    if (_.GetDimension(result_type) != _.GetDimension(input_type))
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected input to have the same dimension as Result Type: "
+             << spvOpcodeString(opcode);
+  }
+
+  if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input to have different bit width from Result "
+              "Type: "
+           << spvOpcodeString(opcode);
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateSConvert(ValidationState_t& _, const Instruction* inst,
+                              uint32_t operand_index = 2) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) &&
+      !_.IsIntCooperativeMatrixType(result_type) &&
+      !_.IsIntCooperativeVectorNVType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected int scalar or vector type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t input_type = _.GetOperandTypeId(inst, operand_index);
+  if (!input_type ||
+      (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) &&
+       !_.IsIntCooperativeMatrixType(input_type) &&
+       !_.IsIntCooperativeVectorNVType(input_type)))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input to be int scalar or vector: "
+           << spvOpcodeString(opcode);
+
+  if (_.IsCooperativeVectorNVType(result_type) ||
+      _.IsCooperativeVectorNVType(input_type)) {
+    spv_result_t ret =
+        _.CooperativeVectorDimensionsMatch(inst, result_type, input_type);
+    if (ret != SPV_SUCCESS) return ret;
+  } else if (_.IsCooperativeMatrixType(result_type) ||
+             _.IsCooperativeMatrixType(input_type)) {
+    spv_result_t ret =
+        _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
+    if (ret != SPV_SUCCESS) return ret;
+  } else {
+    if (_.GetDimension(result_type) != _.GetDimension(input_type))
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected input to have the same dimension as Result Type: "
+             << spvOpcodeString(opcode);
+  }
+
+  if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input to have different bit width from Result "
+              "Type: "
+           << spvOpcodeString(opcode);
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateFConvert(ValidationState_t& _, const Instruction* inst,
+                              uint32_t operand_index = 2) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsFloatScalarType(result_type) && !_.IsFloatVectorType(result_type) &&
+      !_.IsFloatCooperativeMatrixType(result_type) &&
+      !_.IsFloatCooperativeVectorNVType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected float scalar or vector type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t input_type = _.GetOperandTypeId(inst, operand_index);
+  if (!input_type ||
+      (!_.IsFloatScalarType(input_type) && !_.IsFloatVectorType(input_type) &&
+       !_.IsFloatCooperativeMatrixType(input_type) &&
+       !_.IsFloatCooperativeVectorNVType(input_type)))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input to be float scalar or vector: "
+           << spvOpcodeString(opcode);
+
+  if (_.IsCooperativeVectorNVType(result_type) ||
+      _.IsCooperativeVectorNVType(input_type)) {
+    spv_result_t ret =
+        _.CooperativeVectorDimensionsMatch(inst, result_type, input_type);
+    if (ret != SPV_SUCCESS) return ret;
+  } else if (_.IsCooperativeMatrixType(result_type) ||
+             _.IsCooperativeMatrixType(input_type)) {
+    spv_result_t ret =
+        _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
+    if (ret != SPV_SUCCESS) return ret;
+  } else {
+    if (_.GetDimension(result_type) != _.GetDimension(input_type))
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected input to have the same dimension as Result Type: "
+             << spvOpcodeString(opcode);
+  }
+
+  // Scalar type
+  const uint32_t resScalarType = _.GetComponentType(result_type);
+  const uint32_t inputScalartype = _.GetComponentType(input_type);
+  if (resScalarType == inputScalartype) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected component type of Value to be different from "
+              "component type of Result Type: "
+           << spvOpcodeString(opcode);
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateQuantizeToF16(ValidationState_t& _,
+                                   const Instruction* inst,
+                                   uint32_t operand_index = 2) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  if ((!_.IsFloatScalarType(result_type) &&
+       !_.IsFloatVectorType(result_type)) ||
+      _.GetBitWidth(result_type) != 32)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected 32-bit float scalar or vector type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t input_type = _.GetOperandTypeId(inst, operand_index);
+  if (input_type != result_type)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input type to be equal to Result Type: "
+           << spvOpcodeString(opcode);
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateConvertPtrToU(ValidationState_t& _,
+                                   const Instruction* inst,
+                                   uint32_t operand_index = 2) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  const bool has_masked_gather_scatter =
+      _.HasCapability(spv::Capability::MaskedGatherScatterINTEL);
+
+  bool valid_result_type = _.IsUnsignedIntScalarType(result_type);
+  if (!valid_result_type && has_masked_gather_scatter) {
+    valid_result_type = _.IsUnsignedIntVectorType(result_type);
+  }
+
+  if (!valid_result_type) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected unsigned int scalar type as Result Type"
+           << (has_masked_gather_scatter ? " (or vector of unsigned int with "
+                                           "MaskedGatherScatterINTEL)"
+                                         : "")
+           << ": " << spvOpcodeString(opcode);
+  }
+
+  const uint32_t input_type = _.GetOperandTypeId(inst, operand_index);
+
+  bool valid_input_type = _.IsPointerType(input_type);
+  if (!valid_input_type && has_masked_gather_scatter && input_type) {
+    if (_.IsVectorType(input_type)) {
+      const uint32_t component_type = _.GetComponentType(input_type);
+      valid_input_type = _.IsPointerType(component_type);
     }
   }
 
+  if (!valid_input_type) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input to be a pointer"
+           << (has_masked_gather_scatter
+                   ? " (or vector of pointers with MaskedGatherScatterINTEL)"
+                   : "")
+           << ": " << spvOpcodeString(opcode);
+  }
+
+  if (has_masked_gather_scatter && _.IsVectorType(result_type)) {
+    if (!_.IsVectorType(input_type)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected input to be a vector when Result Type is a vector: "
+             << spvOpcodeString(opcode);
+    }
+    if (_.GetDimension(result_type) != _.GetDimension(input_type)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected input to have the same dimension as Result Type: "
+             << spvOpcodeString(opcode);
+    }
+  }
+
+  if (_.addressing_model() == spv::AddressingModel::Logical)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Logical addressing not supported: " << spvOpcodeString(opcode);
+
+  if (_.addressing_model() == spv::AddressingModel::PhysicalStorageBuffer64) {
+    uint32_t ptr_type = input_type;
+    if (_.IsVectorType(input_type)) {
+      ptr_type = _.GetComponentType(input_type);
+    }
+    spv::StorageClass input_storage_class;
+    uint32_t input_data_type = 0;
+    _.GetPointerTypeInfo(ptr_type, &input_data_type, &input_storage_class);
+    if (input_storage_class != spv::StorageClass::PhysicalStorageBuffer)
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Pointer storage class must be PhysicalStorageBuffer: "
+             << spvOpcodeString(opcode);
+
+    if (spvIsVulkanEnv(_.context()->target_env)) {
+      if (_.GetBitWidth(result_type) != 64) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << _.VkErrorID(4710)
+               << "PhysicalStorageBuffer64 addressing mode requires the "
+                  "result integer type to have a 64-bit width for Vulkan "
+                  "environment.";
+      }
+    }
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateSatConvertInt(ValidationState_t& _,
+                                   const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected int scalar or vector type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t input_type = _.GetOperandTypeId(inst, 2);
+  if (!input_type ||
+      (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type)))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected int scalar or vector as input: "
+           << spvOpcodeString(opcode);
+
+  if (_.GetDimension(result_type) != _.GetDimension(input_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input to have the same dimension as Result Type: "
+           << spvOpcodeString(opcode);
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateConvertUToPtr(ValidationState_t& _,
+                                   const Instruction* inst,
+                                   uint32_t operand_index = 2) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  const bool has_masked_gather_scatter =
+      _.HasCapability(spv::Capability::MaskedGatherScatterINTEL);
+
+  bool valid_result_type = _.IsPointerType(result_type);
+  if (!valid_result_type && has_masked_gather_scatter) {
+    if (_.IsVectorType(result_type)) {
+      const uint32_t component_type = _.GetComponentType(result_type);
+      valid_result_type = _.IsPointerType(component_type);
+    }
+  }
+
+  if (!valid_result_type) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Result Type to be a pointer"
+           << (has_masked_gather_scatter
+                   ? " (or vector of pointers with MaskedGatherScatterINTEL)"
+                   : "")
+           << ": " << spvOpcodeString(opcode);
+  }
+
+  const uint32_t input_type = _.GetOperandTypeId(inst, operand_index);
+
+  bool valid_input_type = input_type && _.IsIntScalarType(input_type);
+  if (!valid_input_type && has_masked_gather_scatter && input_type) {
+    valid_input_type = _.IsIntVectorType(input_type);
+  }
+
+  if (!valid_input_type) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected int scalar as input"
+           << (has_masked_gather_scatter
+                   ? " (or vector of int with MaskedGatherScatterINTEL)"
+                   : "")
+           << ": " << spvOpcodeString(opcode);
+  }
+
+  if (has_masked_gather_scatter && _.IsVectorType(result_type)) {
+    if (!_.IsVectorType(input_type)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected input to be a vector when Result Type is a vector: "
+             << spvOpcodeString(opcode);
+    }
+    if (_.GetDimension(result_type) != _.GetDimension(input_type)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected input to have the same dimension as Result Type: "
+             << spvOpcodeString(opcode);
+    }
+  }
+
+  if (_.addressing_model() == spv::AddressingModel::Logical)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Logical addressing not supported: " << spvOpcodeString(opcode);
+
+  if (_.addressing_model() == spv::AddressingModel::PhysicalStorageBuffer64) {
+    uint32_t ptr_type = result_type;
+    if (_.IsVectorType(result_type)) {
+      ptr_type = _.GetComponentType(result_type);
+    }
+    spv::StorageClass result_storage_class;
+    uint32_t result_data_type = 0;
+    _.GetPointerTypeInfo(ptr_type, &result_data_type, &result_storage_class);
+    if (result_storage_class != spv::StorageClass::PhysicalStorageBuffer)
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Pointer storage class must be PhysicalStorageBuffer: "
+             << spvOpcodeString(opcode);
+
+    if (spvIsVulkanEnv(_.context()->target_env)) {
+      if (_.GetBitWidth(input_type) != 64) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << _.VkErrorID(4710)
+               << "PhysicalStorageBuffer64 addressing mode requires the "
+                  "input integer to have a 64-bit width for Vulkan "
+                  "environment.";
+      }
+    }
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidatePtrCastToGeneric(ValidationState_t& _,
+                                      const Instruction* inst,
+                                      uint32_t operand_index = 2) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  spv::StorageClass result_storage_class;
+  uint32_t result_data_type = 0;
+  if (!_.GetPointerTypeInfo(result_type, &result_data_type,
+                            &result_storage_class))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Result Type to be a pointer: "
+           << spvOpcodeString(opcode);
+
+  if (result_storage_class != spv::StorageClass::Generic)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Result Type to have storage class Generic: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t input_type = _.GetOperandTypeId(inst, operand_index);
+  spv::StorageClass input_storage_class;
+  uint32_t input_data_type = 0;
+  if (!_.GetPointerTypeInfo(input_type, &input_data_type, &input_storage_class))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input to be a pointer: " << spvOpcodeString(opcode);
+
+  if (input_storage_class != spv::StorageClass::Workgroup &&
+      input_storage_class != spv::StorageClass::CrossWorkgroup &&
+      input_storage_class != spv::StorageClass::Function)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input to have storage class Workgroup, "
+           << "CrossWorkgroup or Function: " << spvOpcodeString(opcode);
+
+  if (result_data_type != input_data_type)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input and Result Type to point to the same type: "
+           << spvOpcodeString(opcode);
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateGenericCastToPtr(ValidationState_t& _,
+                                      const Instruction* inst,
+                                      uint32_t operand_index = 2) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  spv::StorageClass result_storage_class;
+  uint32_t result_data_type = 0;
+  if (!_.GetPointerTypeInfo(result_type, &result_data_type,
+                            &result_storage_class))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Result Type to be a pointer: "
+           << spvOpcodeString(opcode);
+
+  if (result_storage_class != spv::StorageClass::Workgroup &&
+      result_storage_class != spv::StorageClass::CrossWorkgroup &&
+      result_storage_class != spv::StorageClass::Function)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Result Type to have storage class Workgroup, "
+           << "CrossWorkgroup or Function: " << spvOpcodeString(opcode);
+
+  const uint32_t input_type = _.GetOperandTypeId(inst, operand_index);
+  spv::StorageClass input_storage_class;
+  uint32_t input_data_type = 0;
+  if (!_.GetPointerTypeInfo(input_type, &input_data_type, &input_storage_class))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input to be a pointer: " << spvOpcodeString(opcode);
+
+  if (input_storage_class != spv::StorageClass::Generic)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input to have storage class Generic: "
+           << spvOpcodeString(opcode);
+
+  if (result_data_type != input_data_type)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input and Result Type to point to the same type: "
+           << spvOpcodeString(opcode);
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateGenericCastToPtrExplicit(ValidationState_t& _,
+                                              const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  spv::StorageClass result_storage_class;
+  uint32_t result_data_type = 0;
+  if (!_.GetPointerTypeInfo(result_type, &result_data_type,
+                            &result_storage_class))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Result Type to be a pointer: "
+           << spvOpcodeString(opcode);
+
+  const auto target_storage_class = inst->GetOperandAs<spv::StorageClass>(3);
+  if (result_storage_class != target_storage_class)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Result Type to be of target storage class: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t input_type = _.GetOperandTypeId(inst, 2);
+  spv::StorageClass input_storage_class;
+  uint32_t input_data_type = 0;
+  if (!_.GetPointerTypeInfo(input_type, &input_data_type, &input_storage_class))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input to be a pointer: " << spvOpcodeString(opcode);
+
+  if (input_storage_class != spv::StorageClass::Generic)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input to have storage class Generic: "
+           << spvOpcodeString(opcode);
+
+  if (result_data_type != input_data_type)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input and Result Type to point to the same type: "
+           << spvOpcodeString(opcode);
+
+  if (target_storage_class != spv::StorageClass::Workgroup &&
+      target_storage_class != spv::StorageClass::CrossWorkgroup &&
+      target_storage_class != spv::StorageClass::Function)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected target storage class to be Workgroup, "
+           << "CrossWorkgroup or Function: " << spvOpcodeString(opcode);
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateBitcast(ValidationState_t& _, const Instruction* inst,
+                             uint32_t operand_index = 2) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  const uint32_t input_type = _.GetOperandTypeId(inst, operand_index);
+  if (!input_type)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input to have a type: " << spvOpcodeString(opcode);
+
+  const bool result_is_pointer = _.IsPointerType(result_type);
+  const bool result_is_int_scalar = _.IsIntScalarType(result_type);
+  const bool input_is_pointer = _.IsPointerType(input_type);
+  const bool input_is_int_scalar = _.IsIntScalarType(input_type);
+
+  const bool result_is_coopmat = _.IsCooperativeMatrixType(result_type);
+  const bool input_is_coopmat = _.IsCooperativeMatrixType(input_type);
+  const bool result_is_coopvec = _.IsCooperativeVectorNVType(result_type);
+  const bool input_is_coopvec = _.IsCooperativeVectorNVType(input_type);
+
+  if (!result_is_pointer && !result_is_int_scalar && !result_is_coopmat &&
+      !result_is_coopvec && !_.IsIntVectorType(result_type) &&
+      !_.IsFloatScalarType(result_type) && !_.IsFloatVectorType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Result Type to be a pointer or int or float vector "
+           << "or scalar type: " << spvOpcodeString(opcode);
+
+  if (!input_is_pointer && !input_is_int_scalar && !input_is_coopmat &&
+      !input_is_coopvec && !_.IsIntVectorType(input_type) &&
+      !_.IsFloatScalarType(input_type) && !_.IsFloatVectorType(input_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected input to be a pointer or int or float vector "
+           << "or scalar: " << spvOpcodeString(opcode);
+
+  // NV_cooperative_vector doesn't allow bitcasting between vec<->coopvec,
+  // but long_vector does.
+  if (result_is_coopvec != input_is_coopvec &&
+      !_.HasCapability(spv::Capability::LongVectorEXT))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Cooperative vector can only be cast to another cooperative "
+           << "vector: " << spvOpcodeString(opcode);
+
+  if (result_is_coopmat != input_is_coopmat)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Cooperative matrix can only be cast to another cooperative "
+           << "matrix: " << spvOpcodeString(opcode);
+
+  if (result_is_coopvec && input_is_coopvec &&
+      !_.HasCapability(spv::Capability::LongVectorEXT)) {
+    spv_result_t ret =
+        _.CooperativeVectorDimensionsMatch(inst, result_type, input_type);
+    if (ret != SPV_SUCCESS) return ret;
+  }
+
+  if (result_is_coopmat) {
+    spv_result_t ret =
+        _.CooperativeMatrixShapesMatch(inst, result_type, input_type, false);
+    if (ret != SPV_SUCCESS) return ret;
+  }
+
+  if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 5) ||
+      _.HasExtension(kSPV_KHR_physical_storage_buffer)) {
+    const bool result_is_int_vector = _.IsIntVectorType(result_type);
+    const bool result_has_int32 =
+        _.ContainsSizedIntOrFloatType(result_type, spv::Op::OpTypeInt, 32);
+    const bool input_is_int_vector = _.IsIntVectorType(input_type);
+    const bool input_has_int32 =
+        _.ContainsSizedIntOrFloatType(input_type, spv::Op::OpTypeInt, 32);
+    if (result_is_pointer && !input_is_pointer && !input_is_int_scalar &&
+        !(input_is_int_vector && input_has_int32))
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "In SPIR-V 1.5 or later (or with "
+                "SPV_KHR_physical_storage_buffer), expected input to be a "
+                "pointer, "
+                "int scalar or 32-bit int "
+                "vector if Result Type is pointer: "
+             << spvOpcodeString(opcode);
+
+    if (input_is_pointer && !result_is_pointer && !result_is_int_scalar &&
+        !(result_is_int_vector && result_has_int32))
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "In SPIR-V 1.5 or later (or with "
+                "SPV_KHR_physical_storage_buffer), pointer can only be "
+                "converted to "
+                "another pointer, int "
+                "scalar or 32-bit int vector: "
+             << spvOpcodeString(opcode);
+  } else {
+    if (result_is_pointer && !input_is_pointer && !input_is_int_scalar)
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "In SPIR-V 1.4 or earlier (and without "
+                "SPV_KHR_physical_storage_buffer), expected input to be a "
+                "pointer "
+                "or int scalar if Result "
+                "Type is pointer: "
+             << spvOpcodeString(opcode);
+
+    if (input_is_pointer && !result_is_pointer && !result_is_int_scalar)
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "In SPIR-V 1.4 or earlier (and without "
+                "SPV_KHR_physical_storage_buffer), pointer can only be "
+                "converted "
+                "to another pointer or int "
+                "scalar: "
+             << spvOpcodeString(opcode);
+  }
+
+  if (!result_is_pointer && !input_is_pointer) {
+    const uint32_t result_size =
+        _.GetBitWidth(result_type) * _.GetDimension(result_type);
+    const uint32_t input_size =
+        _.GetBitWidth(input_type) * _.GetDimension(input_type);
+    if (result_size != input_size)
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected input to have the same total bit width as "
+             << "Result Type: " << spvOpcodeString(opcode);
+  }
+
+  if (auto error = ValidateShaderBitWidth(_, inst)) return error;
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateConvertUToAccelerationStructure(ValidationState_t& _,
+                                                     const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsAccelerationStructureType(result_type)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Result Type to be a Acceleration Structure: "
+           << spvOpcodeString(opcode);
+  }
+
+  const uint32_t input_type = _.GetOperandTypeId(inst, 2);
+  if (!input_type || !_.IsUnsigned64BitHandle(input_type)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected 64-bit uint scalar or 2-component 32-bit uint "
+              "vector as input: "
+           << spvOpcodeString(opcode);
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateCooperativeMatrix(ValidationState_t& _,
+                                       const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsCooperativeMatrixType(result_type)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected cooperative matrix Result Type: "
+           << spvOpcodeString(opcode);
+  }
+  const uint32_t input_type = _.GetOperandTypeId(inst, 2);
+  if (!_.IsCooperativeMatrixType(input_type)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected cooperative matrix type for Matrix input: "
+           << spvOpcodeString(opcode);
+  }
+
+  bool swap_row_col = (opcode == spv::Op::OpCooperativeMatrixTransposeNV);
+  if (auto error = _.CooperativeMatrixShapesMatch(inst, result_type, input_type,
+                                                  true, swap_row_col))
+    return error;
+
+  if (opcode == spv::Op::OpCooperativeMatrixConvertNV) {
+    if (_.FindDef(result_type)->GetOperandAs<uint32_t>(1) !=
+        _.FindDef(input_type)->GetOperandAs<uint32_t>(1)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Result Type and Matrix component types mismatch: "
+             << spvOpcodeString(opcode);
+    }
+  }
+
+  if (opcode == spv::Op::OpCooperativeMatrixTransposeNV) {
+    if (!_.IsCooperativeMatrixBType(result_type)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Result Type must have UseB: " << spvOpcodeString(opcode);
+    }
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateBitCastArray(ValidationState_t& _,
+                                  const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  const auto result_type_inst = _.FindDef(result_type);
+  const auto source = _.FindDef(inst->GetOperandAs<uint32_t>(2u));
+  const auto source_type_inst = _.FindDef(source->type_id());
+
+  // Are the input and the result arrays?
+  if (result_type_inst->opcode() != spv::Op::OpTypeArray ||
+      source_type_inst->opcode() != spv::Op::OpTypeArray) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Opcode " << spvOpcodeString(opcode)
+           << " requires OpTypeArray operands for the input and the "
+              "result.";
+  }
+
+  const auto source_elt_type = _.GetComponentType(source_type_inst->id());
+  const auto result_elt_type = _.GetComponentType(result_type_inst->id());
+
+  if (!_.IsIntNOrFP32OrFP16<32>(source_elt_type)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Opcode " << spvOpcodeString(opcode)
+           << " requires the source element type be one of 32-bit "
+              "OpTypeInt "
+              "(signed/unsigned), 32-bit OpTypeFloat and 16-bit "
+              "OpTypeFloat";
+  }
+
+  if (!_.IsIntNOrFP32OrFP16<32>(result_elt_type)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Opcode " << spvOpcodeString(opcode)
+           << " requires the result element type be one of 32-bit "
+              "OpTypeInt "
+              "(signed/unsigned), 32-bit OpTypeFloat and 16-bit "
+              "OpTypeFloat";
+  }
+
+  unsigned src_arr_len_id = source_type_inst->GetOperandAs<unsigned>(2u);
+  unsigned res_arr_len_id = result_type_inst->GetOperandAs<unsigned>(2u);
+
+  // Are the input and result element types compatible?
+  unsigned src_arr_len = UINT_MAX, res_arr_len = UINT_MAX;
+  bool src_arr_len_status =
+      _.GetConstantValueAs<unsigned>(src_arr_len_id, src_arr_len);
+  bool res_arr_len_status =
+      _.GetConstantValueAs<unsigned>(res_arr_len_id, res_arr_len);
+
+  bool is_src_arr_len_spec_const =
+      spvOpcodeIsSpecConstant(_.FindDef(src_arr_len_id)->opcode());
+  bool is_res_arr_len_spec_const =
+      spvOpcodeIsSpecConstant(_.FindDef(res_arr_len_id)->opcode());
+
+  unsigned source_bitlen = _.GetBitWidth(source_elt_type) * src_arr_len;
+  unsigned result_bitlen = _.GetBitWidth(result_elt_type) * res_arr_len;
+  if (!is_src_arr_len_spec_const && !is_res_arr_len_spec_const &&
+      (!src_arr_len_status || !res_arr_len_status ||
+       source_bitlen != result_bitlen)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Opcode " << spvOpcodeString(opcode)
+           << " requires source and result types be compatible for "
+              "conversion.";
+  }
+  return SPV_SUCCESS;
+}
+
+// Validates correctness of conversion instructions.
+spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) {
+  switch (inst->opcode()) {
+    case spv::Op::OpConvertFToU:
+      return ValidateConvertFToU(_, inst);
+    case spv::Op::OpConvertFToS:
+      return ValidateConvertFToS(_, inst);
+    case spv::Op::OpConvertSToF:
+    case spv::Op::OpConvertUToF:
+      return ValidateConvertIntToF(_, inst);
+    case spv::Op::OpUConvert:
+      return ValidateUConvert(_, inst);
+    case spv::Op::OpSConvert:
+      return ValidateSConvert(_, inst);
+    case spv::Op::OpFConvert:
+      return ValidateFConvert(_, inst);
+    case spv::Op::OpQuantizeToF16:
+      return ValidateQuantizeToF16(_, inst);
+    case spv::Op::OpConvertPtrToU:
+      return ValidateConvertPtrToU(_, inst);
+    case spv::Op::OpSatConvertSToU:
+    case spv::Op::OpSatConvertUToS:
+      return ValidateSatConvertInt(_, inst);
+    case spv::Op::OpConvertUToPtr:
+      return ValidateConvertUToPtr(_, inst);
+    case spv::Op::OpPtrCastToGeneric:
+      return ValidatePtrCastToGeneric(_, inst);
+    case spv::Op::OpGenericCastToPtr:
+      return ValidateGenericCastToPtr(_, inst);
+    case spv::Op::OpGenericCastToPtrExplicit:
+      return ValidateGenericCastToPtrExplicit(_, inst);
+    case spv::Op::OpBitcast:
+      return ValidateBitcast(_, inst);
+    case spv::Op::OpConvertUToAccelerationStructureKHR:
+      return ValidateConvertUToAccelerationStructure(_, inst);
+    case spv::Op::OpCooperativeMatrixConvertNV:
+    case spv::Op::OpCooperativeMatrixTransposeNV:
+      return ValidateCooperativeMatrix(_, inst);
+    case spv::Op::OpBitCastArrayQCOM:
+      return ValidateBitCastArray(_, inst);
+
+    case spv::Op::OpSpecConstantOp: {
+      switch (inst->GetOperandAs<spv::Op>(2u)) {
+        case spv::Op::OpUConvert:
+          return ValidateUConvert(_, inst, 3);
+        case spv::Op::OpSConvert:
+          return ValidateSConvert(_, inst, 3);
+        case spv::Op::OpFConvert:
+          return ValidateFConvert(_, inst, 3);
+        case spv::Op::OpConvertSToF:
+        case spv::Op::OpConvertUToF:
+          return ValidateConvertIntToF(_, inst, 3);
+        case spv::Op::OpConvertFToS:
+          return ValidateConvertFToS(_, inst, 3);
+        case spv::Op::OpConvertFToU:
+          return ValidateConvertFToU(_, inst, 3);
+        case spv::Op::OpQuantizeToF16:
+          return ValidateQuantizeToF16(_, inst, 3);
+        case spv::Op::OpConvertPtrToU:
+          return ValidateConvertPtrToU(_, inst, 3);
+        case spv::Op::OpConvertUToPtr:
+          return ValidateConvertUToPtr(_, inst, 3);
+        case spv::Op::OpGenericCastToPtr:
+          return ValidateGenericCastToPtr(_, inst, 3);
+        case spv::Op::OpPtrCastToGeneric:
+          return ValidatePtrCastToGeneric(_, inst, 3);
+        case spv::Op::OpBitcast:
+          return ValidateBitcast(_, inst, 3);
+        default:
+          break;
+      }
+      break;
+    }
+    default:
+      break;
+  }
+
   return SPV_SUCCESS;
 }
 
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp
index e680bd6..406a5e6 100644
--- a/source/val/validate_decorations.cpp
+++ b/source/val/validate_decorations.cpp
@@ -122,7 +122,8 @@
     hasOffset.resize(struct_members.size(), false);
 
     for (auto& decoration : vstate.id_decorations(struct_id)) {
-      if (spv::Decoration::Offset == decoration.dec_type() &&
+      if ((spv::Decoration::Offset == decoration.dec_type() ||
+           spv::Decoration::OffsetIdEXT == decoration.dec_type()) &&
           Decoration::kInvalidMember != decoration.struct_member_index()) {
         // Offset 0xffffffff is not valid so ignore it for simplicity's sake.
         if (decoration.params()[0] == 0xffffffff) return true;
@@ -170,6 +171,10 @@
     case spv::Op::OpTypeImage:
       if (vstate.HasCapability(spv::Capability::BindlessTextureNV))
         return vstate.samplerimage_variable_address_mode() / 8;
+      // SPV_EXT_descriptor_heap provides a way to access opaque images, we
+      // assume alignment is validated at runtime as it is determined by the
+      // client API
+      if (vstate.HasCapability(spv::Capability::DescriptorHeapEXT)) return 1;
       assert(0);
       return 0;
     case spv::Op::OpTypeInt:
@@ -182,7 +187,19 @@
       const auto componentAlignment = getBaseAlignment(
           componentId, roundUp, inherited, constraints, vstate);
       baseAlignment =
-          componentAlignment * (numComponents == 3 ? 4 : numComponents);
+          componentAlignment *
+          ((numComponents == 3 || numComponents > 4) ? 4 : numComponents);
+      break;
+    }
+    case spv::Op::OpTypeVectorIdEXT: {
+      const auto componentId = words[2];
+      const auto numComponents = vstate.GetDimension(inst->id());
+      assert(numComponents != 0);
+      const auto componentAlignment = getBaseAlignment(
+          componentId, roundUp, inherited, constraints, vstate);
+      baseAlignment =
+          componentAlignment *
+          ((numComponents == 3 || numComponents > 4) ? 4 : numComponents);
       break;
     }
     case spv::Op::OpTypeMatrix: {
@@ -245,12 +262,17 @@
     case spv::Op::OpTypeImage:
       if (vstate.HasCapability(spv::Capability::BindlessTextureNV))
         return vstate.samplerimage_variable_address_mode() / 8;
+      // SPV_EXT_descriptor_heap provides a way to access opaque images, we
+      // assume alignment is validated at runtime as it is determined by the
+      // client API
+      if (vstate.HasCapability(spv::Capability::DescriptorHeapEXT)) return 1;
       assert(0);
       return 0;
     case spv::Op::OpTypeInt:
     case spv::Op::OpTypeFloat:
       return words[2] / 8;
     case spv::Op::OpTypeVector:
+    case spv::Op::OpTypeVectorIdEXT:
     case spv::Op::OpTypeMatrix:
     case spv::Op::OpTypeArray:
     case spv::Op::OpTypeRuntimeArray: {
@@ -293,6 +315,10 @@
     case spv::Op::OpTypeImage:
       if (vstate.HasCapability(spv::Capability::BindlessTextureNV))
         return vstate.samplerimage_variable_address_mode() / 8;
+      // SPV_EXT_descriptor_heap provides a way to access opaque images, we
+      // assume alignment is validated at runtime as it is determined by the
+      // client API
+      if (vstate.HasCapability(spv::Capability::DescriptorHeapEXT)) return 1;
       assert(0);
       return 0;
     case spv::Op::OpTypeInt:
@@ -306,6 +332,15 @@
       const auto size = componentSize * numComponents;
       return size;
     }
+    case spv::Op::OpTypeVectorIdEXT: {
+      const auto componentId = words[2];
+      const auto numComponents = vstate.GetDimension(inst->id());
+      assert(numComponents != 0);
+      const auto componentSize =
+          getSize(componentId, inherited, constraints, vstate);
+      const auto size = componentSize * numComponents;
+      return size;
+    }
     case spv::Op::OpTypeArray: {
       const auto sizeInst = vstate.FindDef(words[3]);
       if (spvOpcodeIsSpecConstant(sizeInst->opcode())) return 0;
@@ -402,10 +437,9 @@
 // Block or BufferBlocks in Vulkan.  Otherwise emits a diagnostic and returns
 // something other than SPV_SUCCESS.  Matrices inherit the specified column
 // or row major-ness.
-spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
+spv_result_t checkLayout(uint32_t struct_id, spv::StorageClass storage_class,
                          const char* decoration_str, bool blockRules,
-                         bool scalar_block_layout,
-                         uint32_t incoming_offset,
+                         bool scalar_block_layout, uint32_t incoming_offset,
                          MemberConstraints& constraints,
                          ValidationState_t& vstate) {
   if (vstate.options()->skip_block_layout) return SPV_SUCCESS;
@@ -419,22 +453,48 @@
   // is more permissive than relaxed layout.
   const bool relaxed_block_layout = vstate.IsRelaxedBlockLayout();
 
-  auto fail = [&vstate, struct_id, storage_class_str, decoration_str,
-               blockRules, relaxed_block_layout,
+  auto fail = [&vstate, struct_id, storage_class, decoration_str, blockRules,
+               relaxed_block_layout,
                scalar_block_layout](uint32_t member_idx) -> DiagnosticStream {
-    DiagnosticStream ds =
-        std::move(vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(struct_id))
-                  << "Structure id " << struct_id << " decorated as "
-                  << decoration_str << " for variable in " << storage_class_str
-                  << " storage class must follow "
-                  << (scalar_block_layout
-                          ? "scalar "
-                          : (relaxed_block_layout ? "relaxed " : "standard "))
-                  << (blockRules ? "uniform buffer" : "storage buffer")
-                  << " layout rules: member " << member_idx << " ");
+    DiagnosticStream ds = std::move(
+        vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(struct_id))
+        << "Structure id " << struct_id << " decorated as " << decoration_str
+        << " for variable in " << StorageClassToString(storage_class)
+        << " storage class must follow "
+        << (scalar_block_layout
+                ? "scalar "
+                : (relaxed_block_layout ? "relaxed " : "standard "))
+        << (blockRules ? "uniform buffer" : "storage buffer")
+        << " layout rules: member " << member_idx << " ");
     return ds;
   };
 
+  // People often use spirv-val from Vulkan Validation Layers, it ends up
+  // mapping the various block layout rules from the enabled feature. This
+  // offers a hint to help the user understand possbily why things are not
+  // working when the shader itself "seems" valid, but just was a lack of adding
+  // a supported feature
+  auto extra = [&vstate, scalar_block_layout, storage_class,
+                relaxed_block_layout, blockRules]() {
+    if (!scalar_block_layout) {
+      if (storage_class == spv::StorageClass::Workgroup) {
+        return vstate.MissingFeature(
+            "workgroupMemoryExplicitLayoutScalarBlockLayout feature",
+            "--workgroup-scalar-block-layout", true);
+      } else if (!relaxed_block_layout) {
+        return vstate.MissingFeature("VK_KHR_relaxed_block_layout extension",
+                                     "--relax-block-layout", true);
+      } else if (blockRules) {
+        return vstate.MissingFeature("uniformBufferStandardLayout feature",
+                                     "--uniform-buffer-standard-layout", true);
+      } else {
+        return vstate.MissingFeature("scalarBlockLayout feature",
+                                     "--scalar-block-layout", true);
+      }
+    }
+    return std::string("");
+  };
+
   // If we are checking the layout of untyped pointers or physical storage
   // buffer pointers, we may not actually have a struct here. Instead, pretend
   // we have a struct with a single member at offset 0.
@@ -508,50 +568,63 @@
     const auto size = getSize(id, constraint, constraints, vstate);
     // Check offset.
     if (offset == 0xffffffff)
-      return fail(memberIdx) << "is missing an Offset decoration";
+      return fail(memberIdx) << "is missing an Offset decoration" << extra();
+
+    if (opcode == spv::Op::OpTypeRuntimeArray &&
+        ordered_member_idx != member_offsets.size() - 1) {
+      return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(struct_id))
+             << vstate.VkErrorID(4680) << "Structure id " << struct_id
+             << " has a runtime array at offset " << offset
+             << ", but other members at larger offsets";
+    }
+
     if (!scalar_block_layout && relaxed_block_layout &&
-        opcode == spv::Op::OpTypeVector) {
+        (opcode == spv::Op::OpTypeVector ||
+         opcode == spv::Op::OpTypeVectorIdEXT)) {
       // In relaxed block layout, the vector offset must be aligned to the
       // vector's scalar element type.
       const auto componentId = inst->words()[2];
       const auto scalar_alignment = getScalarAlignment(componentId, vstate);
       if (!IsAlignedTo(offset, scalar_alignment)) {
-        return fail(memberIdx)
-               << "at offset " << offset
-               << " is not aligned to scalar element size " << scalar_alignment;
+        return fail(memberIdx) << "at offset " << offset
+                               << " is not aligned to scalar element size "
+                               << scalar_alignment << extra();
       }
     } else {
       // Without relaxed block layout, the offset must be divisible by the
       // alignment requirement.
       if (!IsAlignedTo(offset, alignment)) {
-        return fail(memberIdx)
-               << "at offset " << offset << " is not aligned to " << alignment;
+        return fail(memberIdx) << "at offset " << offset
+                               << " is not aligned to " << alignment << extra();
       }
     }
     if (offset < nextValidOffset)
       return fail(memberIdx) << "at offset " << offset
                              << " overlaps previous member ending at offset "
-                             << nextValidOffset - 1;
+                             << nextValidOffset - 1 << extra();
     if (!scalar_block_layout && relaxed_block_layout) {
       // Check improper straddle of vectors.
-      if (spv::Op::OpTypeVector == opcode &&
+      if ((spv::Op::OpTypeVector == opcode ||
+           spv::Op::OpTypeVectorIdEXT == opcode) &&
           hasImproperStraddle(id, offset, constraint, constraints, vstate))
         return fail(memberIdx)
-               << "is an improperly straddling vector at offset " << offset;
+               << "is an improperly straddling vector at offset " << offset
+               << extra();
     }
     // Check struct members recursively.
     spv_result_t recursive_status = SPV_SUCCESS;
     if (spv::Op::OpTypeStruct == opcode &&
         SPV_SUCCESS != (recursive_status = checkLayout(
-                            id, storage_class_str, decoration_str, blockRules,
+                            id, storage_class, decoration_str, blockRules,
                             scalar_block_layout, offset, constraints, vstate)))
       return recursive_status;
     // Check matrix stride.
     if (spv::Op::OpTypeMatrix == opcode) {
       const auto stride = constraint.matrix_stride;
       if (!IsAlignedTo(stride, alignment)) {
-        return fail(memberIdx) << "is a matrix with stride " << stride
-                               << " not satisfying alignment to " << alignment;
+        return fail(memberIdx)
+               << "is a matrix with stride " << stride
+               << " not satisfying alignment to " << alignment << extra();
       }
     }
 
@@ -568,12 +641,13 @@
         if (spv::Decoration::ArrayStride == decoration.dec_type()) {
           array_stride = decoration.params()[0];
           if (array_stride == 0) {
-            return fail(memberIdx) << "contains an array with stride 0";
+            return fail(memberIdx)
+                   << "contains an array with stride 0" << extra();
           }
           if (!IsAlignedTo(array_stride, array_alignment))
             return fail(memberIdx)
                    << "contains an array with stride " << decoration.params()[0]
-                   << " not satisfying alignment to " << alignment;
+                   << " not satisfying alignment to " << alignment << extra();
         }
       }
 
@@ -600,7 +674,7 @@
 
           if (SPV_SUCCESS !=
               (recursive_status = checkLayout(
-                   typeId, storage_class_str, decoration_str, blockRules,
+                   typeId, storage_class, decoration_str, blockRules,
                    scalar_block_layout, next_offset, constraints, vstate)))
             return recursive_status;
 
@@ -612,7 +686,7 @@
         if (!IsAlignedTo(stride, alignment)) {
           return fail(memberIdx)
                  << "is a matrix with stride " << stride
-                 << " not satisfying alignment to " << alignment;
+                 << " not satisfying alignment to " << alignment << extra();
         }
       }
 
@@ -628,7 +702,7 @@
       if (element_size > array_stride) {
         return fail(memberIdx)
                << "contains an array with stride " << array_stride
-               << ", but with an element size of " << element_size;
+               << ", but with an element size of " << element_size << extra();
       }
     }
     nextValidOffset = offset + size;
@@ -767,6 +841,7 @@
     int num_workgroup_variables = 0;
     int num_workgroup_variables_with_block = 0;
     int num_workgroup_variables_with_aliased = 0;
+    bool has_task_payload = false;
     for (const auto& desc : descs) {
       std::unordered_set<Instruction*> seen_vars;
       std::unordered_set<spv::BuiltIn> input_var_builtin;
@@ -787,24 +862,40 @@
         const spv::StorageClass storage_class =
             var_instr->GetOperandAs<spv::StorageClass>(sc_index);
         if (vstate.version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
+          // SPV_EXT_mesh_shader, at most one task payload is permitted
+          // per entry point
+          if (storage_class == spv::StorageClass::TaskPayloadWorkgroupEXT) {
+            if (has_task_payload) {
+              return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
+                     << "There can be at most one "
+                        "OpVariable with storage "
+                        "class TaskPayloadWorkgroupEXT associated with "
+                        "an OpEntryPoint";
+            }
+            has_task_payload = true;
+          }
+
           // Starting in 1.4, OpEntryPoint must list all global variables
           // it statically uses and those interfaces must be unique.
           if (storage_class == spv::StorageClass::Function) {
             return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
-                   << "OpEntryPoint interfaces should only list global "
+                   << "In SPIR-V 1.4 or later, OpEntryPoint interfaces should "
+                      "only list global "
                       "variables";
           }
 
           if (!seen_vars.insert(var_instr).second) {
             return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
-                   << "Non-unique OpEntryPoint interface "
+                   << "In SPIR-V 1.4 or later, non-unique OpEntryPoint "
+                      "interface "
                    << vstate.getIdName(interface) << " is disallowed";
           }
         } else {
           if (storage_class != spv::StorageClass::Input &&
               storage_class != spv::StorageClass::Output) {
             return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
-                   << "OpEntryPoint interfaces must be OpVariables with "
+                   << "In SPIR-V 1.3 or earlier, OpEntryPoint interfaces must "
+                      "be OpVariables with "
                       "Storage Class of Input(1) or Output(3). Found Storage "
                       "Class "
                    << uint32_t(storage_class) << " for Entry Point id "
@@ -812,6 +903,11 @@
           }
         }
 
+        // Descriptor heap's base variables have no data type in declaration.
+        if (untyped_pointers && var_instr->words().size() < 5 &&
+            vstate.IsDescriptorHeapBaseVariable(var_instr))
+          continue;
+
         // It is guaranteed (by validator ID checks) that ptr_instr is
         // OpTypePointer. Word 3 of this instruction is the type being pointed
         // to. For untyped variables, the pointee type comes from the data type
@@ -944,8 +1040,7 @@
               hasDecoration(var_instr->id(), spv::Decoration::Flat, vstate);
           if (has_frag && storage_class == spv::StorageClass::Input &&
               !has_flat &&
-              ((vstate.IsFloatScalarType(type_id) &&
-                vstate.GetBitWidth(type_id) == 64) ||
+              (vstate.IsFloatScalarType(type_id, 64) ||
                vstate.IsIntScalarOrVectorType(type_id))) {
             return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
                      << vstate.VkErrorID(4744)
@@ -1000,7 +1095,7 @@
         }
         if (num_workgroup_variables_with_block > 1 &&
             num_workgroup_variables_with_block !=
-            num_workgroup_variables_with_aliased) {
+                num_workgroup_variables_with_aliased) {
           return vstate.diag(SPV_ERROR_INVALID_BINARY,
                              vstate.FindDef(entry_point))
                  << "When declaring WorkgroupMemoryExplicitLayoutKHR, "
@@ -1107,6 +1202,59 @@
   }
 }
 
+spv_result_t CheckDecorationsOfVariables(ValidationState_t& vstate) {
+  if (!spvIsVulkanEnv(vstate.context()->target_env)) {
+    return SPV_SUCCESS;
+  }
+  for (const auto& inst : vstate.ordered_instructions()) {
+    if ((spv::Op::OpVariable == inst.opcode()) ||
+        (spv::Op::OpUntypedVariableKHR == inst.opcode())) {
+      const auto var_id = inst.id();
+      const auto storageClass = inst.GetOperandAs<spv::StorageClass>(2);
+      const bool uniform = storageClass == spv::StorageClass::Uniform;
+      const bool uniform_constant =
+          storageClass == spv::StorageClass::UniformConstant;
+      const bool storage_buffer =
+          storageClass == spv::StorageClass::StorageBuffer;
+
+      const char* sc_str = uniform            ? "Uniform"
+                           : uniform_constant ? "UniformConstant"
+                                              : "StorageBuffer";
+      // Check variables in the UniformConstant, StorageBuffer, and Uniform
+      // storage classes are decorated with DescriptorSet and Binding
+      // (VUID-06677).
+      if (uniform_constant || storage_buffer || uniform) {
+        if (vstate.IsDescriptorHeapBaseVariable(&inst)) {
+          continue;
+        }
+        // Skip validation if the variable is not used and we're looking
+        // at a module coming from HLSL that has not been legalized yet.
+        if (vstate.options()->before_hlsl_legalization &&
+            vstate.EntryPointReferences(var_id).empty()) {
+          continue;
+        }
+        if (!hasDecoration(var_id, spv::Decoration::DescriptorSet, vstate)) {
+          return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
+                 << vstate.VkErrorID(6677) << sc_str << " id '" << var_id
+                 << "' is missing DescriptorSet decoration.\n"
+                 << "From Vulkan spec:\n"
+                 << "These variables must have DescriptorSet and Binding "
+                    "decorations specified";
+        }
+        if (!hasDecoration(var_id, spv::Decoration::Binding, vstate)) {
+          return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
+                 << vstate.VkErrorID(6677) << sc_str << " id '" << var_id
+                 << "' is missing Binding decoration.\n"
+                 << "From Vulkan spec:\n"
+                 << "These variables must have DescriptorSet and Binding "
+                    "decorations specified";
+        }
+      }
+    }
+  }
+  return SPV_SUCCESS;
+}
+
 spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
   // Set of entry points that are known to use a push constant.
   std::unordered_set<uint32_t> uses_push_constant;
@@ -1126,8 +1274,6 @@
       const auto storageClassVal = words[3];
       const auto storageClass = spv::StorageClass(storageClassVal);
       const bool uniform = storageClass == spv::StorageClass::Uniform;
-      const bool uniform_constant =
-          storageClass == spv::StorageClass::UniformConstant;
       const bool push_constant =
           storageClass == spv::StorageClass::PushConstant;
       const bool storage_buffer =
@@ -1136,7 +1282,8 @@
       if (spvIsVulkanEnv(vstate.context()->target_env)) {
         // Vulkan: There must be no more than one PushConstant block per entry
         // point.
-        if (push_constant) {
+        if (push_constant &&
+            !(vstate.HasCapability(spv::Capability::PushConstantBanksNV))) {
           auto entry_points = vstate.EntryPointReferences(var_id);
           for (auto ep_id : entry_points) {
             const bool already_used = !uses_push_constant.insert(ep_id).second;
@@ -1150,29 +1297,6 @@
             }
           }
         }
-        // Vulkan: Check DescriptorSet and Binding decoration for
-        // UniformConstant which cannot be a struct.
-        if (uniform_constant) {
-          auto entry_points = vstate.EntryPointReferences(var_id);
-          if (!entry_points.empty() &&
-              !hasDecoration(var_id, spv::Decoration::DescriptorSet, vstate)) {
-            return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
-                   << vstate.VkErrorID(6677) << "UniformConstant id '" << var_id
-                   << "' is missing DescriptorSet decoration.\n"
-                   << "From Vulkan spec:\n"
-                   << "These variables must have DescriptorSet and Binding "
-                      "decorations specified";
-          }
-          if (!entry_points.empty() &&
-              !hasDecoration(var_id, spv::Decoration::Binding, vstate)) {
-            return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
-                   << vstate.VkErrorID(6677) << "UniformConstant id '" << var_id
-                   << "' is missing Binding decoration.\n"
-                   << "From Vulkan spec:\n"
-                   << "These variables must have DescriptorSet and Binding "
-                      "decorations specified";
-          }
-        }
       }
 
       if (spvIsOpenGLEnv(vstate.context()->target_env)) {
@@ -1185,8 +1309,8 @@
           if (!entry_points.empty() &&
               !hasDecoration(var_id, spv::Decoration::Binding, vstate)) {
             return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
-                   << (uniform ? "Uniform" : "Storage Buffer") << " id '"
-                   << var_id << "' is missing Binding decoration.\n"
+                   << StorageClassToString(storageClass) << " id '" << var_id
+                   << "' is missing Binding decoration.\n"
                    << "From ARB_gl_spirv extension:\n"
                    << "Uniform and shader storage block variables must "
                    << "also be decorated with a *Binding*.";
@@ -1200,6 +1324,24 @@
           storageClass == spv::StorageClass::Workgroup &&
           vstate.HasCapability(
               spv::Capability::WorkgroupMemoryExplicitLayoutKHR);
+
+      if (spvIsVulkanEnv(vstate.context()->target_env) &&
+          inst.opcode() == spv::Op::OpUntypedVariableKHR &&
+          storageClass != spv::StorageClass::UniformConstant &&
+          vstate.IsDescriptorHeapBaseVariable(&inst)) {
+        if (vstate.IsBuiltin(inst.id(), spv::BuiltIn::ResourceHeapEXT)) {
+          return vstate.diag(SPV_ERROR_INVALID_DATA, &inst)
+                 << vstate.VkErrorID(11241)
+                 << "The variable decorated with ResourceHeapEXT must be "
+                 << "declared using the UniformConstant storage class.";
+        }
+        if (vstate.IsBuiltin(inst.id(), spv::BuiltIn::SamplerHeapEXT)) {
+          return vstate.diag(SPV_ERROR_INVALID_DATA, &inst)
+                 << vstate.VkErrorID(11239)
+                 << "The variable decorated with SamplerHeapEXT must be "
+                 << "declared using the UniformConstant storage class.";
+        }
+      }
       if (uniform || push_constant || storage_buffer || phys_storage_buffer ||
           workgroup) {
         const auto ptrInst = vstate.FindDef(words[1]);
@@ -1221,12 +1363,6 @@
           ComputeMemberConstraintsForStruct(&constraints, id,
                                             LayoutConstraints(), vstate);
         }
-        // Prepare for messages
-        const char* sc_str =
-            uniform ? "Uniform"
-                    : (push_constant ? "PushConstant"
-                                     : (workgroup ? "Workgroup"
-                                                  : "StorageBuffer"));
 
         if (spvIsVulkanEnv(vstate.context()->target_env)) {
           const bool block = hasDecoration(id, spv::Decoration::Block, vstate);
@@ -1264,30 +1400,6 @@
                    << "Such variables must be identified with a Block or "
                       "BufferBlock decoration";
           }
-          // Vulkan: Check DescriptorSet and Binding decoration for
-          // Uniform and StorageBuffer variables.
-          if (uniform || storage_buffer) {
-            auto entry_points = vstate.EntryPointReferences(var_id);
-            if (!entry_points.empty() &&
-                !hasDecoration(var_id, spv::Decoration::DescriptorSet,
-                               vstate)) {
-              return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
-                     << vstate.VkErrorID(6677) << sc_str << " id '" << var_id
-                     << "' is missing DescriptorSet decoration.\n"
-                     << "From Vulkan spec:\n"
-                     << "These variables must have DescriptorSet and Binding "
-                        "decorations specified";
-            }
-            if (!entry_points.empty() &&
-                !hasDecoration(var_id, spv::Decoration::Binding, vstate)) {
-              return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
-                     << vstate.VkErrorID(6677) << sc_str << " id '" << var_id
-                     << "' is missing Binding decoration.\n"
-                     << "From Vulkan spec:\n"
-                     << "These variables must have DescriptorSet and Binding "
-                        "decorations specified";
-            }
-          }
         }
 
         if (id != 0) {
@@ -1327,12 +1439,14 @@
               if (!checkForRequiredDecoration(
                       id,
                       [](spv::Decoration d) {
-                        return d == spv::Decoration::ArrayStride;
+                        return d == spv::Decoration::ArrayStride ||
+                               d == spv::Decoration::ArrayStrideIdEXT;
                       },
                       spv::Op::OpTypeArray, vstate)) {
                 return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
                        << "Structure id " << id << " decorated as " << deco_str
-                       << " must be explicitly laid out with ArrayStride "
+                       << " must be explicitly laid out with ArrayStride or "
+                          "ArrayStrideIdEXT "
                           "decorations.";
               }
 
@@ -1364,14 +1478,14 @@
               if (spvIsVulkanEnv(vstate.context()->target_env)) {
                 if (blockRules &&
                     (SPV_SUCCESS !=
-                     (recursive_status = checkLayout(id, sc_str, deco_str, true,
-                                                     scalar_block_layout, 0,
-                                                     constraints, vstate)))) {
+                     (recursive_status = checkLayout(
+                          id, storageClass, deco_str, true, scalar_block_layout,
+                          0, constraints, vstate)))) {
                   return recursive_status;
                 } else if (bufferRules &&
                            (SPV_SUCCESS != (recursive_status = checkLayout(
-                                                id, sc_str, deco_str, false,
-                                                scalar_block_layout, 0,
+                                                id, storageClass, deco_str,
+                                                false, scalar_block_layout, 0,
                                                 constraints, vstate)))) {
                   return recursive_status;
                 }
@@ -1391,9 +1505,9 @@
         ComputeMemberConstraintsForStruct(&constraints, pointee_type_id,
                                           LayoutConstraints(), vstate);
       }
-      if (auto res = checkLayout(pointee_type_id, "PhysicalStorageBuffer",
-                                 "Block", !buffer, scalar_block_layout, 0,
-                                 constraints, vstate)) {
+      if (auto res = checkLayout(
+              pointee_type_id, spv::StorageClass::PhysicalStorageBuffer,
+              "Block", !buffer, scalar_block_layout, 0, constraints, vstate)) {
         return res;
       }
     } else if (vstate.HasCapability(spv::Capability::UntypedPointersKHR) &&
@@ -1442,19 +1556,29 @@
       const auto sc =
           vstate.FindDef(ptr_ty_id)->GetOperandAs<spv::StorageClass>(1);
 
-      const char* sc_str =
-          sc == spv::StorageClass::Uniform
-              ? "Uniform"
-              : (sc == spv::StorageClass::PushConstant
-                     ? "PushConstant"
-                     : (sc == spv::StorageClass::Workgroup ? "Workgroup"
-                                                           : "StorageBuffer"));
-
-      const auto data_type = vstate.FindDef(data_type_id);
+      auto data_type = vstate.FindDef(data_type_id);
       scalar_block_layout =
           sc == spv::StorageClass::Workgroup
               ? vstate.options()->workgroup_scalar_block_layout
               : vstate.options()->scalar_block_layout;
+
+      // If the data type is an array that contains a Block- or
+      // BufferBlock-decorated struct, then use the struct for layout checks
+      // instead of the array. In this case, the array represents a descriptor
+      // array which should not have an explicit layout.
+      if (data_type->opcode() == spv::Op::OpTypeArray ||
+          data_type->opcode() == spv::Op::OpTypeRuntimeArray) {
+        const auto ele_type =
+            vstate.FindDef(data_type->GetOperandAs<uint32_t>(1u));
+        if (ele_type->opcode() == spv::Op::OpTypeStruct &&
+            (vstate.HasDecoration(ele_type->id(), spv::Decoration::Block) ||
+             vstate.HasDecoration(ele_type->id(),
+                                  spv::Decoration::BufferBlock))) {
+          data_type = ele_type;
+          data_type_id = ele_type->id();
+        }
+      }
+
       // Assume uniform storage class uses block rules unless we see a
       // BufferBlock decorated struct in the data type.
       bool bufferRules = sc == spv::StorageClass::Uniform ? false : true;
@@ -1470,10 +1594,13 @@
           bufferRules
               ? (sc == spv::StorageClass::Uniform ? "BufferBlock" : "Block")
               : "Block";
-      if (auto result =
-              checkLayout(data_type_id, sc_str, deco_str, !bufferRules,
-                          scalar_block_layout, 0, constraints, vstate)) {
-        return result;
+
+      if (!vstate.IsDescriptorHeapBaseVariable(&inst)) {
+        if (auto result =
+                checkLayout(data_type_id, sc, deco_str, !bufferRules,
+                            scalar_block_layout, 0, constraints, vstate)) {
+          return result;
+        }
       }
     }
   }
@@ -1500,14 +1627,16 @@
   // to the same id.
   static const spv::Decoration mutually_exclusive_per_id[][2] = {
       {spv::Decoration::Block, spv::Decoration::BufferBlock},
-      {spv::Decoration::Restrict, spv::Decoration::Aliased}};
+      {spv::Decoration::Restrict, spv::Decoration::Aliased},
+      {spv::Decoration::RestrictPointer, spv::Decoration::AliasedPointer}};
   static const auto num_mutually_exclusive_per_id_pairs =
       sizeof(mutually_exclusive_per_id) / (2 * sizeof(spv::Decoration));
 
   // An Array of pairs where the decorations in the pair cannot both be applied
   // to the same member.
   static const spv::Decoration mutually_exclusive_per_member[][2] = {
-      {spv::Decoration::RowMajor, spv::Decoration::ColMajor}};
+      {spv::Decoration::RowMajor, spv::Decoration::ColMajor},
+      {spv::Decoration::Offset, spv::Decoration::OffsetIdEXT}};
   static const auto num_mutually_exclusive_per_mem_pairs =
       sizeof(mutually_exclusive_per_member) / (2 * sizeof(spv::Decoration));
 
@@ -1549,7 +1678,8 @@
                  << " is not allowed.";
         }
       }
-    } else if (spv::Op::OpMemberDecorate == inst.opcode()) {
+    } else if (spv::Op::OpMemberDecorate == inst.opcode() ||
+               spv::Op::OpMemberDecorateIdEXT == inst.opcode()) {
       const auto id = words[1];
       const auto member_id = words[2];
       const auto dec_type = static_cast<spv::Decoration>(words[3]);
@@ -1691,14 +1821,33 @@
   return SPV_SUCCESS;
 }
 
-// Returns SPV_SUCCESS if validation rules are satisfied for the NonWritable
+spv_result_t CheckFPRoundingModeForKernels(ValidationState_t& vstate,
+                                           const Instruction& inst) {
+  // Validates conversion instruction to or from a floating-point type
+  const auto opcode = inst.opcode();
+  if (opcode != spv::Op::OpConvertFToU && opcode != spv::Op::OpConvertFToS &&
+      opcode != spv::Op::OpConvertSToF && opcode != spv::Op::OpConvertUToF &&
+      opcode != spv::Op::OpFConvert) {
+    return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+           << "FPRoundingMode decoration can be applied only to a conversion "
+              "instruction to or from a floating-point type.";
+  }
+  return SPV_SUCCESS;
+}
+
+// Returns SPV_SUCCESS if validation rules are satisfied for the NonReadable or
+// NonWritable
 // decoration.  Otherwise emits a diagnostic and returns something other than
 // SPV_SUCCESS.  The |inst| parameter is the object being decorated.  This must
 // be called after TypePass and AnnotateCheckDecorationsOfBuffers are called.
-spv_result_t CheckNonWritableDecoration(ValidationState_t& vstate,
-                                        const Instruction& inst,
-                                        const Decoration& decoration) {
+spv_result_t CheckNonReadableWritableDecorations(ValidationState_t& vstate,
+                                                 const Instruction& inst,
+                                                 const Decoration& decoration) {
   assert(inst.id() && "Parser ensures the target of the decoration has an ID");
+  const bool is_non_writable =
+      decoration.dec_type() == spv::Decoration::NonWritable;
+  assert(is_non_writable ||
+         decoration.dec_type() == spv::Decoration::NonReadable);
 
   if (decoration.struct_member_index() == Decoration::kInvalidMember) {
     // The target must be a memory object declaration.
@@ -1707,10 +1856,14 @@
     const auto type_id = inst.type_id();
     if (opcode != spv::Op::OpVariable &&
         opcode != spv::Op::OpUntypedVariableKHR &&
+        opcode != spv::Op::OpBufferPointerEXT &&
         opcode != spv::Op::OpFunctionParameter &&
         opcode != spv::Op::OpRawAccessChainNV) {
       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
-             << "Target of NonWritable decoration must be a memory object "
+             << "Target of "
+             << (is_non_writable ? "NonWritable" : "NonReadable")
+             << " decoration must be a "
+                "memory object "
                 "declaration (a variable or a function parameter)";
     }
     const auto var_storage_class =
@@ -1719,21 +1872,42 @@
             : opcode == spv::Op::OpUntypedVariableKHR
                   ? inst.GetOperandAs<spv::StorageClass>(3)
                   : spv::StorageClass::Max;
+
+    if (opcode == spv::Op::OpBufferPointerEXT) {
+      auto result_type = vstate.FindDef(inst.type_id());
+      auto sc = result_type->GetOperandAs<spv::StorageClass>(1);
+      if (sc == spv::StorageClass::Uniform && is_non_writable) {
+        return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+               << "Target of NonWritable decoration is invalid: "
+               << "cannot be used to OpBufferPointerEXT "
+               << "with Uniform storage class";
+      }
+      return SPV_SUCCESS;
+    }
+
     if ((var_storage_class == spv::StorageClass::Function ||
          var_storage_class == spv::StorageClass::Private) &&
-        vstate.features().nonwritable_var_in_function_or_private) {
+        vstate.features().nonwritable_var_in_function_or_private &&
+        is_non_writable) {
       // New permitted feature in SPIR-V 1.4.
+    } else if (var_storage_class == spv::StorageClass::TileAttachmentQCOM) {
     } else if (
         // It may point to a UBO, SSBO, storage image, or raw access chain.
         vstate.IsPointerToUniformBlock(type_id) ||
         vstate.IsPointerToStorageBuffer(type_id) ||
         vstate.IsPointerToStorageImage(type_id) ||
+        vstate.IsPointerToTensor(type_id) ||
         opcode == spv::Op::OpRawAccessChainNV) {
     } else {
       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
-             << "Target of NonWritable decoration is invalid: must point to a "
-                "storage image, uniform block, "
-             << (vstate.features().nonwritable_var_in_function_or_private
+             << "Target of "
+             << (is_non_writable ? "NonWritable" : "NonReadable")
+             << " decoration is invalid: "
+                "must point to a "
+                "storage image, tensor variable in UniformConstant storage "
+                "class, uniform block, "
+             << (vstate.features().nonwritable_var_in_function_or_private &&
+                         is_non_writable
                      ? "storage buffer, or variable in Private or Function "
                        "storage class"
                      : "or storage buffer");
@@ -1883,7 +2057,7 @@
     if (!vstate.IsIntScalarOrVectorType(type_id) &&
         !vstate.IsFloatScalarOrVectorType(type_id)) {
       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
-             << vstate.VkErrorID(4924)
+             << vstate.VkErrorID(10583)
              << "Component decoration specified for type "
              << vstate.getIdName(type_id) << " that is not a scalar or vector";
     }
@@ -1988,15 +2162,16 @@
   {                                             \
     spv_result_t e##LINE = (X);                 \
     if (e##LINE != SPV_SUCCESS) return e##LINE; \
-  } static_assert(true, "require extra semicolon")
+  }                                             \
+  static_assert(true, "require extra semicolon")
 #define PASS_OR_BAIL(X) PASS_OR_BAIL_AT_LINE(X, __LINE__)
 
 // Check rules for decorations where we start from the decoration rather
 // than the decorated object.  Assumes each decoration on a group have been
 // propagated down to the group members.
 spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) {
-  // Some rules are only checked for shaders.
   const bool is_shader = vstate.HasCapability(spv::Capability::Shader);
+  const bool is_kernel = vstate.HasCapability(spv::Capability::Kernel);
 
   for (const auto& kv : vstate.id_decorations()) {
     const uint32_t id = kv.first;
@@ -2019,9 +2194,13 @@
           if (is_shader)
             PASS_OR_BAIL(
                 CheckFPRoundingModeForShaders(vstate, *inst, decoration));
+          if (is_kernel)
+            PASS_OR_BAIL(CheckFPRoundingModeForKernels(vstate, *inst));
           break;
+        case spv::Decoration::NonReadable:
         case spv::Decoration::NonWritable:
-          PASS_OR_BAIL(CheckNonWritableDecoration(vstate, *inst, decoration));
+          PASS_OR_BAIL(
+              CheckNonReadableWritableDecorations(vstate, *inst, decoration));
           break;
         case spv::Decoration::Uniform:
         case spv::Decoration::UniformId:
@@ -2050,17 +2229,273 @@
   return SPV_SUCCESS;
 }
 
+bool AllowsLayout(ValidationState_t& vstate, const spv::StorageClass sc) {
+  switch (sc) {
+    case spv::StorageClass::StorageBuffer:
+    case spv::StorageClass::Uniform:
+    case spv::StorageClass::PhysicalStorageBuffer:
+    case spv::StorageClass::PushConstant:
+      // Always explicitly laid out.
+      return true;
+    case spv::StorageClass::UniformConstant:
+      return false;
+    case spv::StorageClass::Workgroup:
+      return vstate.HasCapability(
+          spv::Capability::WorkgroupMemoryExplicitLayoutKHR);
+    case spv::StorageClass::Function:
+    case spv::StorageClass::Private:
+      return vstate.version() <= SPV_SPIRV_VERSION_WORD(1, 4);
+    case spv::StorageClass::Input:
+    case spv::StorageClass::Output:
+      // Block is used generally and mesh shaders use Offset.
+      return true;
+    default:
+      // TODO: Some storage classes in ray tracing use explicit layout
+      // decorations, but it is not well documented which. For now treat other
+      // storage classes as allowed to be laid out. See Vulkan internal issue
+      // 4192.
+      return true;
+  }
+}
+
+// Returns a decoration used to make it explicit
+spv::Decoration UsesExplicitLayout(
+    ValidationState_t& vstate, uint32_t type_id,
+    std::unordered_map<uint32_t, spv::Decoration>& cache) {
+  if (type_id == 0) {
+    return spv::Decoration::Max;
+  }
+
+  if (cache.count(type_id)) {
+    return cache[type_id];
+  }
+
+  spv::Decoration res = spv::Decoration::Max;
+  const auto type_inst = vstate.FindDef(type_id);
+  if (type_inst->opcode() == spv::Op::OpTypeStruct ||
+      type_inst->opcode() == spv::Op::OpTypeArray ||
+      type_inst->opcode() == spv::Op::OpTypeRuntimeArray ||
+      type_inst->opcode() == spv::Op::OpTypePointer ||
+      type_inst->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
+    const auto& id_decs = vstate.id_decorations();
+    const auto iter = id_decs.find(type_id);
+    if (iter != id_decs.end()) {
+      bool allowLayoutDecorations = false;
+      if (type_inst->opcode() == spv::Op::OpTypePointer ||
+          type_inst->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
+        const auto sc = type_inst->GetOperandAs<spv::StorageClass>(1);
+        allowLayoutDecorations = AllowsLayout(vstate, sc);
+      }
+      if (!allowLayoutDecorations) {
+        for (const auto& d : iter->second) {
+          const spv::Decoration dec = d.dec_type();
+          if (dec == spv::Decoration::Block ||
+              dec == spv::Decoration::BufferBlock ||
+              dec == spv::Decoration::Offset ||
+              dec == spv::Decoration::ArrayStride ||
+              dec == spv::Decoration::MatrixStride) {
+            res = dec;
+            break;
+          }
+        }
+      }
+    }
+
+    if (res == spv::Decoration::Max) {
+      switch (type_inst->opcode()) {
+        case spv::Op::OpTypeStruct:
+          for (uint32_t i = 1;
+               res == spv::Decoration::Max && i < type_inst->operands().size();
+               i++) {
+            res = UsesExplicitLayout(
+                vstate, type_inst->GetOperandAs<uint32_t>(i), cache);
+          }
+          break;
+        case spv::Op::OpTypeArray:
+        case spv::Op::OpTypeRuntimeArray:
+          res = UsesExplicitLayout(vstate, type_inst->GetOperandAs<uint32_t>(1),
+                                   cache);
+          break;
+        case spv::Op::OpTypePointer: {
+          const auto sc = type_inst->GetOperandAs<spv::StorageClass>(1);
+          if (!AllowsLayout(vstate, sc)) {
+            res = UsesExplicitLayout(
+                vstate, type_inst->GetOperandAs<uint32_t>(2), cache);
+          }
+        }
+        default:
+          break;
+      }
+    }
+  }
+
+  cache[type_id] = res;
+  return res;
+}
+
+spv_result_t CheckInvalidVulkanExplicitLayout(ValidationState_t& vstate) {
+  if (!spvIsVulkanEnv(vstate.context()->target_env)) {
+    return SPV_SUCCESS;
+  }
+
+  std::unordered_map<uint32_t, spv::Decoration> cache;
+  for (const auto& inst : vstate.ordered_instructions()) {
+    const auto type_id = inst.type_id();
+    const auto type_inst = vstate.FindDef(type_id);
+
+    spv::StorageClass sc = spv::StorageClass::Max;
+    spv::Decoration layout_dec = spv::Decoration::Max;
+    uint32_t fail_id = 0;
+    uint32_t base_id = 0;
+    // Variables are the main place to check for improper decorations, but some
+    // untyped pointer instructions must also be checked since those types may
+    // never be instantiated by a variable. Unlike verifying a valid layout,
+    // physical storage buffer does not need checked here since it is always
+    // explicitly laid out.
+    switch (inst.opcode()) {
+      case spv::Op::OpVariable:
+      case spv::Op::OpUntypedVariableKHR: {
+        sc = inst.GetOperandAs<spv::StorageClass>(2);
+        auto check_id = type_id;
+        base_id = inst.id();
+        if (inst.opcode() == spv::Op::OpUntypedVariableKHR) {
+          if (inst.operands().size() > 3) {
+            check_id = inst.GetOperandAs<uint32_t>(3);
+          }
+        }
+        if (!AllowsLayout(vstate, sc)) {
+          layout_dec = UsesExplicitLayout(vstate, check_id, cache);
+          if (layout_dec != spv::Decoration::Max) {
+            fail_id = check_id;
+          }
+        }
+        break;
+      }
+      case spv::Op::OpUntypedAccessChainKHR:
+      case spv::Op::OpUntypedInBoundsAccessChainKHR:
+      case spv::Op::OpUntypedPtrAccessChainKHR:
+      case spv::Op::OpUntypedInBoundsPtrAccessChainKHR: {
+        // Check both the base type and return type. The return type may have an
+        // invalid array stride.
+        sc = type_inst->GetOperandAs<spv::StorageClass>(1);
+        base_id = vstate.FindDef(inst.GetOperandAs<uint32_t>(3))->id();
+        if (!AllowsLayout(vstate, sc)) {
+          const auto base_type_id = inst.GetOperandAs<uint32_t>(2);
+          layout_dec = UsesExplicitLayout(vstate, base_type_id, cache);
+          if (layout_dec != spv::Decoration::Max) {
+            fail_id = base_type_id;
+          } else {
+            layout_dec = UsesExplicitLayout(vstate, type_id, cache);
+            if (layout_dec != spv::Decoration::Max) {
+              fail_id = type_id;
+            }
+          }
+        }
+        break;
+      }
+      case spv::Op::OpUntypedArrayLengthKHR: {
+        // Check the data type.
+        const auto ptr_ty_id =
+            vstate.FindDef(inst.GetOperandAs<uint32_t>(3))->type_id();
+        const auto ptr_ty = vstate.FindDef(ptr_ty_id);
+        sc = ptr_ty->GetOperandAs<spv::StorageClass>(1);
+        base_id = vstate.FindDef(inst.GetOperandAs<uint32_t>(3))->id();
+        if (!AllowsLayout(vstate, sc)) {
+          const auto base_type_id = inst.GetOperandAs<uint32_t>(2);
+          layout_dec = UsesExplicitLayout(vstate, base_type_id, cache);
+          if (layout_dec != spv::Decoration::Max) {
+            fail_id = base_type_id;
+          }
+        }
+        break;
+      }
+      case spv::Op::OpLoad: {
+        const auto ptr_id = inst.GetOperandAs<uint32_t>(2);
+        const auto ptr_type = vstate.FindDef(vstate.FindDef(ptr_id)->type_id());
+        base_id = ptr_id;
+        if (ptr_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
+          // For untyped pointers check the return type for an invalid layout.
+          sc = ptr_type->GetOperandAs<spv::StorageClass>(1);
+          if (!AllowsLayout(vstate, sc)) {
+            layout_dec = UsesExplicitLayout(vstate, type_id, cache);
+            if (layout_dec != spv::Decoration::Max) {
+              fail_id = type_id;
+            }
+          }
+        }
+        break;
+      }
+      case spv::Op::OpStore: {
+        const auto ptr_id = inst.GetOperandAs<uint32_t>(1);
+        const auto ptr_type = vstate.FindDef(vstate.FindDef(ptr_id)->type_id());
+        base_id = inst.GetOperandAs<uint32_t>(0);
+        if (ptr_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
+          // For untyped pointers, check the type of the data operand for an
+          // invalid layout.
+          sc = ptr_type->GetOperandAs<spv::StorageClass>(1);
+          if (!AllowsLayout(vstate, sc)) {
+            const auto data_type_id = vstate.GetOperandTypeId(&inst, 1);
+            layout_dec = UsesExplicitLayout(vstate, data_type_id, cache);
+            if (layout_dec != spv::Decoration::Max) {
+              fail_id = inst.GetOperandAs<uint32_t>(2);
+            }
+          }
+        }
+        break;
+      }
+      case spv::Op::OpBufferPointerEXT: {
+        const auto ptr_id = inst.GetOperandAs<uint32_t>(1);
+        const auto ptr_type = vstate.FindDef(vstate.FindDef(ptr_id)->type_id());
+        // Check the type of the data operand for an invalid layout.
+        sc = ptr_type->GetOperandAs<spv::StorageClass>(1);
+        if (!AllowsLayout(vstate, sc) &&
+            UsesExplicitLayout(vstate, type_id, cache) !=
+                spv::Decoration::Max) {
+          return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+                 << vstate.VkErrorID(11346)
+                 << "The result type operand of OpBufferPointerEXT must have "
+                 << "a Type operand that is explicitly laid out : "
+                 << vstate.getIdName(type_id);
+        } else if (sc != spv::StorageClass::StorageBuffer &&
+                   sc != spv::StorageClass::Uniform) {
+          return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+                 << "OpBufferPointerEXT's Result Type must be a pointer type "
+                 << "with a Storage Class of Uniform or StorageBuffer.";
+        }
+        break;
+      }
+      default:
+        break;
+    }
+
+    if (fail_id != 0 &&
+        !vstate.IsDescriptorHeapBaseVariable(vstate.FindDef(base_id))) {
+      return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+             << vstate.VkErrorID(10684)
+             << "Invalid explicit layout decorations on type for operand "
+             << vstate.getIdName(fail_id) << ", the "
+             << spvtools::StorageClassToString(sc)
+             << " storage class has a explicit layout from the "
+             << vstate.SpvDecorationString(layout_dec) << " decoration.";
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
 }  // namespace
 
 spv_result_t ValidateDecorations(ValidationState_t& vstate) {
   if (auto error = CheckImportedVariableInitialization(vstate)) return error;
   if (auto error = CheckDecorationsOfEntryPoints(vstate)) return error;
   if (auto error = CheckDecorationsOfBuffers(vstate)) return error;
+  if (auto error = CheckDecorationsOfVariables(vstate)) return error;
   if (auto error = CheckDecorationsCompatibility(vstate)) return error;
   if (auto error = CheckLinkageAttrOfFunctions(vstate)) return error;
   if (auto error = CheckVulkanMemoryModelDeprecatedDecorations(vstate))
     return error;
   if (auto error = CheckDecorationsFromDecoration(vstate)) return error;
+  if (auto error = CheckInvalidVulkanExplicitLayout(vstate)) return error;
   return SPV_SUCCESS;
 }
 
diff --git a/source/val/validate_dot_product.cpp b/source/val/validate_dot_product.cpp
new file mode 100644
index 0000000..298baf4
--- /dev/null
+++ b/source/val/validate_dot_product.cpp
@@ -0,0 +1,407 @@
+// Copyright (c) 2026 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 <cstdint>
+
+#include "source/val/instruction.h"
+#include "source/val/validate.h"
+#include "source/val/validate_scopes.h"
+#include "source/val/validation_state.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+spv_result_t ValidateSameSignedDot(ValidationState_t& _,
+                                   const Instruction* inst) {
+  const uint32_t result_id = inst->type_id();
+  if (!_.IsIntScalarType(result_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result must be an int scalar type.";
+  }
+
+  const spv::Op opcode = inst->opcode();
+  const bool has_accumulator = opcode == spv::Op::OpSDotAccSat ||
+                               opcode == spv::Op::OpUDotAccSat ||
+                               opcode == spv::Op::OpSUDotAccSat;
+  if (has_accumulator) {
+    const uint32_t accumulator_type = _.GetOperandTypeId(inst, 4);
+    if (accumulator_type != result_id) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Result must be the same as the Accumulator type.";
+    }
+  }
+
+  if (opcode == spv::Op::OpUDot || opcode == spv::Op::OpUDotAccSat) {
+    if (!_.IsIntScalarTypeWithSignedness(result_id, 0)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Result must be an unsigned int scalar type.";
+    }
+  }
+
+  const uint32_t vec_1_id = _.GetOperandTypeId(inst, 2);
+  const uint32_t vec_2_id = _.GetOperandTypeId(inst, 3);
+
+  const bool is_vec_1_scalar = _.IsIntScalarType(vec_1_id, 32);
+  const bool is_vec_2_scalar = _.IsIntScalarType(vec_2_id, 32);
+  if (is_vec_1_scalar != is_vec_2_scalar) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "'Vector 1' and 'Vector 2' must be the same type.";
+  } else if (is_vec_1_scalar && is_vec_2_scalar) {
+    if (!_.HasCapability(spv::Capability::DotProductInput4x8BitPacked)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "DotProductInput4x8BitPacked capability is required to use "
+                "scalar integers.";
+    }
+
+    // If both are scalar, spec doesn't say Signedness needs to match
+    const uint32_t vec_1_width = _.GetBitWidth(vec_1_id);
+    const uint32_t vec_2_width = _.GetBitWidth(vec_2_id);
+    if (vec_1_width != 32) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected 'Vector 1' to be 32-bit when a scalar.";
+    } else if (vec_2_width != 32) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected 'Vector 2' to be 32-bit when a scalar.";
+    }
+
+    // When packed, the result can be 8-bit
+    const uint32_t result_width = _.GetBitWidth(result_id);
+    if (result_width < 8) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Result width (" << result_width
+             << ") must be greater than or equal to the packed vector width of "
+                "8";
+    }
+
+    // PackedVectorFormat4x8Bit is used when the "Vector" operand are really
+    // scalar
+    const uint32_t packed_operand = has_accumulator ? 6 : 5;
+    const bool has_packed_vec_format =
+        inst->operands().size() == packed_operand;
+    if (!has_packed_vec_format) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "'Vector 1' and 'Vector 2' are a 32-bit int scalar, but no "
+                "Packed Vector "
+                "Format was provided.";
+    }
+  } else {
+    // both should be vectors
+
+    if (!_.IsVectorType(vec_1_id)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected 'Vector 1' to be an int scalar or vector.";
+    } else if (!_.IsVectorType(vec_2_id)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected 'Vector 2' to be an int scalar or vector.";
+    }
+
+    const uint32_t vec_1_length = _.GetDimension(vec_1_id);
+    const uint32_t vec_2_length = _.GetDimension(vec_2_id);
+    // If using OpTypeVectorIdEXT with a spec constant, this can be evaluated
+    // when spec constants are frozen
+    if (vec_1_length != 0 && vec_2_length != 0 &&
+        vec_1_length != vec_2_length) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "'Vector 1' is " << vec_1_length
+             << " components but 'Vector 2' is " << vec_2_length
+             << " components";
+    }
+
+    const uint32_t vec_1_type = _.GetComponentType(vec_1_id);
+    const uint32_t vec_2_type = _.GetComponentType(vec_2_id);
+    if (!_.IsIntScalarType(vec_1_type)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected 'Vector 1' to be a vector of integers.";
+    } else if (!_.IsIntScalarType(vec_2_type)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected 'Vector 2' to be a vector of integers.";
+    }
+
+    const uint32_t vec_1_width = _.GetBitWidth(vec_1_type);
+    const uint32_t vec_2_width = _.GetBitWidth(vec_2_type);
+    if (vec_1_width != vec_2_width) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "'Vector 1' component is " << vec_1_width
+             << "-bit but 'Vector 2' component is " << vec_2_width << "-bit";
+    }
+
+    const uint32_t result_width = _.GetBitWidth(result_id);
+    if (result_width < vec_1_width) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Result width (" << result_width
+             << ") must be greater than or equal to the vectors width ("
+             << vec_1_width << ").";
+    }
+
+    if (!_.HasCapability(spv::Capability::DotProductInputAll)) {
+      // 4-wide 8-bit ints are special exception that has its own capability
+      if (vec_1_length == 4 && vec_1_width == 8) {
+        if (!_.HasCapability(spv::Capability::DotProductInput4x8Bit)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "DotProductInput4x8Bit or DotProductInputAll capability is "
+                    "required to use 4-component vectors of 8-bit integers.";
+        }
+      } else {
+        // provide a more helpful message what is going on if we are here
+        // reporting this error
+        if (_.HasCapability(spv::Capability::DotProductInput4x8BitPacked)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "DotProductInputAll capability is required use vectors. "
+                    "(DotProductInput4x8BitPacked capability declared allows "
+                    "for only 32-bit int scalars)";
+        } else {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "DotProductInputAll capability is additionally required to "
+                    "the DotProduct capability to use vectors. (It is possible "
+                    "to set DotProductInput4x8BitPacked to only use 32-bit "
+                    "scalars packed as a 4-wide 8-byte vector)";
+        }
+      }
+    }
+
+    if (opcode == spv::Op::OpUDot || opcode == spv::Op::OpUDotAccSat) {
+      const bool vec_1_unsigned =
+          _.IsIntScalarTypeWithSignedness(vec_1_type, 0);
+      const bool vec_2_unsigned =
+          _.IsIntScalarTypeWithSignedness(vec_2_type, 0);
+      if (!vec_1_unsigned) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 'Vector 1' to be an vector of unsigned integers.";
+      } else if (!vec_2_unsigned) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 'Vector 2' to be an vector of unsigned integers.";
+      }
+    } else if (opcode == spv::Op::OpSUDot || opcode == spv::Op::OpSUDotAccSat) {
+      const bool vec_2_unsigned =
+          _.IsIntScalarTypeWithSignedness(vec_2_type, 0);
+      if (!vec_2_unsigned) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 'Vector 2' to be an vector of unsigned integers.";
+      }
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateFDotMixVectors(ValidationState_t& _,
+                                    const Instruction* inst, uint32_t vec_1_id,
+                                    uint32_t vec_2_id, uint32_t length) {
+  if (!_.IsVectorType(vec_1_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected 'Vector 1' to be an vector.";
+  } else if (!_.IsVectorType(vec_2_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected 'Vector 2' to be an vector.";
+  }
+
+  // If using OpTypeVectorIdEXT with a spec constant,
+  // this can be evaluated when spec constants are frozen
+  const uint32_t vec_1_length = _.GetDimension(vec_1_id);
+  const uint32_t vec_2_length = _.GetDimension(vec_2_id);
+  if (vec_1_length != 0 && vec_1_length != length && vec_2_length != 0 &&
+      vec_2_length != length) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "'Vector 1' is " << vec_1_length
+           << " components and 'Vector 2' is " << vec_2_length
+           << " components, but both need to be " << length << "-components";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateFDot2MixAcc32(ValidationState_t& _,
+                                   const Instruction* inst) {
+  const uint32_t result_id = inst->type_id();
+  if (!_.IsFloatScalarType(result_id, 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result must be a 32-bit IEEE 754 float scalar type.";
+  }
+
+  const uint32_t vec_1_id = _.GetOperandTypeId(inst, 2);
+  const uint32_t vec_2_id = _.GetOperandTypeId(inst, 3);
+
+  if (auto error = ValidateFDotMixVectors(_, inst, vec_1_id, vec_2_id, 2))
+    return error;
+
+  const uint32_t vec_1_type = _.GetComponentType(vec_1_id);
+  const uint32_t vec_2_type = _.GetComponentType(vec_2_id);
+  if (!_.IsFloatScalarType(vec_1_type, 16)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected 'Vector 1' to be a vector of 16-bit floats.";
+  } else if (!_.IsFloatScalarType(vec_2_type, 16)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected 'Vector 2' to be a vector of 16-bit floats.";
+  }
+
+  // Currently 16-bit floats are only BFloat or IEEE 754
+  const bool is_vec_1_bfloat = _.IsBfloat16ScalarType(vec_1_type);
+  const bool is_vec_2_bfloat = _.IsBfloat16ScalarType(vec_2_type);
+  if (is_vec_1_bfloat != is_vec_2_bfloat) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "'Vector 1' and 'Vector 2' must be the same float encoding.";
+  }
+
+  if (is_vec_1_bfloat) {
+    if (!_.HasCapability(spv::Capability::DotProductBFloat16AccVALVE)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "DotProductBFloat16AccVALVE capability is required to use "
+                "BFloat16 encoded floats.";
+    }
+  } else {
+    if (!_.HasCapability(spv::Capability::DotProductFloat16AccFloat32VALVE)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "DotProductFloat16AccFloat32VALVE capability is required to "
+                "use "
+                "IEEE 754 encoded 16-bit floats.";
+    }
+  }
+
+  const uint32_t accumulator_type = _.GetOperandTypeId(inst, 4);
+  if (accumulator_type != result_id) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Accumulator Type must be the same as the Result Type.";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateFDot2MixAcc16(ValidationState_t& _,
+                                   const Instruction* inst) {
+  const uint32_t vec_1_id = _.GetOperandTypeId(inst, 2);
+  const uint32_t vec_2_id = _.GetOperandTypeId(inst, 3);
+
+  if (auto error = ValidateFDotMixVectors(_, inst, vec_1_id, vec_2_id, 2))
+    return error;
+
+  const uint32_t vec_1_type = _.GetComponentType(vec_1_id);
+  const uint32_t vec_2_type = _.GetComponentType(vec_2_id);
+  if (!_.IsFloatScalarType(vec_1_type, 16)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected 'Vector 1' to be a vector of 16-bit floats.";
+  } else if (!_.IsFloatScalarType(vec_2_type, 16)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected 'Vector 2' to be a vector of 16-bit floats.";
+  }
+
+  // Currently 16-bit floats are only BFloat or IEEE 754
+  const bool is_vec_1_bfloat = _.IsBfloat16ScalarType(vec_1_type);
+  const bool is_vec_2_bfloat = _.IsBfloat16ScalarType(vec_2_type);
+  if (is_vec_1_bfloat != is_vec_2_bfloat) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "'Vector 1' and 'Vector 2' must be the same float encoding.";
+  }
+
+  if (is_vec_1_bfloat) {
+    if (!_.HasCapability(spv::Capability::DotProductBFloat16AccVALVE)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "DotProductBFloat16AccVALVE capability is required to use "
+                "BFloat16 encoded floats.";
+    }
+  } else {
+    if (!_.HasCapability(spv::Capability::DotProductFloat16AccFloat16VALVE)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "DotProductFloat16AccFloat16VALVE capability is required to "
+                "use "
+                "IEEE 754 encoded 16-bit floats.";
+    }
+  }
+
+  const uint32_t result_id = inst->type_id();
+  if (!_.IsFloatScalarType(result_id, 16)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result must be a 16-bit float scalar type.";
+  }
+
+  const bool is_result_bfloat = _.IsBfloat16ScalarType(result_id);
+  if (is_result_bfloat != is_vec_1_bfloat) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result must have the same float encoding as 'Vector 1' and "
+              "'Vector 2'.";
+  }
+
+  const uint32_t accumulator_type = _.GetOperandTypeId(inst, 4);
+  if (accumulator_type != result_id) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Accumulator Type must be the same as the Result Type.";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateFDot4MixAcc32(ValidationState_t& _,
+                                   const Instruction* inst) {
+  const uint32_t result_id = inst->type_id();
+  if (!_.IsFloatScalarType(result_id, 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result must be a 32-bit IEEE 754 float scalar type.";
+  }
+
+  const uint32_t vec_1_id = _.GetOperandTypeId(inst, 2);
+  const uint32_t vec_2_id = _.GetOperandTypeId(inst, 3);
+
+  if (auto error = ValidateFDotMixVectors(_, inst, vec_1_id, vec_2_id, 4))
+    return error;
+
+  // Currently 8-bit floats are only Float8E4M3/Float8E5M2
+  const uint32_t vec_1_type = _.GetComponentType(vec_1_id);
+  const uint32_t vec_2_type = _.GetComponentType(vec_2_id);
+  if (!_.IsFloatScalarType(vec_1_type, 8)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected 'Vector 1' to be a vector of 8-bit floats.";
+  } else if (!_.IsFloatScalarType(vec_2_type, 8)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected 'Vector 2' to be a vector of 8-bit floats.";
+  }
+
+  const uint32_t accumulator_type = _.GetOperandTypeId(inst, 4);
+  if (accumulator_type != result_id) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Accumulator Type must be the same as the Result Type.";
+  }
+
+  return SPV_SUCCESS;
+}
+
+}  // namespace
+
+spv_result_t DotProductPass(ValidationState_t& _, const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+
+  switch (opcode) {
+    case spv::Op::OpSDot:
+    case spv::Op::OpUDot:
+    case spv::Op::OpSUDot:
+    case spv::Op::OpSDotAccSat:
+    case spv::Op::OpUDotAccSat:
+    case spv::Op::OpSUDotAccSat:
+      return ValidateSameSignedDot(_, inst);
+    // Tried combining these to a single validate function, but they are less
+    // similar than appeared at first glance
+    case spv::Op::OpFDot2MixAcc32VALVE:
+      return ValidateFDot2MixAcc32(_, inst);
+    case spv::Op::OpFDot2MixAcc16VALVE:
+      return ValidateFDot2MixAcc16(_, inst);
+    case spv::Op::OpFDot4MixAcc32VALVE:
+      return ValidateFDot4MixAcc32(_, inst);
+    default:
+      break;
+  }
+
+  return SPV_SUCCESS;
+}
+
+}  // namespace val
+}  // namespace spvtools
diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp
index cb4768d..831823b 100644
--- a/source/val/validate_extensions.cpp
+++ b/source/val/validate_extensions.cpp
@@ -13,38 +13,41 @@
 // limitations under the License.
 
 // Validates correctness of extension SPIR-V instructions.
+#include <algorithm>
+#include <cstdint>
 #include <cstdlib>
 #include <sstream>
 #include <string>
 #include <vector>
 
-#include "NonSemanticShaderDebugInfo100.h"
 #include "OpenCLDebugInfo100.h"
 #include "source/common_debug_info.h"
-#include "source/enum_string_mapping.h"
 #include "source/extensions.h"
 #include "source/latest_version_glsl_std_450_header.h"
 #include "source/latest_version_opencl_std_header.h"
+#include "source/opcode.h"
 #include "source/spirv_constant.h"
+#include "source/table2.h"
 #include "source/val/instruction.h"
 #include "source/val/validate.h"
 #include "source/val/validation_state.h"
+#include "spirv-tools/libspirv.h"
 #include "spirv/unified1/NonSemanticClspvReflection.h"
+#include "spirv/unified1/NonSemanticShaderDebugInfo.h"
 
 namespace spvtools {
 namespace val {
 namespace {
 
-std::string ReflectionInstructionName(ValidationState_t& _,
-                                      const Instruction* inst) {
-  spv_ext_inst_desc desc = nullptr;
-  if (_.grammar().lookupExtInst(SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION,
-                                inst->word(4), &desc) != SPV_SUCCESS ||
+std::string ReflectionInstructionName(const Instruction* inst) {
+  const ExtInstDesc* desc = nullptr;
+  if (LookupExtInst(SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION,
+                    inst->word(4), &desc) != SPV_SUCCESS ||
       !desc) {
     return std::string("Unknown ExtInst");
   }
   std::ostringstream ss;
-  ss << desc->name;
+  ss << desc->name().data();
 
   return ss.str();
 }
@@ -85,25 +88,63 @@
   return inst->word(3);
 }
 
+std::string GetExtInstName(const ValidationState_t& _,
+                           const Instruction* inst) {
+  const uint32_t ext_inst_set = inst->word(3);
+  const uint32_t ext_inst_index = inst->word(4);
+  const spv_ext_inst_type_t ext_inst_type =
+      spv_ext_inst_type_t(inst->ext_inst_type());
+
+  const ExtInstDesc* desc = nullptr;
+  if (LookupExtInst(ext_inst_type, ext_inst_index, &desc) != SPV_SUCCESS ||
+      !desc) {
+    return std::string("Unknown ExtInst");
+  }
+
+  auto* import_inst = _.FindDef(ext_inst_set);
+  assert(import_inst);
+
+  std::ostringstream ss;
+  ss << import_inst->GetOperandAs<std::string>(1);
+  ss << " ";
+  ss << desc->name().data();
+
+  return ss.str();
+}
+
+// Returns the declared NSDI version from the OpExtInstImport referenced by
+// |inst|.  Returns 0 if not a NonSemantic.Shader.DebugInfo import.
+uint32_t GetNSDIVersion(const ValidationState_t& _, const Instruction* inst) {
+  const auto* import_inst = _.FindDef(inst->word(3));
+  if (!import_inst) return 0;
+  const std::string name = import_inst->GetOperandAs<std::string>(1);
+  const char kPrefix[] = "NonSemantic.Shader.DebugInfo.";
+  if (name.find(kPrefix) != 0) return 0;
+  return static_cast<uint32_t>(
+      std::strtoul(name.c_str() + sizeof(kPrefix) - 1, nullptr, 10));
+}
+
 // Check that the operand of a debug info instruction |inst| at |word_index|
 // is a result id of an instruction with |expected_opcode|.
-spv_result_t ValidateOperandForDebugInfo(
-    ValidationState_t& _, const std::string& operand_name,
-    spv::Op expected_opcode, const Instruction* inst, uint32_t word_index,
-    const std::function<std::string()>& ext_inst_name) {
+spv_result_t ValidateOperandForDebugInfo(ValidationState_t& _,
+                                         const std::string& operand_name,
+                                         spv::Op expected_opcode,
+                                         const Instruction* inst,
+                                         uint32_t word_index) {
   auto* operand = _.FindDef(inst->word(word_index));
   if (operand->opcode() != expected_opcode) {
-    spv_opcode_desc desc = nullptr;
-    if (_.grammar().lookupOpcode(expected_opcode, &desc) != SPV_SUCCESS ||
+    const spvtools::InstructionDesc* desc = nullptr;
+    if (spvtools::LookupOpcodeForEnv(_.context()->target_env, expected_opcode,
+                                     &desc) != SPV_SUCCESS ||
         !desc) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << ext_inst_name() << ": "
-             << "expected operand " << operand_name << " is invalid";
+             << GetExtInstName(_, inst) << ": " << "expected operand "
+             << operand_name << " is invalid";
     }
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << ext_inst_name() << ": "
-           << "expected operand " << operand_name << " must be a result id of "
-           << "Op" << desc->name;
+           << GetExtInstName(_, inst) << ": " << "expected operand "
+           << operand_name << " must be a result id of " << "Op"
+           << desc->name().data();
   }
   return SPV_SUCCESS;
 }
@@ -115,27 +156,57 @@
 spv_result_t ValidateUint32ConstantOperandForDebugInfo(
     ValidationState_t& _, const std::string& operand_name,
     const Instruction* inst, uint32_t word_index,
-    const std::function<std::string()>& ext_inst_name) {
-  if (!IsUint32Constant(_, inst->word(word_index))) {
+    bool allow_spec_const = false) {
+  const uint32_t id = inst->word(word_index);
+  if (!IsUint32Constant(_, id)) {
+    if (allow_spec_const) {
+      auto* def = _.FindDef(id);
+      if (def && spvOpcodeIsSpecConstant(def->opcode()) &&
+          IsIntScalar(_, def->type_id(), true, true))
+        return SPV_SUCCESS;
+    }
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << ext_inst_name() << ": expected operand " << operand_name
-           << " must be a result id of 32-bit unsigned OpConstant";
+           << GetExtInstName(_, inst) << ": expected operand " << operand_name
+           << " must be a result id of "
+           << (allow_spec_const
+                   ? "a 32-bit unsigned integer constant or specialization"
+                     " constant"
+                   : "32-bit unsigned OpConstant");
   }
   return SPV_SUCCESS;
 }
 
-#define CHECK_OPERAND(NAME, opcode, index)                                  \
-  do {                                                                      \
-    auto result = ValidateOperandForDebugInfo(_, NAME, opcode, inst, index, \
-                                              ext_inst_name);               \
-    if (result != SPV_SUCCESS) return result;                               \
+#define CHECK_OPERAND(NAME, opcode, index)                                   \
+  do {                                                                       \
+    auto result = ValidateOperandForDebugInfo(_, NAME, opcode, inst, index); \
+    if (result != SPV_SUCCESS) return result;                                \
   } while (0)
 
-#define CHECK_CONST_UINT_OPERAND(NAME, index)                \
-  if (vulkanDebugInfo) {                                     \
-    auto result = ValidateUint32ConstantOperandForDebugInfo( \
-        _, NAME, inst, index, ext_inst_name);                \
-    if (result != SPV_SUCCESS) return result;                \
+#define CHECK_CONST_UINT_OPERAND(NAME, index)                            \
+  if (vulkanDebugInfo) {                                                 \
+    auto result =                                                        \
+        ValidateUint32ConstantOperandForDebugInfo(_, NAME, inst, index); \
+    if (result != SPV_SUCCESS) return result;                            \
+  }
+
+// Like CHECK_CONST_UINT_OPERAND but also allows spec-constants.  Used for
+// NonSemantic.Shader.DebugInfo.101 cooperative type instructions, where
+// dimension operands may be specialization constants.
+#define CHECK_CONST_OR_SPEC_UINT_OPERAND(NAME, index)                          \
+  if (vulkanDebugInfo) {                                                       \
+    auto result =                                                              \
+        ValidateUint32ConstantOperandForDebugInfo(_, NAME, inst, index, true); \
+    if (result != SPV_SUCCESS) return result;                                  \
+  }
+
+// Checks that the NSDI version for the current instruction is at least |v|.
+// Used to guard opcodes added after version 100.
+#define CHECK_NSDI_MIN_VERSION(v)                                       \
+  if (nsdi_version < (v)) {                                             \
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)                         \
+           << GetExtInstName(_, inst)                                   \
+           << ": requires NonSemantic.Shader.DebugInfo version " << (v) \
+           << " or later";                                              \
   }
 
 // True if the operand of a debug info instruction |inst| at |word_index|
@@ -157,10 +228,10 @@
   return true;
 }
 
-// Overload for NonSemanticShaderDebugInfo100Instructions.
+// Overload for NonSemanticShaderDebugInfoInstructions.
 bool DoesDebugInfoOperandMatchExpectation(
     const ValidationState_t& _,
-    const std::function<bool(NonSemanticShaderDebugInfo100Instructions)>&
+    const std::function<bool(NonSemanticShaderDebugInfoInstructions)>&
         expectation,
     const Instruction* inst, uint32_t word_index) {
   if (inst->words().size() <= word_index) return false;
@@ -169,7 +240,7 @@
       (debug_inst->ext_inst_type() !=
        SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) ||
       !expectation(
-          NonSemanticShaderDebugInfo100Instructions(debug_inst->word(4)))) {
+          NonSemanticShaderDebugInfoInstructions(debug_inst->word(4)))) {
     return false;
   }
   return true;
@@ -181,7 +252,7 @@
 spv_result_t ValidateDebugInfoOperand(
     ValidationState_t& _, const std::string& debug_inst_name,
     CommonDebugInfoInstructions expected_debug_inst, const Instruction* inst,
-    uint32_t word_index, const std::function<std::string()>& ext_inst_name) {
+    uint32_t word_index) {
   std::function<bool(CommonDebugInfoInstructions)> expectation =
       [expected_debug_inst](CommonDebugInfoInstructions dbg_inst) {
         return dbg_inst == expected_debug_inst;
@@ -189,44 +260,44 @@
   if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index))
     return SPV_SUCCESS;
 
-  spv_ext_inst_desc desc = nullptr;
-  if (_.grammar().lookupExtInst(inst->ext_inst_type(), expected_debug_inst,
-                                &desc) != SPV_SUCCESS ||
+  const ExtInstDesc* desc = nullptr;
+  if (LookupExtInst(inst->ext_inst_type(), expected_debug_inst, &desc) !=
+          SPV_SUCCESS ||
       !desc) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << ext_inst_name() << ": "
-           << "expected operand " << debug_inst_name << " is invalid";
+           << GetExtInstName(_, inst) << ": " << "expected operand "
+           << debug_inst_name << " is invalid";
   }
   return _.diag(SPV_ERROR_INVALID_DATA, inst)
-         << ext_inst_name() << ": "
-         << "expected operand " << debug_inst_name << " must be a result id of "
-         << desc->name;
+         << GetExtInstName(_, inst) << ": " << "expected operand "
+         << debug_inst_name << " must be a result id of "
+         << desc->name().data();
 }
 
-#define CHECK_DEBUG_OPERAND(NAME, debug_opcode, index)                         \
-  do {                                                                         \
-    auto result = ValidateDebugInfoOperand(_, NAME, debug_opcode, inst, index, \
-                                           ext_inst_name);                     \
-    if (result != SPV_SUCCESS) return result;                                  \
+#define CHECK_DEBUG_OPERAND(NAME, debug_opcode, index)                \
+  do {                                                                \
+    auto result =                                                     \
+        ValidateDebugInfoOperand(_, NAME, debug_opcode, inst, index); \
+    if (result != SPV_SUCCESS) return result;                         \
   } while (0)
 
 // Check that the operand of a debug info instruction |inst| at |word_index|
 // is a result id of an debug info instruction with DebugTypeBasic.
-spv_result_t ValidateOperandBaseType(
-    ValidationState_t& _, const Instruction* inst, uint32_t word_index,
-    const std::function<std::string()>& ext_inst_name) {
+spv_result_t ValidateOperandBaseType(ValidationState_t& _,
+                                     const Instruction* inst,
+                                     uint32_t word_index) {
   return ValidateDebugInfoOperand(_, "Base Type", CommonDebugInfoDebugTypeBasic,
-                                  inst, word_index, ext_inst_name);
+                                  inst, word_index);
 }
 
 // Check that the operand of a debug info instruction |inst| at |word_index|
 // is a result id of a debug lexical scope instruction which is one of
 // DebugCompilationUnit, DebugFunction, DebugLexicalBlock, or
 // DebugTypeComposite.
-spv_result_t ValidateOperandLexicalScope(
-    ValidationState_t& _, const std::string& debug_inst_name,
-    const Instruction* inst, uint32_t word_index,
-    const std::function<std::string()>& ext_inst_name) {
+spv_result_t ValidateOperandLexicalScope(ValidationState_t& _,
+                                         const std::string& debug_inst_name,
+                                         const Instruction* inst,
+                                         uint32_t word_index) {
   std::function<bool(CommonDebugInfoInstructions)> expectation =
       [](CommonDebugInfoInstructions dbg_inst) {
         return dbg_inst == CommonDebugInfoDebugCompilationUnit ||
@@ -238,25 +309,34 @@
     return SPV_SUCCESS;
 
   return _.diag(SPV_ERROR_INVALID_DATA, inst)
-         << ext_inst_name() << ": "
-         << "expected operand " << debug_inst_name
-         << " must be a result id of a lexical scope";
+         << GetExtInstName(_, inst) << ": " << "expected operand "
+         << debug_inst_name << " must be a result id of a lexical scope";
 }
 
 // Check that the operand of a debug info instruction |inst| at |word_index|
 // is a result id of a debug type instruction (See DebugTypeXXX in
 // "4.3. Type instructions" section of OpenCL.DebugInfo.100 spec.
-spv_result_t ValidateOperandDebugType(
-    ValidationState_t& _, const std::string& debug_inst_name,
-    const Instruction* inst, uint32_t word_index,
-    const std::function<std::string()>& ext_inst_name,
-    bool allow_template_param) {
-  // Check for NonSemanticShaderDebugInfo100 specific types.
+spv_result_t ValidateOperandDebugType(ValidationState_t& _,
+                                      const std::string& debug_inst_name,
+                                      const Instruction* inst,
+                                      uint32_t word_index,
+                                      bool allow_template_param) {
+  // Check for NonSemanticShaderDebugInfo specific types.
   if (inst->ext_inst_type() ==
       SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
-    std::function<bool(NonSemanticShaderDebugInfo100Instructions)> expectation =
-        [](NonSemanticShaderDebugInfo100Instructions dbg_inst) {
-          return dbg_inst == NonSemanticShaderDebugInfo100DebugTypeMatrix;
+    const uint32_t nsdi_version = GetNSDIVersion(_, inst);
+    std::function<bool(NonSemanticShaderDebugInfoInstructions)> expectation =
+        [nsdi_version](NonSemanticShaderDebugInfoInstructions dbg_inst) {
+          if (dbg_inst == NonSemanticShaderDebugInfoDebugTypeMatrix)
+            return true;
+          // DebugTypeVectorIdEXT and DebugTypeCooperativeMatrixKHR were added
+          // in NonSemantic.Shader.DebugInfo version 101.
+          if (nsdi_version >= NonSemanticShaderDebugInfoVersion &&
+              (dbg_inst == NonSemanticShaderDebugInfoDebugTypeVectorIdEXT ||
+               dbg_inst ==
+                   NonSemanticShaderDebugInfoDebugTypeCooperativeMatrixKHR))
+            return true;
+          return false;
         };
     if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index))
       return SPV_SUCCESS;
@@ -277,15 +357,59 @@
     return SPV_SUCCESS;
 
   return _.diag(SPV_ERROR_INVALID_DATA, inst)
-         << ext_inst_name() << ": "
-         << "expected operand " << debug_inst_name
-         << " is not a valid debug type";
+         << GetExtInstName(_, inst) << ": " << "expected operand "
+         << debug_inst_name << " is not a valid debug type";
+}
+
+spv_result_t ValidateOperandDebugSource(ValidationState_t& _,
+                                        const Instruction* inst,
+                                        uint32_t source_index,
+                                        uint32_t line_index,
+                                        uint32_t column_index,
+                                        spv_ext_inst_type_t ext_inst_type) {
+  auto* debug_source_inst = _.FindDef(inst->word(source_index));
+  const std::vector<uint32_t>& line_lengths =
+      _.GetDebugSourceLineLength(debug_source_inst->id());
+  if (line_lengths.empty())
+    return SPV_SUCCESS;  // Text not provide in DebugSource
+
+  const bool vulkanDebugInfo =
+      ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100;
+  uint32_t line = 0;
+  uint32_t column = 0;
+  // NonSemantic uses OpConstant for all operands
+  if (vulkanDebugInfo) {
+    bool is_int32 = false, is_const_int32 = false;
+    std::tie(is_int32, is_const_int32, line) =
+        _.EvalInt32IfConst(inst->word(line_index));
+    std::tie(is_int32, is_const_int32, column) =
+        _.EvalInt32IfConst(inst->word(column_index));
+  } else {
+    line = inst->word(line_index);
+    column = inst->word(column_index);
+  }
+
+  if (line > line_lengths.size()) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << GetExtInstName(_, inst) << ": operand Line (" << line
+           << ") is larger then the " << line_lengths.size()
+           << " lines found in the DebugSource text";
+  } else if (line != 0) {
+    const uint32_t line_length = line_lengths[line - 1];
+    if (column > line_length) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << GetExtInstName(_, inst) << ": operand Column End (" << column
+             << ") is larger then Line " << line << " column length of "
+             << line_length << " found in the DebugSource text";
+    }
+  }
+  return SPV_SUCCESS;
 }
 
 spv_result_t ValidateClspvReflectionKernel(ValidationState_t& _,
                                            const Instruction* inst,
                                            uint32_t version) {
-  const auto inst_name = ReflectionInstructionName(_, inst);
+  const auto inst_name = ReflectionInstructionName(inst);
   const auto kernel_id = inst->GetOperandAs<uint32_t>(4);
   const auto kernel = _.FindDef(kernel_id);
   if (kernel->opcode() != spv::Op::OpFunction) {
@@ -930,7 +1054,7 @@
   }
   if (version < required_version) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << ReflectionInstructionName(_, inst) << " requires version "
+           << ReflectionInstructionName(inst) << " requires version "
            << required_version << ", but parsed version is " << version;
   }
 
@@ -1002,6 +1126,104 @@
   return SPV_SUCCESS;
 }
 
+std::string GetDebugSourceText(ValidationState_t& _, const Instruction* inst,
+                               uint32_t ext_inst_opcode) {
+  assert(ext_inst_opcode == NonSemanticShaderDebugInfoDebugSource ||
+         ext_inst_opcode == NonSemanticShaderDebugInfoDebugSourceContinued);
+  const uint32_t string_operand =
+      (ext_inst_opcode == NonSemanticShaderDebugInfoDebugSource) ? 6 : 5;
+  auto* debug_source_text_insn = _.FindDef(inst->word(string_operand));
+  // Validated to be an OpString
+  assert(debug_source_text_insn->opcode() == spv::Op::OpString);
+  return debug_source_text_insn->GetOperandAs<std::string>(1);
+}
+
+// We build up a vector that is length of the DebugSource lines and get how long
+// they are to make sure anyone using a DebugSource provides valid Line/Columns
+// inside
+void BuildDebugSourceLineLength(ValidationState_t& _, const Instruction* inst,
+                                uint32_t ext_inst_index) {
+  if (ext_inst_index == NonSemanticShaderDebugInfoDebugSource &&
+      inst->words().size() < 7) {
+    return;  // The optional text was not provided
+  }
+
+  std::string debug_source_text = GetDebugSourceText(_, inst, ext_inst_index);
+
+  // walk back to get DebugSource to update it's line length list
+  uint32_t debug_source_id = inst->id();
+  uint32_t continue_count = 0;
+
+  // There might be
+  //   %a = OpString "line starts here"
+  //   %b = OpString " and still the same line"
+  //
+  //   %c = OpExtInst %void %1 DebugSource %_ %a
+  //   %d = OpExtInst %void %1 DebugSourceContinued %b
+  // So we want to find the previous line for checking if we need to append on
+  // to the length of the previous line
+  bool start_new_line = true;
+  if (ext_inst_index == NonSemanticShaderDebugInfoDebugSourceContinued) {
+    auto prev_index = inst - &_.ordered_instructions()[0] - 1;
+    auto prev_inst = &_.ordered_instructions()[prev_index];
+
+    std::string previous_line_text =
+        GetDebugSourceText(_, prev_inst, prev_inst->GetOperandAs<uint32_t>(3));
+    if (!previous_line_text.empty() && previous_line_text.back() != '\n') {
+      start_new_line = false;
+    }
+  }
+
+  while (ext_inst_index == NonSemanticShaderDebugInfoDebugSourceContinued) {
+    continue_count++;  // might have multiple Continues in a row
+    auto prev_index = inst - &_.ordered_instructions()[0] - continue_count;
+    auto prev_inst = &_.ordered_instructions()[prev_index];
+    debug_source_id = prev_inst->id();
+    ext_inst_index = prev_inst->GetOperandAs<uint32_t>(3);
+  }
+
+  std::vector<uint32_t>& line_lengths =
+      _.GetDebugSourceLineLength(debug_source_id);
+  uint32_t line_start = 0;
+  // If we have a line like "abc", it really column 1-to-4.
+  // Even an empty line should have a column of 1
+  // Add 1 to length to emulate this later
+  uint32_t length = 1;
+
+  // Continue from the previous line length
+  if (!start_new_line) {
+    length = line_lengths.back();
+    line_lengths.pop_back();
+  }
+
+  for (uint32_t i = 0; i < debug_source_text.size(); ++i) {
+    if (debug_source_text[i] == '\n') {
+      // Unix-style new line
+      line_lengths.push_back(length);
+      length = 1;
+      line_start = i + 1;
+    } else if (debug_source_text[i] == '\r') {
+      // Handle Windows-style \r\n
+      if (i + 1 < debug_source_text.size() &&
+          debug_source_text[i + 1] == '\n') {
+        line_lengths.push_back(length);
+        ++i;  // Skip '\n'
+      } else {
+        line_lengths.push_back(length);
+      }
+      length = 1;
+      line_start = i + 1;
+    } else {
+      ++length;
+    }
+  }
+
+  // Capture last line if the string does not end in a newline
+  if (line_start < debug_source_text.size()) {
+    line_lengths.push_back(length);
+  }
+}
+
 bool IsConstIntScalarTypeWith32Or64Bits(ValidationState_t& _,
                                         Instruction* instr) {
   if (instr->opcode() != spv::Op::OpConstant) return false;
@@ -1010,6 +1232,14 @@
   return size_in_bits == 32 || size_in_bits == 64;
 }
 
+bool IsSpecConstIntScalarTypeWith32Or64Bits(ValidationState_t& _,
+                                            Instruction* instr) {
+  if (!spvOpcodeIsSpecConstant(instr->opcode())) return false;
+  if (!_.IsIntScalarType(instr->type_id())) return false;
+  uint32_t size_in_bits = _.GetBitWidth(instr->type_id());
+  return size_in_bits == 32 || size_in_bits == 64;
+}
+
 bool IsConstWithIntScalarType(ValidationState_t& _, const Instruction* inst,
                               uint32_t word_index) {
   auto* int_scalar_const = _.FindDef(inst->word(word_index));
@@ -1052,12 +1282,26 @@
 }  // anonymous namespace
 
 spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) {
+  std::string extension = GetExtensionString(&(inst->c_inst()));
+  if (_.version() < SPV_SPIRV_VERSION_WORD(1, 3)) {
+    if (extension == ExtensionToString(kSPV_KHR_vulkan_memory_model) ||
+        extension ==
+            ExtensionToString(kSPV_QCOM_cooperative_matrix_conversion)) {
+      return _.diag(SPV_ERROR_WRONG_VERSION, inst)
+             << extension << " extension requires SPIR-V version 1.3 or later.";
+    }
+  }
   if (_.version() < SPV_SPIRV_VERSION_WORD(1, 4)) {
-    std::string extension = GetExtensionString(&(inst->c_inst()));
     if (extension ==
             ExtensionToString(kSPV_KHR_workgroup_memory_explicit_layout) ||
         extension == ExtensionToString(kSPV_EXT_mesh_shader) ||
-        extension == ExtensionToString(kSPV_NV_shader_invocation_reorder)) {
+        extension == ExtensionToString(kSPV_NV_shader_invocation_reorder) ||
+        extension == ExtensionToString(kSPV_EXT_shader_invocation_reorder) ||
+        extension ==
+            ExtensionToString(kSPV_NV_cluster_acceleration_structure) ||
+        extension == ExtensionToString(kSPV_NV_linear_swept_spheres) ||
+        extension == ExtensionToString(kSPV_QCOM_image_processing) ||
+        extension == ExtensionToString(kSPV_QCOM_image_processing2)) {
       return _.diag(SPV_ERROR_WRONG_VERSION, inst)
              << extension << " extension requires SPIR-V version 1.4 or later.";
     }
@@ -1069,2735 +1313,2916 @@
 spv_result_t ValidateExtInstImport(ValidationState_t& _,
                                    const Instruction* inst) {
   const auto name_id = 1;
+  const std::string name = inst->GetOperandAs<std::string>(name_id);
   if (_.version() <= SPV_SPIRV_VERSION_WORD(1, 5) &&
       !_.HasExtension(kSPV_KHR_non_semantic_info)) {
-    const std::string name = inst->GetOperandAs<std::string>(name_id);
     if (name.find("NonSemantic.") == 0) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "NonSemantic extended instruction sets cannot be declared "
-                "without SPV_KHR_non_semantic_info.";
+             << "NonSemantic extended instruction "
+                "sets cannot be declared "
+                "without SPV_KHR_non_semantic_info. (This can also be fixed "
+                "having SPIR-V 1.6 or later)";
+    }
+  }
+
+  // Validate the version suffix of a NonSemantic.Shader.DebugInfo import.
+  // Accept any version >= kNSDIMinVersion; no upper bound is imposed because
+  // later versions are backward-compatible supersets of earlier ones.
+  const std::string nsdi_prefix = "NonSemantic.Shader.DebugInfo.";
+  if (name.find(nsdi_prefix) == 0) {
+    static const uint32_t kNSDIMinVersion = 100;
+    auto version_string = name.substr(nsdi_prefix.size());
+    if (version_string.empty()) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "NonSemantic.Shader.DebugInfo import does not encode the "
+                "version correctly";
+    }
+    char* end_ptr;
+    uint32_t ver = static_cast<uint32_t>(
+        std::strtoul(version_string.c_str(), &end_ptr, 10));
+    if (end_ptr && *end_ptr != '\0') {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "NonSemantic.Shader.DebugInfo import does not encode the "
+                "version correctly";
+    }
+    if (ver < kNSDIMinVersion) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "NonSemantic.Shader.DebugInfo import version " << ver
+             << " is below the minimum supported version " << kNSDIMinVersion;
     }
   }
 
   return SPV_SUCCESS;
 }
 
-spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
+spv_result_t ValidateExtInstGlslStd450(ValidationState_t& _,
+                                       const Instruction* inst) {
   const uint32_t result_type = inst->type_id();
   const uint32_t num_operands = static_cast<uint32_t>(inst->operands().size());
 
-  const uint32_t ext_inst_set = inst->word(3);
   const uint32_t ext_inst_index = inst->word(4);
-  const spv_ext_inst_type_t ext_inst_type =
-      spv_ext_inst_type_t(inst->ext_inst_type());
+  const GLSLstd450 ext_inst_key = GLSLstd450(ext_inst_index);
+  switch (ext_inst_key) {
+    case GLSLstd450Round:
+    case GLSLstd450RoundEven:
+    case GLSLstd450FAbs:
+    case GLSLstd450Trunc:
+    case GLSLstd450FSign:
+    case GLSLstd450Floor:
+    case GLSLstd450Ceil:
+    case GLSLstd450Fract:
+    case GLSLstd450Sqrt:
+    case GLSLstd450InverseSqrt:
+    case GLSLstd450FMin:
+    case GLSLstd450FMax:
+    case GLSLstd450FClamp:
+    case GLSLstd450FMix:
+    case GLSLstd450Step:
+    case GLSLstd450SmoothStep:
+    case GLSLstd450Fma:
+    case GLSLstd450Normalize:
+    case GLSLstd450FaceForward:
+    case GLSLstd450Reflect:
+    case GLSLstd450NMin:
+    case GLSLstd450NMax:
+    case GLSLstd450NClamp: {
+      bool supportsCoopVec =
+          (ext_inst_key == GLSLstd450FMin || ext_inst_key == GLSLstd450FMax ||
+           ext_inst_key == GLSLstd450FClamp || ext_inst_key == GLSLstd450NMin ||
+           ext_inst_key == GLSLstd450NMax || ext_inst_key == GLSLstd450NClamp ||
+           ext_inst_key == GLSLstd450Step || ext_inst_key == GLSLstd450Fma);
 
-  auto ext_inst_name = [&_, ext_inst_set, ext_inst_type, ext_inst_index]() {
-    spv_ext_inst_desc desc = nullptr;
-    if (_.grammar().lookupExtInst(ext_inst_type, ext_inst_index, &desc) !=
-            SPV_SUCCESS ||
-        !desc) {
-      return std::string("Unknown ExtInst");
+      if (!_.IsFloatScalarOrVectorType(result_type) &&
+          !(supportsCoopVec && _.IsFloatCooperativeVectorNVType(result_type))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar or vector type";
+      }
+
+      for (uint32_t operand_index = 4; operand_index < num_operands;
+           ++operand_index) {
+        const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
+        if (result_type != operand_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected types of all operands to be equal to Result "
+                    "Type";
+        }
+      }
+      break;
     }
 
-    auto* import_inst = _.FindDef(ext_inst_set);
-    assert(import_inst);
+    case GLSLstd450SAbs:
+    case GLSLstd450SSign:
+    case GLSLstd450UMin:
+    case GLSLstd450SMin:
+    case GLSLstd450UMax:
+    case GLSLstd450SMax:
+    case GLSLstd450UClamp:
+    case GLSLstd450SClamp:
+    case GLSLstd450FindILsb:
+    case GLSLstd450FindUMsb:
+    case GLSLstd450FindSMsb: {
+      bool supportsCoopVec =
+          (ext_inst_key == GLSLstd450UMin || ext_inst_key == GLSLstd450UMax ||
+           ext_inst_key == GLSLstd450UClamp || ext_inst_key == GLSLstd450SMin ||
+           ext_inst_key == GLSLstd450SMax || ext_inst_key == GLSLstd450SClamp);
 
-    std::ostringstream ss;
-    ss << import_inst->GetOperandAs<std::string>(1);
-    ss << " ";
-    ss << desc->name;
-
-    return ss.str();
-  };
-
-  if (ext_inst_type == SPV_EXT_INST_TYPE_GLSL_STD_450) {
-    const GLSLstd450 ext_inst_key = GLSLstd450(ext_inst_index);
-    switch (ext_inst_key) {
-      case GLSLstd450Round:
-      case GLSLstd450RoundEven:
-      case GLSLstd450FAbs:
-      case GLSLstd450Trunc:
-      case GLSLstd450FSign:
-      case GLSLstd450Floor:
-      case GLSLstd450Ceil:
-      case GLSLstd450Fract:
-      case GLSLstd450Sqrt:
-      case GLSLstd450InverseSqrt:
-      case GLSLstd450FMin:
-      case GLSLstd450FMax:
-      case GLSLstd450FClamp:
-      case GLSLstd450FMix:
-      case GLSLstd450Step:
-      case GLSLstd450SmoothStep:
-      case GLSLstd450Fma:
-      case GLSLstd450Normalize:
-      case GLSLstd450FaceForward:
-      case GLSLstd450Reflect:
-      case GLSLstd450NMin:
-      case GLSLstd450NMax:
-      case GLSLstd450NClamp: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar or vector type";
-        }
-
-        for (uint32_t operand_index = 4; operand_index < num_operands;
-             ++operand_index) {
-          const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
-          if (result_type != operand_type) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected types of all operands to be equal to Result "
-                      "Type";
-          }
-        }
-        break;
-      }
-
-      case GLSLstd450SAbs:
-      case GLSLstd450SSign:
-      case GLSLstd450UMin:
-      case GLSLstd450SMin:
-      case GLSLstd450UMax:
-      case GLSLstd450SMax:
-      case GLSLstd450UClamp:
-      case GLSLstd450SClamp:
-      case GLSLstd450FindILsb:
-      case GLSLstd450FindUMsb:
-      case GLSLstd450FindSMsb: {
-        if (!_.IsIntScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be an int scalar or vector type";
-        }
-
-        const uint32_t result_type_bit_width = _.GetBitWidth(result_type);
-        const uint32_t result_type_dimension = _.GetDimension(result_type);
-
-        for (uint32_t operand_index = 4; operand_index < num_operands;
-             ++operand_index) {
-          const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
-          if (!operand_type || !_.IsIntScalarOrVectorType(operand_type)) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected all operands to be int scalars or vectors";
-          }
-
-          if (result_type_dimension != _.GetDimension(operand_type)) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected all operands to have the same dimension as "
-                   << "Result Type";
-          }
-
-          if (result_type_bit_width != _.GetBitWidth(operand_type)) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected all operands to have the same bit width as "
-                   << "Result Type";
-          }
-
-          if (ext_inst_key == GLSLstd450FindUMsb ||
-              ext_inst_key == GLSLstd450FindSMsb) {
-            if (result_type_bit_width != 32) {
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << ext_inst_name() << ": "
-                     << "this instruction is currently limited to 32-bit width "
-                     << "components";
-            }
-          }
-        }
-        break;
-      }
-
-      case GLSLstd450Radians:
-      case GLSLstd450Degrees:
-      case GLSLstd450Sin:
-      case GLSLstd450Cos:
-      case GLSLstd450Tan:
-      case GLSLstd450Asin:
-      case GLSLstd450Acos:
-      case GLSLstd450Atan:
-      case GLSLstd450Sinh:
-      case GLSLstd450Cosh:
-      case GLSLstd450Tanh:
-      case GLSLstd450Asinh:
-      case GLSLstd450Acosh:
-      case GLSLstd450Atanh:
-      case GLSLstd450Exp:
-      case GLSLstd450Exp2:
-      case GLSLstd450Log:
-      case GLSLstd450Log2:
-      case GLSLstd450Atan2:
-      case GLSLstd450Pow: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a 16 or 32-bit scalar or "
-                    "vector float type";
-        }
-
-        const uint32_t result_type_bit_width = _.GetBitWidth(result_type);
-        if (result_type_bit_width != 16 && result_type_bit_width != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a 16 or 32-bit scalar or "
-                    "vector float type";
-        }
-
-        for (uint32_t operand_index = 4; operand_index < num_operands;
-             ++operand_index) {
-          const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
-          if (result_type != operand_type) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected types of all operands to be equal to Result "
-                      "Type";
-          }
-        }
-        break;
-      }
-
-      case GLSLstd450Determinant: {
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        uint32_t num_rows = 0;
-        uint32_t num_cols = 0;
-        uint32_t col_type = 0;
-        uint32_t component_type = 0;
-        if (!_.GetMatrixTypeInfo(x_type, &num_rows, &num_cols, &col_type,
-                                 &component_type) ||
-            num_rows != num_cols) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X to be a square matrix";
-        }
-
-        if (result_type != component_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X component type to be equal to "
-                 << "Result Type";
-        }
-        break;
-      }
-
-      case GLSLstd450MatrixInverse: {
-        uint32_t num_rows = 0;
-        uint32_t num_cols = 0;
-        uint32_t col_type = 0;
-        uint32_t component_type = 0;
-        if (!_.GetMatrixTypeInfo(result_type, &num_rows, &num_cols, &col_type,
-                                 &component_type) ||
-            num_rows != num_cols) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a square matrix";
-        }
-
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        if (result_type != x_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X type to be equal to Result Type";
-        }
-        break;
-      }
-
-      case GLSLstd450Modf: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or vector float type";
-        }
-
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t i_type = _.GetOperandTypeId(inst, 5);
-
-        if (x_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X type to be equal to Result Type";
-        }
-
-        spv::StorageClass i_storage_class;
-        uint32_t i_data_type = 0;
-        if (!_.GetPointerTypeInfo(i_type, &i_data_type, &i_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand I to be a pointer";
-        }
-
-        if (i_data_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand I data type to be equal to Result Type";
-        }
-
-        break;
-      }
-
-      case GLSLstd450ModfStruct: {
-        std::vector<uint32_t> result_types;
-        if (!_.GetStructMemberTypes(result_type, &result_types) ||
-            result_types.size() != 2 ||
-            !_.IsFloatScalarOrVectorType(result_types[0]) ||
-            result_types[1] != result_types[0]) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a struct with two identical "
-                 << "scalar or vector float type members";
-        }
-
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        if (x_type != result_types[0]) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X type to be equal to members of "
-                 << "Result Type struct";
-        }
-        break;
-      }
-
-      case GLSLstd450Frexp: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or vector float type";
-        }
-
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t exp_type = _.GetOperandTypeId(inst, 5);
-
-        if (x_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X type to be equal to Result Type";
-        }
-
-        spv::StorageClass exp_storage_class;
-        uint32_t exp_data_type = 0;
-        if (!_.GetPointerTypeInfo(exp_type, &exp_data_type,
-                                  &exp_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Exp to be a pointer";
-        }
-
-        if (!_.IsIntScalarOrVectorType(exp_data_type) ||
-            (!_.HasExtension(kSPV_AMD_gpu_shader_int16) &&
-             _.GetBitWidth(exp_data_type) != 32) ||
-            (_.HasExtension(kSPV_AMD_gpu_shader_int16) &&
-             _.GetBitWidth(exp_data_type) != 16 &&
-             _.GetBitWidth(exp_data_type) != 32)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Exp data type to be a "
-                 << (_.HasExtension(kSPV_AMD_gpu_shader_int16)
-                         ? "16-bit or 32-bit "
-                         : "32-bit ")
-                 << "int scalar or vector type";
-        }
-
-        if (_.GetDimension(result_type) != _.GetDimension(exp_data_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Exp data type to have the same component "
-                 << "number as Result Type";
-        }
-
-        break;
-      }
-
-      case GLSLstd450Ldexp: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or vector float type";
-        }
-
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t exp_type = _.GetOperandTypeId(inst, 5);
-
-        if (x_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X type to be equal to Result Type";
-        }
-
-        if (!_.IsIntScalarOrVectorType(exp_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Exp to be a 32-bit int scalar "
-                 << "or vector type";
-        }
-
-        if (_.GetDimension(result_type) != _.GetDimension(exp_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Exp to have the same component "
-                 << "number as Result Type";
-        }
-
-        break;
-      }
-
-      case GLSLstd450FrexpStruct: {
-        std::vector<uint32_t> result_types;
-        if (!_.GetStructMemberTypes(result_type, &result_types) ||
-            result_types.size() != 2 ||
-            !_.IsFloatScalarOrVectorType(result_types[0]) ||
-            !_.IsIntScalarOrVectorType(result_types[1]) ||
-            (!_.HasExtension(kSPV_AMD_gpu_shader_int16) &&
-             _.GetBitWidth(result_types[1]) != 32) ||
-            (_.HasExtension(kSPV_AMD_gpu_shader_int16) &&
-             _.GetBitWidth(result_types[1]) != 16 &&
-             _.GetBitWidth(result_types[1]) != 32) ||
-            _.GetDimension(result_types[0]) !=
-                _.GetDimension(result_types[1])) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a struct with two members, "
-                 << "first member a float scalar or vector, second member a "
-                 << (_.HasExtension(kSPV_AMD_gpu_shader_int16)
-                         ? "16-bit or 32-bit "
-                         : "32-bit ")
-                 << "int scalar or vector with the same number of "
-                 << "components as the first member";
-        }
-
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        if (x_type != result_types[0]) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X type to be equal to the first member "
-                 << "of Result Type struct";
-        }
-        break;
-      }
-
-      case GLSLstd450PackSnorm4x8:
-      case GLSLstd450PackUnorm4x8: {
-        if (!_.IsIntScalarType(result_type) ||
-            _.GetBitWidth(result_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be 32-bit int scalar type";
-        }
-
-        const uint32_t v_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 4 ||
-            _.GetBitWidth(v_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand V to be a 32-bit float vector of size 4";
-        }
-        break;
-      }
-
-      case GLSLstd450PackSnorm2x16:
-      case GLSLstd450PackUnorm2x16:
-      case GLSLstd450PackHalf2x16: {
-        if (!_.IsIntScalarType(result_type) ||
-            _.GetBitWidth(result_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be 32-bit int scalar type";
-        }
-
-        const uint32_t v_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 2 ||
-            _.GetBitWidth(v_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand V to be a 32-bit float vector of size 2";
-        }
-        break;
-      }
-
-      case GLSLstd450PackDouble2x32: {
-        if (!_.IsFloatScalarType(result_type) ||
-            _.GetBitWidth(result_type) != 64) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be 64-bit float scalar type";
-        }
-
-        const uint32_t v_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsIntVectorType(v_type) || _.GetDimension(v_type) != 2 ||
-            _.GetBitWidth(v_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand V to be a 32-bit int vector of size 2";
-        }
-        break;
-      }
-
-      case GLSLstd450UnpackSnorm4x8:
-      case GLSLstd450UnpackUnorm4x8: {
-        if (!_.IsFloatVectorType(result_type) ||
-            _.GetDimension(result_type) != 4 ||
-            _.GetBitWidth(result_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a 32-bit float vector of size "
-                    "4";
-        }
-
-        const uint32_t v_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsIntScalarType(v_type) || _.GetBitWidth(v_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P to be a 32-bit int scalar";
-        }
-        break;
-      }
-
-      case GLSLstd450UnpackSnorm2x16:
-      case GLSLstd450UnpackUnorm2x16:
-      case GLSLstd450UnpackHalf2x16: {
-        if (!_.IsFloatVectorType(result_type) ||
-            _.GetDimension(result_type) != 2 ||
-            _.GetBitWidth(result_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a 32-bit float vector of size "
-                    "2";
-        }
-
-        const uint32_t v_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsIntScalarType(v_type) || _.GetBitWidth(v_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P to be a 32-bit int scalar";
-        }
-        break;
-      }
-
-      case GLSLstd450UnpackDouble2x32: {
-        if (!_.IsIntVectorType(result_type) ||
-            _.GetDimension(result_type) != 2 ||
-            _.GetBitWidth(result_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a 32-bit int vector of size "
-                    "2";
-        }
-
-        const uint32_t v_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsFloatScalarType(v_type) || _.GetBitWidth(v_type) != 64) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand V to be a 64-bit float scalar";
-        }
-        break;
-      }
-
-      case GLSLstd450Length: {
-        if (!_.IsFloatScalarType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar type";
-        }
-
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsFloatScalarOrVectorType(x_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X to be of float scalar or vector type";
-        }
-
-        if (result_type != _.GetComponentType(x_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X component type to be equal to Result "
-                    "Type";
-        }
-        break;
-      }
-
-      case GLSLstd450Distance: {
-        if (!_.IsFloatScalarType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar type";
-        }
-
-        const uint32_t p0_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsFloatScalarOrVectorType(p0_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P0 to be of float scalar or vector type";
-        }
-
-        if (result_type != _.GetComponentType(p0_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P0 component type to be equal to "
-                 << "Result Type";
-        }
-
-        const uint32_t p1_type = _.GetOperandTypeId(inst, 5);
-        if (!_.IsFloatScalarOrVectorType(p1_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P1 to be of float scalar or vector type";
-        }
-
-        if (result_type != _.GetComponentType(p1_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P1 component type to be equal to "
-                 << "Result Type";
-        }
-
-        if (_.GetDimension(p0_type) != _.GetDimension(p1_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operands P0 and P1 to have the same number of "
-                 << "components";
-        }
-        break;
-      }
-
-      case GLSLstd450Cross: {
-        if (!_.IsFloatVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float vector type";
-        }
-
-        if (_.GetDimension(result_type) != 3) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to have 3 components";
-        }
-
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t y_type = _.GetOperandTypeId(inst, 5);
-
-        if (x_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X type to be equal to Result Type";
-        }
-
-        if (y_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Y type to be equal to Result Type";
-        }
-        break;
-      }
-
-      case GLSLstd450Refract: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar or vector type";
-        }
-
-        const uint32_t i_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t n_type = _.GetOperandTypeId(inst, 5);
-        const uint32_t eta_type = _.GetOperandTypeId(inst, 6);
-
-        if (result_type != i_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand I to be of type equal to Result Type";
-        }
-
-        if (result_type != n_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand N to be of type equal to Result Type";
-        }
-
-        if (!_.IsFloatScalarType(eta_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Eta to be a float scalar";
-        }
-        break;
-      }
-
-      case GLSLstd450InterpolateAtCentroid:
-      case GLSLstd450InterpolateAtSample:
-      case GLSLstd450InterpolateAtOffset: {
-        if (!_.HasCapability(spv::Capability::InterpolationFunction)) {
-          return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
-                 << ext_inst_name()
-                 << " requires capability InterpolationFunction";
-        }
-
-        if (!_.IsFloatScalarOrVectorType(result_type) ||
-            _.GetBitWidth(result_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a 32-bit float scalar "
-                 << "or vector type";
-        }
-
-        // If HLSL legalization and first operand is an OpLoad, use load
-        // pointer as the interpolant lvalue. Else use interpolate first
-        // operand.
-        uint32_t interp_id = inst->GetOperandAs<uint32_t>(4);
-        auto* interp_inst = _.FindDef(interp_id);
-        uint32_t interpolant_type = (_.options()->before_hlsl_legalization &&
-                                     interp_inst->opcode() == spv::Op::OpLoad)
-                                        ? _.GetOperandTypeId(interp_inst, 2)
-                                        : _.GetOperandTypeId(inst, 4);
-
-        spv::StorageClass interpolant_storage_class;
-        uint32_t interpolant_data_type = 0;
-        if (!_.GetPointerTypeInfo(interpolant_type, &interpolant_data_type,
-                                  &interpolant_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Interpolant to be a pointer";
-        }
-
-        if (result_type != interpolant_data_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Interpolant data type to be equal to Result Type";
-        }
-
-        if (interpolant_storage_class != spv::StorageClass::Input) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Interpolant storage class to be Input";
-        }
-
-        if (ext_inst_key == GLSLstd450InterpolateAtSample) {
-          const uint32_t sample_type = _.GetOperandTypeId(inst, 5);
-          if (!_.IsIntScalarType(sample_type) ||
-              _.GetBitWidth(sample_type) != 32) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected Sample to be 32-bit integer";
-          }
-        }
-
-        if (ext_inst_key == GLSLstd450InterpolateAtOffset) {
-          const uint32_t offset_type = _.GetOperandTypeId(inst, 5);
-          if (!_.IsFloatVectorType(offset_type) ||
-              _.GetDimension(offset_type) != 2 ||
-              _.GetBitWidth(offset_type) != 32) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected Offset to be a vector of 2 32-bit floats";
-          }
-        }
-
-        _.function(inst->function()->id())
-            ->RegisterExecutionModelLimitation(
-                spv::ExecutionModel::Fragment,
-                ext_inst_name() +
-                    std::string(" requires Fragment execution model"));
-        break;
-      }
-
-      case GLSLstd450IMix: {
+      if (!_.IsIntScalarOrVectorType(result_type) &&
+          !(supportsCoopVec && _.IsIntCooperativeVectorNVType(result_type))) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Extended instruction GLSLstd450IMix is not supported";
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be an int scalar or vector type";
       }
 
-      case GLSLstd450Bad: {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Encountered extended instruction GLSLstd450Bad";
-      }
+      const uint32_t result_type_bit_width = _.GetBitWidth(result_type);
+      const uint32_t result_type_dimension = _.GetDimension(result_type);
 
-      case GLSLstd450Count: {
-        assert(0);
-        break;
+      for (uint32_t operand_index = 4; operand_index < num_operands;
+           ++operand_index) {
+        const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
+        if (!operand_type ||
+            (!_.IsIntScalarOrVectorType(operand_type) &&
+             !(supportsCoopVec &&
+               _.IsIntCooperativeVectorNVType(operand_type)))) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected all operands to be int scalars or vectors";
+        }
+
+        if (result_type_dimension != _.GetDimension(operand_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected all operands to have the same dimension as "
+                 << "Result Type";
+        }
+
+        if (result_type_bit_width != _.GetBitWidth(operand_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected all operands to have the same bit width as "
+                 << "Result Type";
+        }
+
+        if (ext_inst_key == GLSLstd450FindUMsb ||
+            ext_inst_key == GLSLstd450FindSMsb) {
+          if (result_type_bit_width != 32) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << GetExtInstName(_, inst) << ": "
+                   << "this instruction is currently limited to 32-bit width "
+                   << "components";
+          }
+        }
       }
+      break;
     }
-  } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_STD) {
-    const OpenCLLIB::Entrypoints ext_inst_key =
-        OpenCLLIB::Entrypoints(ext_inst_index);
-    switch (ext_inst_key) {
-      case OpenCLLIB::Acos:
-      case OpenCLLIB::Acosh:
-      case OpenCLLIB::Acospi:
-      case OpenCLLIB::Asin:
-      case OpenCLLIB::Asinh:
-      case OpenCLLIB::Asinpi:
-      case OpenCLLIB::Atan:
-      case OpenCLLIB::Atan2:
-      case OpenCLLIB::Atanh:
-      case OpenCLLIB::Atanpi:
-      case OpenCLLIB::Atan2pi:
-      case OpenCLLIB::Cbrt:
-      case OpenCLLIB::Ceil:
-      case OpenCLLIB::Copysign:
-      case OpenCLLIB::Cos:
-      case OpenCLLIB::Cosh:
-      case OpenCLLIB::Cospi:
-      case OpenCLLIB::Erfc:
-      case OpenCLLIB::Erf:
-      case OpenCLLIB::Exp:
-      case OpenCLLIB::Exp2:
-      case OpenCLLIB::Exp10:
-      case OpenCLLIB::Expm1:
-      case OpenCLLIB::Fabs:
-      case OpenCLLIB::Fdim:
-      case OpenCLLIB::Floor:
-      case OpenCLLIB::Fma:
-      case OpenCLLIB::Fmax:
-      case OpenCLLIB::Fmin:
-      case OpenCLLIB::Fmod:
-      case OpenCLLIB::Hypot:
-      case OpenCLLIB::Lgamma:
-      case OpenCLLIB::Log:
-      case OpenCLLIB::Log2:
-      case OpenCLLIB::Log10:
-      case OpenCLLIB::Log1p:
-      case OpenCLLIB::Logb:
-      case OpenCLLIB::Mad:
-      case OpenCLLIB::Maxmag:
-      case OpenCLLIB::Minmag:
-      case OpenCLLIB::Nextafter:
-      case OpenCLLIB::Pow:
-      case OpenCLLIB::Powr:
-      case OpenCLLIB::Remainder:
-      case OpenCLLIB::Rint:
-      case OpenCLLIB::Round:
-      case OpenCLLIB::Rsqrt:
-      case OpenCLLIB::Sin:
-      case OpenCLLIB::Sinh:
-      case OpenCLLIB::Sinpi:
-      case OpenCLLIB::Sqrt:
-      case OpenCLLIB::Tan:
-      case OpenCLLIB::Tanh:
-      case OpenCLLIB::Tanpi:
-      case OpenCLLIB::Tgamma:
-      case OpenCLLIB::Trunc:
-      case OpenCLLIB::Half_cos:
-      case OpenCLLIB::Half_divide:
-      case OpenCLLIB::Half_exp:
-      case OpenCLLIB::Half_exp2:
-      case OpenCLLIB::Half_exp10:
-      case OpenCLLIB::Half_log:
-      case OpenCLLIB::Half_log2:
-      case OpenCLLIB::Half_log10:
-      case OpenCLLIB::Half_powr:
-      case OpenCLLIB::Half_recip:
-      case OpenCLLIB::Half_rsqrt:
-      case OpenCLLIB::Half_sin:
-      case OpenCLLIB::Half_sqrt:
-      case OpenCLLIB::Half_tan:
-      case OpenCLLIB::Native_cos:
-      case OpenCLLIB::Native_divide:
-      case OpenCLLIB::Native_exp:
-      case OpenCLLIB::Native_exp2:
-      case OpenCLLIB::Native_exp10:
-      case OpenCLLIB::Native_log:
-      case OpenCLLIB::Native_log2:
-      case OpenCLLIB::Native_log10:
-      case OpenCLLIB::Native_powr:
-      case OpenCLLIB::Native_recip:
-      case OpenCLLIB::Native_rsqrt:
-      case OpenCLLIB::Native_sin:
-      case OpenCLLIB::Native_sqrt:
-      case OpenCLLIB::Native_tan:
-      case OpenCLLIB::FClamp:
-      case OpenCLLIB::Degrees:
-      case OpenCLLIB::FMax_common:
-      case OpenCLLIB::FMin_common:
-      case OpenCLLIB::Mix:
-      case OpenCLLIB::Radians:
-      case OpenCLLIB::Step:
-      case OpenCLLIB::Smoothstep:
-      case OpenCLLIB::Sign: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar or vector type";
-        }
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
+    case GLSLstd450Radians:
+    case GLSLstd450Degrees:
+    case GLSLstd450Sin:
+    case GLSLstd450Cos:
+    case GLSLstd450Tan:
+    case GLSLstd450Asin:
+    case GLSLstd450Acos:
+    case GLSLstd450Atan:
+    case GLSLstd450Sinh:
+    case GLSLstd450Cosh:
+    case GLSLstd450Tanh:
+    case GLSLstd450Asinh:
+    case GLSLstd450Acosh:
+    case GLSLstd450Atanh:
+    case GLSLstd450Exp:
+    case GLSLstd450Exp2:
+    case GLSLstd450Log:
+    case GLSLstd450Log2:
+    case GLSLstd450Atan2:
+    case GLSLstd450Pow: {
+      bool supportsCoopVec =
+          (ext_inst_key == GLSLstd450Atan || ext_inst_key == GLSLstd450Tanh ||
+           ext_inst_key == GLSLstd450Exp || ext_inst_key == GLSLstd450Log);
 
-        for (uint32_t operand_index = 4; operand_index < num_operands;
-             ++operand_index) {
-          const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
-          if (result_type != operand_type) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected types of all operands to be equal to Result "
-                      "Type";
-          }
-        }
-        break;
+      if (!_.IsFloatScalarOrVectorType(result_type) &&
+          !(supportsCoopVec && _.IsFloatCooperativeVectorNVType(result_type))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a 16 or 32-bit scalar or "
+                  "vector float type";
       }
 
-      case OpenCLLIB::Fract:
-      case OpenCLLIB::Modf:
-      case OpenCLLIB::Sincos: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar or vector type";
-        }
+      const uint32_t result_type_bit_width = _.GetBitWidth(result_type);
+      if (result_type_bit_width != 16 && result_type_bit_width != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a 16 or 32-bit scalar or "
+                  "vector float type";
+      }
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
+      for (uint32_t operand_index = 4; operand_index < num_operands;
+           ++operand_index) {
+        const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
+        if (result_type != operand_type) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
-
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        if (result_type != x_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected type of operand X to be equal to Result Type";
-        }
-
-        const uint32_t p_type = _.GetOperandTypeId(inst, 5);
-        spv::StorageClass p_storage_class;
-        uint32_t p_data_type = 0;
-        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected the last operand to be a pointer";
-        }
-
-        if (p_storage_class != spv::StorageClass::Generic &&
-            p_storage_class != spv::StorageClass::CrossWorkgroup &&
-            p_storage_class != spv::StorageClass::Workgroup &&
-            p_storage_class != spv::StorageClass::Function) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected storage class of the pointer to be Generic, "
-                    "CrossWorkgroup, Workgroup or Function";
-        }
-
-        if (!_.ContainsUntypedPointer(p_type) && result_type != p_data_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected data type of the pointer to be equal to Result "
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected types of all operands to be equal to Result "
                     "Type";
         }
-        break;
+      }
+      break;
+    }
+
+    case GLSLstd450Determinant: {
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      uint32_t num_rows = 0;
+      uint32_t num_cols = 0;
+      uint32_t col_type = 0;
+      uint32_t component_type = 0;
+      if (!_.GetMatrixTypeInfo(x_type, &num_rows, &num_cols, &col_type,
+                               &component_type) ||
+          num_rows != num_cols) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X to be a square matrix";
       }
 
-      case OpenCLLIB::Frexp:
-      case OpenCLLIB::Lgamma_r:
-      case OpenCLLIB::Remquo: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar or vector type";
-        }
+      if (result_type != component_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X component type to be equal to "
+               << "Result Type";
+      }
+      break;
+    }
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
-
-        uint32_t operand_index = 4;
-        const uint32_t x_type = _.GetOperandTypeId(inst, operand_index++);
-        if (result_type != x_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected type of operand X to be equal to Result Type";
-        }
-
-        if (ext_inst_key == OpenCLLIB::Remquo) {
-          const uint32_t y_type = _.GetOperandTypeId(inst, operand_index++);
-          if (result_type != y_type) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected type of operand Y to be equal to Result Type";
-          }
-        }
-
-        const uint32_t p_type = _.GetOperandTypeId(inst, operand_index++);
-        spv::StorageClass p_storage_class;
-        uint32_t p_data_type = 0;
-        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected the last operand to be a pointer";
-        }
-
-        if (p_storage_class != spv::StorageClass::Generic &&
-            p_storage_class != spv::StorageClass::CrossWorkgroup &&
-            p_storage_class != spv::StorageClass::Workgroup &&
-            p_storage_class != spv::StorageClass::Function) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected storage class of the pointer to be Generic, "
-                    "CrossWorkgroup, Workgroup or Function";
-        }
-
-        if ((!_.IsIntScalarOrVectorType(p_data_type) ||
-             _.GetBitWidth(p_data_type) != 32) &&
-            !_.ContainsUntypedPointer(p_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected data type of the pointer to be a 32-bit int "
-                    "scalar or vector type";
-        }
-
-        if (!_.ContainsUntypedPointer(p_type) &&
-            _.GetDimension(p_data_type) != num_components) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected data type of the pointer to have the same number "
-                    "of components as Result Type";
-        }
-        break;
+    case GLSLstd450MatrixInverse: {
+      uint32_t num_rows = 0;
+      uint32_t num_cols = 0;
+      uint32_t col_type = 0;
+      uint32_t component_type = 0;
+      if (!_.GetMatrixTypeInfo(result_type, &num_rows, &num_cols, &col_type,
+                               &component_type) ||
+          num_rows != num_cols) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a square matrix";
       }
 
-      case OpenCLLIB::Ilogb: {
-        if (!_.IsIntScalarOrVectorType(result_type) ||
-            _.GetBitWidth(result_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a 32-bit int scalar or vector "
-                    "type";
-        }
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      if (result_type != x_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X type to be equal to Result Type";
+      }
+      break;
+    }
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
-
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsFloatScalarOrVectorType(x_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X to be a float scalar or vector";
-        }
-
-        if (_.GetDimension(x_type) != num_components) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X to have the same number of components "
-                    "as Result Type";
-        }
-        break;
+    case GLSLstd450Modf: {
+      if (!_.IsFloatScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or vector float type";
       }
 
-      case OpenCLLIB::Ldexp:
-      case OpenCLLIB::Pown:
-      case OpenCLLIB::Rootn: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar or vector type";
-        }
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t i_type = _.GetOperandTypeId(inst, 5);
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
-
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        if (result_type != x_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected type of operand X to be equal to Result Type";
-        }
-
-        const uint32_t exp_type = _.GetOperandTypeId(inst, 5);
-        if (!_.IsIntScalarOrVectorType(exp_type) ||
-            _.GetBitWidth(exp_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected the exponent to be a 32-bit int scalar or vector";
-        }
-
-        if (_.GetDimension(exp_type) != num_components) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected the exponent to have the same number of "
-                    "components as Result Type";
-        }
-        break;
+      if (x_type != result_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X type to be equal to Result Type";
       }
 
-      case OpenCLLIB::Nan: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar or vector type";
-        }
-
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
-
-        const uint32_t nancode_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsIntScalarOrVectorType(nancode_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Nancode to be an int scalar or vector type";
-        }
-
-        if (_.GetDimension(nancode_type) != num_components) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Nancode to have the same number of components as "
-                    "Result Type";
-        }
-
-        if (_.GetBitWidth(result_type) != _.GetBitWidth(nancode_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Nancode to have the same bit width as Result "
-                    "Type";
-        }
-        break;
+      spv::StorageClass i_storage_class;
+      uint32_t i_data_type = 0;
+      if (!_.GetPointerTypeInfo(i_type, &i_data_type, &i_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand I to be a pointer";
       }
 
-      case OpenCLLIB::SAbs:
-      case OpenCLLIB::SAbs_diff:
-      case OpenCLLIB::SAdd_sat:
-      case OpenCLLIB::UAdd_sat:
-      case OpenCLLIB::SHadd:
-      case OpenCLLIB::UHadd:
-      case OpenCLLIB::SRhadd:
-      case OpenCLLIB::URhadd:
-      case OpenCLLIB::SClamp:
-      case OpenCLLIB::UClamp:
-      case OpenCLLIB::Clz:
-      case OpenCLLIB::Ctz:
-      case OpenCLLIB::SMad_hi:
-      case OpenCLLIB::UMad_sat:
-      case OpenCLLIB::SMad_sat:
-      case OpenCLLIB::SMax:
-      case OpenCLLIB::UMax:
-      case OpenCLLIB::SMin:
-      case OpenCLLIB::UMin:
-      case OpenCLLIB::SMul_hi:
-      case OpenCLLIB::Rotate:
-      case OpenCLLIB::SSub_sat:
-      case OpenCLLIB::USub_sat:
-      case OpenCLLIB::Popcount:
-      case OpenCLLIB::UAbs:
-      case OpenCLLIB::UAbs_diff:
-      case OpenCLLIB::UMul_hi:
-      case OpenCLLIB::UMad_hi: {
-        if (!_.IsIntScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be an int scalar or vector type";
-        }
-
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
-
-        for (uint32_t operand_index = 4; operand_index < num_operands;
-             ++operand_index) {
-          const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
-          if (result_type != operand_type) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected types of all operands to be equal to Result "
-                      "Type";
-          }
-        }
-        break;
+      if (i_data_type != result_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand I data type to be equal to Result Type";
       }
 
-      case OpenCLLIB::U_Upsample:
-      case OpenCLLIB::S_Upsample: {
-        if (!_.IsIntScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be an int scalar or vector "
-                    "type";
-        }
+      break;
+    }
 
-        const uint32_t result_num_components = _.GetDimension(result_type);
-        if (result_num_components > 4 && result_num_components != 8 &&
-            result_num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
-
-        const uint32_t result_bit_width = _.GetBitWidth(result_type);
-        if (result_bit_width != 16 && result_bit_width != 32 &&
-            result_bit_width != 64) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected bit width of Result Type components to be 16, 32 "
-                    "or 64";
-        }
-
-        const uint32_t hi_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t lo_type = _.GetOperandTypeId(inst, 5);
-
-        if (hi_type != lo_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Hi and Lo operands to have the same type";
-        }
-
-        if (result_num_components != _.GetDimension(hi_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Hi and Lo operands to have the same number of "
-                    "components as Result Type";
-        }
-
-        if (result_bit_width != 2 * _.GetBitWidth(hi_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected bit width of components of Hi and Lo operands to "
-                    "be half of the bit width of components of Result Type";
-        }
-        break;
+    case GLSLstd450ModfStruct: {
+      std::vector<uint32_t> result_types;
+      if (!_.GetStructMemberTypes(result_type, &result_types) ||
+          result_types.size() != 2 ||
+          !_.IsFloatScalarOrVectorType(result_types[0]) ||
+          result_types[1] != result_types[0]) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a struct with two identical "
+               << "scalar or vector float type members";
       }
 
-      case OpenCLLIB::SMad24:
-      case OpenCLLIB::UMad24:
-      case OpenCLLIB::SMul24:
-      case OpenCLLIB::UMul24: {
-        if (!_.IsIntScalarOrVectorType(result_type) ||
-            _.GetBitWidth(result_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a 32-bit int scalar or vector "
-                    "type";
-        }
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      if (x_type != result_types[0]) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X type to be equal to members of "
+               << "Result Type struct";
+      }
+      break;
+    }
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
-
-        for (uint32_t operand_index = 4; operand_index < num_operands;
-             ++operand_index) {
-          const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
-          if (result_type != operand_type) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected types of all operands to be equal to Result "
-                      "Type";
-          }
-        }
-        break;
+    case GLSLstd450Frexp: {
+      if (!_.IsFloatScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or vector float type";
       }
 
-      case OpenCLLIB::Cross: {
-        if (!_.IsFloatVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float vector type";
-        }
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t exp_type = _.GetOperandTypeId(inst, 5);
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components != 3 && num_components != 4) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to have 3 or 4 components";
-        }
-
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t y_type = _.GetOperandTypeId(inst, 5);
-
-        if (x_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X type to be equal to Result Type";
-        }
-
-        if (y_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Y type to be equal to Result Type";
-        }
-        break;
+      if (x_type != result_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X type to be equal to Result Type";
       }
 
-      case OpenCLLIB::Distance:
-      case OpenCLLIB::Fast_distance: {
-        if (!_.IsFloatScalarType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar type";
-        }
-
-        const uint32_t p0_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsFloatScalarOrVectorType(p0_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P0 to be of float scalar or vector type";
-        }
-
-        const uint32_t num_components = _.GetDimension(p0_type);
-        if (num_components > 4) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P0 to have no more than 4 components";
-        }
-
-        if (result_type != _.GetComponentType(p0_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P0 component type to be equal to "
-                 << "Result Type";
-        }
-
-        const uint32_t p1_type = _.GetOperandTypeId(inst, 5);
-        if (p0_type != p1_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operands P0 and P1 to be of the same type";
-        }
-        break;
+      spv::StorageClass exp_storage_class;
+      uint32_t exp_data_type = 0;
+      if (!_.GetPointerTypeInfo(exp_type, &exp_data_type, &exp_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Exp to be a pointer";
       }
 
-      case OpenCLLIB::Length:
-      case OpenCLLIB::Fast_length: {
-        if (!_.IsFloatScalarType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar type";
-        }
-
-        const uint32_t p_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsFloatScalarOrVectorType(p_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P to be a float scalar or vector";
-        }
-
-        const uint32_t num_components = _.GetDimension(p_type);
-        if (num_components > 4) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P to have no more than 4 components";
-        }
-
-        if (result_type != _.GetComponentType(p_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P component type to be equal to Result "
-                    "Type";
-        }
-        break;
+      if (!_.IsIntScalarOrVectorType(exp_data_type) ||
+          (!_.HasExtension(kSPV_AMD_gpu_shader_int16) &&
+           _.GetBitWidth(exp_data_type) != 32) ||
+          (_.HasExtension(kSPV_AMD_gpu_shader_int16) &&
+           _.GetBitWidth(exp_data_type) != 16 &&
+           _.GetBitWidth(exp_data_type) != 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Exp data type to be a "
+               << (_.HasExtension(kSPV_AMD_gpu_shader_int16)
+                       ? "16-bit or 32-bit "
+                       : "32-bit ")
+               << "int scalar or vector type";
       }
 
-      case OpenCLLIB::Normalize:
-      case OpenCLLIB::Fast_normalize: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar or vector type";
-        }
-
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to have no more than 4 components";
-        }
-
-        const uint32_t p_type = _.GetOperandTypeId(inst, 4);
-        if (p_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P type to be equal to Result Type";
-        }
-        break;
+      if (_.GetDimension(result_type) != _.GetDimension(exp_data_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Exp data type to have the same component "
+               << "number as Result Type";
       }
 
-      case OpenCLLIB::Bitselect: {
-        if (!_.IsFloatScalarOrVectorType(result_type) &&
-            !_.IsIntScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be an int or float scalar or "
-                    "vector type";
-        }
+      break;
+    }
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
-
-        for (uint32_t operand_index = 4; operand_index < num_operands;
-             ++operand_index) {
-          const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
-          if (result_type != operand_type) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected types of all operands to be equal to Result "
-                      "Type";
-          }
-        }
-        break;
+    case GLSLstd450Ldexp: {
+      if (!_.IsFloatScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or vector float type";
       }
 
-      case OpenCLLIB::Select: {
-        if (!_.IsFloatScalarOrVectorType(result_type) &&
-            !_.IsIntScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be an int or float scalar or "
-                    "vector type";
-        }
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t exp_type = _.GetOperandTypeId(inst, 5);
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
-
-        const uint32_t a_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t b_type = _.GetOperandTypeId(inst, 5);
-        const uint32_t c_type = _.GetOperandTypeId(inst, 6);
-
-        if (result_type != a_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand A type to be equal to Result Type";
-        }
-
-        if (result_type != b_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand B type to be equal to Result Type";
-        }
-
-        if (!_.IsIntScalarOrVectorType(c_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand C to be an int scalar or vector";
-        }
-
-        if (num_components != _.GetDimension(c_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand C to have the same number of components "
-                    "as Result Type";
-        }
-
-        if (_.GetBitWidth(result_type) != _.GetBitWidth(c_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand C to have the same bit width as Result "
-                    "Type";
-        }
-        break;
+      if (x_type != result_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X type to be equal to Result Type";
       }
 
-      case OpenCLLIB::Vloadn: {
-        if (!_.IsFloatVectorType(result_type) &&
-            !_.IsIntVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be an int or float vector type";
-        }
-
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to have 2, 3, 4, 8 or 16 components";
-        }
-
-        const uint32_t offset_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t p_type = _.GetOperandTypeId(inst, 5);
-
-        const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
-        if (!size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name()
-                 << " can only be used with physical addressing models";
-        }
-
-        if (!_.IsIntScalarType(offset_type) ||
-            _.GetBitWidth(offset_type) != size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Offset to be of type size_t ("
-                 << size_t_bit_width
-                 << "-bit integer for the addressing model used in the module)";
-        }
-
-        spv::StorageClass p_storage_class;
-        uint32_t p_data_type = 0;
-        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P to be a pointer";
-        }
-
-        if (p_storage_class != spv::StorageClass::UniformConstant &&
-            p_storage_class != spv::StorageClass::Generic &&
-            p_storage_class != spv::StorageClass::CrossWorkgroup &&
-            p_storage_class != spv::StorageClass::Workgroup &&
-            p_storage_class != spv::StorageClass::Function) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P storage class to be UniformConstant, "
-                    "Generic, CrossWorkgroup, Workgroup or Function";
-        }
-
-        if (_.GetComponentType(result_type) != p_data_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P data type to be equal to component "
-                    "type of Result Type";
-        }
-
-        const uint32_t n_value = inst->word(7);
-        if (num_components != n_value) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected literal N to be equal to the number of "
-                    "components of Result Type";
-        }
-        break;
+      if (!_.IsIntScalarOrVectorType(exp_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Exp to be a 32-bit int scalar "
+               << "or vector type";
       }
 
-      case OpenCLLIB::Vstoren: {
-        if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": expected Result Type to be void";
-        }
+      if (_.GetDimension(result_type) != _.GetDimension(exp_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Exp to have the same component "
+               << "number as Result Type";
+      }
 
-        const uint32_t data_type = _.GetOperandTypeId(inst, 4);
+      break;
+    }
+
+    case GLSLstd450FrexpStruct: {
+      std::vector<uint32_t> result_types;
+      if (!_.GetStructMemberTypes(result_type, &result_types) ||
+          result_types.size() != 2 ||
+          !_.IsFloatScalarOrVectorType(result_types[0]) ||
+          !_.IsIntScalarOrVectorType(result_types[1]) ||
+          (!_.HasExtension(kSPV_AMD_gpu_shader_int16) &&
+           _.GetBitWidth(result_types[1]) != 32) ||
+          (_.HasExtension(kSPV_AMD_gpu_shader_int16) &&
+           _.GetBitWidth(result_types[1]) != 16 &&
+           _.GetBitWidth(result_types[1]) != 32) ||
+          _.GetDimension(result_types[0]) != _.GetDimension(result_types[1])) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a struct with two members, "
+               << "first member a float scalar or vector, second member a "
+               << (_.HasExtension(kSPV_AMD_gpu_shader_int16)
+                       ? "16-bit or 32-bit "
+                       : "32-bit ")
+               << "int scalar or vector with the same number of "
+               << "components as the first member";
+      }
+
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      if (x_type != result_types[0]) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X type to be equal to the first member "
+               << "of Result Type struct";
+      }
+      break;
+    }
+
+    case GLSLstd450PackSnorm4x8:
+    case GLSLstd450PackUnorm4x8: {
+      if (!_.IsIntScalarType(result_type, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be 32-bit int scalar type";
+      }
+
+      const uint32_t v_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 4 ||
+          _.GetBitWidth(v_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand V to be a 32-bit float vector of size 4";
+      }
+      break;
+    }
+
+    case GLSLstd450PackSnorm2x16:
+    case GLSLstd450PackUnorm2x16:
+    case GLSLstd450PackHalf2x16: {
+      if (!_.IsIntScalarType(result_type, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be 32-bit int scalar type";
+      }
+
+      const uint32_t v_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 2 ||
+          _.GetBitWidth(v_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand V to be a 32-bit float vector of size 2";
+      }
+      break;
+    }
+
+    case GLSLstd450PackDouble2x32: {
+      if (!_.IsFloatScalarType(result_type, 64)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be 64-bit float scalar type";
+      }
+
+      const uint32_t v_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsIntVectorType(v_type) || _.GetDimension(v_type) != 2 ||
+          _.GetBitWidth(v_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand V to be a 32-bit int vector of size 2";
+      }
+      break;
+    }
+
+    case GLSLstd450UnpackSnorm4x8:
+    case GLSLstd450UnpackUnorm4x8: {
+      if (!_.IsFloatVectorType(result_type) ||
+          _.GetDimension(result_type) != 4 ||
+          _.GetBitWidth(result_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a 32-bit float vector of size "
+                  "4";
+      }
+
+      const uint32_t v_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsIntScalarType(v_type, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P to be a 32-bit int scalar";
+      }
+      break;
+    }
+
+    case GLSLstd450UnpackSnorm2x16:
+    case GLSLstd450UnpackUnorm2x16:
+    case GLSLstd450UnpackHalf2x16: {
+      if (!_.IsFloatVectorType(result_type) ||
+          _.GetDimension(result_type) != 2 ||
+          _.GetBitWidth(result_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a 32-bit float vector of size "
+                  "2";
+      }
+
+      const uint32_t v_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsIntScalarType(v_type, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P to be a 32-bit int scalar";
+      }
+      break;
+    }
+
+    case GLSLstd450UnpackDouble2x32: {
+      if (!_.IsIntVectorType(result_type) || _.GetDimension(result_type) != 2 ||
+          _.GetBitWidth(result_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a 32-bit int vector of size "
+                  "2";
+      }
+
+      const uint32_t v_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsFloatScalarType(v_type, 64)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand V to be a 64-bit float scalar";
+      }
+      break;
+    }
+
+    case GLSLstd450Length: {
+      if (!_.IsFloatScalarType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar type";
+      }
+
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsFloatScalarOrVectorType(x_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X to be of float scalar or vector type";
+      }
+
+      if (result_type != _.GetComponentType(x_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X component type to be equal to Result "
+                  "Type";
+      }
+      break;
+    }
+
+    case GLSLstd450Distance: {
+      if (!_.IsFloatScalarType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar type";
+      }
+
+      const uint32_t p0_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsFloatScalarOrVectorType(p0_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P0 to be of float scalar or vector type";
+      }
+
+      if (result_type != _.GetComponentType(p0_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P0 component type to be equal to "
+               << "Result Type";
+      }
+
+      const uint32_t p1_type = _.GetOperandTypeId(inst, 5);
+      if (!_.IsFloatScalarOrVectorType(p1_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P1 to be of float scalar or vector type";
+      }
+
+      if (result_type != _.GetComponentType(p1_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P1 component type to be equal to "
+               << "Result Type";
+      }
+
+      if (_.GetDimension(p0_type) != _.GetDimension(p1_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operands P0 and P1 to have the same number of "
+               << "components";
+      }
+      break;
+    }
+
+    case GLSLstd450Cross: {
+      if (!_.IsFloatVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float vector type";
+      }
+
+      if (_.GetDimension(result_type) != 3) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to have 3 components";
+      }
+
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t y_type = _.GetOperandTypeId(inst, 5);
+
+      if (x_type != result_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X type to be equal to Result Type";
+      }
+
+      if (y_type != result_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Y type to be equal to Result Type";
+      }
+      break;
+    }
+
+    case GLSLstd450Refract: {
+      if (!_.IsFloatScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar or vector type";
+      }
+
+      const uint32_t i_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t n_type = _.GetOperandTypeId(inst, 5);
+      const uint32_t eta_type = _.GetOperandTypeId(inst, 6);
+
+      if (result_type != i_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand I to be of type equal to Result Type";
+      }
+
+      if (result_type != n_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand N to be of type equal to Result Type";
+      }
+
+      if (!_.IsFloatScalarType(eta_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Eta to be a float scalar";
+      }
+      break;
+    }
+
+    case GLSLstd450InterpolateAtCentroid:
+    case GLSLstd450InterpolateAtSample:
+    case GLSLstd450InterpolateAtOffset: {
+      if (!_.HasCapability(spv::Capability::InterpolationFunction)) {
+        return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
+               << GetExtInstName(_, inst)
+               << " requires capability InterpolationFunction";
+      }
+
+      if (!_.IsFloatScalarOrVectorType(result_type) ||
+          _.GetBitWidth(result_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a 32-bit float scalar "
+               << "or vector type";
+      }
+
+      // If HLSL legalization and first operand is an OpLoad, use load
+      // pointer as the interpolant lvalue. Else use interpolate first
+      // operand.
+      uint32_t interp_id = inst->GetOperandAs<uint32_t>(4);
+      auto* interp_inst = _.FindDef(interp_id);
+      uint32_t interpolant_type = (_.options()->before_hlsl_legalization &&
+                                   interp_inst->opcode() == spv::Op::OpLoad)
+                                      ? _.GetOperandTypeId(interp_inst, 2)
+                                      : _.GetOperandTypeId(inst, 4);
+
+      spv::StorageClass interpolant_storage_class;
+      uint32_t interpolant_data_type = 0;
+      if (!_.GetPointerTypeInfo(interpolant_type, &interpolant_data_type,
+                                &interpolant_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Interpolant to be a pointer";
+      }
+
+      if (result_type != interpolant_data_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Interpolant data type to be equal to Result Type";
+      }
+
+      if (interpolant_storage_class != spv::StorageClass::Input) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Interpolant storage class to be Input";
+      }
+
+      if (ext_inst_key == GLSLstd450InterpolateAtSample) {
+        const uint32_t sample_type = _.GetOperandTypeId(inst, 5);
+        if (!_.IsIntScalarType(sample_type, 32)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected Sample to be 32-bit integer";
+        }
+      }
+
+      if (ext_inst_key == GLSLstd450InterpolateAtOffset) {
         const uint32_t offset_type = _.GetOperandTypeId(inst, 5);
-        const uint32_t p_type = _.GetOperandTypeId(inst, 6);
-
-        if (!_.IsFloatVectorType(data_type) && !_.IsIntVectorType(data_type)) {
+        if (!_.IsFloatVectorType(offset_type) ||
+            _.GetDimension(offset_type) != 2 ||
+            _.GetBitWidth(offset_type) != 32) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Data to be an int or float vector";
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected Offset to be a vector of 2 32-bit floats";
+        }
+      }
+
+      _.function(inst->function()->id())
+          ->RegisterExecutionModelLimitation(
+              spv::ExecutionModel::Fragment,
+              GetExtInstName(_, inst) +
+                  std::string(" requires Fragment execution model"));
+      break;
+    }
+
+    case GLSLstd450IMix: {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Extended instruction GLSLstd450IMix is not supported";
+    }
+
+    case GLSLstd450Bad: {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Encountered extended instruction GLSLstd450Bad";
+    }
+
+    case GLSLstd450Count: {
+      assert(0);
+      break;
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateExtInstOpenClStd(ValidationState_t& _,
+                                      const Instruction* inst) {
+  const uint32_t result_type = inst->type_id();
+  const uint32_t num_operands = static_cast<uint32_t>(inst->operands().size());
+  const uint32_t ext_inst_index = inst->word(4);
+  const OpenCLLIB::Entrypoints ext_inst_key =
+      OpenCLLIB::Entrypoints(ext_inst_index);
+  switch (ext_inst_key) {
+    case OpenCLLIB::Acos:
+    case OpenCLLIB::Acosh:
+    case OpenCLLIB::Acospi:
+    case OpenCLLIB::Asin:
+    case OpenCLLIB::Asinh:
+    case OpenCLLIB::Asinpi:
+    case OpenCLLIB::Atan:
+    case OpenCLLIB::Atan2:
+    case OpenCLLIB::Atanh:
+    case OpenCLLIB::Atanpi:
+    case OpenCLLIB::Atan2pi:
+    case OpenCLLIB::Cbrt:
+    case OpenCLLIB::Ceil:
+    case OpenCLLIB::Copysign:
+    case OpenCLLIB::Cos:
+    case OpenCLLIB::Cosh:
+    case OpenCLLIB::Cospi:
+    case OpenCLLIB::Erfc:
+    case OpenCLLIB::Erf:
+    case OpenCLLIB::Exp:
+    case OpenCLLIB::Exp2:
+    case OpenCLLIB::Exp10:
+    case OpenCLLIB::Expm1:
+    case OpenCLLIB::Fabs:
+    case OpenCLLIB::Fdim:
+    case OpenCLLIB::Floor:
+    case OpenCLLIB::Fma:
+    case OpenCLLIB::Fmax:
+    case OpenCLLIB::Fmin:
+    case OpenCLLIB::Fmod:
+    case OpenCLLIB::Hypot:
+    case OpenCLLIB::Lgamma:
+    case OpenCLLIB::Log:
+    case OpenCLLIB::Log2:
+    case OpenCLLIB::Log10:
+    case OpenCLLIB::Log1p:
+    case OpenCLLIB::Logb:
+    case OpenCLLIB::Mad:
+    case OpenCLLIB::Maxmag:
+    case OpenCLLIB::Minmag:
+    case OpenCLLIB::Nextafter:
+    case OpenCLLIB::Pow:
+    case OpenCLLIB::Powr:
+    case OpenCLLIB::Remainder:
+    case OpenCLLIB::Rint:
+    case OpenCLLIB::Round:
+    case OpenCLLIB::Rsqrt:
+    case OpenCLLIB::Sin:
+    case OpenCLLIB::Sinh:
+    case OpenCLLIB::Sinpi:
+    case OpenCLLIB::Sqrt:
+    case OpenCLLIB::Tan:
+    case OpenCLLIB::Tanh:
+    case OpenCLLIB::Tanpi:
+    case OpenCLLIB::Tgamma:
+    case OpenCLLIB::Trunc:
+    case OpenCLLIB::Half_cos:
+    case OpenCLLIB::Half_divide:
+    case OpenCLLIB::Half_exp:
+    case OpenCLLIB::Half_exp2:
+    case OpenCLLIB::Half_exp10:
+    case OpenCLLIB::Half_log:
+    case OpenCLLIB::Half_log2:
+    case OpenCLLIB::Half_log10:
+    case OpenCLLIB::Half_powr:
+    case OpenCLLIB::Half_recip:
+    case OpenCLLIB::Half_rsqrt:
+    case OpenCLLIB::Half_sin:
+    case OpenCLLIB::Half_sqrt:
+    case OpenCLLIB::Half_tan:
+    case OpenCLLIB::Native_cos:
+    case OpenCLLIB::Native_divide:
+    case OpenCLLIB::Native_exp:
+    case OpenCLLIB::Native_exp2:
+    case OpenCLLIB::Native_exp10:
+    case OpenCLLIB::Native_log:
+    case OpenCLLIB::Native_log2:
+    case OpenCLLIB::Native_log10:
+    case OpenCLLIB::Native_powr:
+    case OpenCLLIB::Native_recip:
+    case OpenCLLIB::Native_rsqrt:
+    case OpenCLLIB::Native_sin:
+    case OpenCLLIB::Native_sqrt:
+    case OpenCLLIB::Native_tan:
+    case OpenCLLIB::FClamp:
+    case OpenCLLIB::Degrees:
+    case OpenCLLIB::FMax_common:
+    case OpenCLLIB::FMin_common:
+    case OpenCLLIB::Mix:
+    case OpenCLLIB::Radians:
+    case OpenCLLIB::Step:
+    case OpenCLLIB::Smoothstep:
+    case OpenCLLIB::Sign: {
+      if (!_.IsFloatScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar or vector type";
+      }
+
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
+
+      for (uint32_t operand_index = 4; operand_index < num_operands;
+           ++operand_index) {
+        const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
+        if (result_type != operand_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected types of all operands to be equal to Result "
+                    "Type";
+        }
+      }
+      break;
+    }
+
+    case OpenCLLIB::Fract:
+    case OpenCLLIB::Modf:
+    case OpenCLLIB::Sincos: {
+      if (!_.IsFloatScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar or vector type";
+      }
+
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
+
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      if (result_type != x_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected type of operand X to be equal to Result Type";
+      }
+
+      const uint32_t p_type = _.GetOperandTypeId(inst, 5);
+      spv::StorageClass p_storage_class;
+      uint32_t p_data_type = 0;
+      if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected the last operand to be a pointer";
+      }
+
+      if (p_storage_class != spv::StorageClass::Generic &&
+          p_storage_class != spv::StorageClass::CrossWorkgroup &&
+          p_storage_class != spv::StorageClass::Workgroup &&
+          p_storage_class != spv::StorageClass::Function) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected storage class of the pointer to be Generic, "
+                  "CrossWorkgroup, Workgroup or Function";
+      }
+
+      if (!_.ContainsUntypedPointer(p_type) && result_type != p_data_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected data type of the pointer to be equal to Result "
+                  "Type";
+      }
+      break;
+    }
+
+    case OpenCLLIB::Frexp:
+    case OpenCLLIB::Lgamma_r:
+    case OpenCLLIB::Remquo: {
+      if (!_.IsFloatScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar or vector type";
+      }
+
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
+
+      uint32_t operand_index = 4;
+      const uint32_t x_type = _.GetOperandTypeId(inst, operand_index++);
+      if (result_type != x_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected type of operand X to be equal to Result Type";
+      }
+
+      if (ext_inst_key == OpenCLLIB::Remquo) {
+        const uint32_t y_type = _.GetOperandTypeId(inst, operand_index++);
+        if (result_type != y_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected type of operand Y to be equal to Result Type";
+        }
+      }
+
+      const uint32_t p_type = _.GetOperandTypeId(inst, operand_index++);
+      spv::StorageClass p_storage_class;
+      uint32_t p_data_type = 0;
+      if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected the last operand to be a pointer";
+      }
+
+      if (p_storage_class != spv::StorageClass::Generic &&
+          p_storage_class != spv::StorageClass::CrossWorkgroup &&
+          p_storage_class != spv::StorageClass::Workgroup &&
+          p_storage_class != spv::StorageClass::Function) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected storage class of the pointer to be Generic, "
+                  "CrossWorkgroup, Workgroup or Function";
+      }
+
+      if ((!_.IsIntScalarOrVectorType(p_data_type) ||
+           _.GetBitWidth(p_data_type) != 32) &&
+          !_.ContainsUntypedPointer(p_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected data type of the pointer to be a 32-bit int "
+                  "scalar or vector type";
+      }
+
+      if (!_.ContainsUntypedPointer(p_type) &&
+          _.GetDimension(p_data_type) != num_components) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected data type of the pointer to have the same number "
+                  "of components as Result Type";
+      }
+      break;
+    }
+
+    case OpenCLLIB::Ilogb: {
+      if (!_.IsIntScalarOrVectorType(result_type) ||
+          _.GetBitWidth(result_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a 32-bit int scalar or vector "
+                  "type";
+      }
+
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
+
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsFloatScalarOrVectorType(x_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X to be a float scalar or vector";
+      }
+
+      if (_.GetDimension(x_type) != num_components) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X to have the same number of components "
+                  "as Result Type";
+      }
+      break;
+    }
+
+    case OpenCLLIB::Ldexp:
+    case OpenCLLIB::Pown:
+    case OpenCLLIB::Rootn: {
+      if (!_.IsFloatScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar or vector type";
+      }
+
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
+
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      if (result_type != x_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected type of operand X to be equal to Result Type";
+      }
+
+      const uint32_t exp_type = _.GetOperandTypeId(inst, 5);
+      if (!_.IsIntScalarOrVectorType(exp_type) ||
+          _.GetBitWidth(exp_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected the exponent to be a 32-bit int scalar or vector";
+      }
+
+      if (_.GetDimension(exp_type) != num_components) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected the exponent to have the same number of "
+                  "components as Result Type";
+      }
+      break;
+    }
+
+    case OpenCLLIB::Nan: {
+      if (!_.IsFloatScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar or vector type";
+      }
+
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
+
+      const uint32_t nancode_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsIntScalarOrVectorType(nancode_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Nancode to be an int scalar or vector type";
+      }
+
+      if (_.GetDimension(nancode_type) != num_components) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Nancode to have the same number of components as "
+                  "Result Type";
+      }
+
+      if (_.GetBitWidth(result_type) != _.GetBitWidth(nancode_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Nancode to have the same bit width as Result "
+                  "Type";
+      }
+      break;
+    }
+
+    case OpenCLLIB::SAbs:
+    case OpenCLLIB::SAbs_diff:
+    case OpenCLLIB::SAdd_sat:
+    case OpenCLLIB::UAdd_sat:
+    case OpenCLLIB::SHadd:
+    case OpenCLLIB::UHadd:
+    case OpenCLLIB::SRhadd:
+    case OpenCLLIB::URhadd:
+    case OpenCLLIB::SClamp:
+    case OpenCLLIB::UClamp:
+    case OpenCLLIB::Clz:
+    case OpenCLLIB::Ctz:
+    case OpenCLLIB::SMad_hi:
+    case OpenCLLIB::UMad_sat:
+    case OpenCLLIB::SMad_sat:
+    case OpenCLLIB::SMax:
+    case OpenCLLIB::UMax:
+    case OpenCLLIB::SMin:
+    case OpenCLLIB::UMin:
+    case OpenCLLIB::SMul_hi:
+    case OpenCLLIB::Rotate:
+    case OpenCLLIB::SSub_sat:
+    case OpenCLLIB::USub_sat:
+    case OpenCLLIB::Popcount:
+    case OpenCLLIB::UAbs:
+    case OpenCLLIB::UAbs_diff:
+    case OpenCLLIB::UMul_hi:
+    case OpenCLLIB::UMad_hi: {
+      if (!_.IsIntScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be an int scalar or vector type";
+      }
+
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
+
+      for (uint32_t operand_index = 4; operand_index < num_operands;
+           ++operand_index) {
+        const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
+        if (result_type != operand_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected types of all operands to be equal to Result "
+                    "Type";
+        }
+      }
+      break;
+    }
+
+    case OpenCLLIB::U_Upsample:
+    case OpenCLLIB::S_Upsample: {
+      if (!_.IsIntScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be an int scalar or vector "
+                  "type";
+      }
+
+      const uint32_t result_num_components = _.GetDimension(result_type);
+      if (result_num_components > 4 && result_num_components != 8 &&
+          result_num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
+
+      const uint32_t result_bit_width = _.GetBitWidth(result_type);
+      if (result_bit_width != 16 && result_bit_width != 32 &&
+          result_bit_width != 64) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected bit width of Result Type components to be 16, 32 "
+                  "or 64";
+      }
+
+      const uint32_t hi_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t lo_type = _.GetOperandTypeId(inst, 5);
+
+      if (hi_type != lo_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Hi and Lo operands to have the same type";
+      }
+
+      if (result_num_components != _.GetDimension(hi_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Hi and Lo operands to have the same number of "
+                  "components as Result Type";
+      }
+
+      if (result_bit_width != 2 * _.GetBitWidth(hi_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected bit width of components of Hi and Lo operands to "
+                  "be half of the bit width of components of Result Type";
+      }
+      break;
+    }
+
+    case OpenCLLIB::SMad24:
+    case OpenCLLIB::UMad24:
+    case OpenCLLIB::SMul24:
+    case OpenCLLIB::UMul24: {
+      if (!_.IsIntScalarOrVectorType(result_type) ||
+          _.GetBitWidth(result_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a 32-bit int scalar or vector "
+                  "type";
+      }
+
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
+
+      for (uint32_t operand_index = 4; operand_index < num_operands;
+           ++operand_index) {
+        const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
+        if (result_type != operand_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected types of all operands to be equal to Result "
+                    "Type";
+        }
+      }
+      break;
+    }
+
+    case OpenCLLIB::Cross: {
+      if (!_.IsFloatVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float vector type";
+      }
+
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components != 3 && num_components != 4) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to have 3 or 4 components";
+      }
+
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t y_type = _.GetOperandTypeId(inst, 5);
+
+      if (x_type != result_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X type to be equal to Result Type";
+      }
+
+      if (y_type != result_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Y type to be equal to Result Type";
+      }
+      break;
+    }
+
+    case OpenCLLIB::Distance:
+    case OpenCLLIB::Fast_distance: {
+      if (!_.IsFloatScalarType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar type";
+      }
+
+      const uint32_t p0_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsFloatScalarOrVectorType(p0_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P0 to be of float scalar or vector type";
+      }
+
+      const uint32_t num_components = _.GetDimension(p0_type);
+      if (num_components > 4) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P0 to have no more than 4 components";
+      }
+
+      if (result_type != _.GetComponentType(p0_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P0 component type to be equal to "
+               << "Result Type";
+      }
+
+      const uint32_t p1_type = _.GetOperandTypeId(inst, 5);
+      if (p0_type != p1_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operands P0 and P1 to be of the same type";
+      }
+      break;
+    }
+
+    case OpenCLLIB::Length:
+    case OpenCLLIB::Fast_length: {
+      if (!_.IsFloatScalarType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar type";
+      }
+
+      const uint32_t p_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsFloatScalarOrVectorType(p_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P to be a float scalar or vector";
+      }
+
+      const uint32_t num_components = _.GetDimension(p_type);
+      if (num_components > 4) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P to have no more than 4 components";
+      }
+
+      if (result_type != _.GetComponentType(p_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P component type to be equal to Result "
+                  "Type";
+      }
+      break;
+    }
+
+    case OpenCLLIB::Normalize:
+    case OpenCLLIB::Fast_normalize: {
+      if (!_.IsFloatScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar or vector type";
+      }
+
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to have no more than 4 components";
+      }
+
+      const uint32_t p_type = _.GetOperandTypeId(inst, 4);
+      if (p_type != result_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P type to be equal to Result Type";
+      }
+      break;
+    }
+
+    case OpenCLLIB::Bitselect: {
+      if (!_.IsFloatScalarOrVectorType(result_type) &&
+          !_.IsIntScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be an int or float scalar or "
+                  "vector type";
+      }
+
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
+
+      for (uint32_t operand_index = 4; operand_index < num_operands;
+           ++operand_index) {
+        const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
+        if (result_type != operand_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected types of all operands to be equal to Result "
+                    "Type";
+        }
+      }
+      break;
+    }
+
+    case OpenCLLIB::Select: {
+      if (!_.IsFloatScalarOrVectorType(result_type) &&
+          !_.IsIntScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be an int or float scalar or "
+                  "vector type";
+      }
+
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
+
+      const uint32_t a_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t b_type = _.GetOperandTypeId(inst, 5);
+      const uint32_t c_type = _.GetOperandTypeId(inst, 6);
+
+      if (result_type != a_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand A type to be equal to Result Type";
+      }
+
+      if (result_type != b_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand B type to be equal to Result Type";
+      }
+
+      if (!_.IsIntScalarOrVectorType(c_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand C to be an int scalar or vector";
+      }
+
+      if (num_components != _.GetDimension(c_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand C to have the same number of components "
+                  "as Result Type";
+      }
+
+      if (_.GetBitWidth(result_type) != _.GetBitWidth(c_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand C to have the same bit width as Result "
+                  "Type";
+      }
+      break;
+    }
+
+    case OpenCLLIB::Vloadn: {
+      if (!_.IsFloatVectorType(result_type) &&
+          !_.IsIntVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be an int or float vector type";
+      }
+
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to have 2, 3, 4, 8 or 16 components";
+      }
+
+      const uint32_t offset_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t p_type = _.GetOperandTypeId(inst, 5);
+
+      const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
+      if (!size_t_bit_width) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst)
+               << " can only be used with physical addressing models";
+      }
+
+      if (!_.IsIntScalarType(offset_type, size_t_bit_width)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Offset to be of type size_t ("
+               << size_t_bit_width
+               << "-bit integer for the addressing model used in the module)";
+      }
+
+      spv::StorageClass p_storage_class;
+      uint32_t p_data_type = 0;
+      if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P to be a pointer";
+      }
+
+      if (p_storage_class != spv::StorageClass::UniformConstant &&
+          p_storage_class != spv::StorageClass::Generic &&
+          p_storage_class != spv::StorageClass::CrossWorkgroup &&
+          p_storage_class != spv::StorageClass::Workgroup &&
+          p_storage_class != spv::StorageClass::Function) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P storage class to be UniformConstant, "
+                  "Generic, CrossWorkgroup, Workgroup or Function";
+      }
+
+      if (_.GetComponentType(result_type) != p_data_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P data type to be equal to component "
+                  "type of Result Type";
+      }
+
+      const uint32_t n_value = inst->word(7);
+      if (num_components != n_value) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected literal N to be equal to the number of "
+                  "components of Result Type";
+      }
+      break;
+    }
+
+    case OpenCLLIB::Vstoren: {
+      if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst)
+               << ": expected Result Type to be void";
+      }
+
+      const uint32_t data_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t offset_type = _.GetOperandTypeId(inst, 5);
+      const uint32_t p_type = _.GetOperandTypeId(inst, 6);
+
+      if (!_.IsFloatVectorType(data_type) && !_.IsIntVectorType(data_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Data to be an int or float vector";
+      }
+
+      const uint32_t num_components = _.GetDimension(data_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Data to have 2, 3, 4, 8 or 16 components";
+      }
+
+      const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
+      if (!size_t_bit_width) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst)
+               << " can only be used with physical addressing models";
+      }
+
+      if (!_.IsIntScalarType(offset_type, size_t_bit_width)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Offset to be of type size_t ("
+               << size_t_bit_width
+               << "-bit integer for the addressing model used in the module)";
+      }
+
+      spv::StorageClass p_storage_class;
+      uint32_t p_data_type = 0;
+      if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P to be a pointer";
+      }
+
+      if (p_storage_class != spv::StorageClass::Generic &&
+          p_storage_class != spv::StorageClass::CrossWorkgroup &&
+          p_storage_class != spv::StorageClass::Workgroup &&
+          p_storage_class != spv::StorageClass::Function) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P storage class to be Generic, "
+                  "CrossWorkgroup, Workgroup or Function";
+      }
+
+      if (_.GetComponentType(data_type) != p_data_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P data type to be equal to the type of "
+                  "operand Data components";
+      }
+      break;
+    }
+
+    case OpenCLLIB::Vload_half: {
+      if (!_.IsFloatScalarType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar type";
+      }
+
+      const uint32_t offset_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t p_type = _.GetOperandTypeId(inst, 5);
+
+      const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
+      if (!size_t_bit_width) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst)
+               << " can only be used with physical addressing models";
+      }
+
+      if (!_.IsIntScalarType(offset_type, size_t_bit_width)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Offset to be of type size_t ("
+               << size_t_bit_width
+               << "-bit integer for the addressing model used in the module)";
+      }
+
+      spv::StorageClass p_storage_class;
+      uint32_t p_data_type = 0;
+      if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P to be a pointer";
+      }
+
+      if (p_storage_class != spv::StorageClass::UniformConstant &&
+          p_storage_class != spv::StorageClass::Generic &&
+          p_storage_class != spv::StorageClass::CrossWorkgroup &&
+          p_storage_class != spv::StorageClass::Workgroup &&
+          p_storage_class != spv::StorageClass::Function) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P storage class to be UniformConstant, "
+                  "Generic, CrossWorkgroup, Workgroup or Function";
+      }
+
+      if ((!_.IsFloatScalarType(p_data_type, 16)) &&
+          !_.ContainsUntypedPointer(p_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P data type to be 16-bit float scalar";
+      }
+      break;
+    }
+
+    case OpenCLLIB::Vload_halfn:
+    case OpenCLLIB::Vloada_halfn: {
+      if (!_.IsFloatVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float vector type";
+      }
+
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to have 2, 3, 4, 8 or 16 components";
+      }
+
+      const uint32_t offset_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t p_type = _.GetOperandTypeId(inst, 5);
+
+      const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
+      if (!size_t_bit_width) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst)
+               << " can only be used with physical addressing models";
+      }
+
+      if (!_.IsIntScalarType(offset_type, size_t_bit_width)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Offset to be of type size_t ("
+               << size_t_bit_width
+               << "-bit integer for the addressing model used in the module)";
+      }
+
+      spv::StorageClass p_storage_class;
+      uint32_t p_data_type = 0;
+      if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P to be a pointer";
+      }
+
+      if (p_storage_class != spv::StorageClass::UniformConstant &&
+          p_storage_class != spv::StorageClass::Generic &&
+          p_storage_class != spv::StorageClass::CrossWorkgroup &&
+          p_storage_class != spv::StorageClass::Workgroup &&
+          p_storage_class != spv::StorageClass::Function) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P storage class to be UniformConstant, "
+                  "Generic, CrossWorkgroup, Workgroup or Function";
+      }
+
+      if ((!_.IsFloatScalarType(p_data_type, 16)) &&
+          !_.ContainsUntypedPointer(p_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P data type to be 16-bit float scalar";
+      }
+
+      const uint32_t n_value = inst->word(7);
+      if (num_components != n_value) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected literal N to be equal to the number of "
+                  "components of Result Type";
+      }
+      break;
+    }
+
+    case OpenCLLIB::Vstore_half:
+    case OpenCLLIB::Vstore_half_r:
+    case OpenCLLIB::Vstore_halfn:
+    case OpenCLLIB::Vstore_halfn_r:
+    case OpenCLLIB::Vstorea_halfn:
+    case OpenCLLIB::Vstorea_halfn_r: {
+      if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst)
+               << ": expected Result Type to be void";
+      }
+
+      const uint32_t data_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t offset_type = _.GetOperandTypeId(inst, 5);
+      const uint32_t p_type = _.GetOperandTypeId(inst, 6);
+      const uint32_t data_type_bit_width = _.GetBitWidth(data_type);
+
+      if (ext_inst_key == OpenCLLIB::Vstore_half ||
+          ext_inst_key == OpenCLLIB::Vstore_half_r) {
+        if (!_.IsFloatScalarType(data_type) ||
+            (data_type_bit_width != 32 && data_type_bit_width != 64)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected Data to be a 32 or 64-bit float scalar";
+        }
+      } else {
+        if (!_.IsFloatVectorType(data_type) ||
+            (data_type_bit_width != 32 && data_type_bit_width != 64)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected Data to be a 32 or 64-bit float vector";
         }
 
         const uint32_t num_components = _.GetDimension(data_type);
         if (num_components > 4 && num_components != 8 && num_components != 16) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
+                 << GetExtInstName(_, inst) << ": "
                  << "expected Data to have 2, 3, 4, 8 or 16 components";
         }
-
-        const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
-        if (!size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name()
-                 << " can only be used with physical addressing models";
-        }
-
-        if (!_.IsIntScalarType(offset_type) ||
-            _.GetBitWidth(offset_type) != size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Offset to be of type size_t ("
-                 << size_t_bit_width
-                 << "-bit integer for the addressing model used in the module)";
-        }
-
-        spv::StorageClass p_storage_class;
-        uint32_t p_data_type = 0;
-        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P to be a pointer";
-        }
-
-        if (p_storage_class != spv::StorageClass::Generic &&
-            p_storage_class != spv::StorageClass::CrossWorkgroup &&
-            p_storage_class != spv::StorageClass::Workgroup &&
-            p_storage_class != spv::StorageClass::Function) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P storage class to be Generic, "
-                    "CrossWorkgroup, Workgroup or Function";
-        }
-
-        if (_.GetComponentType(data_type) != p_data_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P data type to be equal to the type of "
-                    "operand Data components";
-        }
-        break;
       }
 
-      case OpenCLLIB::Vload_half: {
-        if (!_.IsFloatScalarType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar type";
-        }
-
-        const uint32_t offset_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t p_type = _.GetOperandTypeId(inst, 5);
-
-        const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
-        if (!size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name()
-                 << " can only be used with physical addressing models";
-        }
-
-        if (!_.IsIntScalarType(offset_type) ||
-            _.GetBitWidth(offset_type) != size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Offset to be of type size_t ("
-                 << size_t_bit_width
-                 << "-bit integer for the addressing model used in the module)";
-        }
-
-        spv::StorageClass p_storage_class;
-        uint32_t p_data_type = 0;
-        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P to be a pointer";
-        }
-
-        if (p_storage_class != spv::StorageClass::UniformConstant &&
-            p_storage_class != spv::StorageClass::Generic &&
-            p_storage_class != spv::StorageClass::CrossWorkgroup &&
-            p_storage_class != spv::StorageClass::Workgroup &&
-            p_storage_class != spv::StorageClass::Function) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P storage class to be UniformConstant, "
-                    "Generic, CrossWorkgroup, Workgroup or Function";
-        }
-
-        if ((!_.IsFloatScalarType(p_data_type) ||
-             _.GetBitWidth(p_data_type) != 16) &&
-            !_.ContainsUntypedPointer(p_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P data type to be 16-bit float scalar";
-        }
-        break;
+      const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
+      if (!size_t_bit_width) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst)
+               << " can only be used with physical addressing models";
       }
 
-      case OpenCLLIB::Vload_halfn:
-      case OpenCLLIB::Vloada_halfn: {
-        if (!_.IsFloatVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float vector type";
-        }
-
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to have 2, 3, 4, 8 or 16 components";
-        }
-
-        const uint32_t offset_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t p_type = _.GetOperandTypeId(inst, 5);
-
-        const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
-        if (!size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name()
-                 << " can only be used with physical addressing models";
-        }
-
-        if (!_.IsIntScalarType(offset_type) ||
-            _.GetBitWidth(offset_type) != size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Offset to be of type size_t ("
-                 << size_t_bit_width
-                 << "-bit integer for the addressing model used in the module)";
-        }
-
-        spv::StorageClass p_storage_class;
-        uint32_t p_data_type = 0;
-        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P to be a pointer";
-        }
-
-        if (p_storage_class != spv::StorageClass::UniformConstant &&
-            p_storage_class != spv::StorageClass::Generic &&
-            p_storage_class != spv::StorageClass::CrossWorkgroup &&
-            p_storage_class != spv::StorageClass::Workgroup &&
-            p_storage_class != spv::StorageClass::Function) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P storage class to be UniformConstant, "
-                    "Generic, CrossWorkgroup, Workgroup or Function";
-        }
-
-        if ((!_.IsFloatScalarType(p_data_type) ||
-             _.GetBitWidth(p_data_type) != 16) &&
-            !_.ContainsUntypedPointer(p_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P data type to be 16-bit float scalar";
-        }
-
-        const uint32_t n_value = inst->word(7);
-        if (num_components != n_value) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected literal N to be equal to the number of "
-                    "components of Result Type";
-        }
-        break;
+      if (!_.IsIntScalarType(offset_type, size_t_bit_width)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Offset to be of type size_t ("
+               << size_t_bit_width
+               << "-bit integer for the addressing model used in the module)";
       }
 
-      case OpenCLLIB::Vstore_half:
-      case OpenCLLIB::Vstore_half_r:
-      case OpenCLLIB::Vstore_halfn:
-      case OpenCLLIB::Vstore_halfn_r:
-      case OpenCLLIB::Vstorea_halfn:
-      case OpenCLLIB::Vstorea_halfn_r: {
-        if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": expected Result Type to be void";
-        }
-
-        const uint32_t data_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t offset_type = _.GetOperandTypeId(inst, 5);
-        const uint32_t p_type = _.GetOperandTypeId(inst, 6);
-        const uint32_t data_type_bit_width = _.GetBitWidth(data_type);
-
-        if (ext_inst_key == OpenCLLIB::Vstore_half ||
-            ext_inst_key == OpenCLLIB::Vstore_half_r) {
-          if (!_.IsFloatScalarType(data_type) ||
-              (data_type_bit_width != 32 && data_type_bit_width != 64)) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected Data to be a 32 or 64-bit float scalar";
-          }
-        } else {
-          if (!_.IsFloatVectorType(data_type) ||
-              (data_type_bit_width != 32 && data_type_bit_width != 64)) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected Data to be a 32 or 64-bit float vector";
-          }
-
-          const uint32_t num_components = _.GetDimension(data_type);
-          if (num_components > 4 && num_components != 8 &&
-              num_components != 16) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected Data to have 2, 3, 4, 8 or 16 components";
-          }
-        }
-
-        const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
-        if (!size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name()
-                 << " can only be used with physical addressing models";
-        }
-
-        if (!_.IsIntScalarType(offset_type) ||
-            _.GetBitWidth(offset_type) != size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Offset to be of type size_t ("
-                 << size_t_bit_width
-                 << "-bit integer for the addressing model used in the module)";
-        }
-
-        spv::StorageClass p_storage_class;
-        uint32_t p_data_type = 0;
-        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P to be a pointer";
-        }
-
-        if (p_storage_class != spv::StorageClass::Generic &&
-            p_storage_class != spv::StorageClass::CrossWorkgroup &&
-            p_storage_class != spv::StorageClass::Workgroup &&
-            p_storage_class != spv::StorageClass::Function) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P storage class to be Generic, "
-                    "CrossWorkgroup, Workgroup or Function";
-        }
-
-        if ((!_.IsFloatScalarType(p_data_type) ||
-             _.GetBitWidth(p_data_type) != 16) &&
-            !_.ContainsUntypedPointer(p_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P data type to be 16-bit float scalar";
-        }
-
-        // Rounding mode enum is checked by assembler.
-        break;
+      spv::StorageClass p_storage_class;
+      uint32_t p_data_type = 0;
+      if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P to be a pointer";
       }
 
-      case OpenCLLIB::Shuffle:
-      case OpenCLLIB::Shuffle2: {
-        if (!_.IsFloatVectorType(result_type) &&
-            !_.IsIntVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be an int or float vector type";
-        }
-
-        const uint32_t result_num_components = _.GetDimension(result_type);
-        if (result_num_components != 2 && result_num_components != 4 &&
-            result_num_components != 8 && result_num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to have 2, 4, 8 or 16 components";
-        }
-
-        uint32_t operand_index = 4;
-        const uint32_t x_type = _.GetOperandTypeId(inst, operand_index++);
-
-        if (ext_inst_key == OpenCLLIB::Shuffle2) {
-          const uint32_t y_type = _.GetOperandTypeId(inst, operand_index++);
-          if (x_type != y_type) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected operands X and Y to be of the same type";
-          }
-        }
-
-        const uint32_t shuffle_mask_type =
-            _.GetOperandTypeId(inst, operand_index++);
-
-        if (!_.IsFloatVectorType(x_type) && !_.IsIntVectorType(x_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X to be an int or float vector";
-        }
-
-        const uint32_t x_num_components = _.GetDimension(x_type);
-        if (x_num_components != 2 && x_num_components != 4 &&
-            x_num_components != 8 && x_num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X to have 2, 4, 8 or 16 components";
-        }
-
-        const uint32_t result_component_type = _.GetComponentType(result_type);
-
-        if (result_component_type != _.GetComponentType(x_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X and Result Type to have equal "
-                    "component types";
-        }
-
-        if (!_.IsIntVectorType(shuffle_mask_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Shuffle Mask to be an int vector";
-        }
-
-        if (result_num_components != _.GetDimension(shuffle_mask_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Shuffle Mask to have the same number of "
-                    "components as Result Type";
-        }
-
-        if (_.GetBitWidth(result_component_type) !=
-            _.GetBitWidth(shuffle_mask_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Shuffle Mask components to have the same "
-                    "bit width as Result Type components";
-        }
-        break;
+      if (p_storage_class != spv::StorageClass::Generic &&
+          p_storage_class != spv::StorageClass::CrossWorkgroup &&
+          p_storage_class != spv::StorageClass::Workgroup &&
+          p_storage_class != spv::StorageClass::Function) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P storage class to be Generic, "
+                  "CrossWorkgroup, Workgroup or Function";
       }
 
-      case OpenCLLIB::Printf: {
-        if (!_.IsIntScalarType(result_type) ||
-            _.GetBitWidth(result_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a 32-bit int type";
-        }
-
-        const uint32_t format_type = _.GetOperandTypeId(inst, 4);
-        spv::StorageClass format_storage_class;
-        uint32_t format_data_type = 0;
-        if (!_.GetPointerTypeInfo(format_type, &format_data_type,
-                                  &format_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Format to be a pointer";
-        }
-
-        if (_.HasExtension(
-                Extension::kSPV_EXT_relaxed_printf_string_address_space)) {
-          if (format_storage_class != spv::StorageClass::UniformConstant &&
-              // Extension SPV_EXT_relaxed_printf_string_address_space allows
-              // format strings in Global, Local, Private and Generic address
-              // spaces
-
-              // Global
-              format_storage_class != spv::StorageClass::CrossWorkgroup &&
-              // Local
-              format_storage_class != spv::StorageClass::Workgroup &&
-              // Private
-              format_storage_class != spv::StorageClass::Function &&
-              // Generic
-              format_storage_class != spv::StorageClass::Generic) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected Format storage class to be UniformConstant, "
-                      "Crossworkgroup, Workgroup, Function, or Generic";
-          }
-        } else {
-          if (format_storage_class != spv::StorageClass::UniformConstant) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected Format storage class to be UniformConstant";
-          }
-        }
-
-        // If pointer points to an array, get the type of an element
-        if (_.IsIntArrayType(format_data_type))
-          format_data_type = _.GetComponentType(format_data_type);
-
-        if ((!_.IsIntScalarType(format_data_type) ||
-             _.GetBitWidth(format_data_type) != 8) &&
-            !_.ContainsUntypedPointer(format_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Format data type to be 8-bit int";
-        }
-        break;
+      if ((!_.IsFloatScalarType(p_data_type, 16)) &&
+          !_.ContainsUntypedPointer(p_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P data type to be 16-bit float scalar";
       }
 
-      case OpenCLLIB::Prefetch: {
-        if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": expected Result Type to be void";
-        }
-
-        const uint32_t p_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t num_elements_type = _.GetOperandTypeId(inst, 5);
-
-        spv::StorageClass p_storage_class;
-        uint32_t p_data_type = 0;
-        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Ptr to be a pointer";
-        }
-
-        if (p_storage_class != spv::StorageClass::CrossWorkgroup) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Ptr storage class to be CrossWorkgroup";
-        }
-
-        if (!_.IsFloatScalarOrVectorType(p_data_type) &&
-            !_.IsIntScalarOrVectorType(p_data_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Ptr data type to be int or float scalar or "
-                    "vector";
-        }
-
-        const uint32_t num_components = _.GetDimension(p_data_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
-
-        const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
-        if (!size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name()
-                 << " can only be used with physical addressing models";
-        }
-
-        if (!_.IsIntScalarType(num_elements_type) ||
-            _.GetBitWidth(num_elements_type) != size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Num Elements to be of type size_t ("
-                 << size_t_bit_width
-                 << "-bit integer for the addressing model used in the module)";
-        }
-        break;
-      }
-    }
-  } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
-             ext_inst_type ==
-                 SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
-    if (!_.IsVoidType(result_type)) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << ext_inst_name() << ": "
-             << "expected result type must be a result id of "
-             << "OpTypeVoid";
+      // Rounding mode enum is checked by assembler.
+      break;
     }
 
-    const bool vulkanDebugInfo =
-        ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100;
+    case OpenCLLIB::Shuffle:
+    case OpenCLLIB::Shuffle2: {
+      if (!_.IsFloatVectorType(result_type) &&
+          !_.IsIntVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be an int or float vector type";
+      }
 
-    auto num_words = inst->words().size();
+      const uint32_t result_num_components = _.GetDimension(result_type);
+      if (result_num_components != 2 && result_num_components != 4 &&
+          result_num_components != 8 && result_num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to have 2, 4, 8 or 16 components";
+      }
 
-    // Handle any non-common NonSemanticShaderDebugInfo instructions.
-    if (vulkanDebugInfo) {
-      const NonSemanticShaderDebugInfo100Instructions ext_inst_key =
-          NonSemanticShaderDebugInfo100Instructions(ext_inst_index);
-      switch (ext_inst_key) {
-        // The following block of instructions will be handled by the common
-        // validation.
-        case NonSemanticShaderDebugInfo100DebugInfoNone:
-        case NonSemanticShaderDebugInfo100DebugCompilationUnit:
-        case NonSemanticShaderDebugInfo100DebugTypePointer:
-        case NonSemanticShaderDebugInfo100DebugTypeQualifier:
-        case NonSemanticShaderDebugInfo100DebugTypeArray:
-        case NonSemanticShaderDebugInfo100DebugTypeVector:
-        case NonSemanticShaderDebugInfo100DebugTypedef:
-        case NonSemanticShaderDebugInfo100DebugTypeFunction:
-        case NonSemanticShaderDebugInfo100DebugTypeEnum:
-        case NonSemanticShaderDebugInfo100DebugTypeComposite:
-        case NonSemanticShaderDebugInfo100DebugTypeMember:
-        case NonSemanticShaderDebugInfo100DebugTypeInheritance:
-        case NonSemanticShaderDebugInfo100DebugTypePtrToMember:
-        case NonSemanticShaderDebugInfo100DebugTypeTemplate:
-        case NonSemanticShaderDebugInfo100DebugTypeTemplateParameter:
-        case NonSemanticShaderDebugInfo100DebugTypeTemplateTemplateParameter:
-        case NonSemanticShaderDebugInfo100DebugTypeTemplateParameterPack:
-        case NonSemanticShaderDebugInfo100DebugGlobalVariable:
-        case NonSemanticShaderDebugInfo100DebugFunctionDeclaration:
-        case NonSemanticShaderDebugInfo100DebugFunction:
-        case NonSemanticShaderDebugInfo100DebugLexicalBlock:
-        case NonSemanticShaderDebugInfo100DebugLexicalBlockDiscriminator:
-        case NonSemanticShaderDebugInfo100DebugScope:
-        case NonSemanticShaderDebugInfo100DebugNoScope:
-        case NonSemanticShaderDebugInfo100DebugInlinedAt:
-        case NonSemanticShaderDebugInfo100DebugLocalVariable:
-        case NonSemanticShaderDebugInfo100DebugInlinedVariable:
-        case NonSemanticShaderDebugInfo100DebugValue:
-        case NonSemanticShaderDebugInfo100DebugOperation:
-        case NonSemanticShaderDebugInfo100DebugExpression:
-        case NonSemanticShaderDebugInfo100DebugMacroDef:
-        case NonSemanticShaderDebugInfo100DebugMacroUndef:
-        case NonSemanticShaderDebugInfo100DebugImportedEntity:
-        case NonSemanticShaderDebugInfo100DebugSource:
-          break;
+      uint32_t operand_index = 4;
+      const uint32_t x_type = _.GetOperandTypeId(inst, operand_index++);
 
-        // These checks are for operands that are differnet in
-        // ShaderDebugInfo100
-        case NonSemanticShaderDebugInfo100DebugTypeBasic: {
-          CHECK_CONST_UINT_OPERAND("Flags", 8);
-          break;
+      if (ext_inst_key == OpenCLLIB::Shuffle2) {
+        const uint32_t y_type = _.GetOperandTypeId(inst, operand_index++);
+        if (x_type != y_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected operands X and Y to be of the same type";
         }
-        case NonSemanticShaderDebugInfo100DebugDeclare: {
-          for (uint32_t word_index = 8; word_index < num_words; ++word_index) {
-            auto index_inst = _.FindDef(inst->word(word_index));
-            auto type_id = index_inst != nullptr ? index_inst->type_id() : 0;
-            if (type_id == 0 || !IsIntScalar(_, type_id, false, false))
+      }
+
+      const uint32_t shuffle_mask_type =
+          _.GetOperandTypeId(inst, operand_index++);
+
+      if (!_.IsFloatVectorType(x_type) && !_.IsIntVectorType(x_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X to be an int or float vector";
+      }
+
+      const uint32_t x_num_components = _.GetDimension(x_type);
+      if (x_num_components != 2 && x_num_components != 4 &&
+          x_num_components != 8 && x_num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X to have 2, 4, 8 or 16 components";
+      }
+
+      const uint32_t result_component_type = _.GetComponentType(result_type);
+
+      if (result_component_type != _.GetComponentType(x_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X and Result Type to have equal "
+                  "component types";
+      }
+
+      if (!_.IsIntVectorType(shuffle_mask_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Shuffle Mask to be an int vector";
+      }
+
+      if (result_num_components != _.GetDimension(shuffle_mask_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Shuffle Mask to have the same number of "
+                  "components as Result Type";
+      }
+
+      if (_.GetBitWidth(result_component_type) !=
+          _.GetBitWidth(shuffle_mask_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Shuffle Mask components to have the same "
+                  "bit width as Result Type components";
+      }
+      break;
+    }
+
+    case OpenCLLIB::Printf: {
+      if (!_.IsIntScalarType(result_type, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a 32-bit int type";
+      }
+
+      const uint32_t format_type = _.GetOperandTypeId(inst, 4);
+      spv::StorageClass format_storage_class;
+      uint32_t format_data_type = 0;
+      if (!_.GetPointerTypeInfo(format_type, &format_data_type,
+                                &format_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Format to be a pointer";
+      }
+
+      if (_.HasExtension(
+              Extension::kSPV_EXT_relaxed_printf_string_address_space)) {
+        if (format_storage_class != spv::StorageClass::UniformConstant &&
+            // Extension SPV_EXT_relaxed_printf_string_address_space allows
+            // format strings in Global, Local, Private and Generic address
+            // spaces
+
+            // Global
+            format_storage_class != spv::StorageClass::CrossWorkgroup &&
+            // Local
+            format_storage_class != spv::StorageClass::Workgroup &&
+            // Private
+            format_storage_class != spv::StorageClass::Function &&
+            // Generic
+            format_storage_class != spv::StorageClass::Generic) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected Format storage class to be UniformConstant, "
+                    "Crossworkgroup, Workgroup, Function, or Generic";
+        }
+      } else {
+        if (format_storage_class != spv::StorageClass::UniformConstant) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected Format storage class to be UniformConstant";
+        }
+      }
+
+      // If pointer points to an array, get the type of an element
+      if (_.IsIntArrayType(format_data_type))
+        format_data_type = _.GetComponentType(format_data_type);
+
+      if (!_.IsIntScalarType(format_data_type, 8) &&
+          !_.ContainsUntypedPointer(format_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Format data type to be 8-bit int";
+      }
+      break;
+    }
+
+    case OpenCLLIB::Prefetch: {
+      if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst)
+               << ": expected Result Type to be void";
+      }
+
+      const uint32_t p_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t num_elements_type = _.GetOperandTypeId(inst, 5);
+
+      spv::StorageClass p_storage_class;
+      uint32_t p_data_type = 0;
+      if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Ptr to be a pointer";
+      }
+
+      if (p_storage_class != spv::StorageClass::CrossWorkgroup) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Ptr storage class to be CrossWorkgroup";
+      }
+
+      if (!_.IsFloatScalarOrVectorType(p_data_type) &&
+          !_.IsIntScalarOrVectorType(p_data_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Ptr data type to be int or float scalar or "
+                  "vector";
+      }
+
+      const uint32_t num_components = _.GetDimension(p_data_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
+
+      const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
+      if (!size_t_bit_width) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst)
+               << " can only be used with physical addressing models";
+      }
+
+      if (!_.IsIntScalarType(num_elements_type) ||
+          _.GetBitWidth(num_elements_type) != size_t_bit_width) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Num Elements to be of type size_t ("
+               << size_t_bit_width
+               << "-bit integer for the addressing model used in the module)";
+      }
+      break;
+    }
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateExtInstDebugInfo(ValidationState_t& _,
+                                      const Instruction* inst) {
+  const uint32_t result_type = inst->type_id();
+  const uint32_t ext_inst_index = inst->word(4);
+  if (!_.IsVoidType(result_type)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << GetExtInstName(_, inst) << ": "
+           << "expected result type must be a result id of " << "OpTypeVoid";
+  }
+
+  const spv_ext_inst_type_t ext_inst_type =
+      spv_ext_inst_type_t(inst->ext_inst_type());
+  const bool vulkanDebugInfo =
+      ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100;
+
+  auto num_words = inst->words().size();
+
+  // Parse the declared NSDI version so optional-operand checks are strict
+  // (num_words == n) for version kNSDIKnownVersion and lenient (num_words >= n)
+  // for future versions that may add trailing operands.
+  static const uint32_t kNSDIKnownVersion = NonSemanticShaderDebugInfoVersion;
+  const uint32_t nsdi_version = vulkanDebugInfo ? GetNSDIVersion(_, inst) : 0;
+  // True if the optional operand at word |n| is present and should be checked.
+  auto has_optional_at = [&](uint32_t n) -> bool {
+    return num_words >= n &&
+           (nsdi_version > kNSDIKnownVersion || num_words == n);
+  };
+
+  // Handle any non-common NonSemanticShaderDebugInfo instructions.
+  if (vulkanDebugInfo) {
+    const NonSemanticShaderDebugInfoInstructions ext_inst_key =
+        NonSemanticShaderDebugInfoInstructions(ext_inst_index);
+    switch (ext_inst_key) {
+      // The following block of instructions will be handled by the common
+      // validation.
+      case NonSemanticShaderDebugInfoDebugInfoNone:
+      case NonSemanticShaderDebugInfoDebugCompilationUnit:
+      case NonSemanticShaderDebugInfoDebugTypePointer:
+      case NonSemanticShaderDebugInfoDebugTypeQualifier:
+      case NonSemanticShaderDebugInfoDebugTypeArray:
+      case NonSemanticShaderDebugInfoDebugTypeVector:
+      case NonSemanticShaderDebugInfoDebugTypedef:
+      case NonSemanticShaderDebugInfoDebugTypeFunction:
+      case NonSemanticShaderDebugInfoDebugTypeEnum:
+      case NonSemanticShaderDebugInfoDebugTypeComposite:
+      case NonSemanticShaderDebugInfoDebugTypeMember:
+      case NonSemanticShaderDebugInfoDebugTypeInheritance:
+      case NonSemanticShaderDebugInfoDebugTypePtrToMember:
+      case NonSemanticShaderDebugInfoDebugTypeTemplate:
+      case NonSemanticShaderDebugInfoDebugTypeTemplateParameter:
+      case NonSemanticShaderDebugInfoDebugTypeTemplateTemplateParameter:
+      case NonSemanticShaderDebugInfoDebugTypeTemplateParameterPack:
+      case NonSemanticShaderDebugInfoDebugGlobalVariable:
+      case NonSemanticShaderDebugInfoDebugFunctionDeclaration:
+      case NonSemanticShaderDebugInfoDebugFunction:
+      case NonSemanticShaderDebugInfoDebugLexicalBlock:
+      case NonSemanticShaderDebugInfoDebugLexicalBlockDiscriminator:
+      case NonSemanticShaderDebugInfoDebugScope:
+      case NonSemanticShaderDebugInfoDebugNoScope:
+      case NonSemanticShaderDebugInfoDebugInlinedAt:
+      case NonSemanticShaderDebugInfoDebugLocalVariable:
+      case NonSemanticShaderDebugInfoDebugInlinedVariable:
+      case NonSemanticShaderDebugInfoDebugValue:
+      case NonSemanticShaderDebugInfoDebugOperation:
+      case NonSemanticShaderDebugInfoDebugExpression:
+      case NonSemanticShaderDebugInfoDebugMacroDef:
+      case NonSemanticShaderDebugInfoDebugMacroUndef:
+      case NonSemanticShaderDebugInfoDebugImportedEntity:
+      case NonSemanticShaderDebugInfoDebugSource:
+        break;
+
+      // These checks are for operands that are different in
+      // NonSemantic.Shader.DebugInfo
+      case NonSemanticShaderDebugInfoDebugTypeBasic: {
+        CHECK_CONST_UINT_OPERAND("Flags", 8);
+        // Optional FPEncoding parameter (5th operand, word index 9)
+        if (has_optional_at(10)) {
+          CHECK_CONST_UINT_OPERAND("FPEncoding", 9);
+        }
+        break;
+      }
+      case NonSemanticShaderDebugInfoDebugDeclare: {
+        for (uint32_t word_index = 8; word_index < num_words; ++word_index) {
+          auto index_inst = _.FindDef(inst->word(word_index));
+          auto type_id = index_inst != nullptr ? index_inst->type_id() : 0;
+          if (type_id == 0 || !IsIntScalar(_, type_id, false, false))
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << GetExtInstName(_, inst) << ": "
+                   << "expected index must be scalar integer";
+        }
+        break;
+      }
+      case NonSemanticShaderDebugInfoDebugTypeMatrix: {
+        CHECK_DEBUG_OPERAND("Vector Type", CommonDebugInfoDebugTypeVector, 5);
+
+        CHECK_CONST_UINT_OPERAND("Vector Count", 6);
+
+        uint32_t vector_count = inst->word(6);
+        uint64_t const_val;
+        if (!_.EvalConstantValUint64(vector_count, &const_val)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst)
+                 << ": Vector Count must be 32-bit integer OpConstant";
+        }
+
+        vector_count = const_val & 0xffffffff;
+        if (!vector_count || vector_count > 4) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst)
+                 << ": Vector Count must be positive "
+                 << "integer less than or equal to 4";
+        }
+        break;
+      }
+      case NonSemanticShaderDebugInfoDebugTypeVectorIdEXT: {
+        CHECK_NSDI_MIN_VERSION(NonSemanticShaderDebugInfoVersion);
+        CHECK_DEBUG_OPERAND("Component Type", CommonDebugInfoDebugTypeBasic, 5);
+        // Component Count may be OpSpecConstant when the cooperative vector
+        // type uses a specialization constant for its size.
+        CHECK_CONST_OR_SPEC_UINT_OPERAND("Component Count", 6);
+        break;
+      }
+      case NonSemanticShaderDebugInfoDebugTypeCooperativeMatrixKHR: {
+        CHECK_NSDI_MIN_VERSION(NonSemanticShaderDebugInfoVersion);
+        CHECK_DEBUG_OPERAND("Component Type", CommonDebugInfoDebugTypeBasic, 5);
+        // Scope, Rows, Columns, and Use may be OpSpecConstant when the
+        // cooperative matrix type uses specialization constants.
+        CHECK_CONST_OR_SPEC_UINT_OPERAND("Scope", 6);
+        CHECK_CONST_OR_SPEC_UINT_OPERAND("Rows", 7);
+        CHECK_CONST_OR_SPEC_UINT_OPERAND("Columns", 8);
+        CHECK_CONST_OR_SPEC_UINT_OPERAND("Use", 9);
+        break;
+      }
+      case NonSemanticShaderDebugInfoDebugFunctionDefinition: {
+        CHECK_DEBUG_OPERAND("Function", CommonDebugInfoDebugFunction, 5);
+        CHECK_OPERAND("Definition", spv::Op::OpFunction, 6);
+        const auto* current_function = inst->function();
+        if (current_function->first_block()->id() != inst->block()->id()) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst)
+                 << ": must be in the entry basic block of the function";
+        }
+
+        const uint32_t definition_id = inst->word(6);
+        if (definition_id != current_function->id()) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst)
+                 << ": operand Definition must point to the OpFunction it is "
+                    "inside";
+        }
+        break;
+      }
+      case NonSemanticShaderDebugInfoDebugLine: {
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 5);
+        CHECK_CONST_UINT_OPERAND("Line Start", 6);
+        CHECK_CONST_UINT_OPERAND("Line End", 7);
+        CHECK_CONST_UINT_OPERAND("Column Start", 8);
+        CHECK_CONST_UINT_OPERAND("Column End", 9);
+
+        // above already validates if 32-bit and non-spec constant
+        // but want to use EvalInt32IfConst to be consistent with other Eval
+        // locations
+        bool is_int32 = false, is_const_int32 = false;
+        uint32_t line_start = 0;
+        uint32_t line_end = 0;
+        uint32_t column_start = 0;
+        uint32_t column_end = 0;
+        std::tie(is_int32, is_const_int32, line_start) =
+            _.EvalInt32IfConst(inst->word(6));
+        std::tie(is_int32, is_const_int32, line_end) =
+            _.EvalInt32IfConst(inst->word(7));
+        std::tie(is_int32, is_const_int32, column_start) =
+            _.EvalInt32IfConst(inst->word(8));
+        std::tie(is_int32, is_const_int32, column_end) =
+            _.EvalInt32IfConst(inst->word(9));
+        if (line_start == 0) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst)
+                 << ": operand Line Start (0) is not allowed, source lines "
+                    "start at Line 1";
+        } else if (line_end < line_start) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": operand Line End ("
+                 << line_end << ") is less than Line Start (" << line_start
+                 << ")";
+        } else if (line_start == line_end && column_end < column_start) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": operand Column End ("
+                 << column_end << ") is less than Column Start ("
+                 << column_start << ") when Line Start equals Line End";
+        }
+        // Make sure Line is found in the DebugSource
+        auto* debug_source_inst = _.FindDef(inst->word(5));
+        const std::vector<uint32_t>& line_lengths =
+            _.GetDebugSourceLineLength(debug_source_inst->id());
+        if (!line_lengths.empty()) {
+          if (line_end > line_lengths.size()) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << GetExtInstName(_, inst) << ": operand Line End ("
+                   << line_end << ") is larger then the " << line_lengths.size()
+                   << " lines found in the DebugSource text";
+          }
+          if (line_start == line_end) {
+            const uint32_t line_length = line_lengths[line_end - 1];
+            if (column_end > line_length) {
               return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << ext_inst_name() << ": "
-                     << "expected index must be scalar integer";
-          }
-          break;
-        }
-        case NonSemanticShaderDebugInfo100DebugTypeMatrix: {
-          CHECK_DEBUG_OPERAND("Vector Type", CommonDebugInfoDebugTypeVector, 5);
-
-          CHECK_CONST_UINT_OPERAND("Vector Count", 6);
-
-          uint32_t vector_count = inst->word(6);
-          uint64_t const_val;
-          if (!_.EvalConstantValUint64(vector_count, &const_val)) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name()
-                   << ": Vector Count must be 32-bit integer OpConstant";
-          }
-
-          vector_count = const_val & 0xffffffff;
-          if (!vector_count || vector_count > 4) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": Vector Count must be positive "
-                   << "integer less than or equal to 4";
-          }
-          break;
-        }
-        case NonSemanticShaderDebugInfo100DebugFunctionDefinition: {
-          CHECK_DEBUG_OPERAND("Function", CommonDebugInfoDebugFunction, 5);
-          CHECK_OPERAND("Definition", spv::Op::OpFunction, 6);
-          const auto* current_function = inst->function();
-          if (current_function->first_block()->id() != inst->block()->id()) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name()
-                   << ": must be in the entry basic block of the function";
-          }
-
-          const uint32_t definition_id = inst->word(6);
-          if (definition_id != current_function->id()) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name()
-                   << ": operand Definition must point to the OpFunction it is "
-                      "inside";
-          }
-          break;
-        }
-        case NonSemanticShaderDebugInfo100DebugLine: {
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 5);
-          CHECK_CONST_UINT_OPERAND("Line Start", 6);
-          CHECK_CONST_UINT_OPERAND("Line End", 7);
-          CHECK_CONST_UINT_OPERAND("Column Start", 8);
-          CHECK_CONST_UINT_OPERAND("Column End", 9);
-
-          // above already validates if 32-bit and non-spec constant
-          // but want to use EvalInt32IfConst to be consistent with other Eval
-          // locations
-          bool is_int32 = false, is_const_int32 = false;
-          uint32_t line_start = 0;
-          uint32_t line_end = 0;
-          uint32_t column_start = 0;
-          uint32_t column_end = 0;
-          std::tie(is_int32, is_const_int32, line_start) =
-              _.EvalInt32IfConst(inst->word(6));
-          std::tie(is_int32, is_const_int32, line_end) =
-              _.EvalInt32IfConst(inst->word(7));
-          std::tie(is_int32, is_const_int32, column_start) =
-              _.EvalInt32IfConst(inst->word(8));
-          std::tie(is_int32, is_const_int32, column_end) =
-              _.EvalInt32IfConst(inst->word(9));
-          if (line_end < line_start) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": operand Line End (" << line_end
-                   << ") is less than Line Start (" << line_start << ")";
-          } else if (column_end < column_start) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": operand Column End (" << column_end
-                   << ") is less than Column Start (" << column_start << ")";
-          }
-          break;
-        }
-        case NonSemanticShaderDebugInfo100DebugSourceContinued: {
-          CHECK_OPERAND("Text", spv::Op::OpString, 5);
-          break;
-        }
-        case NonSemanticShaderDebugInfo100DebugBuildIdentifier: {
-          CHECK_OPERAND("Identifier", spv::Op::OpString, 5);
-          CHECK_CONST_UINT_OPERAND("Flags", 6);
-          break;
-        }
-        case NonSemanticShaderDebugInfo100DebugStoragePath: {
-          CHECK_OPERAND("Path", spv::Op::OpString, 5);
-          break;
-        }
-        case NonSemanticShaderDebugInfo100DebugEntryPoint: {
-          CHECK_DEBUG_OPERAND("Entry Point", CommonDebugInfoDebugFunction, 5);
-          CHECK_DEBUG_OPERAND("Compilation Unit",
-                              CommonDebugInfoDebugCompilationUnit, 6);
-          CHECK_OPERAND("Compiler Signature", spv::Op::OpString, 7);
-          CHECK_OPERAND("Command-line Arguments", spv::Op::OpString, 8);
-          break;
-        }
-
-          // Has no additional checks
-        case NonSemanticShaderDebugInfo100DebugNoLine:
-          break;
-        case NonSemanticShaderDebugInfo100InstructionsMax:
-          assert(0);
-          break;
-      }
-    }
-
-    // Handle any non-common OpenCL insts, then common
-    if (ext_inst_type != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
-        OpenCLDebugInfo100Instructions(ext_inst_index) !=
-            OpenCLDebugInfo100DebugModuleINTEL) {
-      const CommonDebugInfoInstructions ext_inst_key =
-          CommonDebugInfoInstructions(ext_inst_index);
-      switch (ext_inst_key) {
-        case CommonDebugInfoDebugInfoNone:
-        case CommonDebugInfoDebugNoScope:
-          break;
-          // The binary parser validates the opcode for DebugInfoNone,
-          // DebugNoScope, DebugOperation. We just check the parameters to
-          // DebugOperation are properly constants for vulkan debug info.
-        case CommonDebugInfoDebugOperation: {
-          CHECK_CONST_UINT_OPERAND("Operation", 5);
-          for (uint32_t i = 6; i < num_words; ++i) {
-            CHECK_CONST_UINT_OPERAND("Operand", i);
-          }
-          break;
-        }
-        case CommonDebugInfoDebugCompilationUnit: {
-          CHECK_CONST_UINT_OPERAND("Version", 5);
-          CHECK_CONST_UINT_OPERAND("DWARF Version", 6);
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
-          CHECK_CONST_UINT_OPERAND("Language", 8);
-          break;
-        }
-        case CommonDebugInfoDebugSource: {
-          CHECK_OPERAND("File", spv::Op::OpString, 5);
-          if (num_words == 7) CHECK_OPERAND("Text", spv::Op::OpString, 6);
-          break;
-        }
-        case CommonDebugInfoDebugTypeBasic: {
-          CHECK_OPERAND("Name", spv::Op::OpString, 5);
-          CHECK_OPERAND("Size", spv::Op::OpConstant, 6);
-          CHECK_CONST_UINT_OPERAND("Encoding", 7);
-          break;
-        }
-        case CommonDebugInfoDebugTypePointer: {
-          auto validate_base_type = ValidateOperandDebugType(
-              _, "Base Type", inst, 5, ext_inst_name, false);
-          if (validate_base_type != SPV_SUCCESS) return validate_base_type;
-          CHECK_CONST_UINT_OPERAND("Storage Class", 6);
-          CHECK_CONST_UINT_OPERAND("Flags", 7);
-          break;
-        }
-        case CommonDebugInfoDebugTypeQualifier: {
-          auto validate_base_type = ValidateOperandDebugType(
-              _, "Base Type", inst, 5, ext_inst_name, false);
-          if (validate_base_type != SPV_SUCCESS) return validate_base_type;
-          CHECK_CONST_UINT_OPERAND("Type Qualifier", 6);
-          break;
-        }
-        case CommonDebugInfoDebugTypeVector: {
-          auto validate_base_type =
-              ValidateOperandBaseType(_, inst, 5, ext_inst_name);
-          if (validate_base_type != SPV_SUCCESS) return validate_base_type;
-
-          CHECK_CONST_UINT_OPERAND("Component Count", 6);
-          uint32_t component_count = inst->word(6);
-          if (vulkanDebugInfo) {
-            uint64_t const_val;
-            if (!_.EvalConstantValUint64(component_count, &const_val)) {
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << ext_inst_name()
-                     << ": Component Count must be 32-bit integer OpConstant";
+                     << GetExtInstName(_, inst) << ": operand Column End ("
+                     << column_end << ") is larger then Line " << line_end
+                     << " column length of " << line_length
+                     << " found in the DebugSource text";
             }
-            component_count = const_val & 0xffffffff;
+          } else {
+            uint32_t line_length = line_lengths[line_start - 1];
+            if (column_start > line_length) {
+              return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                     << GetExtInstName(_, inst) << ": operand Column Start ("
+                     << column_start << ") is larger then Line " << line_start
+                     << " column length of " << line_length
+                     << " found in the DebugSource text";
+            }
+            line_length = line_lengths[line_end - 1];
+            if (column_end > line_length) {
+              return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                     << GetExtInstName(_, inst) << ": operand Column End ("
+                     << column_end << ") is larger then Line " << line_end
+                     << " column length of " << line_length
+                     << " found in the DebugSource text";
+            }
           }
-
-          if (!component_count || component_count > 4) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": Component Count must be positive "
-                   << "integer less than or equal to 4";
-          }
-          break;
         }
-        case CommonDebugInfoDebugTypeArray: {
-          auto validate_base_type = ValidateOperandDebugType(
-              _, "Base Type", inst, 5, ext_inst_name, false);
-          if (validate_base_type != SPV_SUCCESS) return validate_base_type;
-          for (uint32_t i = 6; i < num_words; ++i) {
-            bool invalid = false;
-            auto* component_count = _.FindDef(inst->word(i));
-            if (IsConstIntScalarTypeWith32Or64Bits(_, component_count)) {
-              // TODO: We need a spec discussion for the runtime array for
-              // OpenCL.
-              if (!vulkanDebugInfo && !component_count->word(3)) {
+
+        break;
+      }
+      case NonSemanticShaderDebugInfoDebugSourceContinued: {
+        CHECK_OPERAND("Text", spv::Op::OpString, 5);
+        // OpenCL didn't have a Continued version
+        BuildDebugSourceLineLength(_, inst, ext_inst_index);
+        break;
+      }
+      case NonSemanticShaderDebugInfoDebugBuildIdentifier: {
+        CHECK_OPERAND("Identifier", spv::Op::OpString, 5);
+        CHECK_CONST_UINT_OPERAND("Flags", 6);
+        break;
+      }
+      case NonSemanticShaderDebugInfoDebugStoragePath: {
+        CHECK_OPERAND("Path", spv::Op::OpString, 5);
+        break;
+      }
+      case NonSemanticShaderDebugInfoDebugEntryPoint: {
+        CHECK_DEBUG_OPERAND("Entry Point", CommonDebugInfoDebugFunction, 5);
+        CHECK_DEBUG_OPERAND("Compilation Unit",
+                            CommonDebugInfoDebugCompilationUnit, 6);
+        CHECK_OPERAND("Compiler Signature", spv::Op::OpString, 7);
+        CHECK_OPERAND("Command-line Arguments", spv::Op::OpString, 8);
+        break;
+      }
+
+        // Has no additional checks
+      case NonSemanticShaderDebugInfoDebugNoLine:
+        break;
+      case NonSemanticShaderDebugInfoInstructionsMax:
+        assert(0);
+        break;
+    }
+  }
+
+  // Handle any non-common OpenCL insts, then common
+  if (ext_inst_type != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
+      OpenCLDebugInfo100Instructions(ext_inst_index) !=
+          OpenCLDebugInfo100DebugModuleINTEL) {
+    const CommonDebugInfoInstructions ext_inst_key =
+        CommonDebugInfoInstructions(ext_inst_index);
+    switch (ext_inst_key) {
+      case CommonDebugInfoDebugInfoNone:
+      case CommonDebugInfoDebugNoScope:
+        break;
+        // The binary parser validates the opcode for DebugInfoNone,
+        // DebugNoScope, DebugOperation. We just check the parameters to
+        // DebugOperation are properly constants for vulkan debug info.
+      case CommonDebugInfoDebugOperation: {
+        CHECK_CONST_UINT_OPERAND("Operation", 5);
+        for (uint32_t i = 6; i < num_words; ++i) {
+          CHECK_CONST_UINT_OPERAND("Operand", i);
+        }
+        break;
+      }
+      case CommonDebugInfoDebugCompilationUnit: {
+        CHECK_CONST_UINT_OPERAND("Version", 5);
+        CHECK_CONST_UINT_OPERAND("DWARF Version", 6);
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+        CHECK_CONST_UINT_OPERAND("Language", 8);
+        break;
+      }
+      case CommonDebugInfoDebugSource: {
+        CHECK_OPERAND("File", spv::Op::OpString, 5);
+        if (has_optional_at(7)) CHECK_OPERAND("Text", spv::Op::OpString, 6);
+        BuildDebugSourceLineLength(_, inst, ext_inst_index);
+        break;
+      }
+      case CommonDebugInfoDebugTypeBasic: {
+        CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        CHECK_OPERAND("Size", spv::Op::OpConstant, 6);
+        CHECK_CONST_UINT_OPERAND("Encoding", 7);
+        break;
+      }
+      case CommonDebugInfoDebugTypePointer: {
+        auto validate_base_type =
+            ValidateOperandDebugType(_, "Base Type", inst, 5, false);
+        if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+        CHECK_CONST_UINT_OPERAND("Storage Class", 6);
+        CHECK_CONST_UINT_OPERAND("Flags", 7);
+        break;
+      }
+      case CommonDebugInfoDebugTypeQualifier: {
+        auto validate_base_type =
+            ValidateOperandDebugType(_, "Base Type", inst, 5, false);
+        if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+        CHECK_CONST_UINT_OPERAND("Type Qualifier", 6);
+        break;
+      }
+      case CommonDebugInfoDebugTypeVector: {
+        auto validate_base_type = ValidateOperandBaseType(_, inst, 5);
+        if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+
+        CHECK_CONST_UINT_OPERAND("Component Count", 6);
+        uint32_t component_count = inst->word(6);
+        if (vulkanDebugInfo) {
+          uint64_t const_val;
+          if (!_.EvalConstantValUint64(component_count, &const_val)) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << GetExtInstName(_, inst)
+                   << ": Component Count must be 32-bit integer OpConstant";
+          }
+          component_count = const_val & 0xffffffff;
+        }
+
+        if (!component_count || component_count > 4) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst)
+                 << ": Component Count must be positive "
+                 << "integer less than or equal to 4";
+        }
+        break;
+      }
+      case CommonDebugInfoDebugTypeArray: {
+        auto validate_base_type =
+            ValidateOperandDebugType(_, "Base Type", inst, 5, false);
+        if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+        for (uint32_t i = 6; i < num_words; ++i) {
+          bool invalid = false;
+          auto* component_count = _.FindDef(inst->word(i));
+          if (IsConstIntScalarTypeWith32Or64Bits(_, component_count)) {
+            // TODO: We need a spec discussion for the runtime array for
+            // OpenCL.
+            if (!vulkanDebugInfo && !component_count->word(3)) {
+              invalid = true;
+            }
+          } else if (vulkanDebugInfo && IsSpecConstIntScalarTypeWith32Or64Bits(
+                                            _, component_count)) {
+            // Spec constants are valid component counts for
+            // NonSemantic.Shader.DebugInfo.
+          } else if (component_count->words().size() > 6 &&
+                     (CommonDebugInfoInstructions(component_count->word(4)) ==
+                          CommonDebugInfoDebugLocalVariable ||
+                      CommonDebugInfoInstructions(component_count->word(4)) ==
+                          CommonDebugInfoDebugGlobalVariable)) {
+            auto* component_count_type = _.FindDef(component_count->word(6));
+            if (component_count_type->words().size() > 7) {
+              uint32_t encoding = component_count_type->word(7);
+              if (CommonDebugInfoInstructions(component_count_type->word(4)) !=
+                      CommonDebugInfoDebugTypeBasic ||
+                  (vulkanDebugInfo && !IsUint32Constant(_, encoding)) ||
+                  OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(
+                      vulkanDebugInfo
+                          ? GetUint32Constant(_, encoding)
+                          : encoding) != OpenCLDebugInfo100Unsigned) {
                 invalid = true;
-              }
-            } else if (component_count->words().size() > 6 &&
-                       (CommonDebugInfoInstructions(component_count->word(4)) ==
-                            CommonDebugInfoDebugLocalVariable ||
-                        CommonDebugInfoInstructions(component_count->word(4)) ==
-                            CommonDebugInfoDebugGlobalVariable)) {
-              auto* component_count_type = _.FindDef(component_count->word(6));
-              if (component_count_type->words().size() > 7) {
-                uint32_t encoding = component_count_type->word(7);
-                if (CommonDebugInfoInstructions(component_count_type->word(
-                        4)) != CommonDebugInfoDebugTypeBasic ||
-                    (vulkanDebugInfo && !IsUint32Constant(_, encoding)) ||
-                    OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(
-                        vulkanDebugInfo
-                            ? GetUint32Constant(_, encoding)
-                            : encoding) != OpenCLDebugInfo100Unsigned) {
-                  invalid = true;
-                } else {
-                  // DebugTypeBasic for DebugLocalVariable/DebugGlobalVariable
-                  // must have Unsigned encoding and 32 or 64 as its size in
-                  // bits.
-                  Instruction* size_in_bits =
-                      _.FindDef(component_count_type->word(6));
-                  if (!_.IsIntScalarType(size_in_bits->type_id()) ||
-                      (size_in_bits->word(3) != 32 &&
-                       size_in_bits->word(3) != 64)) {
-                    invalid = true;
-                  }
-                }
               } else {
-                invalid = true;
+                // DebugTypeBasic for DebugLocalVariable/DebugGlobalVariable
+                // must have Unsigned encoding and 32 or 64 as its size in
+                // bits.
+                Instruction* size_in_bits =
+                    _.FindDef(component_count_type->word(6));
+                if (!_.IsIntScalarType(size_in_bits->type_id()) ||
+                    (size_in_bits->word(3) != 32 &&
+                     size_in_bits->word(3) != 64)) {
+                  invalid = true;
+                }
               }
             } else {
               invalid = true;
             }
-            if (invalid) {
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << ext_inst_name() << ": Component Count must be "
-                     << "OpConstant with a 32- or 64-bits integer scalar type "
-                        "or "
-                     << "DebugGlobalVariable or DebugLocalVariable with a 32- "
-                        "or "
-                     << "64-bits unsigned integer scalar type";
-            }
-          }
-          break;
-        }
-        case CommonDebugInfoDebugTypedef: {
-          CHECK_OPERAND("Name", spv::Op::OpString, 5);
-          auto validate_base_type =
-              ValidateOperandBaseType(_, inst, 6, ext_inst_name);
-          if (validate_base_type != SPV_SUCCESS) return validate_base_type;
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
-          CHECK_CONST_UINT_OPERAND("Line", 8);
-          CHECK_CONST_UINT_OPERAND("Column", 9);
-          auto validate_parent =
-              ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
-          if (validate_parent != SPV_SUCCESS) return validate_parent;
-          break;
-        }
-        case CommonDebugInfoDebugTypeFunction: {
-          CHECK_CONST_UINT_OPERAND("Flags", 5);
-          auto* return_type = _.FindDef(inst->word(6));
-          // TODO: We need a spec discussion that we have to allow return and
-          // parameter types of a DebugTypeFunction to have template parameter.
-          if (return_type->opcode() != spv::Op::OpTypeVoid) {
-            auto validate_return = ValidateOperandDebugType(
-                _, "Return Type", inst, 6, ext_inst_name, true);
-            if (validate_return != SPV_SUCCESS) return validate_return;
-          }
-          for (uint32_t word_index = 7; word_index < num_words; ++word_index) {
-            auto validate_param = ValidateOperandDebugType(
-                _, "Parameter Types", inst, word_index, ext_inst_name, true);
-            if (validate_param != SPV_SUCCESS) return validate_param;
-          }
-          break;
-        }
-        case CommonDebugInfoDebugTypeEnum: {
-          CHECK_OPERAND("Name", spv::Op::OpString, 5);
-          if (!DoesDebugInfoOperandMatchExpectation(
-                  _,
-                  [](CommonDebugInfoInstructions dbg_inst) {
-                    return dbg_inst == CommonDebugInfoDebugInfoNone;
-                  },
-                  inst, 6)) {
-            auto validate_underlying_type = ValidateOperandDebugType(
-                _, "Underlying Types", inst, 6, ext_inst_name, false);
-            if (validate_underlying_type != SPV_SUCCESS)
-              return validate_underlying_type;
-          }
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
-          CHECK_CONST_UINT_OPERAND("Line", 8);
-          CHECK_CONST_UINT_OPERAND("Column", 9);
-          auto validate_parent =
-              ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
-          if (validate_parent != SPV_SUCCESS) return validate_parent;
-          CHECK_OPERAND("Size", spv::Op::OpConstant, 11);
-          auto* size = _.FindDef(inst->word(11));
-          if (!_.IsIntScalarType(size->type_id()) || !size->word(3)) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": expected operand Size is a "
-                   << "positive integer";
-          }
-          CHECK_CONST_UINT_OPERAND("Flags", 12);
-          for (uint32_t word_index = 13; word_index + 1 < num_words;
-               word_index += 2) {
-            CHECK_OPERAND("Value", spv::Op::OpConstant, word_index);
-            CHECK_OPERAND("Name", spv::Op::OpString, word_index + 1);
-          }
-          break;
-        }
-        case CommonDebugInfoDebugTypeComposite: {
-          CHECK_OPERAND("Name", spv::Op::OpString, 5);
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
-          CHECK_CONST_UINT_OPERAND("Line", 8);
-          CHECK_CONST_UINT_OPERAND("Column", 9);
-          auto validate_parent =
-              ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
-          if (validate_parent != SPV_SUCCESS) return validate_parent;
-          CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11);
-          if (!DoesDebugInfoOperandMatchExpectation(
-                  _,
-                  [](CommonDebugInfoInstructions dbg_inst) {
-                    return dbg_inst == CommonDebugInfoDebugInfoNone;
-                  },
-                  inst, 12)) {
-            CHECK_OPERAND("Size", spv::Op::OpConstant, 12);
-          }
-          CHECK_CONST_UINT_OPERAND("Flags", 13);
-          for (uint32_t word_index = 14; word_index < num_words; ++word_index) {
-            if (!DoesDebugInfoOperandMatchExpectation(
-                    _,
-                    [](CommonDebugInfoInstructions dbg_inst) {
-                      return dbg_inst == CommonDebugInfoDebugTypeMember ||
-                             dbg_inst == CommonDebugInfoDebugFunction ||
-                             dbg_inst == CommonDebugInfoDebugTypeInheritance;
-                    },
-                    inst, word_index)) {
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << ext_inst_name() << ": "
-                     << "expected operand Members "
-                     << "must be DebugTypeMember, DebugFunction, or "
-                        "DebugTypeInheritance";
-            }
-          }
-          break;
-        }
-        case CommonDebugInfoDebugTypeMember: {
-          CHECK_OPERAND("Name", spv::Op::OpString, 5);
-          // TODO: We need a spec discussion that we have to allow member types
-          // to have template parameter.
-          auto validate_type =
-              ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true);
-          if (validate_type != SPV_SUCCESS) return validate_type;
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
-          CHECK_CONST_UINT_OPERAND("Line", 8);
-          CHECK_CONST_UINT_OPERAND("Column", 9);
-          // NonSemantic.Shader.DebugInfo doesn't have the Parent operand
-          if (vulkanDebugInfo) {
-            CHECK_OPERAND("Offset", spv::Op::OpConstant, 10);
-            CHECK_OPERAND("Size", spv::Op::OpConstant, 11);
-            CHECK_CONST_UINT_OPERAND("Flags", 12);
-            if (num_words == 14)
-              CHECK_OPERAND("Value", spv::Op::OpConstant, 13);
           } else {
-            CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite,
-                                10);
-            CHECK_OPERAND("Offset", spv::Op::OpConstant, 11);
-            CHECK_OPERAND("Size", spv::Op::OpConstant, 12);
-            CHECK_CONST_UINT_OPERAND("Flags", 13);
-            if (num_words == 15)
-              CHECK_OPERAND("Value", spv::Op::OpConstant, 14);
+            invalid = true;
           }
-          break;
-        }
-        case CommonDebugInfoDebugTypeInheritance: {
-          CHECK_DEBUG_OPERAND("Child", CommonDebugInfoDebugTypeComposite, 5);
-          auto* debug_inst = _.FindDef(inst->word(5));
-          auto composite_type =
-              OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6));
-          if (composite_type != OpenCLDebugInfo100Class &&
-              composite_type != OpenCLDebugInfo100Structure) {
+          if (invalid) {
             return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected operand Child must be class or struct debug "
-                      "type";
+                   << GetExtInstName(_, inst) << ": Component Count must be "
+                   << (vulkanDebugInfo ? "a constant instruction"
+                                       : "OpConstant")
+                   << " with a 32- or 64-bits integer scalar type or "
+                   << "DebugGlobalVariable or DebugLocalVariable with a 32- "
+                      "or "
+                   << "64-bits unsigned integer scalar type";
           }
-          CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite, 6);
-          debug_inst = _.FindDef(inst->word(6));
-          composite_type =
-              OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6));
-          if (composite_type != OpenCLDebugInfo100Class &&
-              composite_type != OpenCLDebugInfo100Structure) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected operand Parent must be class or struct debug "
-                      "type";
-          }
-          CHECK_OPERAND("Offset", spv::Op::OpConstant, 7);
-          CHECK_OPERAND("Size", spv::Op::OpConstant, 8);
-          CHECK_CONST_UINT_OPERAND("Flags", 9);
-          break;
         }
-        case CommonDebugInfoDebugFunction: {
-          CHECK_OPERAND("Name", spv::Op::OpString, 5);
-          CHECK_DEBUG_OPERAND("Type", CommonDebugInfoDebugTypeFunction, 6);
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
-          CHECK_CONST_UINT_OPERAND("Line", 8);
-          CHECK_CONST_UINT_OPERAND("Column", 9);
-          auto validate_parent =
-              ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
-          if (validate_parent != SPV_SUCCESS) return validate_parent;
-          CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11);
-          CHECK_CONST_UINT_OPERAND("Flags", 12);
-          CHECK_CONST_UINT_OPERAND("Scope Line", 13);
-          // NonSemantic.Shader.DebugInfo.100 doesn't include a reference to the
-          // OpFunction
-          if (vulkanDebugInfo) {
-            if (num_words == 15) {
-              CHECK_DEBUG_OPERAND("Declaration",
-                                  CommonDebugInfoDebugFunctionDeclaration, 14);
-            }
-          } else {
-            if (!DoesDebugInfoOperandMatchExpectation(
-                    _,
-                    [](CommonDebugInfoInstructions dbg_inst) {
-                      return dbg_inst == CommonDebugInfoDebugInfoNone;
-                    },
-                    inst, 14)) {
-              CHECK_OPERAND("Function", spv::Op::OpFunction, 14);
-            }
-            if (num_words == 16) {
-              CHECK_DEBUG_OPERAND("Declaration",
-                                  CommonDebugInfoDebugFunctionDeclaration, 15);
-            }
-          }
-          break;
-        }
-        case CommonDebugInfoDebugFunctionDeclaration: {
-          CHECK_OPERAND("Name", spv::Op::OpString, 5);
-          CHECK_DEBUG_OPERAND("Type", CommonDebugInfoDebugTypeFunction, 6);
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
-          CHECK_CONST_UINT_OPERAND("Line", 8);
-          CHECK_CONST_UINT_OPERAND("Column", 9);
-          auto validate_parent =
-              ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
-          if (validate_parent != SPV_SUCCESS) return validate_parent;
-          CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11);
-          CHECK_CONST_UINT_OPERAND("Flags", 12);
-          break;
-        }
-        case CommonDebugInfoDebugLexicalBlock: {
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 5);
-          CHECK_CONST_UINT_OPERAND("Line", 6);
-          CHECK_CONST_UINT_OPERAND("Column", 7);
-          auto validate_parent =
-              ValidateOperandLexicalScope(_, "Parent", inst, 8, ext_inst_name);
-          if (validate_parent != SPV_SUCCESS) return validate_parent;
-          if (num_words == 10) CHECK_OPERAND("Name", spv::Op::OpString, 9);
-          break;
-        }
-        case CommonDebugInfoDebugScope: {
-          auto validate_scope =
-              ValidateOperandLexicalScope(_, "Scope", inst, 5, ext_inst_name);
-          if (validate_scope != SPV_SUCCESS) return validate_scope;
-          if (num_words == 7) {
-            CHECK_DEBUG_OPERAND("Inlined At", CommonDebugInfoDebugInlinedAt, 6);
-          }
-          break;
-        }
-        case CommonDebugInfoDebugLocalVariable: {
-          CHECK_OPERAND("Name", spv::Op::OpString, 5);
-          // TODO: We need a spec discussion that we have to allow local
-          // variable types to have template parameter.
-          auto validate_type =
-              ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true);
-          if (validate_type != SPV_SUCCESS) return validate_type;
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
-          CHECK_CONST_UINT_OPERAND("Line", 8);
-          CHECK_CONST_UINT_OPERAND("Column", 9);
-          auto validate_parent =
-              ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
-          if (validate_parent != SPV_SUCCESS) return validate_parent;
-          CHECK_CONST_UINT_OPERAND("Flags", 11);
-          if (num_words == 13) {
-            CHECK_CONST_UINT_OPERAND("ArgNumber", 12);
-          }
-          break;
-        }
-        case CommonDebugInfoDebugDeclare: {
-          CHECK_DEBUG_OPERAND("Local Variable",
-                              CommonDebugInfoDebugLocalVariable, 5);
-          auto* operand = _.FindDef(inst->word(6));
-          if (operand->opcode() != spv::Op::OpVariable &&
-              operand->opcode() != spv::Op::OpFunctionParameter) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected operand Variable must be a result id of "
-                      "OpVariable or OpFunctionParameter";
-          }
-
-          CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7);
-          break;
-        }
-        case CommonDebugInfoDebugExpression: {
-          for (uint32_t word_index = 5; word_index < num_words; ++word_index) {
-            CHECK_DEBUG_OPERAND("Operation", CommonDebugInfoDebugOperation,
-                                word_index);
-          }
-          break;
-        }
-        case CommonDebugInfoDebugTypeTemplate: {
-          if (!DoesDebugInfoOperandMatchExpectation(
-                  _,
-                  [](CommonDebugInfoInstructions dbg_inst) {
-                    return dbg_inst == CommonDebugInfoDebugTypeComposite ||
-                           dbg_inst == CommonDebugInfoDebugFunction;
-                  },
-                  inst, 5)) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected operand Target must be DebugTypeComposite "
-                   << "or DebugFunction";
-          }
-          for (uint32_t word_index = 6; word_index < num_words; ++word_index) {
-            if (!DoesDebugInfoOperandMatchExpectation(
-                    _,
-                    [](CommonDebugInfoInstructions dbg_inst) {
-                      return dbg_inst ==
-                                 CommonDebugInfoDebugTypeTemplateParameter ||
-                             dbg_inst ==
-                                 CommonDebugInfoDebugTypeTemplateTemplateParameter;
-                    },
-                    inst, word_index)) {
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << ext_inst_name() << ": "
-                     << "expected operand Parameters must be "
-                     << "DebugTypeTemplateParameter or "
-                     << "DebugTypeTemplateTemplateParameter";
-            }
-          }
-          break;
-        }
-        case CommonDebugInfoDebugTypeTemplateParameter: {
-          CHECK_OPERAND("Name", spv::Op::OpString, 5);
-          auto validate_actual_type = ValidateOperandDebugType(
-              _, "Actual Type", inst, 6, ext_inst_name, false);
-          if (validate_actual_type != SPV_SUCCESS) return validate_actual_type;
-          if (!DoesDebugInfoOperandMatchExpectation(
-                  _,
-                  [](CommonDebugInfoInstructions dbg_inst) {
-                    return dbg_inst == CommonDebugInfoDebugInfoNone;
-                  },
-                  inst, 7)) {
-            CHECK_OPERAND("Value", spv::Op::OpConstant, 7);
-          }
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 8);
-          CHECK_CONST_UINT_OPERAND("Line", 9);
-          CHECK_CONST_UINT_OPERAND("Column", 10);
-          break;
-        }
-        case CommonDebugInfoDebugGlobalVariable: {
-          CHECK_OPERAND("Name", spv::Op::OpString, 5);
-          auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6,
-                                                        ext_inst_name, false);
-          if (validate_type != SPV_SUCCESS) return validate_type;
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
-          CHECK_CONST_UINT_OPERAND("Line", 8);
-          CHECK_CONST_UINT_OPERAND("Column", 9);
-          auto validate_scope =
-              ValidateOperandLexicalScope(_, "Scope", inst, 10, ext_inst_name);
-          if (validate_scope != SPV_SUCCESS) return validate_scope;
-          CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11);
-          if (!DoesDebugInfoOperandMatchExpectation(
-                  _,
-                  [](CommonDebugInfoInstructions dbg_inst) {
-                    return dbg_inst == CommonDebugInfoDebugInfoNone;
-                  },
-                  inst, 12)) {
-            auto* operand = _.FindDef(inst->word(12));
-            if (operand->opcode() != spv::Op::OpVariable &&
-                operand->opcode() != spv::Op::OpConstant) {
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << ext_inst_name() << ": "
-                     << "expected operand Variable must be a result id of "
-                        "OpVariable or OpConstant or DebugInfoNone";
-            }
-          }
-          if (num_words == 15) {
-            CHECK_DEBUG_OPERAND("Static Member Declaration",
-                                CommonDebugInfoDebugTypeMember, 14);
-          }
-          break;
-        }
-        case CommonDebugInfoDebugInlinedAt: {
-          CHECK_CONST_UINT_OPERAND("Line", 5);
-          auto validate_scope =
-              ValidateOperandLexicalScope(_, "Scope", inst, 6, ext_inst_name);
-          if (validate_scope != SPV_SUCCESS) return validate_scope;
-          if (num_words == 8) {
-            CHECK_DEBUG_OPERAND("Inlined", CommonDebugInfoDebugInlinedAt, 7);
-          }
-          break;
-        }
-        case CommonDebugInfoDebugValue: {
-          CHECK_DEBUG_OPERAND("Local Variable",
-                              CommonDebugInfoDebugLocalVariable, 5);
-          CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7);
-
-          for (uint32_t word_index = 8; word_index < num_words; ++word_index) {
-            // TODO: The following code simply checks if it is a const int
-            // scalar or a DebugLocalVariable or DebugGlobalVariable, but we
-            // have to check it using the same validation for Indexes of
-            // OpAccessChain.
-            if (!IsConstWithIntScalarType(_, inst, word_index) &&
-                !IsDebugVariableWithIntScalarType(_, inst, word_index)) {
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << ext_inst_name() << ": expected operand Indexes is "
-                     << "OpConstant, DebugGlobalVariable, or "
-                     << "type is OpConstant with an integer scalar type";
-            }
-          }
-          break;
-        }
-
-        // TODO: Add validation rules for remaining cases as well.
-        case CommonDebugInfoDebugTypePtrToMember:
-        case CommonDebugInfoDebugTypeTemplateTemplateParameter:
-        case CommonDebugInfoDebugTypeTemplateParameterPack:
-        case CommonDebugInfoDebugLexicalBlockDiscriminator:
-        case CommonDebugInfoDebugInlinedVariable:
-        case CommonDebugInfoDebugMacroDef:
-        case CommonDebugInfoDebugMacroUndef:
-        case CommonDebugInfoDebugImportedEntity:
-          break;
-        case CommonDebugInfoInstructionsMax:
-          assert(0);
-          break;
+        break;
       }
-    }
-  } else if (ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) {
-    auto import_inst = _.FindDef(inst->GetOperandAs<uint32_t>(2));
-    const std::string name = import_inst->GetOperandAs<std::string>(1);
-    const std::string reflection = "NonSemantic.ClspvReflection.";
-    char* end_ptr;
-    auto version_string = name.substr(reflection.size());
-    if (version_string.empty()) {
-      return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
-             << "Missing NonSemantic.ClspvReflection import version";
-    }
-    uint32_t version = static_cast<uint32_t>(
-        std::strtoul(version_string.c_str(), &end_ptr, 10));
-    if (end_ptr && *end_ptr != '\0') {
-      return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
-             << "NonSemantic.ClspvReflection import does not encode the "
-                "version correctly";
-    }
-    if (version == 0 || version > NonSemanticClspvReflectionRevision) {
-      return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
-             << "Unknown NonSemantic.ClspvReflection import version";
-    }
+      case CommonDebugInfoDebugTypedef: {
+        CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        auto validate_base_type = ValidateOperandBaseType(_, inst, 6);
+        if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+        CHECK_CONST_UINT_OPERAND("Line", 8);
+        CHECK_CONST_UINT_OPERAND("Column", 9);
+        if (auto error =
+                ValidateOperandDebugSource(_, inst, 7, 8, 9, ext_inst_type))
+          return error;
 
-    return ValidateClspvReflectionInstruction(_, inst, version);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        break;
+      }
+      case CommonDebugInfoDebugTypeFunction: {
+        CHECK_CONST_UINT_OPERAND("Flags", 5);
+        auto* return_type = _.FindDef(inst->word(6));
+        // TODO: We need a spec discussion that we have to allow return and
+        // parameter types of a DebugTypeFunction to have template parameter.
+        if (return_type->opcode() != spv::Op::OpTypeVoid) {
+          auto validate_return =
+              ValidateOperandDebugType(_, "Return Type", inst, 6, true);
+          if (validate_return != SPV_SUCCESS) return validate_return;
+        }
+        for (uint32_t word_index = 7; word_index < num_words; ++word_index) {
+          auto validate_param = ValidateOperandDebugType(
+              _, "Parameter Types", inst, word_index, true);
+          if (validate_param != SPV_SUCCESS) return validate_param;
+        }
+        break;
+      }
+      case CommonDebugInfoDebugTypeEnum: {
+        CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        if (!DoesDebugInfoOperandMatchExpectation(
+                _,
+                [](CommonDebugInfoInstructions dbg_inst) {
+                  return dbg_inst == CommonDebugInfoDebugInfoNone;
+                },
+                inst, 6)) {
+          auto validate_underlying_type =
+              ValidateOperandDebugType(_, "Underlying Types", inst, 6, false);
+          if (validate_underlying_type != SPV_SUCCESS)
+            return validate_underlying_type;
+        }
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+        CHECK_CONST_UINT_OPERAND("Line", 8);
+        CHECK_CONST_UINT_OPERAND("Column", 9);
+        if (auto error =
+                ValidateOperandDebugSource(_, inst, 7, 8, 9, ext_inst_type))
+          return error;
+
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        CHECK_OPERAND("Size", spv::Op::OpConstant, 11);
+        auto* size = _.FindDef(inst->word(11));
+        if (!_.IsIntScalarType(size->type_id()) || !size->word(3)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": expected operand Size is a "
+                 << "positive integer";
+        }
+        CHECK_CONST_UINT_OPERAND("Flags", 12);
+        for (uint32_t word_index = 13; word_index + 1 < num_words;
+             word_index += 2) {
+          CHECK_OPERAND("Value", spv::Op::OpConstant, word_index);
+          CHECK_OPERAND("Name", spv::Op::OpString, word_index + 1);
+        }
+        break;
+      }
+      case CommonDebugInfoDebugTypeComposite: {
+        CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+        CHECK_CONST_UINT_OPERAND("Line", 8);
+        CHECK_CONST_UINT_OPERAND("Column", 9);
+        if (auto error =
+                ValidateOperandDebugSource(_, inst, 7, 8, 9, ext_inst_type))
+          return error;
+
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11);
+        if (!DoesDebugInfoOperandMatchExpectation(
+                _,
+                [](CommonDebugInfoInstructions dbg_inst) {
+                  return dbg_inst == CommonDebugInfoDebugInfoNone;
+                },
+                inst, 12)) {
+          CHECK_OPERAND("Size", spv::Op::OpConstant, 12);
+        }
+        CHECK_CONST_UINT_OPERAND("Flags", 13);
+        for (uint32_t word_index = 14; word_index < num_words; ++word_index) {
+          if (!DoesDebugInfoOperandMatchExpectation(
+                  _,
+                  [](CommonDebugInfoInstructions dbg_inst) {
+                    return dbg_inst == CommonDebugInfoDebugTypeMember ||
+                           dbg_inst == CommonDebugInfoDebugFunction ||
+                           dbg_inst == CommonDebugInfoDebugTypeInheritance;
+                  },
+                  inst, word_index)) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << GetExtInstName(_, inst) << ": "
+                   << "expected operand Members "
+                   << "must be DebugTypeMember, DebugFunction, or "
+                      "DebugTypeInheritance";
+          }
+        }
+        break;
+      }
+      case CommonDebugInfoDebugTypeMember: {
+        CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        // TODO: We need a spec discussion that we have to allow member types
+        // to have template parameter.
+        auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6, true);
+        if (validate_type != SPV_SUCCESS) return validate_type;
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+        CHECK_CONST_UINT_OPERAND("Line", 8);
+        CHECK_CONST_UINT_OPERAND("Column", 9);
+        if (auto error =
+                ValidateOperandDebugSource(_, inst, 7, 8, 9, ext_inst_type))
+          return error;
+        // NonSemantic.Shader.DebugInfo doesn't have the Parent operand
+        if (vulkanDebugInfo) {
+          CHECK_OPERAND("Offset", spv::Op::OpConstant, 10);
+          CHECK_OPERAND("Size", spv::Op::OpConstant, 11);
+          CHECK_CONST_UINT_OPERAND("Flags", 12);
+          if (has_optional_at(14))
+            CHECK_OPERAND("Value", spv::Op::OpConstant, 13);
+        } else {
+          CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite, 10);
+          CHECK_OPERAND("Offset", spv::Op::OpConstant, 11);
+          CHECK_OPERAND("Size", spv::Op::OpConstant, 12);
+          CHECK_CONST_UINT_OPERAND("Flags", 13);
+          if (has_optional_at(15))
+            CHECK_OPERAND("Value", spv::Op::OpConstant, 14);
+        }
+        break;
+      }
+      case CommonDebugInfoDebugTypeInheritance: {
+        CHECK_DEBUG_OPERAND("Child", CommonDebugInfoDebugTypeComposite, 5);
+        auto* debug_inst = _.FindDef(inst->word(5));
+        auto composite_type =
+            OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6));
+        if (composite_type != OpenCLDebugInfo100Class &&
+            composite_type != OpenCLDebugInfo100Structure) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected operand Child must be class or struct debug "
+                    "type";
+        }
+        CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite, 6);
+        debug_inst = _.FindDef(inst->word(6));
+        composite_type =
+            OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6));
+        if (composite_type != OpenCLDebugInfo100Class &&
+            composite_type != OpenCLDebugInfo100Structure) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected operand Parent must be class or struct debug "
+                    "type";
+        }
+        CHECK_OPERAND("Offset", spv::Op::OpConstant, 7);
+        CHECK_OPERAND("Size", spv::Op::OpConstant, 8);
+        CHECK_CONST_UINT_OPERAND("Flags", 9);
+        break;
+      }
+      case CommonDebugInfoDebugFunction: {
+        CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        CHECK_DEBUG_OPERAND("Type", CommonDebugInfoDebugTypeFunction, 6);
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+        CHECK_CONST_UINT_OPERAND("Line", 8);
+        CHECK_CONST_UINT_OPERAND("Column", 9);
+        if (auto error =
+                ValidateOperandDebugSource(_, inst, 7, 8, 9, ext_inst_type))
+          return error;
+
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11);
+        CHECK_CONST_UINT_OPERAND("Flags", 12);
+        CHECK_CONST_UINT_OPERAND("Scope Line", 13);
+        // NonSemantic.Shader.DebugInfo.100 doesn't include a reference to the
+        // OpFunction
+        if (vulkanDebugInfo) {
+          if (has_optional_at(15)) {
+            CHECK_DEBUG_OPERAND("Declaration",
+                                CommonDebugInfoDebugFunctionDeclaration, 14);
+          }
+        } else {
+          if (!DoesDebugInfoOperandMatchExpectation(
+                  _,
+                  [](CommonDebugInfoInstructions dbg_inst) {
+                    return dbg_inst == CommonDebugInfoDebugInfoNone;
+                  },
+                  inst, 14)) {
+            CHECK_OPERAND("Function", spv::Op::OpFunction, 14);
+          }
+          if (has_optional_at(16)) {
+            CHECK_DEBUG_OPERAND("Declaration",
+                                CommonDebugInfoDebugFunctionDeclaration, 15);
+          }
+        }
+        break;
+      }
+      case CommonDebugInfoDebugFunctionDeclaration: {
+        CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        CHECK_DEBUG_OPERAND("Type", CommonDebugInfoDebugTypeFunction, 6);
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+        CHECK_CONST_UINT_OPERAND("Line", 8);
+        CHECK_CONST_UINT_OPERAND("Column", 9);
+        if (auto error =
+                ValidateOperandDebugSource(_, inst, 7, 8, 9, ext_inst_type))
+          return error;
+
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11);
+        CHECK_CONST_UINT_OPERAND("Flags", 12);
+        break;
+      }
+      case CommonDebugInfoDebugLexicalBlock: {
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 5);
+        CHECK_CONST_UINT_OPERAND("Line", 6);
+        CHECK_CONST_UINT_OPERAND("Column", 7);
+        if (auto error =
+                ValidateOperandDebugSource(_, inst, 5, 6, 7, ext_inst_type))
+          return error;
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 8);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        if (has_optional_at(10)) CHECK_OPERAND("Name", spv::Op::OpString, 9);
+        break;
+      }
+      case CommonDebugInfoDebugScope: {
+        auto validate_scope = ValidateOperandLexicalScope(_, "Scope", inst, 5);
+        if (validate_scope != SPV_SUCCESS) return validate_scope;
+        if (has_optional_at(7)) {
+          CHECK_DEBUG_OPERAND("Inlined At", CommonDebugInfoDebugInlinedAt, 6);
+        }
+        break;
+      }
+      case CommonDebugInfoDebugLocalVariable: {
+        CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        // TODO: We need a spec discussion that we have to allow local
+        // variable types to have template parameter.
+        auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6, true);
+        if (validate_type != SPV_SUCCESS) return validate_type;
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+        CHECK_CONST_UINT_OPERAND("Line", 8);
+        CHECK_CONST_UINT_OPERAND("Column", 9);
+        if (auto error =
+                ValidateOperandDebugSource(_, inst, 7, 8, 9, ext_inst_type))
+          return error;
+
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        CHECK_CONST_UINT_OPERAND("Flags", 11);
+        if (has_optional_at(13)) {
+          CHECK_CONST_UINT_OPERAND("ArgNumber", 12);
+        }
+        break;
+      }
+      case CommonDebugInfoDebugDeclare: {
+        CHECK_DEBUG_OPERAND("Local Variable", CommonDebugInfoDebugLocalVariable,
+                            5);
+        auto* operand = _.FindDef(inst->word(6));
+        if (operand->opcode() != spv::Op::OpVariable &&
+            operand->opcode() != spv::Op::OpFunctionParameter) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected operand Variable must be a result id of "
+                    "OpVariable or OpFunctionParameter";
+        }
+
+        CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7);
+        break;
+      }
+      case CommonDebugInfoDebugExpression: {
+        for (uint32_t word_index = 5; word_index < num_words; ++word_index) {
+          CHECK_DEBUG_OPERAND("Operation", CommonDebugInfoDebugOperation,
+                              word_index);
+        }
+        break;
+      }
+      case CommonDebugInfoDebugTypeTemplate: {
+        if (!DoesDebugInfoOperandMatchExpectation(
+                _,
+                [](CommonDebugInfoInstructions dbg_inst) {
+                  return dbg_inst == CommonDebugInfoDebugTypeComposite ||
+                         dbg_inst == CommonDebugInfoDebugFunction;
+                },
+                inst, 5)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected operand Target must be DebugTypeComposite "
+                 << "or DebugFunction";
+        }
+        for (uint32_t word_index = 6; word_index < num_words; ++word_index) {
+          if (!DoesDebugInfoOperandMatchExpectation(
+                  _,
+                  [](CommonDebugInfoInstructions dbg_inst) {
+                    return dbg_inst ==
+                               CommonDebugInfoDebugTypeTemplateParameter ||
+                           dbg_inst ==
+                               CommonDebugInfoDebugTypeTemplateTemplateParameter;
+                  },
+                  inst, word_index)) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << GetExtInstName(_, inst) << ": "
+                   << "expected operand Parameters must be "
+                   << "DebugTypeTemplateParameter or "
+                   << "DebugTypeTemplateTemplateParameter";
+          }
+        }
+        break;
+      }
+      case CommonDebugInfoDebugTypeTemplateParameter: {
+        CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        auto validate_actual_type =
+            ValidateOperandDebugType(_, "Actual Type", inst, 6, false);
+        if (validate_actual_type != SPV_SUCCESS) return validate_actual_type;
+        if (!DoesDebugInfoOperandMatchExpectation(
+                _,
+                [](CommonDebugInfoInstructions dbg_inst) {
+                  return dbg_inst == CommonDebugInfoDebugInfoNone;
+                },
+                inst, 7)) {
+          CHECK_OPERAND("Value", spv::Op::OpConstant, 7);
+        }
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 8);
+        CHECK_CONST_UINT_OPERAND("Line", 9);
+        CHECK_CONST_UINT_OPERAND("Column", 10);
+        if (auto error =
+                ValidateOperandDebugSource(_, inst, 8, 9, 10, ext_inst_type))
+          return error;
+        break;
+      }
+      case CommonDebugInfoDebugGlobalVariable: {
+        CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        auto validate_type =
+            ValidateOperandDebugType(_, "Type", inst, 6, false);
+        if (validate_type != SPV_SUCCESS) return validate_type;
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+        CHECK_CONST_UINT_OPERAND("Line", 8);
+        CHECK_CONST_UINT_OPERAND("Column", 9);
+        if (auto error =
+                ValidateOperandDebugSource(_, inst, 7, 8, 9, ext_inst_type))
+          return error;
+
+        auto validate_scope = ValidateOperandLexicalScope(_, "Scope", inst, 10);
+        if (validate_scope != SPV_SUCCESS) return validate_scope;
+        CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11);
+        if (!DoesDebugInfoOperandMatchExpectation(
+                _,
+                [](CommonDebugInfoInstructions dbg_inst) {
+                  return dbg_inst == CommonDebugInfoDebugInfoNone;
+                },
+                inst, 12)) {
+          auto* operand = _.FindDef(inst->word(12));
+          std::initializer_list<spv::Op> allowed_opcodes = {
+              spv::Op::OpVariable,
+              spv::Op::OpConstantTrue,
+              spv::Op::OpConstantFalse,
+              spv::Op::OpConstant,
+              spv::Op::OpConstantComposite,
+              spv::Op::OpConstantSampler,
+              spv::Op::OpConstantNull,
+              spv::Op::OpSpecConstantTrue,
+              spv::Op::OpSpecConstantFalse,
+              spv::Op::OpSpecConstant,
+              spv::Op::OpSpecConstantComposite,
+              spv::Op::OpSpecConstantOp};
+          if (std::find(allowed_opcodes.begin(), allowed_opcodes.end(),
+                        operand->opcode()) == allowed_opcodes.end()) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << GetExtInstName(_, inst) << ": "
+                   << "expected operand Variable must be a result id of "
+                      "OpVariable, OpConstant variant, OpSpecConstant variant "
+                      "or DebugInfoNone";
+          }
+        }
+        if (has_optional_at(15)) {
+          CHECK_DEBUG_OPERAND("Static Member Declaration",
+                              CommonDebugInfoDebugTypeMember, 14);
+        }
+        break;
+      }
+      case CommonDebugInfoDebugInlinedAt: {
+        CHECK_CONST_UINT_OPERAND("Line", 5);
+        auto validate_scope = ValidateOperandLexicalScope(_, "Scope", inst, 6);
+        if (validate_scope != SPV_SUCCESS) return validate_scope;
+        if (has_optional_at(8)) {
+          CHECK_DEBUG_OPERAND("Inlined", CommonDebugInfoDebugInlinedAt, 7);
+        }
+        break;
+      }
+      case CommonDebugInfoDebugValue: {
+        CHECK_DEBUG_OPERAND("Local Variable", CommonDebugInfoDebugLocalVariable,
+                            5);
+        CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7);
+
+        for (uint32_t word_index = 8; word_index < num_words; ++word_index) {
+          // TODO: The following code simply checks if it is a const int
+          // scalar or a DebugLocalVariable or DebugGlobalVariable, but we
+          // have to check it using the same validation for Indexes of
+          // OpAccessChain.
+          if (!IsConstWithIntScalarType(_, inst, word_index) &&
+              !IsDebugVariableWithIntScalarType(_, inst, word_index)) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << GetExtInstName(_, inst)
+                   << ": expected operand Indexes is "
+                   << "OpConstant, DebugGlobalVariable, or "
+                   << "type is OpConstant with an integer scalar type";
+          }
+        }
+        break;
+      }
+
+      // TODO: Add validation rules for remaining cases as well.
+      case CommonDebugInfoDebugTypePtrToMember:
+      case CommonDebugInfoDebugTypeTemplateTemplateParameter:
+      case CommonDebugInfoDebugTypeTemplateParameterPack:
+      case CommonDebugInfoDebugLexicalBlockDiscriminator:
+      case CommonDebugInfoDebugInlinedVariable:
+      case CommonDebugInfoDebugMacroDef:
+      case CommonDebugInfoDebugMacroUndef:
+      case CommonDebugInfoDebugImportedEntity:
+        break;
+      case CommonDebugInfoInstructionsMax:
+        assert(0);
+        break;
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateExtInstNonsemanticClspvReflection(
+    ValidationState_t& _, const Instruction* inst) {
+  auto import_inst = _.FindDef(inst->GetOperandAs<uint32_t>(2));
+  const std::string name = import_inst->GetOperandAs<std::string>(1);
+  const std::string reflection = "NonSemantic.ClspvReflection.";
+  char* end_ptr;
+  auto version_string = name.substr(reflection.size());
+  if (version_string.empty()) {
+    return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
+           << "Missing NonSemantic.ClspvReflection import version";
+  }
+  uint32_t version =
+      static_cast<uint32_t>(std::strtoul(version_string.c_str(), &end_ptr, 10));
+  if (end_ptr && *end_ptr != '\0') {
+    return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
+           << "NonSemantic.ClspvReflection import does not encode the "
+              "version correctly";
+  }
+  if (version == 0 || version > NonSemanticClspvReflectionRevision) {
+    return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
+           << "Unknown NonSemantic.ClspvReflection import version";
+  }
+
+  return ValidateClspvReflectionInstruction(_, inst, version);
+}
+
+spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
+  const spv_ext_inst_type_t ext_inst_type =
+      spv_ext_inst_type_t(inst->ext_inst_type());
+
+  if (ext_inst_type == SPV_EXT_INST_TYPE_GLSL_STD_450) {
+    return ValidateExtInstGlslStd450(_, inst);
+  } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_STD) {
+    return ValidateExtInstOpenClStd(_, inst);
+  } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
+             ext_inst_type ==
+                 SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
+    return ValidateExtInstDebugInfo(_, inst);
+  } else if (ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) {
+    return ValidateExtInstNonsemanticClspvReflection(_, inst);
   }
 
   return SPV_SUCCESS;
diff --git a/source/val/validate_function.cpp b/source/val/validate_function.cpp
index 624b4e2..9892b2c 100644
--- a/source/val/validate_function.cpp
+++ b/source/val/validate_function.cpp
@@ -14,8 +14,8 @@
 
 #include <algorithm>
 
-#include "source/enum_string_mapping.h"
 #include "source/opcode.h"
+#include "source/table2.h"
 #include "source/val/instruction.h"
 #include "source/val/validate.h"
 #include "source/val/validation_state.h"
@@ -89,12 +89,15 @@
       spv::Op::OpName,
       spv::Op::OpCooperativeMatrixPerElementOpNV,
       spv::Op::OpCooperativeMatrixReduceNV,
-      spv::Op::OpCooperativeMatrixLoadTensorNV};
+      spv::Op::OpCooperativeMatrixLoadTensorNV,
+      spv::Op::OpConditionalEntryPointINTEL,
+      spv::Op::OpConstantFunctionPointerINTEL};
   for (auto& pair : inst->uses()) {
     const auto* use = pair.first;
     if (std::find(acceptable.begin(), acceptable.end(), use->opcode()) ==
             acceptable.end() &&
-        !use->IsNonSemantic() && !use->IsDebugInfo()) {
+        !use->IsNonSemantic() && !use->IsDebugInfo() &&
+        !spvOpcodeIsDecoration(use->opcode())) {
       return _.diag(SPV_ERROR_INVALID_ID, use)
              << "Invalid use of function result id " << _.getIdName(inst->id())
              << ".";
@@ -109,11 +112,6 @@
   // NOTE: Find OpFunction & ensure OpFunctionParameter is not out of place.
   size_t param_index = 0;
   size_t inst_num = inst->LineNum() - 1;
-  if (inst_num == 0) {
-    return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
-           << "Function parameter cannot be the first instruction.";
-  }
-
   auto func_inst = &_.ordered_instructions()[inst_num];
   while (--inst_num) {
     func_inst = &_.ordered_instructions()[inst_num];
@@ -152,80 +150,6 @@
               "type of the same index.";
   }
 
-  // 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) == spv::Op::OpTypeArray) {
-    param_nonarray_type_id =
-        _.FindDef(param_nonarray_type_id)->GetOperandAs<uint32_t>(1u);
-  }
-  if (_.GetIdOpcode(param_nonarray_type_id) == spv::Op::OpTypePointer ||
-      _.GetIdOpcode(param_nonarray_type_id) ==
-          spv::Op::OpTypeUntypedPointerKHR) {
-    auto param_nonarray_type = _.FindDef(param_nonarray_type_id);
-    if (param_nonarray_type->GetOperandAs<spv::StorageClass>(1u) ==
-        spv::StorageClass::PhysicalStorageBuffer) {
-      // check for Aliased or Restrict
-      const auto& decorations = _.id_decorations(inst->id());
-
-      bool foundAliased = std::any_of(
-          decorations.begin(), decorations.end(), [](const Decoration& d) {
-            return spv::Decoration::Aliased == d.dec_type();
-          });
-
-      bool foundRestrict = std::any_of(
-          decorations.begin(), decorations.end(), [](const Decoration& d) {
-            return spv::Decoration::Restrict == d.dec_type();
-          });
-
-      if (!foundAliased && !foundRestrict) {
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << "OpFunctionParameter " << inst->id()
-               << ": 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 "
-                  "PhysicalStorageBuffer pointer.";
-      }
-    } else if (param_nonarray_type->opcode() == spv::Op::OpTypePointer) {
-      const auto pointee_type_id =
-          param_nonarray_type->GetOperandAs<uint32_t>(2);
-      const auto pointee_type = _.FindDef(pointee_type_id);
-      if (spv::Op::OpTypePointer == pointee_type->opcode() &&
-          pointee_type->GetOperandAs<spv::StorageClass>(1u) ==
-              spv::StorageClass::PhysicalStorageBuffer) {
-        // check for AliasedPointer/RestrictPointer
-        const auto& decorations = _.id_decorations(inst->id());
-
-        bool foundAliased = std::any_of(
-            decorations.begin(), decorations.end(), [](const Decoration& d) {
-              return spv::Decoration::AliasedPointer == d.dec_type();
-            });
-
-        bool foundRestrict = std::any_of(
-            decorations.begin(), decorations.end(), [](const Decoration& d) {
-              return spv::Decoration::RestrictPointer == d.dec_type();
-            });
-
-        if (!foundAliased && !foundRestrict) {
-          return _.diag(SPV_ERROR_INVALID_ID, inst)
-                 << "OpFunctionParameter " << inst->id()
-                 << ": expected AliasedPointer or RestrictPointer for "
-                    "PhysicalStorageBuffer pointer.";
-        }
-        if (foundAliased && foundRestrict) {
-          return _.diag(SPV_ERROR_INVALID_ID, inst)
-                 << "OpFunctionParameter " << inst->id()
-                 << ": can't specify both AliasedPointer and "
-                    "RestrictPointer for PhysicalStorageBuffer pointer.";
-        }
-      }
-    }
-  }
-
   return SPV_SUCCESS;
 }
 
@@ -246,6 +170,34 @@
            << "s type does not match Function <id> "
            << _.getIdName(return_type->id()) << "s return type.";
   }
+  if (!_.options()->relax_logical_pointer &&
+      (_.addressing_model() == spv::AddressingModel::Logical ||
+       _.addressing_model() == spv::AddressingModel::PhysicalStorageBuffer64)) {
+    if (return_type->opcode() == spv::Op::OpTypePointer ||
+        return_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
+      const auto sc = return_type->GetOperandAs<spv::StorageClass>(1);
+      if (sc != spv::StorageClass::PhysicalStorageBuffer) {
+        if (!_.HasCapability(spv::Capability::VariablePointersStorageBuffer) &&
+            sc == spv::StorageClass::StorageBuffer) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "In Logical addressing, functions may only return a "
+                    "storage buffer pointer if the "
+                    "VariablePointersStorageBuffer capability is declared";
+        } else if (!_.HasCapability(spv::Capability::VariablePointers) &&
+                   sc == spv::StorageClass::Workgroup) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "In Logical addressing, functions may only return a "
+                    "workgroup pointer if the VariablePointers capability is "
+                    "declared";
+        } else if (sc != spv::StorageClass::StorageBuffer &&
+                   sc != spv::StorageClass::Workgroup) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "In Logical addressing, functions may not return a pointer "
+                    "in this storage class";
+        }
+      }
+    }
+  }
 
   const auto function_type_id = function->GetOperandAs<uint32_t>(3);
   const auto function_type = _.FindDef(function_type_id);
@@ -292,50 +244,59 @@
       }
     }
 
-    if (_.addressing_model() == spv::AddressingModel::Logical) {
+    if (_.addressing_model() == spv::AddressingModel::Logical ||
+        _.addressing_model() == spv::AddressingModel::PhysicalStorageBuffer64) {
       if ((parameter_type->opcode() == spv::Op::OpTypePointer ||
            parameter_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) &&
           !_.options()->relax_logical_pointer) {
         spv::StorageClass sc =
             parameter_type->GetOperandAs<spv::StorageClass>(1u);
-        // Validate which storage classes can be pointer operands.
-        switch (sc) {
-          case spv::StorageClass::UniformConstant:
-          case spv::StorageClass::Function:
-          case spv::StorageClass::Private:
-          case spv::StorageClass::Workgroup:
-          case spv::StorageClass::AtomicCounter:
-            // These are always allowed.
-            break;
-          case spv::StorageClass::StorageBuffer:
-            if (!_.features().variable_pointers) {
+        if (sc != spv::StorageClass::PhysicalStorageBuffer) {
+          // Validate which storage classes can be pointer operands.
+          switch (sc) {
+            case spv::StorageClass::UniformConstant:
+            case spv::StorageClass::Function:
+            case spv::StorageClass::Private:
+            case spv::StorageClass::Workgroup:
+            case spv::StorageClass::AtomicCounter:
+            // SPV_EXT_tile_image
+            case spv::StorageClass::TileImageEXT:
+            // SPV_KHR_ray_tracing
+            case spv::StorageClass::ShaderRecordBufferKHR:
+              // These are always allowed.
+              break;
+            case spv::StorageClass::StorageBuffer:
+              if (!_.features().variable_pointers) {
+                return _.diag(SPV_ERROR_INVALID_ID, inst)
+                       << "StorageBuffer pointer operand "
+                       << _.getIdName(argument_id)
+                       << " requires a variable pointers capability";
+              }
+              break;
+            default:
               return _.diag(SPV_ERROR_INVALID_ID, inst)
-                     << "StorageBuffer pointer operand "
-                     << _.getIdName(argument_id)
-                     << " requires a variable pointers capability";
-            }
-            break;
-          default:
-            return _.diag(SPV_ERROR_INVALID_ID, inst)
-                   << "Invalid storage class for pointer operand "
-                   << _.getIdName(argument_id);
-        }
+                     << "Invalid storage class for pointer operand "
+                     << _.getIdName(argument_id);
+          }
 
-        // Validate memory object declaration requirements.
-        if (argument->opcode() != spv::Op::OpVariable &&
-            argument->opcode() != spv::Op::OpUntypedVariableKHR &&
-            argument->opcode() != spv::Op::OpFunctionParameter) {
-          const bool ssbo_vptr =
-              _.HasCapability(spv::Capability::VariablePointersStorageBuffer) &&
-              sc == spv::StorageClass::StorageBuffer;
-          const bool wg_vptr =
-              _.HasCapability(spv::Capability::VariablePointers) &&
-              sc == spv::StorageClass::Workgroup;
-          const bool uc_ptr = sc == spv::StorageClass::UniformConstant;
-          if (!ssbo_vptr && !wg_vptr && !uc_ptr) {
-            return _.diag(SPV_ERROR_INVALID_ID, inst)
-                   << "Pointer operand " << _.getIdName(argument_id)
-                   << " must be a memory object declaration";
+          // Validate memory object declaration requirements.
+          if (argument->opcode() != spv::Op::OpVariable &&
+              argument->opcode() != spv::Op::OpUntypedVariableKHR &&
+              argument->opcode() != spv::Op::OpFunctionParameter) {
+            const bool ssbo_vptr =
+                _.HasCapability(
+                    spv::Capability::VariablePointersStorageBuffer) &&
+                sc == spv::StorageClass::StorageBuffer;
+            const bool wg_vptr =
+                _.HasCapability(spv::Capability::VariablePointers) &&
+                sc == spv::StorageClass::Workgroup;
+            const bool uc_ptr = sc == spv::StorageClass::UniformConstant;
+            if (!_.options()->before_hlsl_legalization && !ssbo_vptr &&
+                !wg_vptr && !uc_ptr) {
+              return _.diag(SPV_ERROR_INVALID_ID, inst)
+                     << "Pointer operand " << _.getIdName(argument_id)
+                     << " must be a memory object declaration";
+            }
           }
         }
       }
@@ -394,14 +355,14 @@
   const auto param0_id = function_type->GetOperandAs<uint32_t>(2);
   const auto param1_id = function_type->GetOperandAs<uint32_t>(3);
   const auto param2_id = function_type->GetOperandAs<uint32_t>(4);
-  if (!_.IsIntScalarType(param0_id) || _.GetBitWidth(param0_id) != 32) {
+  if (!_.IsIntScalarType(param0_id, 32)) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpCooperativeMatrixPerElementOpNV function type first parameter "
               "type <id> "
            << _.getIdName(param0_id) << " must be a 32-bit integer.";
   }
 
-  if (!_.IsIntScalarType(param1_id) || _.GetBitWidth(param1_id) != 32) {
+  if (!_.IsIntScalarType(param1_id, 32)) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpCooperativeMatrixPerElementOpNV function type second "
               "parameter type <id> "
diff --git a/source/val/validate_graph.cpp b/source/val/validate_graph.cpp
new file mode 100644
index 0000000..09d53ea
--- /dev/null
+++ b/source/val/validate_graph.cpp
@@ -0,0 +1,547 @@
+// Copyright (c) 2023-2025 Arm Ltd.
+//
+// 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.
+
+// Validates correctness of graph instructions.
+
+#include <deque>
+
+#include "source/opcode.h"
+#include "source/val/validate.h"
+#include "source/val/validation_state.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+bool IsTensorArray(ValidationState_t& _, uint32_t id) {
+  auto def = _.FindDef(id);
+  if (!def || (def->opcode() != spv::Op::OpTypeArray &&
+               def->opcode() != spv::Op::OpTypeRuntimeArray)) {
+    return false;
+  }
+  auto tdef = _.FindDef(def->word(2));
+  if (!tdef || tdef->opcode() != spv::Op::OpTypeTensorARM) {
+    return false;
+  }
+  return true;
+}
+
+bool IsGraphInterfaceType(ValidationState_t& _, uint32_t id) {
+  return _.IsTensorType(id) || IsTensorArray(_, id);
+}
+
+bool IsGraph(ValidationState_t& _, uint32_t id) {
+  auto def = _.FindDef(id);
+  if (!def || def->opcode() != spv::Op::OpGraphARM) {
+    return false;
+  }
+  return true;
+}
+
+bool IsGraphType(ValidationState_t& _, uint32_t id) {
+  auto def = _.FindDef(id);
+  if (!def || def->opcode() != spv::Op::OpTypeGraphARM) {
+    return false;
+  }
+  return true;
+}
+
+const uint32_t kGraphTypeIOStartWord = 3;
+
+uint32_t GraphTypeInstNumIO(const Instruction* inst) {
+  return static_cast<uint32_t>(inst->words().size()) - kGraphTypeIOStartWord;
+}
+
+uint32_t GraphTypeInstNumInputs(const Instruction* inst) {
+  return inst->word(2);
+}
+
+uint32_t GraphTypeInstNumOutputs(const Instruction* inst) {
+  return GraphTypeInstNumIO(inst) - GraphTypeInstNumInputs(inst);
+}
+
+uint32_t GraphTypeInstGetOutputAtIndex(const Instruction* inst,
+                                       uint64_t index) {
+  return inst->word(kGraphTypeIOStartWord + GraphTypeInstNumInputs(inst) +
+                    static_cast<uint32_t>(index));
+}
+
+uint32_t GraphTypeInstGetInputAtIndex(const Instruction* inst, uint64_t index) {
+  return inst->word(kGraphTypeIOStartWord + static_cast<uint32_t>(index));
+}
+
+spv_result_t ValidateGraphType(ValidationState_t& _, const Instruction* inst) {
+  // Check there are at least NumInputs types
+  uint32_t NumInputs = GraphTypeInstNumInputs(inst);
+  size_t NumIOTypes = GraphTypeInstNumIO(inst);
+  if (NumIOTypes < NumInputs) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << NumIOTypes << " I/O types were provided but the graph has "
+           << NumInputs << " inputs.";
+  }
+
+  // Check there is at least one output
+  if (NumIOTypes == NumInputs) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "A graph type must have at least one output.";
+  }
+
+  // Check all I/O types are graph interface type
+  for (unsigned i = kGraphTypeIOStartWord; i < inst->words().size(); i++) {
+    auto tid = inst->word(i);
+    if (!IsGraphInterfaceType(_, tid)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "I/O type " << _.getIdName(tid)
+             << " is not a Graph Interface Type.";
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateGraphConstant(ValidationState_t& _,
+                                   const Instruction* inst) {
+  // Check Result Type
+  if (!_.IsTensorType(inst->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << spvOpcodeString(inst->opcode())
+           << " must have a Result Type that is a tensor type.";
+  }
+
+  // Check the instruction is not preceded by another OpGraphConstantARM with
+  // the same ID
+  const uint32_t cst_id = inst->word(3);
+  size_t inst_num = inst->LineNum() - 1;
+  while (--inst_num) {
+    auto prev_inst = &_.ordered_instructions()[inst_num];
+    if (prev_inst->opcode() == spv::Op::OpGraphConstantARM) {
+      const uint32_t prev_cst_id = prev_inst->word(3);
+      if (prev_cst_id == cst_id) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "No two OpGraphConstantARM instructions may have the same "
+                  "GraphConstantID";
+      }
+    }
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateGraphEntryPoint(ValidationState_t& _,
+                                     const Instruction* inst) {
+  // Graph must be an OpGraphARM
+  uint32_t graph = inst->GetOperandAs<uint32_t>(0);
+  auto graph_inst = _.FindDef(graph);
+  if (!IsGraph(_, graph)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << spvOpcodeString(inst->opcode())
+           << " Graph must be a OpGraphARM but found "
+           << spvOpcodeString(graph_inst->opcode()) << ".";
+  }
+
+  // Check number of Interface IDs matches number of I/Os of graph
+  auto graph_type_inst = _.FindDef(graph_inst->type_id());
+  size_t graph_type_num_io = GraphTypeInstNumIO(graph_type_inst);
+  size_t graph_entry_point_num_interface_id = inst->operands().size() - 2;
+  if (graph_type_inst->opcode() != spv::Op::OpTypeGraphARM) {
+    // This is invalid but we want ValidateGraph to report a clear error
+    // so stop validating the graph entry point instruction
+    return SPV_SUCCESS;
+  }
+  if (graph_type_num_io != graph_entry_point_num_interface_id) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << spvOpcodeString(inst->opcode()) << " Interface list contains "
+           << graph_entry_point_num_interface_id << " IDs but Graph's type "
+           << _.getIdName(graph_inst->type_id()) << " has " << graph_type_num_io
+           << " inputs and outputs.";
+  }
+
+  // Check Interface IDs
+  for (uint32_t i = 2; i < inst->operands().size(); i++) {
+    uint32_t interface_id = inst->GetOperandAs<uint32_t>(i);
+    auto interface_inst = _.FindDef(interface_id);
+
+    // Check interface IDs come from OpVariable
+    if ((interface_inst->opcode() != spv::Op::OpVariable) ||
+        (interface_inst->GetOperandAs<spv::StorageClass>(2) !=
+         spv::StorageClass::UniformConstant)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, interface_inst)
+             << spvOpcodeString(inst->opcode()) << " Interface ID "
+             << _.getIdName(interface_id)
+             << " must come from OpVariable with UniformConstant Storage "
+                "Class.";
+    }
+
+    // Check type of interface variable matches type of the corresponding graph
+    // I/O
+    uint32_t corresponding_graph_io_type =
+        graph_type_inst->GetOperandAs<uint32_t>(i);
+
+    uint32_t interface_ptr_type = interface_inst->type_id();
+    auto interface_ptr_inst = _.FindDef(interface_ptr_type);
+    auto interface_pointee_type = interface_ptr_inst->GetOperandAs<uint32_t>(2);
+    if (interface_pointee_type != corresponding_graph_io_type) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << spvOpcodeString(inst->opcode()) << " Interface ID type "
+             << _.getIdName(interface_pointee_type)
+             << " must match the type of the corresponding graph I/O "
+             << _.getIdName(corresponding_graph_io_type);
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateGraph(ValidationState_t& _, const Instruction* inst) {
+  // Result Type must be an OpTypeGraphARM
+  if (!IsGraphType(_, inst->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << spvOpcodeString(inst->opcode())
+           << " Result Type must be an OpTypeGraphARM.";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateGraphInput(ValidationState_t& _, const Instruction* inst) {
+  // Check type of InputIndex
+  auto input_index_inst = _.FindDef(inst->GetOperandAs<uint32_t>(2));
+  if (!input_index_inst ||
+      !_.IsIntScalarType(input_index_inst->type_id(), 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << spvOpcodeString(inst->opcode())
+           << " InputIndex must be a 32-bit integer.";
+  }
+
+  bool has_element_index = inst->operands().size() > 3;
+
+  // Check type of ElementIndex
+  if (has_element_index) {
+    auto element_index_inst = _.FindDef(inst->GetOperandAs<uint32_t>(3));
+    if (!element_index_inst ||
+        !_.IsIntScalarType(element_index_inst->type_id(), 32)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << spvOpcodeString(inst->opcode())
+             << " ElementIndex must be a 32-bit integer.";
+    }
+  }
+
+  // Find graph definition
+  size_t inst_num = inst->LineNum() - 1;
+  auto graph_inst = &_.ordered_instructions()[inst_num];
+  while (--inst_num) {
+    graph_inst = &_.ordered_instructions()[inst_num];
+    if (graph_inst->opcode() == spv::Op::OpGraphARM) {
+      break;
+    }
+  }
+
+  // Can the InputIndex be evaluated?
+  // If not, there's nothing more we can validate here.
+  uint64_t input_index;
+  if (!_.EvalConstantValUint64(inst->GetOperandAs<uint32_t>(2), &input_index)) {
+    return SPV_SUCCESS;
+  }
+
+  auto const graph_type_inst = _.FindDef(graph_inst->type_id());
+  size_t graph_type_num_inputs = graph_type_inst->GetOperandAs<uint32_t>(1);
+
+  // Check InputIndex is in range
+  if (input_index >= graph_type_num_inputs) {
+    std::string disassembly = _.Disassemble(*inst);
+    return _.diag(SPV_ERROR_INVALID_DATA, nullptr)
+           << "Type " << _.getIdName(graph_type_inst->id()) << " for graph "
+           << _.getIdName(graph_inst->id()) << " has " << graph_type_num_inputs
+           << " inputs but found an OpGraphInputARM instruction with an "
+              "InputIndex that is "
+           << input_index << ": " << disassembly;
+  }
+
+  uint32_t graph_type_input_type =
+      GraphTypeInstGetInputAtIndex(graph_type_inst, input_index);
+
+  if (has_element_index) {
+    // Check ElementIndex is allowed
+    if (!IsTensorArray(_, graph_type_input_type)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "OpGraphInputARM ElementIndex not allowed when the graph input "
+                "selected by "
+             << "InputIndex is not an OpTypeArray or OpTypeRuntimeArray";
+    }
+
+    // Check ElementIndex is in range if it can be evaluated and the input is a
+    // fixed-sized array whose Length can be evaluated
+    uint64_t element_index;
+    if (_.IsArrayType(graph_type_input_type) &&
+        _.EvalConstantValUint64(inst->GetOperandAs<uint32_t>(3),
+                                &element_index)) {
+      uint64_t array_length;
+      auto graph_type_input_type_inst = _.FindDef(graph_type_input_type);
+      if (_.EvalConstantValUint64(
+              graph_type_input_type_inst->GetOperandAs<uint32_t>(2),
+              &array_length)) {
+        if (element_index >= array_length) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "OpGraphInputARM ElementIndex out of range. The type of "
+                    "the graph input being accessed "
+                 << _.getIdName(graph_type_input_type) << " is an array of "
+                 << array_length << " elements but " << "ElementIndex is "
+                 << element_index;
+        }
+      }
+    }
+  }
+
+  // Check result type matches with graph type
+  if (has_element_index) {
+    uint32_t expected_type = _.GetComponentType(graph_type_input_type);
+    if (inst->type_id() != expected_type) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Result Type " << _.getIdName(inst->type_id())
+             << " of graph input instruction " << _.getIdName(inst->id())
+             << " does not match the component type "
+             << _.getIdName(expected_type) << " of input " << input_index
+             << " in the graph type.";
+    }
+  } else {
+    if (inst->type_id() != graph_type_input_type) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Result Type " << _.getIdName(inst->type_id())
+             << " of graph input instruction " << _.getIdName(inst->id())
+             << " does not match the type "
+             << _.getIdName(graph_type_input_type) << " of input "
+             << input_index << " in the graph type.";
+    }
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateGraphSetOutput(ValidationState_t& _,
+                                    const Instruction* inst) {
+  // Check type of OutputIndex
+  auto output_index_inst = _.FindDef(inst->GetOperandAs<uint32_t>(1));
+  if (!output_index_inst ||
+      !_.IsIntScalarType(output_index_inst->type_id(), 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << spvOpcodeString(inst->opcode())
+           << " OutputIndex must be a 32-bit integer.";
+  }
+
+  bool has_element_index = inst->operands().size() > 2;
+
+  // Check type of ElementIndex
+  if (has_element_index) {
+    auto element_index_inst = _.FindDef(inst->GetOperandAs<uint32_t>(2));
+    if (!element_index_inst ||
+        !_.IsIntScalarType(element_index_inst->type_id(), 32)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << spvOpcodeString(inst->opcode())
+             << " ElementIndex must be a 32-bit integer.";
+    }
+  }
+
+  // Find graph definition
+  size_t inst_num = inst->LineNum() - 1;
+  auto graph_inst = &_.ordered_instructions()[inst_num];
+  while (--inst_num) {
+    graph_inst = &_.ordered_instructions()[inst_num];
+    if (graph_inst->opcode() == spv::Op::OpGraphARM) {
+      break;
+    }
+  }
+
+  // Can the OutputIndex be evaluated?
+  // If not, there's nothing more we can validate here.
+  uint64_t output_index;
+  if (!_.EvalConstantValUint64(inst->GetOperandAs<uint32_t>(1),
+                               &output_index)) {
+    return SPV_SUCCESS;
+  }
+
+  // Check that the OutputIndex is valid with respect to the graph type
+  auto graph_type_inst = _.FindDef(graph_inst->type_id());
+  size_t graph_type_num_outputs = GraphTypeInstNumOutputs(graph_type_inst);
+
+  if (output_index >= graph_type_num_outputs) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << spvOpcodeString(inst->opcode()) << " setting OutputIndex "
+           << output_index << " but graph only has " << graph_type_num_outputs
+           << " outputs.";
+  }
+
+  uint32_t graph_type_output_type =
+      GraphTypeInstGetOutputAtIndex(graph_type_inst, output_index);
+
+  if (has_element_index) {
+    // Check ElementIndex is allowed
+    if (!IsTensorArray(_, graph_type_output_type)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "OpGraphSetOutputARM ElementIndex not allowed when the graph "
+                "output selected by "
+             << "OutputIndex is not an OpTypeArray or OpTypeRuntimeArray";
+    }
+
+    // Check ElementIndex is in range if it can be evaluated and the output is a
+    // fixed-sized array whose Length can be evaluated
+    uint64_t element_index;
+    if (_.IsArrayType(graph_type_output_type) &&
+        _.EvalConstantValUint64(inst->GetOperandAs<uint32_t>(2),
+                                &element_index)) {
+      uint64_t array_length;
+      auto graph_type_output_type_inst = _.FindDef(graph_type_output_type);
+      if (_.EvalConstantValUint64(
+              graph_type_output_type_inst->GetOperandAs<uint32_t>(2),
+              &array_length)) {
+        if (element_index >= array_length) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "OpGraphSetOutputARM ElementIndex out of range. The type "
+                    "of the graph output being accessed "
+                 << _.getIdName(graph_type_output_type) << " is an array of "
+                 << array_length << " elements but " << "ElementIndex is "
+                 << element_index;
+        }
+      }
+    }
+  }
+
+  // Check Value's type matches with graph type
+  uint32_t value = inst->GetOperandAs<uint32_t>(0);
+  uint32_t value_type = _.FindDef(value)->type_id();
+  if (has_element_index) {
+    uint32_t expected_type = _.GetComponentType(graph_type_output_type);
+    if (value_type != expected_type) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "The type " << _.getIdName(value_type)
+             << " of Value provided to the graph output instruction "
+             << _.getIdName(value) << " does not match the component type "
+             << _.getIdName(expected_type) << " of output " << output_index
+             << " in the graph type.";
+    }
+  } else {
+    if (value_type != graph_type_output_type) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "The type " << _.getIdName(value_type)
+             << " of Value provided to the graph output instruction "
+             << _.getIdName(value) << " does not match the type "
+             << _.getIdName(graph_type_output_type) << " of output "
+             << output_index << " in the graph type.";
+    }
+  }
+  return SPV_SUCCESS;
+}
+
+bool InputOutputInstructionsHaveDuplicateIndices(
+    ValidationState_t& _, std::deque<const Instruction*>& inout_insts,
+    const Instruction** first_dup) {
+  std::set<std::pair<uint64_t, uint64_t>> inout_element_indices;
+  for (auto const inst : inout_insts) {
+    const bool is_input = inst->opcode() == spv::Op::OpGraphInputARM;
+    bool has_element_index = inst->operands().size() > (is_input ? 3 : 2);
+    uint64_t inout_index;
+    if (!_.EvalConstantValUint64(inst->GetOperandAs<uint32_t>(is_input ? 2 : 1),
+                                 &inout_index)) {
+      continue;
+    }
+    uint64_t element_index = -1;  // -1 means no ElementIndex
+    if (has_element_index) {
+      if (!_.EvalConstantValUint64(
+              inst->GetOperandAs<uint32_t>(is_input ? 3 : 2), &element_index)) {
+        continue;
+      }
+    }
+    auto inout_element_pair = std::make_pair(inout_index, element_index);
+    auto inout_noelement_pair = std::make_pair(inout_index, -1);
+    if (inout_element_indices.count(inout_element_pair) ||
+        inout_element_indices.count(inout_noelement_pair)) {
+      *first_dup = inst;
+      return true;
+    }
+    inout_element_indices.insert(inout_element_pair);
+  }
+  return false;
+}
+
+spv_result_t ValidateGraphEnd(ValidationState_t& _, const Instruction* inst) {
+  size_t end_inst_num = inst->LineNum() - 1;
+
+  // Gather OpGraphInputARM and OpGraphSetOutputARM instructions
+  std::deque<const Instruction*> graph_inputs, graph_outputs;
+  size_t in_inst_num = end_inst_num;
+  auto graph_inst = &_.ordered_instructions()[in_inst_num];
+  while (--in_inst_num) {
+    graph_inst = &_.ordered_instructions()[in_inst_num];
+    if (graph_inst->opcode() == spv::Op::OpGraphInputARM) {
+      graph_inputs.push_front(graph_inst);
+      continue;
+    }
+    if (graph_inst->opcode() == spv::Op::OpGraphSetOutputARM) {
+      graph_outputs.push_front(graph_inst);
+      continue;
+    }
+    if (graph_inst->opcode() == spv::Op::OpGraphARM) {
+      break;
+    }
+  }
+
+  const Instruction* first_dup;
+
+  // Check that there are no duplicate InputIndex and ElementIndex values
+  if (InputOutputInstructionsHaveDuplicateIndices(_, graph_inputs,
+                                                  &first_dup)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, first_dup)
+           << "Two OpGraphInputARM instructions with the same InputIndex "
+              "must not be part of the same "
+           << "graph definition unless ElementIndex is present in both with "
+              "different values.";
+  }
+
+  // Check that there are no duplicate OutputIndex and ElementIndex values
+  if (InputOutputInstructionsHaveDuplicateIndices(_, graph_outputs,
+                                                  &first_dup)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, first_dup)
+           << "Two OpGraphSetOutputARM instructions with the same "
+              "OutputIndex must not be part of the same "
+           << "graph definition unless ElementIndex is present in both with "
+              "different values.";
+  }
+
+  return SPV_SUCCESS;
+}
+
+}  // namespace
+
+// Validates correctness of graph instructions.
+spv_result_t GraphPass(ValidationState_t& _, const Instruction* inst) {
+  switch (inst->opcode()) {
+    case spv::Op::OpTypeGraphARM:
+      return ValidateGraphType(_, inst);
+    case spv::Op::OpGraphConstantARM:
+      return ValidateGraphConstant(_, inst);
+    case spv::Op::OpGraphEntryPointARM:
+      return ValidateGraphEntryPoint(_, inst);
+    case spv::Op::OpGraphARM:
+      return ValidateGraph(_, inst);
+    case spv::Op::OpGraphInputARM:
+      return ValidateGraphInput(_, inst);
+    case spv::Op::OpGraphSetOutputARM:
+      return ValidateGraphSetOutput(_, inst);
+    case spv::Op::OpGraphEndARM:
+      return ValidateGraphEnd(_, inst);
+    default:
+      break;
+  }
+  return SPV_SUCCESS;
+}
+
+}  // namespace val
+}  // namespace spvtools
diff --git a/source/val/validate_group.cpp b/source/val/validate_group.cpp
new file mode 100644
index 0000000..cccb0ab
--- /dev/null
+++ b/source/val/validate_group.cpp
@@ -0,0 +1,229 @@
+// Copyright (c) 2026 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 <cstdint>
+
+#include "source/val/instruction.h"
+#include "source/val/validate.h"
+#include "source/val/validate_scopes.h"
+#include "source/val/validation_state.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+spv_result_t ValidateGroupAnyAll(ValidationState_t& _,
+                                 const Instruction* inst) {
+  if (!_.IsBoolScalarType(inst->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result must be a boolean scalar type";
+  }
+
+  if (!_.IsBoolScalarType(_.GetOperandTypeId(inst, 3))) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Predicate must be a boolean scalar type";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateGroupBroadcast(ValidationState_t& _,
+                                    const Instruction* inst) {
+  const uint32_t type_id = inst->type_id();
+  if (!_.IsFloatScalarOrVectorType(type_id) &&
+      !_.IsIntScalarOrVectorType(type_id) &&
+      !_.IsBoolScalarOrVectorType(type_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result must be a scalar or vector of integer, floating-point, "
+              "or boolean type";
+  }
+
+  const uint32_t value_type_id = _.GetOperandTypeId(inst, 3);
+  if (value_type_id != type_id) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "The type of Value must match the Result type";
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateGroupFloat(ValidationState_t& _, const Instruction* inst) {
+  const uint32_t type_id = inst->type_id();
+  if (!_.IsFloatScalarOrVectorType(type_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result must be a scalar or vector of float type";
+  }
+
+  const uint32_t x_type_id = _.GetOperandTypeId(inst, 4);
+  if (x_type_id != type_id) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "The type of X must match the Result type";
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateGroupInt(ValidationState_t& _, const Instruction* inst) {
+  const uint32_t type_id = inst->type_id();
+  if (!_.IsIntScalarOrVectorType(type_id)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result must be a scalar or vector of integer type";
+  }
+
+  const uint32_t x_type_id = _.GetOperandTypeId(inst, 4);
+  if (x_type_id != type_id) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "The type of X must match the Result type";
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateGroupAsyncCopy(ValidationState_t& _,
+                                    const Instruction* inst) {
+  if (_.FindDef(inst->type_id())->opcode() != spv::Op::OpTypeEvent) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "The result type must be OpTypeEvent.";
+  }
+
+  const uint32_t destination = _.GetOperandTypeId(inst, 3);
+  const Instruction* destination_pointer = _.FindDef(destination);
+  if (destination_pointer->opcode() != spv::Op::OpTypePointer) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Destination to be a pointer.";
+  }
+  const auto destination_sc =
+      destination_pointer->GetOperandAs<spv::StorageClass>(1);
+  if (destination_sc != spv::StorageClass::Workgroup &&
+      destination_sc != spv::StorageClass::CrossWorkgroup) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Destination to be a pointer with storage class "
+              "Workgroup or CrossWorkgroup.";
+  }
+  const uint32_t destination_type =
+      destination_pointer->GetOperandAs<uint32_t>(2);
+  if (!_.IsIntScalarOrVectorType(destination_type) &&
+      !_.IsFloatScalarOrVectorType(destination_type)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Destination to be a pointer to scalar or vector of "
+              "floating-point type or integer type.";
+  }
+
+  const uint32_t source = _.GetOperandTypeId(inst, 4);
+  const Instruction* source_pointer = _.FindDef(source);
+  const auto source_sc = source_pointer->GetOperandAs<spv::StorageClass>(1);
+  const uint32_t source_type = source_pointer->GetOperandAs<uint32_t>(2);
+  if (destination_type != source_type) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Destination and Source to be the same type.";
+  }
+
+  if (destination_sc == spv::StorageClass::Workgroup &&
+      source_sc != spv::StorageClass::CrossWorkgroup) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "If Destination storage class is Workgroup, then the Source "
+              "storage class must be CrossWorkgroup.";
+  } else if (destination_sc == spv::StorageClass::CrossWorkgroup &&
+             source_sc != spv::StorageClass::Workgroup) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "If Destination storage class is CrossWorkgroup, then the Source "
+              "storage class must be Workgroup.";
+  }
+
+  const bool is_physical_64 =
+      _.addressing_model() == spv::AddressingModel::Physical64;
+  const uint32_t bit_width = is_physical_64 ? 64 : 32;
+
+  const uint32_t num_elements_type =
+      _.GetTypeId(inst->GetOperandAs<uint32_t>(5));
+  if (!_.IsIntScalarType(num_elements_type, bit_width)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "NumElements must be a " << bit_width
+           << "-bit int scalar when Addressing Model is "
+           << (is_physical_64 ? "Physical64" : "Physical32");
+  }
+
+  const uint32_t stride_type = _.GetTypeId(inst->GetOperandAs<uint32_t>(6));
+  if (!_.IsIntScalarType(stride_type, bit_width)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Stride must be a " << bit_width
+           << "-bit int scalar when Addressing Model is "
+           << (is_physical_64 ? "Physical64" : "Physical32");
+  }
+
+  const uint32_t event = _.GetOperandTypeId(inst, 7);
+  const Instruction* event_type = _.FindDef(event);
+  if (event_type->opcode() != spv::Op::OpTypeEvent) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Event to be type OpTypeEvent.";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateGroupWaitEvents(ValidationState_t& _,
+                                     const Instruction* inst) {
+  const uint32_t num_events_id = _.GetOperandTypeId(inst, 1);
+  if (!_.IsIntScalarType(num_events_id, 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Num Events to be a 32-bit int scalar.";
+  }
+
+  const uint32_t events_id = _.GetOperandTypeId(inst, 2);
+  const Instruction* var_pointer = _.FindDef(events_id);
+  if (var_pointer->opcode() != spv::Op::OpTypePointer) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Events List to be a pointer.";
+  }
+  const Instruction* event_list_type =
+      _.FindDef(var_pointer->GetOperandAs<uint32_t>(2));
+  if (event_list_type->opcode() != spv::Op::OpTypeEvent) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Events List to be a pointer to OpTypeEvent.";
+  }
+
+  return SPV_SUCCESS;
+}
+
+}  // namespace
+
+spv_result_t GroupPass(ValidationState_t& _, const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+
+  switch (opcode) {
+    case spv::Op::OpGroupAny:
+    case spv::Op::OpGroupAll:
+      return ValidateGroupAnyAll(_, inst);
+    case spv::Op::OpGroupBroadcast:
+      return ValidateGroupBroadcast(_, inst);
+    case spv::Op::OpGroupFAdd:
+    case spv::Op::OpGroupFMax:
+    case spv::Op::OpGroupFMin:
+      return ValidateGroupFloat(_, inst);
+    case spv::Op::OpGroupIAdd:
+    case spv::Op::OpGroupUMin:
+    case spv::Op::OpGroupSMin:
+    case spv::Op::OpGroupUMax:
+    case spv::Op::OpGroupSMax:
+      return ValidateGroupInt(_, inst);
+    case spv::Op::OpGroupAsyncCopy:
+      return ValidateGroupAsyncCopy(_, inst);
+    case spv::Op::OpGroupWaitEvents:
+      return ValidateGroupWaitEvents(_, inst);
+    default:
+      break;
+  }
+
+  return SPV_SUCCESS;
+}
+
+}  // namespace val
+}  // namespace spvtools
diff --git a/source/val/validate_id.cpp b/source/val/validate_id.cpp
index 00be150..312ecf7 100644
--- a/source/val/validate_id.cpp
+++ b/source/val/validate_id.cpp
@@ -115,6 +115,64 @@
   return SPV_SUCCESS;
 }
 
+bool InstructionCanHaveTypeOperand(const Instruction* inst) {
+  static std::unordered_set<spv::Op> instruction_allow_set{
+      spv::Op::OpSizeOf,
+      spv::Op::OpCooperativeMatrixLengthNV,
+      spv::Op::OpCooperativeMatrixLengthKHR,
+      spv::Op::OpUntypedArrayLengthKHR,
+      spv::Op::OpFunction,
+      spv::Op::OpAsmINTEL,
+      spv::Op::OpConstantSizeOfEXT,
+      spv::Op::OpBufferPointerEXT,
+      spv::Op::OpUntypedImageTexelPointerEXT,
+      spv::Op::OpAbortKHR,
+  };
+  const auto opcode = inst->opcode();
+  bool type_instruction = spvOpcodeGeneratesType(opcode);
+  bool debug_instruction = spvOpcodeIsDebug(opcode) || inst->IsDebugInfo();
+  bool coop_matrix_spec_constant_op_length =
+      (opcode == spv::Op::OpSpecConstantOp) &&
+      (spv::Op(inst->word(3)) == spv::Op::OpCooperativeMatrixLengthNV ||
+       spv::Op(inst->word(3)) == spv::Op::OpCooperativeMatrixLengthKHR);
+  return type_instruction || debug_instruction || inst->IsNonSemantic() ||
+         spvOpcodeIsDecoration(opcode) || instruction_allow_set.count(opcode) ||
+         spvOpcodeGeneratesUntypedPointer(opcode) ||
+         coop_matrix_spec_constant_op_length;
+}
+
+bool InstructionRequiresTypeOperand(const Instruction* inst) {
+  static std::unordered_set<spv::Op> instruction_deny_set{
+      spv::Op::OpExtInst,
+      spv::Op::OpExtInstWithForwardRefsKHR,
+      spv::Op::OpExtInstImport,
+      spv::Op::OpSelectionMerge,
+      spv::Op::OpLoopMerge,
+      spv::Op::OpFunction,
+      spv::Op::OpSizeOf,
+      spv::Op::OpCooperativeMatrixLengthNV,
+      spv::Op::OpCooperativeMatrixLengthKHR,
+      spv::Op::OpPhi,
+      spv::Op::OpUntypedArrayLengthKHR,
+      spv::Op::OpAsmINTEL,
+      spv::Op::OpAliasScopeDeclINTEL,
+      spv::Op::OpAliasScopeListDeclINTEL,
+      spv::Op::OpAbortKHR,
+  };
+  const auto opcode = inst->opcode();
+  bool debug_instruction = spvOpcodeIsDebug(opcode) || inst->IsDebugInfo();
+  bool coop_matrix_spec_constant_op_length =
+      opcode == spv::Op::OpSpecConstantOp &&
+      (spv::Op(inst->word(3)) == spv::Op::OpCooperativeMatrixLengthNV ||
+       spv::Op(inst->word(3)) == spv::Op::OpCooperativeMatrixLengthKHR);
+
+  return !debug_instruction && !inst->IsNonSemantic() &&
+         !spvOpcodeIsDecoration(opcode) && !spvOpcodeIsBranch(opcode) &&
+         !instruction_deny_set.count(opcode) &&
+         !spvOpcodeGeneratesUntypedPointer(opcode) &&
+         !coop_matrix_spec_constant_op_length;
+}
+
 // Performs SSA validation on the IDs of an instruction. The
 // can_have_forward_declared_ids  functor should return true if the
 // instruction operand's ID can be forward referenced.
@@ -158,44 +216,15 @@
       case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
       case SPV_OPERAND_TYPE_SCOPE_ID:
         if (const auto def = _.FindDef(operand_word)) {
-          const auto opcode = inst->opcode();
           if (spvOpcodeGeneratesType(def->opcode()) &&
-              !spvOpcodeGeneratesType(opcode) && !spvOpcodeIsDebug(opcode) &&
-              !inst->IsDebugInfo() && !inst->IsNonSemantic() &&
-              !spvOpcodeIsDecoration(opcode) && opcode != spv::Op::OpFunction &&
-              opcode != spv::Op::OpSizeOf &&
-              opcode != spv::Op::OpCooperativeMatrixLengthNV &&
-              opcode != spv::Op::OpCooperativeMatrixLengthKHR &&
-              !spvOpcodeGeneratesUntypedPointer(opcode) &&
-              opcode != spv::Op::OpUntypedArrayLengthKHR &&
-              !(opcode == spv::Op::OpSpecConstantOp &&
-                (spv::Op(inst->word(3)) ==
-                     spv::Op::OpCooperativeMatrixLengthNV ||
-                 spv::Op(inst->word(3)) ==
-                     spv::Op::OpCooperativeMatrixLengthKHR))) {
+              !InstructionCanHaveTypeOperand(inst)) {
             return _.diag(SPV_ERROR_INVALID_ID, inst)
                    << "Operand " << _.getIdName(operand_word)
                    << " cannot be a type";
-          } else if (def->type_id() == 0 && !spvOpcodeGeneratesType(opcode) &&
-                     !spvOpcodeIsDebug(opcode) && !inst->IsDebugInfo() &&
-                     !inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) &&
-                     !spvOpcodeIsBranch(opcode) && opcode != spv::Op::OpPhi &&
-                     opcode != spv::Op::OpExtInst &&
-                     opcode != spv::Op::OpExtInstWithForwardRefsKHR &&
-                     opcode != spv::Op::OpExtInstImport &&
-                     opcode != spv::Op::OpSelectionMerge &&
-                     opcode != spv::Op::OpLoopMerge &&
-                     opcode != spv::Op::OpFunction &&
-                     opcode != spv::Op::OpSizeOf &&
-                     opcode != spv::Op::OpCooperativeMatrixLengthNV &&
-                     opcode != spv::Op::OpCooperativeMatrixLengthKHR &&
-                     !spvOpcodeGeneratesUntypedPointer(opcode) &&
-                     opcode != spv::Op::OpUntypedArrayLengthKHR &&
-                     !(opcode == spv::Op::OpSpecConstantOp &&
-                       (spv::Op(inst->word(3)) ==
-                            spv::Op::OpCooperativeMatrixLengthNV ||
-                        spv::Op(inst->word(3)) ==
-                            spv::Op::OpCooperativeMatrixLengthKHR))) {
+          } else if (def->type_id() == 0 &&
+                     !spvOpcodeGeneratesType(def->opcode()) &&
+                     InstructionRequiresTypeOperand(inst) &&
+                     InstructionRequiresTypeOperand(def)) {
             return _.diag(SPV_ERROR_INVALID_ID, inst)
                    << "Operand " << _.getIdName(operand_word)
                    << " requires a type";
diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp
index 04100dd..68d8056 100644
--- a/source/val/validate_image.cpp
+++ b/source/val/validate_image.cpp
@@ -178,6 +178,118 @@
   return false;
 }
 
+// Signed or Unsigned Integer Format
+bool IsIntImageFormat(spv::ImageFormat format) {
+  switch (format) {
+    case spv::ImageFormat::Rgba32i:
+    case spv::ImageFormat::Rgba16i:
+    case spv::ImageFormat::Rgba8i:
+    case spv::ImageFormat::R32i:
+    case spv::ImageFormat::Rg32i:
+    case spv::ImageFormat::Rg16i:
+    case spv::ImageFormat::Rg8i:
+    case spv::ImageFormat::R16i:
+    case spv::ImageFormat::R8i:
+    case spv::ImageFormat::Rgba32ui:
+    case spv::ImageFormat::Rgba16ui:
+    case spv::ImageFormat::Rgba8ui:
+    case spv::ImageFormat::R32ui:
+    case spv::ImageFormat::Rgb10a2ui:
+    case spv::ImageFormat::Rg32ui:
+    case spv::ImageFormat::Rg16ui:
+    case spv::ImageFormat::Rg8ui:
+    case spv::ImageFormat::R16ui:
+    case spv::ImageFormat::R8ui:
+    case spv::ImageFormat::R64ui:
+    case spv::ImageFormat::R64i:
+      return true;
+    default:
+      break;
+  }
+  return false;
+}
+
+bool IsInt64ImageFormat(spv::ImageFormat format) {
+  switch (format) {
+    case spv::ImageFormat::R64ui:
+    case spv::ImageFormat::R64i:
+      return true;
+    default:
+      break;
+  }
+  return false;
+}
+
+bool IsSignedIntImageFormat(spv::ImageFormat format) {
+  switch (format) {
+    case spv::ImageFormat::Rgba32i:
+    case spv::ImageFormat::Rgba16i:
+    case spv::ImageFormat::Rgba8i:
+    case spv::ImageFormat::R32i:
+    case spv::ImageFormat::Rg32i:
+    case spv::ImageFormat::Rg16i:
+    case spv::ImageFormat::Rg8i:
+    case spv::ImageFormat::R16i:
+    case spv::ImageFormat::R8i:
+    case spv::ImageFormat::R64i:
+      return true;
+    default:
+      break;
+  }
+  return false;
+}
+
+bool IsFloatImageFormat(spv::ImageFormat format) {
+  switch (format) {
+    case spv::ImageFormat::Rgba32f:
+    case spv::ImageFormat::Rgba16f:
+    case spv::ImageFormat::R32f:
+    case spv::ImageFormat::Rgba8:
+    case spv::ImageFormat::Rgba8Snorm:
+    case spv::ImageFormat::Rg32f:
+    case spv::ImageFormat::Rg16f:
+    case spv::ImageFormat::R11fG11fB10f:
+    case spv::ImageFormat::R16f:
+    case spv::ImageFormat::Rgba16:
+    case spv::ImageFormat::Rgb10A2:
+    case spv::ImageFormat::Rg16:
+    case spv::ImageFormat::Rg8:
+    case spv::ImageFormat::R16:
+    case spv::ImageFormat::R8:
+    case spv::ImageFormat::Rgba16Snorm:
+    case spv::ImageFormat::Rg16Snorm:
+    case spv::ImageFormat::Rg8Snorm:
+    case spv::ImageFormat::R16Snorm:
+    case spv::ImageFormat::R8Snorm:
+      return true;
+    default:
+      break;
+  }
+  return false;
+}
+
+bool IsImageSparse(spv::Op opcode) {
+  switch (opcode) {
+    case spv::Op::OpImageSparseSampleImplicitLod:
+    case spv::Op::OpImageSparseSampleExplicitLod:
+    case spv::Op::OpImageSparseSampleDrefImplicitLod:
+    case spv::Op::OpImageSparseSampleDrefExplicitLod:
+    case spv::Op::OpImageSparseSampleProjImplicitLod:
+    case spv::Op::OpImageSparseSampleProjExplicitLod:
+    case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
+    case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
+    case spv::Op::OpImageSparseFetch:
+    case spv::Op::OpImageSparseGather:
+    case spv::Op::OpImageSparseDrefGather:
+    case spv::Op::OpImageSparseTexelsResident:
+    case spv::Op::OpImageSparseRead:
+      return true;
+    default:
+      break;
+  }
+  return false;
+}
+
 // Returns true if the opcode is a Image instruction which applies
 // homogenous projection to the coordinates.
 bool IsProj(spv::Op opcode) {
@@ -237,6 +349,23 @@
     return 3;
   }
 
+  if (opcode == spv::Op::OpImageQueryLod) {
+    return GetPlaneCoordSize(info);
+  }
+
+  if (opcode == spv::Op::OpImageTexelPointer) {
+    if (info.arrayed == 0) {
+      return GetPlaneCoordSize(info);
+    } else if (info.dim == spv::Dim::Dim1D) {
+      return 2;
+    } else if (info.dim == spv::Dim::Cube || info.dim == spv::Dim::Dim2D) {
+      return 3;
+    } else {
+      assert(false);
+      return 0;  // caught elsewhere
+    }
+  }
+
   return GetPlaneCoordSize(info) + info.arrayed + (IsProj(opcode) ? 1 : 0);
 }
 
@@ -287,6 +416,37 @@
               "multi-sampled image";
   }
 
+  // The following OpTypeImage checks are done here as they depend of if the
+  // SignExtend and ZeroExtend are used to override the signedness
+  const bool is_sign_extend =
+      mask & uint32_t(spv::ImageOperandsMask::SignExtend);
+  const bool is_zero_extend =
+      mask & uint32_t(spv::ImageOperandsMask::ZeroExtend);
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    if (info.format != spv::ImageFormat::Unknown &&
+        _.IsIntScalarType(info.sampled_type)) {
+      const bool is_format_signed = IsSignedIntImageFormat(info.format);
+      const bool is_sampled_type_signed =
+          _.IsSignedIntScalarType(info.sampled_type);
+      // (vkspec.html#spirvenv-image-signedness) has order signedness is set by
+      const bool effective_sampled_type_signed =
+          is_sign_extend ? true
+                         : (is_zero_extend ? false : is_sampled_type_signed);
+      if (is_format_signed != effective_sampled_type_signed) {
+        std::string err_info = "";
+        if (is_sign_extend) {
+          err_info = " (SignExtend makes the access as signed)";
+        } else if (is_zero_extend) {
+          err_info = " (ZeroExtend makes the access as unsigned)";
+        }
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << _.VkErrorID(4965) << "Image Format signedness ("
+               << (is_format_signed ? "signed" : "unsigned")
+               << ") does not match Sample Type operand" << err_info << ".";
+      }
+    }
+  }
+
   // After this point, only set bits in the image operands mask can cause
   // the module to be invalid.
   if (mask == 0) return SPV_SUCCESS;
@@ -314,10 +474,10 @@
              << "Image Operand Bias can only be used with ImplicitLod opcodes";
     }
 
-    const uint32_t type_id = _.GetTypeId(inst->word(word_index++));
-    if (!_.IsFloatScalarType(type_id)) {
+    const uint32_t bias_type_id = _.GetTypeId(inst->word(word_index++));
+    if (!_.IsFloatScalarType(bias_type_id, 32)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Expected Image Operand Bias to be float scalar";
+             << "Expected Image Operand Bias to be a 32-bit float scalar";
     }
 
     if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
@@ -327,7 +487,10 @@
                 "or Cube";
     }
 
-    // Multisampled is already checked.
+    // - |Sample| operand is required to have MS != 0
+    // - |Sample| is only allowed with [Fetch, Write, or Read]
+    // - |Bias| can only be used with |ImplicitLod| opcodes
+    // Multisampled is already checked in all cases
   }
 
   if (mask & uint32_t(spv::ImageOperandsMask::Lod)) {
@@ -345,17 +508,19 @@
                 "time";
     }
 
-    const uint32_t type_id = _.GetTypeId(inst->word(word_index++));
+    const uint32_t lod_type_id = _.GetTypeId(inst->word(word_index++));
     if (is_explicit_lod || is_valid_gather_lod_bias_amd) {
-      if (!_.IsFloatScalarType(type_id)) {
+      if (!_.IsFloatScalarType(lod_type_id, 32)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Image Operand Lod to be float scalar when used "
+               << "Expected Image Operand Lod to be a 32-bit float scalar when "
+                  "used "
                << "with ExplicitLod";
       }
     } else {
-      if (!_.IsIntScalarType(type_id)) {
+      if (!_.IsIntScalarType(lod_type_id, 32)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Image Operand Lod to be int scalar when used with "
+               << "Expected Image Operand Lod to be a 32-bit int scalar when "
+                  "used with "
                << "OpImageFetch";
       }
     }
@@ -367,7 +532,10 @@
                 "or Cube";
     }
 
-    // Multisampled is already checked.
+    if (info.multisampled != 0) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Image Operand Lod requires 'MS' parameter to be 0";
+    }
   }
 
   if (mask & uint32_t(spv::ImageOperandsMask::Grad)) {
@@ -379,9 +547,12 @@
     const uint32_t dx_type_id = _.GetTypeId(inst->word(word_index++));
     const uint32_t dy_type_id = _.GetTypeId(inst->word(word_index++));
     if (!_.IsFloatScalarOrVectorType(dx_type_id) ||
-        !_.IsFloatScalarOrVectorType(dy_type_id)) {
+        _.GetBitWidth(dx_type_id) != 32 ||
+        !_.IsFloatScalarOrVectorType(dy_type_id) ||
+        _.GetBitWidth(dy_type_id) != 32) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Expected both Image Operand Grad ids to be float scalars or "
+             << "Expected both Image Operand Grad ids to be 32-bit float "
+                "scalars or "
              << "vectors";
     }
 
@@ -400,7 +571,10 @@
              << " components, but given " << dy_size;
     }
 
-    // Multisampled is already checked.
+    // - |Sample| operand is required to have MS != 0
+    // - |Sample| is only allowed with [Fetch, Write, or Read]
+    // - |Grad| can only be used with |ExplicitLod| opcodes
+    // Multisampled is already checked in all cases
   }
 
   if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) {
@@ -410,21 +584,23 @@
                 "'Dim'";
     }
 
-    const uint32_t id = inst->word(word_index++);
-    const uint32_t type_id = _.GetTypeId(id);
-    if (!_.IsIntScalarOrVectorType(type_id)) {
+    const uint32_t offset_id = inst->word(word_index++);
+    const uint32_t offset_type_id = _.GetTypeId(offset_id);
+    if (!_.IsIntScalarOrVectorType(offset_type_id) ||
+        _.GetBitWidth(offset_type_id) != 32) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Expected Image Operand ConstOffset to be int scalar or "
+             << "Expected Image Operand ConstOffset to be a 32-bit int scalar "
+                "or "
              << "vector";
     }
 
-    if (!spvOpcodeIsConstant(_.GetIdOpcode(id))) {
+    if (!spvOpcodeIsConstant(_.GetIdOpcode(offset_id))) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Expected Image Operand ConstOffset to be a const object";
     }
 
     const uint32_t plane_size = GetPlaneCoordSize(info);
-    const uint32_t offset_size = _.GetDimension(type_id);
+    const uint32_t offset_size = _.GetDimension(offset_type_id);
     if (plane_size != offset_size) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Expected Image Operand ConstOffset to have " << plane_size
@@ -438,16 +614,17 @@
              << "Image Operand Offset cannot be used with Cube Image 'Dim'";
     }
 
-    const uint32_t id = inst->word(word_index++);
-    const uint32_t type_id = _.GetTypeId(id);
-    if (!_.IsIntScalarOrVectorType(type_id)) {
+    const uint32_t offset_id = inst->word(word_index++);
+    const uint32_t offset_type_id = _.GetTypeId(offset_id);
+    if (!_.IsIntScalarOrVectorType(offset_type_id) ||
+        _.GetBitWidth(offset_type_id) != 32) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Expected Image Operand Offset to be int scalar or "
+             << "Expected Image Operand Offset to be a 32-bit int scalar or "
              << "vector";
     }
 
     const uint32_t plane_size = GetPlaneCoordSize(info);
-    const uint32_t offset_size = _.GetDimension(type_id);
+    const uint32_t offset_size = _.GetDimension(offset_type_id);
     if (plane_size != offset_size) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Expected Image Operand Offset to have " << plane_size
@@ -455,15 +632,18 @@
     }
 
     if (!_.options()->before_hlsl_legalization &&
-        spvIsVulkanEnv(_.context()->target_env)) {
+        spvIsVulkanEnv(_.context()->target_env) &&
+        !_.options()->allow_offset_texture_operand) {
       if (opcode != spv::Op::OpImageGather &&
           opcode != spv::Op::OpImageDrefGather &&
           opcode != spv::Op::OpImageSparseGather &&
           opcode != spv::Op::OpImageSparseDrefGather) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << _.VkErrorID(4663)
+               << _.VkErrorID(10213)
                << "Image Operand Offset can only be used with "
-                  "OpImage*Gather operations";
+                  "OpImage*Gather operations."
+               << _.MissingFeature("maintenance8 feature",
+                                   "--allow-offset-texture-operand", false);
       }
     }
   }
@@ -484,9 +664,9 @@
                 "'Dim'";
     }
 
-    const uint32_t id = inst->word(word_index++);
-    const uint32_t type_id = _.GetTypeId(id);
-    const Instruction* type_inst = _.FindDef(type_id);
+    const uint32_t offset_id = inst->word(word_index++);
+    const uint32_t offset_type_id = _.GetTypeId(offset_id);
+    const Instruction* type_inst = _.FindDef(offset_type_id);
     assert(type_inst);
 
     if (type_inst->opcode() != spv::Op::OpTypeArray) {
@@ -506,13 +686,14 @@
 
     const uint32_t component_type = type_inst->word(2);
     if (!_.IsIntVectorType(component_type) ||
-        _.GetDimension(component_type) != 2) {
+        _.GetDimension(component_type) != 2 ||
+        _.GetBitWidth(component_type) != 32) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Expected Image Operand ConstOffsets array components to be "
-                "int vectors of size 2";
+             << "Expected Image Operand ConstOffsets array components to be a "
+                "32-bit int vectors of size 2";
     }
 
-    if (!spvOpcodeIsConstant(_.GetIdOpcode(id))) {
+    if (!spvOpcodeIsConstant(_.GetIdOpcode(offset_id))) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Expected Image Operand ConstOffsets to be a const object";
     }
@@ -534,10 +715,10 @@
              << "Image Operand Sample requires non-zero 'MS' parameter";
     }
 
-    const uint32_t type_id = _.GetTypeId(inst->word(word_index++));
-    if (!_.IsIntScalarType(type_id)) {
+    const uint32_t sample_type_id = _.GetTypeId(inst->word(word_index++));
+    if (!_.IsIntScalarType(sample_type_id, 32)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Expected Image Operand Sample to be int scalar";
+             << "Expected Image Operand Sample to be a 32-bit int scalar";
     }
   }
 
@@ -548,10 +729,10 @@
              << "opcodes or together with Image Operand Grad";
     }
 
-    const uint32_t type_id = _.GetTypeId(inst->word(word_index++));
-    if (!_.IsFloatScalarType(type_id)) {
+    const uint32_t minlod_type_id = _.GetTypeId(inst->word(word_index++));
+    if (!_.IsFloatScalarType(minlod_type_id, 32)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Expected Image Operand MinLod to be float scalar";
+             << "Expected Image Operand MinLod to be a 32-bit float scalar";
     }
 
     if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D &&
@@ -610,30 +791,31 @@
     if (auto error = ValidateMemoryScope(_, inst, visible_scope)) return error;
   }
 
-  if (mask & uint32_t(spv::ImageOperandsMask::SignExtend)) {
-    // Checked elsewhere: SPIR-V 1.4 version or later.
-
-    // "The texel value is converted to the target value via sign extension.
-    // Only valid when the texel type is a scalar or vector of integer type."
-    //
-    // We don't have enough information to know what the texel type is.
+  // Checked elsewhere: SPIR-V 1.4 version or later.
+  if (is_sign_extend || is_zero_extend) {
+    // We don't have enough information to know what the |texel value type| is.
     // In OpenCL, knowledge is deferred until runtime: the image SampledType is
     // void, and the Format is Unknown.
     // In Vulkan, the texel type is only known in all cases by the pipeline
     // setup.
-  }
 
-  if (mask & uint32_t(spv::ImageOperandsMask::ZeroExtend)) {
-    // Checked elsewhere: SPIR-V 1.4 version or later.
-
-    // "The texel value is converted to the target value via zero extension.
-    // Only valid when the texel type is a scalar or vector of integer type."
-    //
-    // We don't have enough information to know what the texel type is.
-    // In OpenCL, knowledge is deferred until runtime: the image SampledType is
-    // void, and the Format is Unknown.
-    // In Vulkan, the texel type is only known in all cases by the pipeline
-    // setup.
+    if (opcode == spv::Op::OpImageWrite) {
+      // OpImageWrite has no result type.
+      // TODO - Add Validation
+    } else if (IsImageSparse(opcode)) {
+      // Sparse image read/sample return a struct.
+      // TODO - Add Validation
+    } else {
+      if (is_sign_extend && !_.IsIntScalarOrVectorType(inst->type_id())) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Using SignExtend, but result type is not a scalar or vector "
+                  "integer type.";
+      } else if (is_zero_extend &&
+                 !_.IsUnsignedIntScalarOrVectorType(inst->type_id())) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Using ZeroExtend, but result type is a signed integer type.";
+      }
+    }
   }
 
   if (mask & uint32_t(spv::ImageOperandsMask::Offsets)) {
@@ -779,8 +961,7 @@
            << "Corrupt image type definition";
   }
 
-  if (_.IsIntScalarType(info.sampled_type) &&
-      (64 == _.GetBitWidth(info.sampled_type)) &&
+  if (_.IsIntScalarType(info.sampled_type, 64) &&
       !_.HasCapability(spv::Capability::Int64ImageEXT)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Capability Int64ImageEXT is required when using Sampled Type of "
@@ -789,12 +970,9 @@
 
   const auto target_env = _.context()->target_env;
   if (spvIsVulkanEnv(target_env)) {
-    if ((!_.IsFloatScalarType(info.sampled_type) &&
-         !_.IsIntScalarType(info.sampled_type)) ||
-        ((32 != _.GetBitWidth(info.sampled_type)) &&
-         (64 != _.GetBitWidth(info.sampled_type))) ||
-        ((64 == _.GetBitWidth(info.sampled_type)) &&
-         _.IsFloatScalarType(info.sampled_type))) {
+    if (!_.IsFloatScalarType(info.sampled_type, 32) &&
+        !_.IsIntScalarType(info.sampled_type, 32) &&
+        !_.IsIntScalarType(info.sampled_type, 64)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << _.VkErrorID(4656)
              << "Expected Sampled Type to be a 32-bit int, 64-bit int or "
@@ -924,6 +1102,33 @@
              << _.VkErrorID(9638)
              << "Dim must not be Rect in the Vulkan environment";
     }
+
+    // Can't check signedness here due to image operands (SignExtend or
+    // ZeroExtend) ability to overridesampled type
+    if (info.format != spv::ImageFormat::Unknown) {
+      // validated above so can assume this is a
+      // 32-bit float, 32-bit int, or 64-bit int
+      const bool is_int = _.IsIntScalarType(info.sampled_type);
+      const bool is_float = !is_int;
+      if ((is_float && !IsFloatImageFormat(info.format)) ||
+          (is_int && !IsIntImageFormat(info.format))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << _.VkErrorID(4965)
+               << "Image Format type does not match Sample Type operand ("
+               << (is_int ? "integer" : "float") << ")";
+      } else if (is_int) {
+        const uint32_t bit_width = _.GetBitWidth(info.sampled_type);
+        // format check above to be int
+        if ((bit_width == 32 && IsInt64ImageFormat(info.format)) ||
+            (bit_width == 64 && !IsInt64ImageFormat(info.format))) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << _.VkErrorID(4965)
+                 << "Image Format width does not match Sample Type "
+                    "operand (bit width of "
+                 << bit_width << ")";
+        }
+      }
+    }
   }
 
   return SPV_SUCCESS;
@@ -994,6 +1199,7 @@
     case spv::Op::OpImageBlockMatchWindowSSDQCOM:
     case spv::Op::OpImageBlockMatchGatherSADQCOM:
     case spv::Op::OpImageBlockMatchGatherSSDQCOM:
+    case spv::Op::OpImageSampleFootprintNV:
       return true;
     case spv::Op::OpStore:
       if (_.HasCapability(spv::Capability::BindlessTextureNV)) return true;
@@ -1003,6 +1209,110 @@
   }
 }
 
+spv_result_t ValidateImageCoordinate(ValidationState_t& _,
+                                     const Instruction* inst,
+                                     const ImageTypeInfo& info,
+                                     uint32_t word_index) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t coord_type = _.GetOperandTypeId(inst, word_index);
+
+  const bool float_only =
+      opcode == spv::Op::OpImageSampleImplicitLod ||
+      opcode == spv::Op::OpImageSampleDrefImplicitLod ||
+      opcode == spv::Op::OpImageSampleDrefExplicitLod ||
+      opcode == spv::Op::OpImageSampleProjImplicitLod ||
+      opcode == spv::Op::OpImageSampleProjExplicitLod ||
+      opcode == spv::Op::OpImageSampleProjDrefImplicitLod ||
+      opcode == spv::Op::OpImageSampleProjDrefExplicitLod ||
+      opcode == spv::Op::OpImageGather ||
+      opcode == spv::Op::OpImageDrefGather ||
+      opcode == spv::Op::OpImageQueryLod ||
+      opcode == spv::Op::OpImageSparseSampleImplicitLod ||
+      opcode == spv::Op::OpImageSparseSampleDrefImplicitLod ||
+      opcode == spv::Op::OpImageSparseSampleDrefExplicitLod ||
+      opcode == spv::Op::OpImageSparseGather ||
+      opcode == spv::Op::OpImageSparseDrefGather;
+
+  const bool int_only = opcode == spv::Op::OpImageFetch ||
+                        opcode == spv::Op::OpImageSparseFetch ||
+                        opcode == spv::Op::OpImageTexelPointer ||
+                        opcode == spv::Op::OpUntypedImageTexelPointerEXT;
+
+  const bool int_or_float = opcode == spv::Op::OpImageSampleExplicitLod ||
+                            opcode == spv::Op::OpImageSparseSampleExplicitLod ||
+                            opcode == spv::Op::OpImageRead ||
+                            opcode == spv::Op::OpImageWrite ||
+                            opcode == spv::Op::OpImageSparseRead;
+
+  assert(float_only || int_only || int_or_float);
+
+  if (float_only && !_.IsFloatScalarOrVectorType(coord_type)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Coordinate to be a 32-bit float scalar or vector";
+  } else if (int_only && !_.IsIntScalarOrVectorType(coord_type)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Coordinate to be a 32-bit integer scalar or vector";
+  } else if (int_or_float) {
+    if (!_.IsFloatScalarOrVectorType(coord_type) &&
+        !_.IsIntScalarOrVectorType(coord_type)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected Coordinate to be a 32-bit integer or float scalar or "
+                "vector";
+    }
+  }
+
+  // Needs to be after we validate the scalar/vector
+  if (_.GetBitWidth(coord_type) != 32) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Coordinate to be a 32-bit scalar or vector";
+  }
+
+  const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
+  const uint32_t actual_coord_size = _.GetDimension(coord_type);
+
+  if (opcode == spv::Op::OpImageTexelPointer) {
+    if (min_coord_size != actual_coord_size) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected Coordinate to have " << min_coord_size
+             << " components, but given " << actual_coord_size;
+    }
+  } else if (min_coord_size > actual_coord_size) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Coordinate to have at least " << min_coord_size
+           << " components, but given only " << actual_coord_size;
+  }
+
+  if (info.dim == spv::Dim::SubpassData) {
+    const Instruction* coord_inst =
+        _.FindDef(inst->GetOperandAs<uint32_t>(word_index));
+
+    bool is_zero_vector = false;
+    if (coord_inst->opcode() == spv::Op::OpConstantNull) {
+      is_zero_vector = true;
+    } else if (coord_inst->opcode() == spv::Op::OpConstantComposite) {
+      // There is zero reason we should be allowing a OpSpecConstantComposite
+      if (coord_inst->words().size() == 5) {
+        uint64_t val_0 = 0;
+        uint64_t val_1 = 0;
+        if (_.EvalConstantValUint64(coord_inst->word(3), &val_0) &&
+            _.EvalConstantValUint64(coord_inst->word(4), &val_1) &&
+            val_0 == 0 && val_1 == 0) {
+          is_zero_vector = true;
+        }
+      }
+    }
+
+    if (!is_zero_vector) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << _.VkErrorID(4660)
+             << "Expected Coordinate for a SubpassData image to be a "
+                "OpConstantComposite of (0,0) or OpConstantNull";
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
 spv_result_t ValidateSampledImage(ValidationState_t& _,
                                   const Instruction* inst) {
   auto type_inst = _.FindDef(inst->type_id());
@@ -1096,7 +1406,7 @@
                << "Result <id> from OpSampledImage instruction must not appear "
                   "as "
                   "operands of Op"
-               << spvOpcodeString(static_cast<spv::Op>(consumer_opcode)) << "."
+               << spvOpcodeString(consumer_opcode) << "."
                << " Found result <id> " << _.getIdName(inst->id())
                << " as an operand of <id> " << _.getIdName(consumer_instr->id())
                << ".";
@@ -1106,12 +1416,11 @@
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << "Result <id> from OpSampledImage instruction must not appear "
                   "as operand for Op"
-               << spvOpcodeString(static_cast<spv::Op>(consumer_opcode))
+               << spvOpcodeString(consumer_opcode)
                << ", since it is not specified as taking an "
-               << "OpTypeSampledImage."
-               << " Found result <id> " << _.getIdName(inst->id())
-               << " as an operand of <id> " << _.getIdName(consumer_instr->id())
-               << ".";
+               << "OpTypeSampledImage." << " Found result <id> "
+               << _.getIdName(inst->id()) << " as an operand of <id> "
+               << _.getIdName(consumer_instr->id()) << ".";
       }
     }
   }
@@ -1132,6 +1441,8 @@
 
 spv_result_t ValidateImageTexelPointer(ValidationState_t& _,
                                        const Instruction* inst) {
+  bool isUntyped = (inst->opcode() == spv::Op::OpUntypedImageTexelPointerEXT);
+
   const auto result_type = _.FindDef(inst->type_id());
   if (result_type->opcode() != spv::Op::OpTypePointer &&
       result_type->opcode() != spv::Op::OpTypeUntypedPointerKHR) {
@@ -1162,16 +1473,23 @@
     }
   }
 
-  const auto image_ptr = _.FindDef(_.GetOperandTypeId(inst, 2));
-  if (!image_ptr || image_ptr->opcode() != spv::Op::OpTypePointer) {
+  const auto image_ptr =
+      _.FindDef(_.GetOperandTypeId(inst, (isUntyped ? 3 : 2)));
+  if (!image_ptr ||
+      (isUntyped && image_ptr->opcode() != spv::Op::OpTypeUntypedPointerKHR) ||
+      (!isUntyped && image_ptr->opcode() != spv::Op::OpTypePointer)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Expected Image to be OpTypePointer";
+           << "Expected Image to be "
+           << (isUntyped ? "OpTypeUntypedPointerKHR" : "OpTypePointer");
   }
 
-  const auto image_type = image_ptr->GetOperandAs<uint32_t>(2);
+  const auto image_type = isUntyped ? inst->GetOperandAs<uint32_t>(2)
+                                    : image_ptr->GetOperandAs<uint32_t>(2);
   if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Expected Image to be OpTypePointer with Type OpTypeImage";
+           << "Expected Image to be "
+           << (isUntyped ? "OpTypeUntypedPointerKHR" : "OpTypePointer ")
+           << "with Type OpTypeImage";
   }
 
   ImageTypeInfo info;
@@ -1196,49 +1514,23 @@
 
   if (info.dim == spv::Dim::SubpassData) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Image Dim SubpassData cannot be used with OpImageTexelPointer";
+           << "Image Dim SubpassData cannot be used with "
+           << (isUntyped ? "OpUntypedImageTexelPointerEXT"
+                         : "OpImageTexelPointer");
   }
 
   if (info.dim == spv::Dim::TileImageDataEXT) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Image Dim TileImageDataEXT cannot be used with "
-              "OpImageTexelPointer";
+           << (isUntyped ? "OpUntypedImageTexelPointerEXT"
+                         : "OpImageTexelPointer");
   }
 
-  const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
-  if (!coord_type || !_.IsIntScalarOrVectorType(coord_type)) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Expected Coordinate to be integer scalar or vector";
-  }
+  if (spv_result_t result = ValidateImageCoordinate(
+          _, inst, info, /* word_index = */ (isUntyped ? 4 : 3)))
+    return result;
 
-  uint32_t expected_coord_size = 0;
-  if (info.arrayed == 0) {
-    expected_coord_size = GetPlaneCoordSize(info);
-  } else if (info.arrayed == 1) {
-    switch (info.dim) {
-      case spv::Dim::Dim1D:
-        expected_coord_size = 2;
-        break;
-      case spv::Dim::Cube:
-      case spv::Dim::Dim2D:
-        expected_coord_size = 3;
-        break;
-      default:
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Image 'Dim' must be one of 1D, 2D, or Cube when "
-                  "Arrayed is 1";
-        break;
-    }
-  }
-
-  const uint32_t actual_coord_size = _.GetDimension(coord_type);
-  if (expected_coord_size != actual_coord_size) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Expected Coordinate to have " << expected_coord_size
-           << " components, but given " << actual_coord_size;
-  }
-
-  const uint32_t sample_type = _.GetOperandTypeId(inst, 4);
+  const uint32_t sample_type = _.GetOperandTypeId(inst, (isUntyped ? 5 : 4));
   if (!sample_type || !_.IsIntScalarType(sample_type)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Sample to be integer scalar";
@@ -1246,7 +1538,8 @@
 
   if (info.multisampled == 0) {
     uint64_t ms = 0;
-    if (!_.EvalConstantValUint64(inst->GetOperandAs<uint32_t>(4), &ms) ||
+    if (!_.EvalConstantValUint64(
+            inst->GetOperandAs<uint32_t>(isUntyped ? 5 : 4), &ms) ||
         ms != 0) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Expected Sample for Image with MS 0 to be a valid <id> for "
@@ -1255,18 +1548,24 @@
   }
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    if ((info.format != spv::ImageFormat::R64i) &&
-        (info.format != spv::ImageFormat::R64ui) &&
-        (info.format != spv::ImageFormat::R32f) &&
-        (info.format != spv::ImageFormat::R32i) &&
-        (info.format != spv::ImageFormat::R32ui) &&
-        !((info.format == spv::ImageFormat::Rg16f ||
-           info.format == spv::ImageFormat::Rgba16f) &&
-          _.HasCapability(spv::Capability::AtomicFloat16VectorNV))) {
+    bool valid_format = info.format == spv::ImageFormat::R64i ||
+                        info.format == spv::ImageFormat::R64ui ||
+                        info.format == spv::ImageFormat::R32f ||
+                        info.format == spv::ImageFormat::R32i ||
+                        info.format == spv::ImageFormat::R32ui;
+    if (!valid_format &&
+        _.HasCapability(spv::Capability::AtomicFloat16VectorNV)) {
+      valid_format = info.format == spv::ImageFormat::Rg16f ||
+                     info.format == spv::ImageFormat::Rgba16f;
+    }
+
+    if (!valid_format) {
+      const uint32_t vuid = isUntyped ? 11416 : 4658;
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << _.VkErrorID(4658)
+             << _.VkErrorID(vuid)
              << "Expected the Image Format in Image to be R64i, R64ui, R32f, "
-                "R32i, or R32ui for Vulkan environment";
+                "R32i, or R32ui for Vulkan environment using Op"
+             << spvOpcodeString(inst->opcode());
     }
   }
 
@@ -1327,29 +1626,9 @@
     }
   }
 
-  const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
-  if ((opcode == spv::Op::OpImageSampleExplicitLod ||
-       opcode == spv::Op::OpImageSparseSampleExplicitLod) &&
-      _.HasCapability(spv::Capability::Kernel)) {
-    if (!_.IsFloatScalarOrVectorType(coord_type) &&
-        !_.IsIntScalarOrVectorType(coord_type)) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Expected Coordinate to be int or float scalar or vector";
-    }
-  } else {
-    if (!_.IsFloatScalarOrVectorType(coord_type)) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Expected Coordinate to be float scalar or vector";
-    }
-  }
-
-  const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
-  const uint32_t actual_coord_size = _.GetDimension(coord_type);
-  if (min_coord_size > actual_coord_size) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Expected Coordinate to have at least " << min_coord_size
-           << " components, but given only " << actual_coord_size;
-  }
+  if (spv_result_t result =
+          ValidateImageCoordinate(_, inst, info, /* word_index = */ 3))
+    return result;
 
   const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5);
 
@@ -1374,7 +1653,7 @@
 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) {
+  if (!_.IsFloatScalarType(dref_type, 32)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Expected Dref to be of 32-bit float type";
   }
@@ -1436,19 +1715,9 @@
            << GetActualResultTypeStr(opcode);
   }
 
-  const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
-  if (!_.IsFloatScalarOrVectorType(coord_type)) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Expected Coordinate to be float scalar or vector";
-  }
-
-  const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
-  const uint32_t actual_coord_size = _.GetDimension(coord_type);
-  if (min_coord_size > actual_coord_size) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Expected Coordinate to have at least " << min_coord_size
-           << " components, but given only " << actual_coord_size;
-  }
+  if (spv_result_t result =
+          ValidateImageCoordinate(_, inst, info, /* word_index = */ 3))
+    return result;
 
   if (spv_result_t result = ValidateImageDref(_, inst, info)) return result;
 
@@ -1510,19 +1779,9 @@
            << "Expected Image 'Sampled' parameter to be 1";
   }
 
-  const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
-  if (!_.IsIntScalarOrVectorType(coord_type)) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Expected Coordinate to be int scalar or vector";
-  }
-
-  const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
-  const uint32_t actual_coord_size = _.GetDimension(coord_type);
-  if (min_coord_size > actual_coord_size) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Expected Coordinate to have at least " << min_coord_size
-           << " components, but given only " << actual_coord_size;
-  }
+  if (spv_result_t result =
+          ValidateImageCoordinate(_, inst, info, /* word_index = */ 3))
+    return result;
 
   if (spv_result_t result =
           ValidateImageOperands(_, inst, info, /* word_index = */ 6))
@@ -1590,26 +1849,15 @@
            << "Expected Image 'Dim' to be 2D, Cube, or Rect";
   }
 
-  const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
-  if (!_.IsFloatScalarOrVectorType(coord_type)) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Expected Coordinate to be float scalar or vector";
-  }
-
-  const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
-  const uint32_t actual_coord_size = _.GetDimension(coord_type);
-  if (min_coord_size > actual_coord_size) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Expected Coordinate to have at least " << min_coord_size
-           << " components, but given only " << actual_coord_size;
-  }
+  if (spv_result_t result =
+          ValidateImageCoordinate(_, inst, info, /* word_index = */ 3))
+    return result;
 
   if (opcode == spv::Op::OpImageGather ||
       opcode == spv::Op::OpImageSparseGather) {
     const uint32_t component = inst->GetOperandAs<uint32_t>(4);
     const uint32_t component_index_type = _.GetTypeId(component);
-    if (!_.IsIntScalarType(component_index_type) ||
-        _.GetBitWidth(component_index_type) != 32) {
+    if (!_.IsIntScalarType(component_index_type, 32)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Expected Component to be 32-bit int scalar";
     }
@@ -1733,19 +1981,9 @@
   if (spv_result_t result = ValidateImageReadWrite(_, inst, info))
     return result;
 
-  const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
-  if (!_.IsIntScalarOrVectorType(coord_type)) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Expected Coordinate to be int scalar or vector";
-  }
-
-  const uint32_t min_coord_size = GetMinCoordSize(opcode, info);
-  const uint32_t actual_coord_size = _.GetDimension(coord_type);
-  if (min_coord_size > actual_coord_size) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Expected Coordinate to have at least " << min_coord_size
-           << " components, but given only " << actual_coord_size;
-  }
+  if (spv_result_t result =
+          ValidateImageCoordinate(_, inst, info, /* word_index = */ 3))
+    return result;
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
     if (info.format == spv::ImageFormat::Unknown &&
@@ -1790,19 +2028,9 @@
   if (spv_result_t result = ValidateImageReadWrite(_, inst, info))
     return result;
 
-  const uint32_t coord_type = _.GetOperandTypeId(inst, 1);
-  if (!_.IsIntScalarOrVectorType(coord_type)) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Expected Coordinate to be int scalar or vector";
-  }
-
-  const uint32_t min_coord_size = GetMinCoordSize(inst->opcode(), info);
-  const uint32_t actual_coord_size = _.GetDimension(coord_type);
-  if (min_coord_size > actual_coord_size) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Expected Coordinate to have at least " << min_coord_size
-           << " components, but given only " << actual_coord_size;
-  }
+  if (spv_result_t result =
+          ValidateImageCoordinate(_, inst, info, /* word_index = */ 1))
+    return result;
 
   // because it needs to match with 'Sampled Type' the Texel can't be a boolean
   const uint32_t texel_type = _.GetOperandTypeId(inst, 2);
@@ -1930,9 +2158,9 @@
   }
 
   const uint32_t lod_type = _.GetOperandTypeId(inst, 3);
-  if (!_.IsIntScalarType(lod_type)) {
+  if (!_.IsIntScalarType(lod_type, 32)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Expected Level of Detail to be int scalar";
+           << "Expected Level of Detail to be a 32-bit int scalar";
   }
   return SPV_SUCCESS;
 }
@@ -2093,27 +2321,9 @@
            << "Image 'Dim' must be 1D, 2D, 3D or Cube";
   }
 
-  const uint32_t coord_type = _.GetOperandTypeId(inst, 3);
-  if (_.HasCapability(spv::Capability::Kernel)) {
-    if (!_.IsFloatScalarOrVectorType(coord_type) &&
-        !_.IsIntScalarOrVectorType(coord_type)) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Expected Coordinate to be int or float scalar or vector";
-    }
-  } else {
-    if (!_.IsFloatScalarOrVectorType(coord_type)) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Expected Coordinate to be float scalar or vector";
-    }
-  }
-
-  const uint32_t min_coord_size = GetPlaneCoordSize(info);
-  const uint32_t actual_coord_size = _.GetDimension(coord_type);
-  if (min_coord_size > actual_coord_size) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Expected Coordinate to have at least " << min_coord_size
-           << " components, but given only " << actual_coord_size;
-  }
+  if (spv_result_t result =
+          ValidateImageCoordinate(_, inst, info, /* word_index = */ 3))
+    return result;
 
   // The operand is a sampled image.
   // The sampled image type is already checked to be parameterized by an image
@@ -2316,6 +2526,80 @@
   return res;
 }
 
+spv_result_t ValidateTileImageEXT(ValidationState_t& _,
+                                  const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+
+  const uint32_t result_type = inst->type_id();
+  const char* result_type_str = GetActualResultTypeStr(opcode);
+  if (opcode == spv::Op::OpColorAttachmentReadEXT) {
+    if (!_.IsFloatScalarOrVectorType(result_type) &&
+        !_.IsIntScalarOrVectorType(result_type)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected " << result_type_str
+             << " to be int or float scalar or vector type";
+    }
+
+    const uint32_t attachment_type = _.GetOperandTypeId(inst, 2);
+    if (_.GetIdOpcode(attachment_type) != spv::Op::OpTypeImage) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected Image to be of type OpTypeImage";
+    }
+
+    ImageTypeInfo info;
+    if (!GetImageTypeInfo(_, attachment_type, &info)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Corrupt image type definition";
+    }
+
+    if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) {
+      const uint32_t result_component_type = _.GetComponentType(result_type);
+      if (result_component_type != info.sampled_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected Image 'Sampled Type' to be the same as "
+               << GetActualResultTypeStr(opcode) << " components";
+      }
+    }
+
+    if (info.dim != spv::Dim::TileImageDataEXT) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Image 'Dim' must be TileImageDataEXT";
+    }
+  } else if (opcode == spv::Op::OpDepthAttachmentReadEXT) {
+    if (!_.IsFloatScalarType(result_type, 32)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected " << result_type_str
+             << " to be a 32-bit floating-point type scalar";
+    }
+  } else {
+    if (!_.IsIntScalarType(result_type, 32)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected " << result_type_str
+             << " to be a 32-bit integer type scalar";
+    }
+  }
+
+  size_t sample_word_index =
+      opcode == spv::Op::OpColorAttachmentReadEXT ? 4 : 3;
+
+  if (inst->words().size() == sample_word_index + 1) {
+    const uint32_t sample_id = inst->word(sample_word_index);
+    const uint32_t sample_type = _.GetTypeId(sample_id);
+    if (!_.IsIntScalarType(sample_type, 32)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected Sample to be a 32-bit int scalar";
+    }
+  }
+
+  _.function(inst->function()->id())
+      ->RegisterExecutionModelLimitation(
+          spv::ExecutionModel::Fragment,
+          spvOpcodeString(opcode) +
+              std::string(" requires Fragment execution model"));
+
+  return SPV_SUCCESS;
+}
+
 }  // namespace
 
 // Validates correctness of image instructions.
@@ -2378,6 +2662,7 @@
     case spv::Op::OpSampledImage:
       return ValidateSampledImage(_, inst);
     case spv::Op::OpImageTexelPointer:
+    case spv::Op::OpUntypedImageTexelPointerEXT:
       return ValidateImageTexelPointer(_, inst);
 
     case spv::Op::OpImageSampleImplicitLod:
@@ -2450,6 +2735,11 @@
     case spv::Op::OpImageBlockMatchGatherSSDQCOM:
       return ValidateImageProcessingQCOM(_, inst);
 
+    case spv::Op::OpColorAttachmentReadEXT:
+    case spv::Op::OpDepthAttachmentReadEXT:
+    case spv::Op::OpStencilAttachmentReadEXT:
+      return ValidateTileImageEXT(_, inst);
+
     default:
       break;
   }
diff --git a/source/val/validate_instruction.cpp b/source/val/validate_instruction.cpp
index 39b1c02..7b3b3af 100644
--- a/source/val/validate_instruction.cpp
+++ b/source/val/validate_instruction.cpp
@@ -20,13 +20,13 @@
 #include <vector>
 
 #include "source/enum_set.h"
-#include "source/enum_string_mapping.h"
 #include "source/extensions.h"
 #include "source/opcode.h"
 #include "source/operand.h"
 #include "source/spirv_constant.h"
 #include "source/spirv_target_env.h"
 #include "source/spirv_validator_options.h"
+#include "source/table2.h"
 #include "source/util/string_utils.h"
 #include "source/val/validate.h"
 #include "source/val/validation_state.h"
@@ -35,14 +35,13 @@
 namespace val {
 namespace {
 
-std::string ToString(const CapabilitySet& capabilities,
-                     const AssemblyGrammar& grammar) {
+std::string ToString(const CapabilitySet& capabilities) {
   std::stringstream ss;
   for (auto capability : capabilities) {
-    spv_operand_desc desc;
-    if (SPV_SUCCESS == grammar.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY,
-                                             uint32_t(capability), &desc))
-      ss << desc->name << " ";
+    const spvtools::OperandDesc* desc = nullptr;
+    if (SPV_SUCCESS == spvtools::LookupOperand(SPV_OPERAND_TYPE_CAPABILITY,
+                                               uint32_t(capability), &desc))
+      ss << desc->name().data() << " ";
     else
       ss << uint32_t(capability) << " ";
   }
@@ -72,10 +71,11 @@
       break;
   }
   // Look it up in the grammar
-  spv_opcode_desc opcode_desc = {};
-  if (SPV_SUCCESS == state.grammar().lookupOpcode(opcode, &opcode_desc)) {
+  const spvtools::InstructionDesc* opcode_desc = nullptr;
+  if (SPV_SUCCESS ==
+      LookupOpcodeForEnv(state.context()->target_env, opcode, &opcode_desc)) {
     return state.grammar().filterCapsAgainstTargetEnv(
-        opcode_desc->capabilities, opcode_desc->numCapabilities);
+        opcode_desc->capabilities());
   }
   return CapabilitySet();
 }
@@ -86,7 +86,7 @@
 // return an error code.
 spv_result_t OperandVersionExtensionCheck(
     ValidationState_t& _, const Instruction* inst, size_t which_operand,
-    const spv_operand_desc_t& operand_desc, uint32_t word) {
+    const spvtools::OperandDesc& operand_desc, uint32_t word) {
   const uint32_t module_version = _.version();
   const uint32_t operand_min_version = operand_desc.minVersion;
   const uint32_t operand_last_version = operand_desc.lastVersion;
@@ -103,27 +103,29 @@
     return _.diag(SPV_ERROR_WRONG_VERSION, inst)
            << spvtools::utils::CardinalToOrdinal(which_operand)
            << " operand of " << spvOpcodeString(inst->opcode()) << ": operand "
-           << operand_desc.name << "(" << word << ") requires SPIR-V version "
+           << operand_desc.name().data() << "(" << word
+           << ") requires SPIR-V version "
            << SPV_SPIRV_VERSION_MAJOR_PART(operand_last_version) << "."
            << SPV_SPIRV_VERSION_MINOR_PART(operand_last_version)
            << " or earlier";
   }
 
-  if (!reserved && operand_desc.numExtensions == 0) {
+  if (!reserved && operand_desc.extensions_range.empty()) {
     return _.diag(SPV_ERROR_WRONG_VERSION, inst)
            << spvtools::utils::CardinalToOrdinal(which_operand)
            << " operand of " << spvOpcodeString(inst->opcode()) << ": operand "
-           << operand_desc.name << "(" << word << ") requires SPIR-V version "
+           << operand_desc.name().data() << "(" << word
+           << ") requires SPIR-V version "
            << SPV_SPIRV_VERSION_MAJOR_PART(operand_min_version) << "."
            << SPV_SPIRV_VERSION_MINOR_PART(operand_min_version) << " or later";
   } else {
-    ExtensionSet required_extensions(operand_desc.numExtensions,
-                                     operand_desc.extensions);
+    ExtensionSet required_extensions(operand_desc.extensions_range.count(),
+                                     operand_desc.extensions().data());
     if (!_.HasAnyOfExtensions(required_extensions)) {
       return _.diag(SPV_ERROR_MISSING_EXTENSION, inst)
              << spvtools::utils::CardinalToOrdinal(which_operand)
              << " operand of " << spvOpcodeString(inst->opcode())
-             << ": operand " << operand_desc.name << "(" << word
+             << ": operand " << operand_desc.name().data() << "(" << word
              << ") requires one of these extensions: "
              << ExtensionSetToString(required_extensions);
     }
@@ -166,9 +168,9 @@
   }
 
   CapabilitySet enabling_capabilities;
-  spv_operand_desc operand_desc = nullptr;
+  const spvtools::OperandDesc* operand_desc = nullptr;
   const auto lookup_result =
-      state.grammar().lookupOperand(operand.type, word, &operand_desc);
+      spvtools::LookupOperand(operand.type, word, &operand_desc);
   if (lookup_result == SPV_SUCCESS) {
     // Allow FPRoundingMode decoration if requested.
     if (operand.type == SPV_OPERAND_TYPE_DECORATION &&
@@ -186,14 +188,15 @@
       }
     } else {
       enabling_capabilities = state.grammar().filterCapsAgainstTargetEnv(
-          operand_desc->capabilities, operand_desc->numCapabilities);
+          operand_desc->capabilities());
     }
 
     // When encountering an OpCapability instruction, the instruction pass
     // registers a capability with the module *before* checking capabilities.
     // So in the case of an OpCapability instruction, don't bother checking
     // enablement by another capability.
-    if (inst->opcode() != spv::Op::OpCapability) {
+    if (inst->opcode() != spv::Op::OpCapability &&
+        inst->opcode() != spv::Op::OpConditionalCapabilityINTEL) {
       const bool enabled_by_cap =
           state.HasAnyOfCapabilities(enabling_capabilities);
       if (!enabling_capabilities.empty() && !enabled_by_cap) {
@@ -201,7 +204,7 @@
                << "Operand " << which_operand << " of "
                << spvOpcodeString(inst->opcode())
                << " requires one of these capabilities: "
-               << ToString(enabling_capabilities, state.grammar());
+               << ToString(enabling_capabilities);
       }
     }
     return OperandVersionExtensionCheck(state, inst, which_operand,
@@ -222,10 +225,10 @@
     case spv::Op::OpImageSparseSampleProjExplicitLod:
     case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
     case spv::Op::OpImageSparseSampleProjDrefExplicitLod: {
-      spv_opcode_desc inst_desc;
-      _.grammar().lookupOpcode(opcode, &inst_desc);
+      const spvtools::InstructionDesc* inst_desc = nullptr;
+      spvtools::LookupOpcode(opcode, &inst_desc);
       return _.diag(SPV_ERROR_INVALID_BINARY, inst)
-             << "Invalid Opcode name 'Op" << inst_desc->name << "'";
+             << "Invalid Opcode name 'Op" << inst_desc->name().data() << "'";
     }
     default:
       break;
@@ -242,8 +245,7 @@
   if (!_.HasAnyOfCapabilities(opcode_caps)) {
     return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
            << "Opcode " << spvOpcodeString(opcode)
-           << " requires one of these capabilities: "
-           << ToString(opcode_caps, _.grammar());
+           << " requires one of these capabilities: " << ToString(opcode_caps);
   }
   for (size_t i = 0; i < inst->operands().size(); ++i) {
     const auto& operand = inst->operand(i);
@@ -276,8 +278,8 @@
 // dependencies for the opcode.
 spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) {
   const auto opcode = inst->opcode();
-  spv_opcode_desc inst_desc;
-  const spv_result_t r = _.grammar().lookupOpcode(opcode, &inst_desc);
+  const spvtools::InstructionDesc* inst_desc = nullptr;
+  const spv_result_t r = spvtools::LookupOpcode(opcode, &inst_desc);
   assert(r == SPV_SUCCESS);
   (void)r;
 
@@ -297,13 +299,14 @@
   const bool capability_check_is_sufficient =
       inst->opcode() != spv::Op::OpTerminateInvocation;
 
-  if (capability_check_is_sufficient && (inst_desc->numCapabilities > 0u)) {
+  if (capability_check_is_sufficient && !inst_desc->capabilities().empty()) {
     // We already checked that the direct capability dependency has been
     // satisfied. We don't need to check any further.
     return SPV_SUCCESS;
   }
 
-  ExtensionSet exts(inst_desc->numExtensions, inst_desc->extensions);
+  ExtensionSet exts(inst_desc->extensions().begin(),
+                    inst_desc->extensions().end());
   if (exts.empty()) {
     // If no extensions can enable this instruction, then emit error
     // messages only concerning core SPIR-V versions if errors happen.
@@ -459,10 +462,13 @@
 
 spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst) {
   const spv::Op opcode = inst->opcode();
-  if (opcode == spv::Op::OpExtension) {
+  if (opcode == spv::Op::OpExtension ||
+      opcode == spv::Op::OpConditionalExtensionINTEL) {
     CheckIfKnownExtension(_, inst);
   } else if (opcode == spv::Op::OpCapability) {
     _.RegisterCapability(inst->GetOperandAs<spv::Capability>(0));
+  } else if (opcode == spv::Op::OpConditionalCapabilityINTEL) {
+    _.RegisterCapability(inst->GetOperandAs<spv::Capability>(1));
   } else if (opcode == spv::Op::OpMemoryModel) {
     if (_.has_memory_model_specified()) {
       return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
@@ -481,6 +487,14 @@
             spv::ExecutionMode::LocalSizeId) {
       _.RegisterEntryPointLocalSize(entry_point, inst);
     }
+    if (inst->GetOperandAs<spv::ExecutionMode>(1) ==
+        spv::ExecutionMode::OutputPrimitivesEXT) {
+      _.RegisterEntryPointOutputPrimitivesEXT(entry_point, inst);
+    }
+    if (inst->GetOperandAs<spv::ExecutionMode>(1) ==
+        spv::ExecutionMode::OutputVertices) {
+      _.RegisterEntryPointOutputVertices(entry_point, inst);
+    }
   } else if (opcode == spv::Op::OpVariable) {
     const auto storage_class = inst->GetOperandAs<spv::StorageClass>(2);
     if (auto error = LimitCheckNumVars(_, inst->id(), storage_class)) {
diff --git a/source/val/validate_interfaces.cpp b/source/val/validate_interfaces.cpp
index 8b96dc8..3c62960 100644
--- a/source/val/validate_interfaces.cpp
+++ b/source/val/validate_interfaces.cpp
@@ -48,6 +48,29 @@
   }
 }
 
+// Special validation for varibles that are between shader stages
+spv_result_t ValidateInputOutputInterfaceVariables(ValidationState_t& _,
+                                                   const Instruction* var) {
+  auto var_pointer = _.FindDef(var->GetOperandAs<uint32_t>(0));
+  uint32_t pointer_id = var_pointer->GetOperandAs<uint32_t>(2);
+
+  const auto isPhysicalStorageBuffer = [](const Instruction* insn) {
+    return insn->opcode() == spv::Op::OpTypePointer &&
+           insn->GetOperandAs<spv::StorageClass>(1) ==
+               spv::StorageClass::PhysicalStorageBuffer;
+  };
+
+  if (_.ContainsType(pointer_id, isPhysicalStorageBuffer)) {
+    return _.diag(SPV_ERROR_INVALID_ID, var)
+           << _.VkErrorID(9557) << "Input/Output interface variable id <"
+           << var->id()
+           << "> contains a PhysicalStorageBuffer pointer, which is not "
+              "allowed. If you want to interface shader stages with a "
+              "PhysicalStorageBuffer, cast to a uint64 or uvec2 instead.";
+  }
+  return SPV_SUCCESS;
+}
+
 // Checks that \c var is listed as an interface in all the entry points that use
 // it.
 spv_result_t check_interface_variable(ValidationState_t& _,
@@ -107,6 +130,14 @@
     }
   }
 
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    if (var->GetOperandAs<spv::StorageClass>(2) == spv::StorageClass::Input ||
+        var->GetOperandAs<spv::StorageClass>(2) == spv::StorageClass::Output) {
+      if (auto error = ValidateInputOutputInterfaceVariables(_, var))
+        return error;
+    }
+  }
+
   return SPV_SUCCESS;
 }
 
@@ -124,11 +155,12 @@
       *num_locations = 1;
       break;
     case spv::Op::OpTypeVector:
+    case spv::Op::OpTypeVectorIdEXT:
       // 3- and 4-component 64-bit vectors consume two locations.
       if ((_.ContainsSizedIntOrFloatType(type->id(), spv::Op::OpTypeInt, 64) ||
            _.ContainsSizedIntOrFloatType(type->id(), spv::Op::OpTypeFloat,
                                          64)) &&
-          (type->GetOperandAs<uint32_t>(2) > 2)) {
+          (_.GetDimension(type->id()) > 2)) {
         *num_locations = 2;
       } else {
         *num_locations = 1;
@@ -208,12 +240,13 @@
       }
       break;
     case spv::Op::OpTypeVector:
+    case spv::Op::OpTypeVectorIdEXT:
       // Vectors consume components equal to the underlying type's consumption
       // times the number of elements in the vector. Note that 3- and 4-element
       // vectors cannot have a component decoration (i.e. assumed to be zero).
       num_components =
           NumConsumedComponents(_, _.FindDef(type->GetOperandAs<uint32_t>(1)));
-      num_components *= type->GetOperandAs<uint32_t>(2);
+      num_components *= _.GetDimension(type->id());
       break;
     case spv::Op::OpTypeArray:
       // Skip the array.
@@ -518,12 +551,21 @@
       return SPV_SUCCESS;
   }
 
+  const bool is_geometry = entry_point->GetOperandAs<spv::ExecutionModel>(0) ==
+                           spv::ExecutionModel::Geometry;
+  const bool has_geometry_streams =
+      is_geometry && _.HasCapability(spv::Capability::GeometryStreams);
+
   // Locations are stored as a combined location and component values.
   std::unordered_set<uint32_t> input_locations;
   std::unordered_set<uint32_t> output_locations_index0;
   std::unordered_set<uint32_t> output_locations_index1;
   std::unordered_set<uint32_t> patch_locations_index0;
   std::unordered_set<uint32_t> patch_locations_index1;
+  std::unordered_map<uint32_t, std::unordered_set<uint32_t>>
+      output_locations_per_stream;
+  std::unordered_map<uint32_t, std::unordered_set<uint32_t>>
+      output_index1_locations_per_stream;
   std::unordered_set<uint32_t> seen;
   for (uint32_t i = 3; i < entry_point->operands().size(); ++i) {
     auto interface_id = entry_point->GetOperandAs<uint32_t>(i);
@@ -561,12 +603,31 @@
       continue;
     }
 
-    auto locations = (storage_class == spv::StorageClass::Input)
-                         ? &input_locations
-                         : &output_locations_index0;
-    if (auto error = GetLocationsForVariable(
-            _, entry_point, interface_var, locations, &output_locations_index1))
-      return error;
+    // For geometry shader outputs with GeometryStreams,
+    // use per-stream location sets since each stream
+    // has an independent location namespace.
+    if (has_geometry_streams && storage_class == spv::StorageClass::Output) {
+      uint32_t stream = 0;
+      for (auto& dec : _.id_decorations(interface_var->id())) {
+        if (dec.dec_type() == spv::Decoration::Stream) {
+          stream = dec.params()[0];
+          break;
+        }
+      }
+      if (auto error = GetLocationsForVariable(
+              _, entry_point, interface_var,
+              &output_locations_per_stream[stream],
+              &output_index1_locations_per_stream[stream]))
+        return error;
+    } else {
+      auto locations = (storage_class == spv::StorageClass::Input)
+                           ? &input_locations
+                           : &output_locations_index0;
+      if (auto error =
+              GetLocationsForVariable(_, entry_point, interface_var, locations,
+                                      &output_locations_index1))
+        return error;
+    }
   }
 
   return SPV_SUCCESS;
@@ -584,7 +645,8 @@
     auto storage_class = interface_var->GetOperandAs<spv::StorageClass>(2);
     switch (storage_class) {
       case spv::StorageClass::PushConstant: {
-        if (has_push_constant) {
+        if (has_push_constant &&
+            !(_.HasCapability(spv::Capability::PushConstantBanksNV))) {
           return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
                  << _.VkErrorID(6673)
                  << "Entry-point has more than one variable with the "
@@ -623,6 +685,47 @@
         has_callable_data = true;
         break;
       }
+      case spv::StorageClass::Input:
+      case spv::StorageClass::Output: {
+        auto result_type = _.FindDef(interface_var->type_id());
+        if (_.ContainsType(result_type->GetOperandAs<uint32_t>(2),
+                           [](const Instruction* inst) {
+                             if (inst &&
+                                 inst->opcode() == spv::Op::OpTypeFloat) {
+                               if (inst->words().size() > 3) {
+                                 if (inst->GetOperandAs<spv::FPEncoding>(2) ==
+                                     spv::FPEncoding::BFloat16KHR) {
+                                   return true;
+                                 }
+                               }
+                             }
+                             return false;
+                           })) {
+          return _.diag(SPV_ERROR_INVALID_ID, interface_var)
+                 << _.VkErrorID(10370) << "Bfloat16 OpVariable <id> "
+                 << _.getIdName(interface_var->id()) << " must not be declared "
+                 << "with a Storage Class of Input or Output.";
+        }
+        if (_.ContainsType(
+                result_type->GetOperandAs<uint32_t>(2),
+                [](const Instruction* inst) {
+                  if (inst && inst->opcode() == spv::Op::OpTypeFloat) {
+                    if (inst->words().size() > 3) {
+                      auto encoding = inst->GetOperandAs<spv::FPEncoding>(2);
+                      if ((encoding == spv::FPEncoding::Float8E4M3EXT) ||
+                          (encoding == spv::FPEncoding::Float8E5M2EXT)) {
+                        return true;
+                      }
+                    }
+                  }
+                  return false;
+                })) {
+          return _.diag(SPV_ERROR_INVALID_ID, interface_var)
+                 << _.VkErrorID(10823) << "FP8 E4M3/E5M2 OpVariable <id> "
+                 << _.getIdName(interface_var->id()) << " must not be declared "
+                 << "with a Storage Class of Input or Output.";
+        }
+      }
       default:
         break;
     }
diff --git a/source/val/validate_invalid_type.cpp b/source/val/validate_invalid_type.cpp
new file mode 100644
index 0000000..05c09e2
--- /dev/null
+++ b/source/val/validate_invalid_type.cpp
@@ -0,0 +1,177 @@
+// Copyright (c) 2025 Google 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.
+
+// Performs validation of invalid type instructions.
+
+#include <vector>
+
+#include "source/opcode.h"
+#include "source/val/instruction.h"
+#include "source/val/validate.h"
+#include "source/val/validation_state.h"
+
+namespace spvtools {
+namespace val {
+
+// Validates correctness of certain special type instructions.
+spv_result_t InvalidTypePass(ValidationState_t& _, const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+
+  switch (opcode) {
+    // OpExtInst
+    case spv::Op::OpExtInst:
+    // Arithmetic Instructions
+    case spv::Op::OpFAdd:
+    case spv::Op::OpFSub:
+    case spv::Op::OpFMul:
+    case spv::Op::OpFDiv:
+    case spv::Op::OpFRem:
+    case spv::Op::OpFMod:
+    case spv::Op::OpFNegate:
+    // Derivative Instructions
+    case spv::Op::OpDPdx:
+    case spv::Op::OpDPdy:
+    case spv::Op::OpFwidth:
+    case spv::Op::OpDPdxFine:
+    case spv::Op::OpDPdyFine:
+    case spv::Op::OpFwidthFine:
+    case spv::Op::OpDPdxCoarse:
+    case spv::Op::OpDPdyCoarse:
+    case spv::Op::OpFwidthCoarse:
+    // Atomic Instructions
+    case spv::Op::OpAtomicFAddEXT:
+    case spv::Op::OpAtomicFMinEXT:
+    case spv::Op::OpAtomicFMaxEXT:
+    case spv::Op::OpAtomicLoad:
+    case spv::Op::OpAtomicExchange:
+    // Group and Subgroup Instructions
+    case spv::Op::OpGroupNonUniformRotateKHR:
+    case spv::Op::OpGroupNonUniformBroadcast:
+    case spv::Op::OpGroupNonUniformShuffle:
+    case spv::Op::OpGroupNonUniformShuffleXor:
+    case spv::Op::OpGroupNonUniformShuffleUp:
+    case spv::Op::OpGroupNonUniformShuffleDown:
+    case spv::Op::OpGroupNonUniformQuadBroadcast:
+    case spv::Op::OpGroupNonUniformQuadSwap:
+    case spv::Op::OpGroupNonUniformBroadcastFirst:
+    case spv::Op::OpGroupNonUniformFAdd:
+    case spv::Op::OpGroupNonUniformFMul:
+    case spv::Op::OpGroupNonUniformFMin: {
+      const uint32_t result_type = inst->type_id();
+      if (_.IsBfloat16Type(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << spvOpcodeString(opcode) << " doesn't support BFloat16 type.";
+      }
+      if (_.IsFP8Type(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << spvOpcodeString(opcode)
+               << " doesn't support FP8 E4M3/E5M2 types.";
+      }
+      break;
+    }
+
+    case spv::Op::OpAtomicStore: {
+      uint32_t data_type =
+          _.FindDef(inst->GetOperandAs<uint32_t>(3))->type_id();
+      if (_.IsBfloat16VectorType(data_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << spvOpcodeString(opcode) << " doesn't support BFloat16 type.";
+      }
+      if (_.IsFP8VectorType(data_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << spvOpcodeString(opcode)
+               << " doesn't support FP8 E4M3/E5M2 types.";
+      }
+      break;
+    }
+    // Relational and Logical Instructions
+    case spv::Op::OpIsNan:
+    case spv::Op::OpIsInf:
+    case spv::Op::OpIsFinite:
+    case spv::Op::OpIsNormal:
+    case spv::Op::OpFOrdEqual:
+    case spv::Op::OpFUnordEqual:
+    case spv::Op::OpFOrdNotEqual:
+    case spv::Op::OpFUnordNotEqual:
+    case spv::Op::OpFOrdLessThan:
+    case spv::Op::OpFUnordLessThan:
+    case spv::Op::OpFOrdGreaterThan:
+    case spv::Op::OpFUnordGreaterThan:
+    case spv::Op::OpFOrdLessThanEqual:
+    case spv::Op::OpFUnordLessThanEqual:
+    case spv::Op::OpFOrdGreaterThanEqual:
+    case spv::Op::OpFUnordGreaterThanEqual:
+    case spv::Op::OpLessOrGreater:
+    case spv::Op::OpOrdered:
+    case spv::Op::OpUnordered:
+    case spv::Op::OpSignBitSet: {
+      const uint32_t operand_type = _.GetOperandTypeId(inst, 2);
+      if (_.IsBfloat16Type(operand_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << spvOpcodeString(opcode) << " doesn't support BFloat16 type.";
+      }
+      if (_.IsFP8Type(operand_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << spvOpcodeString(opcode)
+               << " doesn't support FP8 E4M3/E5M2 types.";
+      }
+      break;
+    }
+
+    case spv::Op::OpGroupNonUniformAllEqual: {
+      const auto value_type = _.GetOperandTypeId(inst, 3);
+      if (_.IsBfloat16Type(value_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << spvOpcodeString(opcode) << " doesn't support BFloat16 type.";
+      }
+      if (_.IsFP8Type(value_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << spvOpcodeString(opcode)
+               << " doesn't support FP8 E4M3/E5M2 types.";
+      }
+
+      break;
+    }
+
+    case spv::Op::OpMatrixTimesMatrix: {
+      const uint32_t result_type = inst->type_id();
+      uint32_t res_num_rows = 0;
+      uint32_t res_num_cols = 0;
+      uint32_t res_col_type = 0;
+      uint32_t res_component_type = 0;
+      if (_.GetMatrixTypeInfo(result_type, &res_num_rows, &res_num_cols,
+                              &res_col_type, &res_component_type)) {
+        if (_.IsBfloat16Type(res_component_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << spvOpcodeString(opcode)
+                 << " doesn't support BFloat16 type.";
+        }
+        if (_.IsFP8Type(res_component_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << spvOpcodeString(opcode)
+                 << " doesn't support FP8 E4M3/E5M2 types.";
+        }
+      }
+      break;
+    }
+
+    default:
+      break;
+  }
+
+  return SPV_SUCCESS;
+}
+
+}  // namespace val
+}  // namespace spvtools
diff --git a/source/val/validate_layout.cpp b/source/val/validate_layout.cpp
index baf36b8..9a44e79 100644
--- a/source/val/validate_layout.cpp
+++ b/source/val/validate_layout.cpp
@@ -15,7 +15,6 @@
 // Source code for logical layout validation as described in section 2.4
 
 #include "DebugInfo.h"
-#include "NonSemanticShaderDebugInfo100.h"
 #include "OpenCLDebugInfo100.h"
 #include "source/opcode.h"
 #include "source/operand.h"
@@ -23,6 +22,7 @@
 #include "source/val/instruction.h"
 #include "source/val/validate.h"
 #include "source/val/validation_state.h"
+#include "spirv/unified1/NonSemanticShaderDebugInfo.h"
 
 namespace spvtools {
 namespace val {
@@ -50,16 +50,16 @@
           }
         } else if (inst->ext_inst_type() ==
                    SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
-          const NonSemanticShaderDebugInfo100Instructions ext_inst_key =
-              NonSemanticShaderDebugInfo100Instructions(ext_inst_index);
-          if (ext_inst_key == NonSemanticShaderDebugInfo100DebugScope ||
-              ext_inst_key == NonSemanticShaderDebugInfo100DebugNoScope ||
-              ext_inst_key == NonSemanticShaderDebugInfo100DebugDeclare ||
-              ext_inst_key == NonSemanticShaderDebugInfo100DebugValue ||
-              ext_inst_key == NonSemanticShaderDebugInfo100DebugLine ||
-              ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine ||
+          const NonSemanticShaderDebugInfoInstructions ext_inst_key =
+              NonSemanticShaderDebugInfoInstructions(ext_inst_index);
+          if (ext_inst_key == NonSemanticShaderDebugInfoDebugScope ||
+              ext_inst_key == NonSemanticShaderDebugInfoDebugNoScope ||
+              ext_inst_key == NonSemanticShaderDebugInfoDebugDeclare ||
+              ext_inst_key == NonSemanticShaderDebugInfoDebugValue ||
+              ext_inst_key == NonSemanticShaderDebugInfoDebugLine ||
+              ext_inst_key == NonSemanticShaderDebugInfoDebugNoLine ||
               ext_inst_key ==
-                  NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
+                  NonSemanticShaderDebugInfoDebugFunctionDefinition) {
             local_debug_info = true;
           }
         } else {
@@ -259,16 +259,16 @@
             }
           } else if (inst->ext_inst_type() ==
                      SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
-            const NonSemanticShaderDebugInfo100Instructions ext_inst_key =
-                NonSemanticShaderDebugInfo100Instructions(ext_inst_index);
-            if (ext_inst_key == NonSemanticShaderDebugInfo100DebugScope ||
-                ext_inst_key == NonSemanticShaderDebugInfo100DebugNoScope ||
-                ext_inst_key == NonSemanticShaderDebugInfo100DebugDeclare ||
-                ext_inst_key == NonSemanticShaderDebugInfo100DebugValue ||
-                ext_inst_key == NonSemanticShaderDebugInfo100DebugLine ||
-                ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine ||
+            const NonSemanticShaderDebugInfoInstructions ext_inst_key =
+                NonSemanticShaderDebugInfoInstructions(ext_inst_index);
+            if (ext_inst_key == NonSemanticShaderDebugInfoDebugScope ||
+                ext_inst_key == NonSemanticShaderDebugInfoDebugNoScope ||
+                ext_inst_key == NonSemanticShaderDebugInfoDebugDeclare ||
+                ext_inst_key == NonSemanticShaderDebugInfoDebugValue ||
+                ext_inst_key == NonSemanticShaderDebugInfoDebugLine ||
+                ext_inst_key == NonSemanticShaderDebugInfoDebugNoLine ||
                 ext_inst_key ==
-                    NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
+                    NonSemanticShaderDebugInfoDebugFunctionDefinition) {
               local_debug_info = true;
             }
           } else {
@@ -342,13 +342,84 @@
         break;
     }
   } else {
-    return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
-           << spvOpcodeString(opcode)
-           << " cannot appear in a function declaration";
+    _.ProgressToNextLayoutSectionOrder();
+    // All function sections have been processed. Recursively call
+    // ModuleLayoutPass to process the next section of the module
+    return ModuleLayoutPass(_, inst);
   }
   return SPV_SUCCESS;
 }
 
+spv_result_t GraphScopedInstructions(ValidationState_t& _,
+                                     const Instruction* inst, spv::Op opcode) {
+  if (_.IsOpcodeInCurrentLayoutSection(opcode)) {
+    switch (opcode) {
+      case spv::Op::OpGraphARM: {
+        if (_.graph_definition_region() > kGraphDefinitionOutside) {
+          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+                 << "Cannot define a graph in a graph";
+        }
+        _.SetGraphDefinitionRegion(kGraphDefinitionBegin);
+      } break;
+      case spv::Op::OpGraphInputARM: {
+        if ((_.graph_definition_region() != kGraphDefinitionBegin) &&
+            (_.graph_definition_region() != kGraphDefinitionInputs)) {
+          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+                 << "OpGraphInputARM"
+                 << " must immediately follow an OpGraphARM or OpGraphInputARM "
+                    "instruction.";
+        }
+        _.SetGraphDefinitionRegion(kGraphDefinitionInputs);
+      } break;
+      case spv::Op::OpGraphSetOutputARM: {
+        if ((_.graph_definition_region() != kGraphDefinitionBegin) &&
+            (_.graph_definition_region() != kGraphDefinitionInputs) &&
+            (_.graph_definition_region() != kGraphDefinitionBody) &&
+            (_.graph_definition_region() != kGraphDefinitionOutputs)) {
+          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+                 << "Op" << spvOpcodeString(opcode)
+                 << " must immediately precede an OpGraphEndARM or "
+                    "OpGraphSetOutputARM instruction.";
+        }
+        _.SetGraphDefinitionRegion(kGraphDefinitionOutputs);
+      } break;
+      case spv::Op::OpGraphEndARM: {
+        if (_.graph_definition_region() != kGraphDefinitionOutputs) {
+          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+                 << spvOpcodeString(opcode)
+                 << " must be preceded by at least one OpGraphSetOutputARM "
+                    "instruction";
+        }
+        _.SetGraphDefinitionRegion(kGraphDefinitionOutside);
+      } break;
+      case spv::Op::OpGraphEntryPointARM:
+        if (_.graph_definition_region() != kGraphDefinitionOutside) {
+          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+                 << spvOpcodeString(opcode)
+                 << " cannot appear in the definition of a graph";
+        }
+        break;
+      default:
+        if (_.graph_definition_region() == kGraphDefinitionOutside) {
+          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+                 << "Op" << spvOpcodeString(opcode)
+                 << " must appear in a graph body";
+        }
+        if (_.graph_definition_region() == kGraphDefinitionOutputs) {
+          return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+                 << spvOpcodeString(opcode)
+                 << " cannot appear after a graph output instruction";
+        }
+        _.SetGraphDefinitionRegion(kGraphDefinitionBody);
+        break;
+    }
+  } else {
+    return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+           << "Op" << spvOpcodeString(opcode)
+           << " cannot appear in the graph definitions section";
+  }
+  return SPV_SUCCESS;
+}
 }  // namespace
 
 // TODO(umar): Check linkage capabilities for function declarations
@@ -379,6 +450,11 @@
         return error;
       }
       break;
+    case kLayoutGraphDefinitions:
+      if (auto error = GraphScopedInstructions(_, inst, opcode)) {
+        return error;
+      }
+      break;
   }
   return SPV_SUCCESS;
 }
diff --git a/source/val/validate_logical_pointers.cpp b/source/val/validate_logical_pointers.cpp
new file mode 100644
index 0000000..b484312
--- /dev/null
+++ b/source/val/validate_logical_pointers.cpp
@@ -0,0 +1,1023 @@
+// Copyright (c) 2025 Google LLC.
+//
+// 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 <iostream>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "source/opcode.h"
+#include "source/val/validate.h"
+#include "source/val/validation_state.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+// Returns true if inst is a logical pointer.
+bool IsLogicalPointer(const ValidationState_t& _, const Instruction* inst) {
+  if (!_.IsPointerType(inst->type_id())) {
+    return false;
+  }
+
+  // Physical storage buffer pointers are not logical pointers.
+  auto type_inst = _.FindDef(inst->type_id());
+  auto sc = type_inst->GetOperandAs<spv::StorageClass>(1);
+  if (sc == spv::StorageClass::PhysicalStorageBuffer) {
+    return false;
+  }
+
+  return true;
+}
+
+// Returns true if inst is a variable pointer.
+// Caches the result in variable_pointers.
+bool IsVariablePointer(const ValidationState_t& _,
+                       std::unordered_map<uint32_t, bool>& variable_pointers,
+                       const Instruction* inst) {
+  const auto iter = variable_pointers.find(inst->id());
+  if (iter != variable_pointers.end()) {
+    return iter->second;
+  }
+
+  // Temporarily mark the instruction as NOT a variable pointer.
+  variable_pointers[inst->id()] = false;
+
+  bool is_var_ptr = false;
+  switch (inst->opcode()) {
+    case spv::Op::OpPtrAccessChain:
+    case spv::Op::OpUntypedPtrAccessChainKHR:
+    case spv::Op::OpUntypedInBoundsPtrAccessChainKHR:
+    case spv::Op::OpLoad:
+    case spv::Op::OpSelect:
+    case spv::Op::OpPhi:
+    case spv::Op::OpFunctionCall:
+    case spv::Op::OpConstantNull:
+      is_var_ptr = true;
+      break;
+    case spv::Op::OpFunctionParameter:
+      // Special case: skip to function calls.
+      if (IsLogicalPointer(_, inst)) {
+        auto func = inst->function();
+        auto func_inst = _.FindDef(func->id());
+
+        const auto param_inst_num = inst - &_.ordered_instructions()[0];
+        uint32_t param_index = 0;
+        uint32_t inst_index = 1;
+        while (_.ordered_instructions()[param_inst_num - inst_index].opcode() !=
+               spv::Op::OpFunction) {
+          if (_.ordered_instructions()[param_inst_num - inst_index].opcode() ==
+              spv::Op::OpFunctionParameter) {
+            param_index++;
+          }
+          ++inst_index;
+        }
+
+        for (const auto& use_pair : func_inst->uses()) {
+          const auto use_inst = use_pair.first;
+          if (use_inst->opcode() == spv::Op::OpFunctionCall) {
+            const auto arg_id =
+                use_inst->GetOperandAs<uint32_t>(3 + param_index);
+            const auto arg_inst = _.FindDef(arg_id);
+            is_var_ptr |= IsVariablePointer(_, variable_pointers, arg_inst);
+          }
+        }
+      }
+      break;
+    default: {
+      for (uint32_t i = 0; i < inst->operands().size(); ++i) {
+        if (inst->operands()[i].type != SPV_OPERAND_TYPE_ID) {
+          continue;
+        }
+
+        auto op_inst = _.FindDef(inst->GetOperandAs<uint32_t>(i));
+        if (IsLogicalPointer(_, op_inst)) {
+          is_var_ptr |= IsVariablePointer(_, variable_pointers, op_inst);
+        }
+      }
+      break;
+    }
+  }
+  variable_pointers[inst->id()] = is_var_ptr;
+  return is_var_ptr;
+}
+
+spv_result_t ValidateLogicalPointerOperands(ValidationState_t& _,
+                                            const Instruction* inst) {
+  bool has_pointer_operand = false;
+  spv::StorageClass sc = spv::StorageClass::Function;
+  for (uint32_t i = 0; i < inst->operands().size(); ++i) {
+    if (inst->operands()[i].type != SPV_OPERAND_TYPE_ID) {
+      continue;
+    }
+
+    auto op_inst = _.FindDef(inst->GetOperandAs<uint32_t>(i));
+    if (IsLogicalPointer(_, op_inst)) {
+      has_pointer_operand = true;
+
+      // Assume that there are not mixed storage classes in the instruction.
+      // This is not true for OpCopyMemory and OpCopyMemorySized, but they allow
+      // all storage classes.
+      auto type_inst = _.FindDef(op_inst->type_id());
+      sc = type_inst->GetOperandAs<spv::StorageClass>(1);
+      break;
+    }
+  }
+
+  if (!has_pointer_operand) {
+    return SPV_SUCCESS;
+  }
+
+  switch (inst->opcode()) {
+    // The following instructions allow logical pointer operands in all cases
+    // without capabilities.
+    case spv::Op::OpLoad:
+    case spv::Op::OpStore:
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpFunctionCall:
+    case spv::Op::OpImageTexelPointer:
+    case spv::Op::OpCopyMemory:
+    case spv::Op::OpCopyObject:
+    case spv::Op::OpArrayLength:
+    case spv::Op::OpExtInst:
+    // Core spec bugs
+    case spv::Op::OpDecorate:
+    case spv::Op::OpDecorateId:
+    case spv::Op::OpGroupDecorate:
+    case spv::Op::OpEntryPoint:
+    case spv::Op::OpName:
+    case spv::Op::OpDecorateString:
+    // SPV_KHR_untyped_pointers
+    case spv::Op::OpUntypedArrayLengthKHR:
+    case spv::Op::OpUntypedAccessChainKHR:
+    case spv::Op::OpUntypedInBoundsAccessChainKHR:
+    case spv::Op::OpCopyMemorySized:
+    // Cooperative matrix KHR/NV
+    case spv::Op::OpCooperativeMatrixLoadKHR:
+    case spv::Op::OpCooperativeMatrixLoadNV:
+    case spv::Op::OpCooperativeMatrixStoreKHR:
+    case spv::Op::OpCooperativeMatrixStoreNV:
+    // SPV_KHR_ray_tracing
+    case spv::Op::OpTraceRayKHR:
+    case spv::Op::OpExecuteCallableKHR:
+    // SPV_KHR_ray_query
+    case spv::Op::OpRayQueryConfirmIntersectionKHR:
+    case spv::Op::OpRayQueryInitializeKHR:
+    case spv::Op::OpRayQueryTerminateKHR:
+    case spv::Op::OpRayQueryGenerateIntersectionKHR:
+    case spv::Op::OpRayQueryProceedKHR:
+    case spv::Op::OpRayQueryGetIntersectionTypeKHR:
+    case spv::Op::OpRayQueryGetRayTMinKHR:
+    case spv::Op::OpRayQueryGetRayFlagsKHR:
+    case spv::Op::OpRayQueryGetIntersectionTKHR:
+    case spv::Op::OpRayQueryGetIntersectionInstanceCustomIndexKHR:
+    case spv::Op::OpRayQueryGetIntersectionInstanceIdKHR:
+    case spv::Op::
+        OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR:
+    case spv::Op::OpRayQueryGetIntersectionGeometryIndexKHR:
+    case spv::Op::OpRayQueryGetIntersectionPrimitiveIndexKHR:
+    case spv::Op::OpRayQueryGetIntersectionBarycentricsKHR:
+    case spv::Op::OpRayQueryGetIntersectionFrontFaceKHR:
+    case spv::Op::OpRayQueryGetIntersectionCandidateAABBOpaqueKHR:
+    case spv::Op::OpRayQueryGetIntersectionObjectRayDirectionKHR:
+    case spv::Op::OpRayQueryGetIntersectionObjectRayOriginKHR:
+    case spv::Op::OpRayQueryGetWorldRayDirectionKHR:
+    case spv::Op::OpRayQueryGetWorldRayOriginKHR:
+    case spv::Op::OpRayQueryGetIntersectionObjectToWorldKHR:
+    case spv::Op::OpRayQueryGetIntersectionWorldToObjectKHR:
+    // SPV_KHR_ray_tracing_position_fetch
+    case spv::Op::OpRayQueryGetIntersectionTriangleVertexPositionsKHR:
+    // SPV_NV_cluster_acceleration_structure
+    case spv::Op::OpRayQueryGetClusterIdNV:
+    case spv::Op::OpHitObjectGetClusterIdNV:
+    // SPV_NV_ray_tracing_motion_blur
+    case spv::Op::OpTraceMotionNV:
+    case spv::Op::OpTraceRayMotionNV:
+    // SPV_NV_linear_swept_spheres
+    case spv::Op::OpRayQueryGetIntersectionSpherePositionNV:
+    case spv::Op::OpRayQueryGetIntersectionSphereRadiusNV:
+    case spv::Op::OpRayQueryGetIntersectionLSSPositionsNV:
+    case spv::Op::OpRayQueryGetIntersectionLSSRadiiNV:
+    case spv::Op::OpRayQueryGetIntersectionLSSHitValueNV:
+    case spv::Op::OpRayQueryIsSphereHitNV:
+    case spv::Op::OpRayQueryIsLSSHitNV:
+    case spv::Op::OpHitObjectGetSpherePositionNV:
+    case spv::Op::OpHitObjectGetSphereRadiusNV:
+    case spv::Op::OpHitObjectGetLSSPositionsNV:
+    case spv::Op::OpHitObjectGetLSSRadiiNV:
+    case spv::Op::OpHitObjectIsSphereHitNV:
+    case spv::Op::OpHitObjectIsLSSHitNV:
+    // SPV_NV_shader_invocation_reorder
+    case spv::Op::OpReorderThreadWithHitObjectNV:
+    case spv::Op::OpHitObjectTraceRayNV:
+    case spv::Op::OpHitObjectTraceRayMotionNV:
+    case spv::Op::OpHitObjectRecordHitNV:
+    case spv::Op::OpHitObjectRecordHitMotionNV:
+    case spv::Op::OpHitObjectRecordHitWithIndexNV:
+    case spv::Op::OpHitObjectRecordHitWithIndexMotionNV:
+    case spv::Op::OpHitObjectRecordMissNV:
+    case spv::Op::OpHitObjectRecordMissMotionNV:
+    case spv::Op::OpHitObjectRecordEmptyNV:
+    case spv::Op::OpHitObjectExecuteShaderNV:
+    case spv::Op::OpHitObjectGetCurrentTimeNV:
+    case spv::Op::OpHitObjectGetAttributesNV:
+    case spv::Op::OpHitObjectGetHitKindNV:
+    case spv::Op::OpHitObjectGetPrimitiveIndexNV:
+    case spv::Op::OpHitObjectGetGeometryIndexNV:
+    case spv::Op::OpHitObjectGetInstanceIdNV:
+    case spv::Op::OpHitObjectGetInstanceCustomIndexNV:
+    case spv::Op::OpHitObjectGetObjectRayOriginNV:
+    case spv::Op::OpHitObjectGetObjectRayDirectionNV:
+    case spv::Op::OpHitObjectGetWorldRayDirectionNV:
+    case spv::Op::OpHitObjectGetWorldRayOriginNV:
+    case spv::Op::OpHitObjectGetObjectToWorldNV:
+    case spv::Op::OpHitObjectGetWorldToObjectNV:
+    case spv::Op::OpHitObjectGetRayTMaxNV:
+    case spv::Op::OpHitObjectGetRayTMinNV:
+    case spv::Op::OpHitObjectGetShaderBindingTableRecordIndexNV:
+    case spv::Op::OpHitObjectGetShaderRecordBufferHandleNV:
+    case spv::Op::OpHitObjectIsEmptyNV:
+    case spv::Op::OpHitObjectIsHitNV:
+    case spv::Op::OpHitObjectIsMissNV:
+    // SPV_EXT_shader_invocation_reorder
+    case spv::Op::OpHitObjectRecordFromQueryEXT:
+    case spv::Op::OpHitObjectRecordMissEXT:
+    case spv::Op::OpHitObjectRecordMissMotionEXT:
+    case spv::Op::OpHitObjectGetIntersectionTriangleVertexPositionsEXT:
+    case spv::Op::OpHitObjectGetRayFlagsEXT:
+    case spv::Op::OpHitObjectSetShaderBindingTableRecordIndexEXT:
+    case spv::Op::OpHitObjectReorderExecuteShaderEXT:
+    case spv::Op::OpHitObjectTraceReorderExecuteEXT:
+    case spv::Op::OpHitObjectTraceMotionReorderExecuteEXT:
+    case spv::Op::OpReorderThreadWithHintEXT:
+    case spv::Op::OpReorderThreadWithHitObjectEXT:
+    case spv::Op::OpHitObjectTraceRayEXT:
+    case spv::Op::OpHitObjectTraceRayMotionEXT:
+    case spv::Op::OpHitObjectRecordEmptyEXT:
+    case spv::Op::OpHitObjectExecuteShaderEXT:
+    case spv::Op::OpHitObjectGetCurrentTimeEXT:
+    case spv::Op::OpHitObjectGetAttributesEXT:
+    case spv::Op::OpHitObjectGetHitKindEXT:
+    case spv::Op::OpHitObjectGetPrimitiveIndexEXT:
+    case spv::Op::OpHitObjectGetGeometryIndexEXT:
+    case spv::Op::OpHitObjectGetInstanceIdEXT:
+    case spv::Op::OpHitObjectGetInstanceCustomIndexEXT:
+    case spv::Op::OpHitObjectGetObjectRayOriginEXT:
+    case spv::Op::OpHitObjectGetObjectRayDirectionEXT:
+    case spv::Op::OpHitObjectGetWorldRayDirectionEXT:
+    case spv::Op::OpHitObjectGetWorldRayOriginEXT:
+    case spv::Op::OpHitObjectGetObjectToWorldEXT:
+    case spv::Op::OpHitObjectGetWorldToObjectEXT:
+    case spv::Op::OpHitObjectGetRayTMaxEXT:
+    case spv::Op::OpHitObjectGetRayTMinEXT:
+    case spv::Op::OpHitObjectGetShaderBindingTableRecordIndexEXT:
+    case spv::Op::OpHitObjectGetShaderRecordBufferHandleEXT:
+    case spv::Op::OpHitObjectIsEmptyEXT:
+    case spv::Op::OpHitObjectIsHitEXT:
+    case spv::Op::OpHitObjectIsMissEXT:
+    // SPV_NV_raw_access_chains
+    case spv::Op::OpRawAccessChainNV:
+    // SPV_NV_cooperative_matrix2
+    case spv::Op::OpCooperativeMatrixLoadTensorNV:
+    case spv::Op::OpCooperativeMatrixStoreTensorNV:
+    // SPV_NV_cooperative_vector
+    case spv::Op::OpCooperativeVectorLoadNV:
+    case spv::Op::OpCooperativeVectorStoreNV:
+    case spv::Op::OpCooperativeVectorMatrixMulNV:
+    case spv::Op::OpCooperativeVectorMatrixMulAddNV:
+    case spv::Op::OpCooperativeVectorOuterProductAccumulateNV:
+    case spv::Op::OpCooperativeVectorReduceSumAccumulateNV:
+    // SPV_EXT_mesh_shader
+    case spv::Op::OpEmitMeshTasksEXT:
+    // SPV_AMD_shader_enqueue (spec bugs)
+    case spv::Op::OpEnqueueNodePayloadsAMDX:
+    case spv::Op::OpNodePayloadArrayLengthAMDX:
+    case spv::Op::OpIsNodePayloadValidAMDX:
+    case spv::Op::OpFinishWritingNodePayloadAMDX:
+    // SPV_ARM_graph
+    case spv::Op::OpGraphEntryPointARM:
+      return SPV_SUCCESS;
+    // SPV_EXT_descriptor_heap
+    case spv::Op::OpBufferPointerEXT:
+    case spv::Op::OpUntypedImageTexelPointerEXT:
+      return SPV_SUCCESS;
+    // The following cases require a variable pointer capability. Since all
+    // instructions are for variable pointers, the storage class and capability
+    // are also checked.
+    case spv::Op::OpReturnValue:
+    case spv::Op::OpPtrAccessChain:
+    case spv::Op::OpPtrEqual:
+    case spv::Op::OpPtrNotEqual:
+    case spv::Op::OpPtrDiff:
+    // Core spec bugs
+    case spv::Op::OpSelect:
+    case spv::Op::OpPhi:
+    case spv::Op::OpVariable:
+    // SPV_KHR_untyped_pointers
+    case spv::Op::OpUntypedPtrAccessChainKHR:
+      if ((_.HasCapability(spv::Capability::VariablePointersStorageBuffer) &&
+           sc == spv::StorageClass ::StorageBuffer) ||
+          (_.HasCapability(spv::Capability::VariablePointers) &&
+           sc == spv::StorageClass::Workgroup)) {
+        return SPV_SUCCESS;
+      }
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Instruction may only have a logical pointer operand in the "
+                "StorageBuffer or Workgroup storage classes with appropriate "
+                "variable pointers capability";
+    default:
+      if (spvOpcodeIsAtomicOp(inst->opcode())) {
+        return SPV_SUCCESS;
+      }
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Instruction may not have a logical pointer operand";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateLogicalPointerReturns(ValidationState_t& _,
+                                           const Instruction* inst) {
+  if (!IsLogicalPointer(_, inst)) {
+    return SPV_SUCCESS;
+  }
+
+  const auto type_inst = _.FindDef(inst->type_id());
+  const auto sc = type_inst->GetOperandAs<spv::StorageClass>(1u);
+
+  switch (inst->opcode()) {
+    // Core spec without an variable pointer capability.
+    case spv::Op::OpVariable:
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpFunctionParameter:
+    case spv::Op::OpImageTexelPointer:
+    case spv::Op::OpCopyObject:
+    // Core spec bugs
+    case spv::Op::OpUndef:
+    // SPV_INTEL_function_pointers
+    case spv::Op::OpConstantFunctionPointerINTEL:
+    // SPV_KHR_untyped_pointers
+    case spv::Op::OpUntypedAccessChainKHR:
+    case spv::Op::OpUntypedInBoundsAccessChainKHR:
+    case spv::Op::OpUntypedVariableKHR:
+    // SPV_NV_raw_access_chains
+    case spv::Op::OpRawAccessChainNV:
+    // SPV_AMD_shader_enqueue (spec bugs)
+    case spv::Op::OpAllocateNodePayloadsAMDX:
+      return SPV_SUCCESS;
+    // SPV_EXT_descriptor_heap
+    case spv::Op::OpBufferPointerEXT:
+    case spv::Op::OpUntypedImageTexelPointerEXT:
+      return SPV_SUCCESS;
+    // Core spec with variable pointer capability. Check storage classes since
+    // variable pointers can only be in certain storage classes.
+    case spv::Op::OpSelect:
+    case spv::Op::OpPhi:
+    case spv::Op::OpFunctionCall:
+    case spv::Op::OpPtrAccessChain:
+    case spv::Op::OpLoad:
+    case spv::Op::OpConstantNull:
+    case spv::Op::OpFunction:
+    // SPV_KHR_untyped_pointers
+    case spv::Op::OpUntypedPtrAccessChainKHR:
+      if ((_.HasCapability(spv::Capability::VariablePointersStorageBuffer) &&
+           sc == spv::StorageClass ::StorageBuffer) ||
+          (_.HasCapability(spv::Capability::VariablePointers) &&
+           sc == spv::StorageClass::Workgroup)) {
+        return SPV_SUCCESS;
+      }
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Instruction may only return a logical pointer in the "
+                "StorageBuffer or Workgroup storage classes with appropriate "
+                "variable pointers capability";
+    default:
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Instruction may not return a logical pointer";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t IsBlockArray(ValidationState_t& _, const Instruction* type) {
+  if (type->opcode() == spv::Op::OpTypeArray ||
+      type->opcode() == spv::Op::OpTypeRuntimeArray) {
+    const auto element_type = _.FindDef(type->GetOperandAs<uint32_t>(1));
+    if (element_type->opcode() == spv::Op::OpTypeStruct &&
+        (_.HasDecoration(element_type->id(), spv::Decoration::Block) ||
+         _.HasDecoration(element_type->id(), spv::Decoration::BufferBlock))) {
+      return SPV_ERROR_INVALID_DATA;
+    }
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t CheckMatrixElementTyped(ValidationState_t& _,
+                                     const Instruction* inst) {
+  switch (inst->opcode()) {
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpPtrAccessChain: {
+      // Get the type of the base operand.
+      uint32_t start_index =
+          inst->opcode() == spv::Op::OpPtrAccessChain ? 4 : 3;
+      const auto access_type_id = _.GetOperandTypeId(inst, 2);
+      auto access_type = _.FindDef(access_type_id);
+      access_type = _.FindDef(access_type->GetOperandAs<uint32_t>(2));
+
+      // If the base operand is a matrix, then it was definitely pointing to a
+      // sub-component.
+      if (access_type->opcode() == spv::Op::OpTypeMatrix) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Variable pointer must not point to a column or a "
+                  "component of a column of a matrix";
+      }
+
+      // Otherwise, step through the indices to see if we pass a matrix.
+      for (uint32_t i = start_index; i < inst->operands().size(); ++i) {
+        const auto index = inst->GetOperandAs<uint32_t>(i);
+        if (access_type->opcode() == spv::Op::OpTypeStruct) {
+          uint64_t val = 0;
+          _.EvalConstantValUint64(index, &val);
+          access_type = _.FindDef(access_type->GetOperandAs<uint32_t>(
+              1 + static_cast<uint32_t>(val)));
+        } else {
+          access_type = _.FindDef(_.GetComponentType(access_type->id()));
+        }
+
+        if (access_type->opcode() == spv::Op::OpTypeMatrix) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Variable pointer must not point to a column or a "
+                    "component of a column of a matrix";
+        }
+      }
+      break;
+    }
+    default:
+      break;
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t CheckMatrixElementUntyped(ValidationState_t& _,
+                                       const Instruction* inst) {
+  switch (inst->opcode()) {
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpPtrAccessChain:
+    case spv::Op::OpUntypedAccessChainKHR:
+    case spv::Op::OpUntypedInBoundsAccessChainKHR:
+    case spv::Op::OpUntypedPtrAccessChainKHR: {
+      const bool untyped = spvOpcodeGeneratesUntypedPointer(inst->opcode());
+      uint32_t start_index;
+      Instruction* access_type = nullptr;
+      if (untyped) {
+        // Get the type of the base operand.
+        start_index =
+            inst->opcode() == spv::Op::OpUntypedPtrAccessChainKHR ? 5 : 4;
+        const auto access_type_id = inst->GetOperandAs<uint32_t>(2);
+        access_type = _.FindDef(access_type_id);
+      } else {
+        start_index = inst->opcode() == spv::Op::OpPtrAccessChain ? 4 : 3;
+        const auto access_type_id = _.GetOperandTypeId(inst, 2);
+        access_type = _.FindDef(access_type_id);
+        access_type = _.FindDef(access_type->GetOperandAs<uint32_t>(2));
+      }
+
+      // If the base operand is a matrix, then it was definitely pointing to a
+      // sub-component.
+      if (access_type->opcode() == spv::Op::OpTypeMatrix) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Variable pointer must not point to a column or a "
+                  "component of a column of a matrix.";
+      }
+
+      // Otherwise, step through the indices to see if we pass a matrix.
+      for (uint32_t i = start_index; i < inst->operands().size(); ++i) {
+        const auto index = inst->GetOperandAs<uint32_t>(i);
+        if (access_type->opcode() == spv::Op::OpTypeStruct) {
+          uint64_t val = 0;
+          _.EvalConstantValUint64(index, &val);
+          access_type = _.FindDef(access_type->GetOperandAs<uint32_t>(
+              1 + static_cast<uint32_t>(val)));
+        } else {
+          access_type = _.FindDef(_.GetComponentType(access_type->id()));
+        }
+
+        if (access_type->opcode() == spv::Op::OpTypeMatrix) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Variable pointer must not point to a column or a "
+                    "component of a column of a matrix.";
+        }
+      }
+      break;
+    }
+    default:
+      break;
+  }
+  return SPV_SUCCESS;
+}
+
+// Traces the variable pointer inst backwards.
+// checker is called on each visited instruction.
+spv_result_t TraceVariablePointers(
+    ValidationState_t& _, const Instruction* inst,
+    const std::function<spv_result_t(ValidationState_t&, const Instruction*)>&
+        checker) {
+  std::vector<const Instruction*> stack;
+  std::unordered_set<const Instruction*> seen;
+  stack.push_back(inst);
+  while (!stack.empty()) {
+    const Instruction* trace_inst = stack.back();
+    stack.pop_back();
+
+    if (!seen.insert(trace_inst).second) {
+      continue;
+    }
+
+    if (auto error = checker(_, trace_inst)) {
+      return error;
+    }
+
+    const auto untyped = spvOpcodeGeneratesUntypedPointer(trace_inst->opcode());
+    switch (trace_inst->opcode()) {
+      case spv::Op::OpAccessChain:
+      case spv::Op::OpInBoundsAccessChain:
+      case spv::Op::OpPtrAccessChain:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(2)));
+        break;
+      case spv::Op::OpUntypedAccessChainKHR:
+      case spv::Op::OpUntypedInBoundsAccessChainKHR:
+      case spv::Op::OpUntypedPtrAccessChainKHR:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(3)));
+        break;
+      case spv::Op::OpPhi:
+        for (uint32_t i = 2; i < trace_inst->operands().size(); i += 2) {
+          stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(i)));
+        }
+        break;
+      case spv::Op::OpSelect:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(3)));
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(4)));
+        break;
+      case spv::Op::OpFunctionParameter: {
+        // Jump to function calls
+        auto func = trace_inst->function();
+        auto func_inst = _.FindDef(func->id());
+
+        const auto param_inst_num = trace_inst - &_.ordered_instructions()[0];
+        uint32_t param_index = 0;
+        uint32_t inst_index = 1;
+        while (_.ordered_instructions()[param_inst_num - inst_index].opcode() !=
+               spv::Op::OpFunction) {
+          if (_.ordered_instructions()[param_inst_num - inst_index].opcode() ==
+              spv::Op::OpFunctionParameter) {
+            param_index++;
+          }
+          ++inst_index;
+        }
+
+        for (const auto& use_pair : func_inst->uses()) {
+          const auto use_inst = use_pair.first;
+          if (use_inst->opcode() == spv::Op::OpFunctionCall) {
+            const auto arg_id =
+                use_inst->GetOperandAs<uint32_t>(3 + param_index);
+            const auto arg_inst = _.FindDef(arg_id);
+            stack.push_back(arg_inst);
+          }
+        }
+        break;
+      }
+      case spv::Op::OpFunctionCall: {
+        // Jump to return values.
+        const auto* func = _.function(trace_inst->GetOperandAs<uint32_t>(2));
+        for (auto* bb : func->ordered_blocks()) {
+          const auto* terminator = bb->terminator();
+          if (terminator->opcode() == spv::Op::OpReturnValue) {
+            stack.push_back(terminator);
+          }
+        }
+        break;
+      }
+      case spv::Op::OpReturnValue:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(0)));
+        break;
+      case spv::Op::OpCopyObject:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(2)));
+        break;
+      case spv::Op::OpLoad:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(2)));
+        break;
+      case spv::Op::OpStore:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(0)));
+        break;
+      case spv::Op::OpVariable:
+      case spv::Op::OpUntypedVariableKHR: {
+        const auto sc = trace_inst->GetOperandAs<spv::StorageClass>(2);
+        if (sc == spv::StorageClass::Function ||
+            sc == spv::StorageClass::Private) {
+          // Add the initializer
+          const uint32_t init_operand = untyped ? 4 : 3;
+          if (trace_inst->operands().size() > init_operand) {
+            stack.push_back(
+                _.FindDef(trace_inst->GetOperandAs<uint32_t>(init_operand)));
+          }
+          // Jump to stores
+          std::vector<std::pair<const Instruction*, uint32_t>> store_stack(
+              trace_inst->uses());
+          std::unordered_set<const Instruction*> store_seen;
+          while (!store_stack.empty()) {
+            const auto use = store_stack.back();
+            store_stack.pop_back();
+
+            if (!store_seen.insert(use.first).second) {
+              continue;
+            }
+
+            // If the use is a store pointer, trace the store object.
+            // Note: use.second is a word index.
+            if (use.first->opcode() == spv::Op::OpStore && use.second == 1) {
+              stack.push_back(_.FindDef(use.first->GetOperandAs<uint32_t>(1)));
+            } else {
+              // Most likely a gep so keep tracing.
+              for (auto& next_use : use.first->uses()) {
+                store_stack.push_back(next_use);
+              }
+            }
+          }
+        }
+        break;
+      }
+      default:
+        break;
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+// Traces the variable pointer inst backwards, but only unmodified pointers.
+// checker is called on each visited instruction.
+spv_result_t TraceUnmodifiedVariablePointers(
+    ValidationState_t& _, const Instruction* inst,
+    const std::function<spv_result_t(ValidationState_t&, const Instruction*)>&
+        checker) {
+  std::vector<const Instruction*> stack;
+  std::unordered_set<const Instruction*> seen;
+  stack.push_back(inst);
+  while (!stack.empty()) {
+    const Instruction* trace_inst = stack.back();
+    stack.pop_back();
+
+    if (!seen.insert(trace_inst).second) {
+      continue;
+    }
+
+    if (auto error = checker(_, trace_inst)) {
+      return error;
+    }
+
+    const auto untyped = spvOpcodeGeneratesUntypedPointer(trace_inst->opcode());
+    switch (trace_inst->opcode()) {
+      case spv::Op::OpAccessChain:
+      case spv::Op::OpInBoundsAccessChain:
+        if (trace_inst->operands().size() == 2) {
+          stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(2)));
+        }
+        break;
+      case spv::Op::OpUntypedAccessChainKHR:
+      case spv::Op::OpUntypedInBoundsAccessChainKHR:
+      case spv::Op::OpUntypedPtrAccessChainKHR:
+        if (trace_inst->operands().size() == 3) {
+          stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(3)));
+        }
+        break;
+      case spv::Op::OpPhi:
+        for (uint32_t i = 2; i < trace_inst->operands().size(); i += 2) {
+          stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(i)));
+        }
+        break;
+      case spv::Op::OpSelect:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(3)));
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(4)));
+        break;
+      case spv::Op::OpFunctionParameter: {
+        // Jump to function calls
+        auto func = trace_inst->function();
+        auto func_inst = _.FindDef(func->id());
+
+        const auto param_inst_num = trace_inst - &_.ordered_instructions()[0];
+        uint32_t param_index = 0;
+        uint32_t inst_index = 1;
+        while (_.ordered_instructions()[param_inst_num - inst_index].opcode() !=
+               spv::Op::OpFunction) {
+          if (_.ordered_instructions()[param_inst_num - inst_index].opcode() ==
+              spv::Op::OpFunctionParameter) {
+            param_index++;
+          }
+          ++inst_index;
+        }
+
+        for (const auto& use_pair : func_inst->uses()) {
+          const auto use_inst = use_pair.first;
+          if (use_inst->opcode() == spv::Op::OpFunctionCall) {
+            const auto arg_id =
+                use_inst->GetOperandAs<uint32_t>(3 + param_index);
+            const auto arg_inst = _.FindDef(arg_id);
+            stack.push_back(arg_inst);
+          }
+        }
+        break;
+      }
+      case spv::Op::OpFunctionCall: {
+        // Jump to return values.
+        const auto* func = _.function(trace_inst->GetOperandAs<uint32_t>(2));
+        for (auto* bb : func->ordered_blocks()) {
+          const auto* terminator = bb->terminator();
+          if (terminator->opcode() == spv::Op::OpReturnValue) {
+            stack.push_back(terminator);
+          }
+        }
+        break;
+      }
+      case spv::Op::OpReturnValue:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(0)));
+        break;
+      case spv::Op::OpCopyObject:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(2)));
+        break;
+      case spv::Op::OpLoad:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(2)));
+        break;
+      case spv::Op::OpStore:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(0)));
+        break;
+      case spv::Op::OpVariable:
+      case spv::Op::OpUntypedVariableKHR: {
+        const auto sc = trace_inst->GetOperandAs<spv::StorageClass>(2);
+        if (sc == spv::StorageClass::Function ||
+            sc == spv::StorageClass::Private) {
+          // Add the initializer
+          const uint32_t init_operand = untyped ? 4 : 3;
+          if (trace_inst->operands().size() > init_operand) {
+            stack.push_back(
+                _.FindDef(trace_inst->GetOperandAs<uint32_t>(init_operand)));
+          }
+          // Jump to stores
+          std::vector<std::pair<const Instruction*, uint32_t>> store_stack(
+              trace_inst->uses());
+          std::unordered_set<const Instruction*> store_seen;
+          while (!store_stack.empty()) {
+            const auto use = store_stack.back();
+            store_stack.pop_back();
+
+            if (!store_seen.insert(use.first).second) {
+              continue;
+            }
+
+            // If the use is a store pointer, trace the store object.
+            // Note: use.second is a word index.
+            if (use.first->opcode() == spv::Op::OpStore && use.second == 1) {
+              stack.push_back(_.FindDef(use.first->GetOperandAs<uint32_t>(1)));
+            } else {
+              // Most likely a gep so keep tracing.
+              for (auto& next_use : use.first->uses()) {
+                store_stack.push_back(next_use);
+              }
+            }
+          }
+        }
+        break;
+      }
+      default:
+        break;
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateVariablePointers(
+    ValidationState_t& _, std::unordered_map<uint32_t, bool>& variable_pointers,
+    const Instruction* inst) {
+  // Variable pointers cannot be operands to array length.
+  if (inst->opcode() == spv::Op::OpArrayLength ||
+      inst->opcode() == spv::Op::OpUntypedArrayLengthKHR) {
+    const auto ptr_index = inst->opcode() == spv::Op::OpArrayLength ? 2 : 3;
+    const auto ptr_id = inst->GetOperandAs<uint32_t>(ptr_index);
+    const auto ptr_inst = _.FindDef(ptr_id);
+    if (IsVariablePointer(_, variable_pointers, ptr_inst)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Pointer operand must not be a variable pointer";
+    }
+    return SPV_SUCCESS;
+  }
+
+  // Check untyped loads and stores of variable pointers for matrix types.
+  // Neither instruction would be a variable pointer in a such a case.
+  if (inst->opcode() == spv::Op::OpLoad) {
+    const auto pointer = _.FindDef(inst->GetOperandAs<uint32_t>(2));
+    const auto pointer_type = _.FindDef(pointer->type_id());
+    if (pointer_type->opcode() == spv::Op::OpTypeUntypedPointerKHR &&
+        IsVariablePointer(_, variable_pointers, pointer)) {
+      const auto data_type = _.FindDef(inst->type_id());
+      if (_.ContainsType(
+              data_type->id(),
+              [](const Instruction* type_inst) {
+                return type_inst->opcode() == spv::Op::OpTypeMatrix;
+              },
+              /* traverse_all_types = */ false)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Variable pointer must not point to an object that is or "
+                  "contains a matrix";
+      }
+    }
+  } else if (inst->opcode() == spv::Op::OpStore) {
+    const auto pointer = _.FindDef(inst->GetOperandAs<uint32_t>(0));
+    const auto pointer_type = _.FindDef(pointer->type_id());
+    if (pointer_type->opcode() == spv::Op::OpTypeUntypedPointerKHR &&
+        IsVariablePointer(_, variable_pointers, pointer)) {
+      const auto data_type_id = _.GetOperandTypeId(inst, 1);
+      const auto data_type = _.FindDef(data_type_id);
+      if (_.ContainsType(
+              data_type->id(),
+              [](const Instruction* type_inst) {
+                return type_inst->opcode() == spv::Op::OpTypeMatrix;
+              },
+              /* traverse_all_types = */ false)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Variable pointer must not point to an object that is or "
+                  "contains a matrix";
+      }
+    }
+  }
+
+  if (!IsLogicalPointer(_, inst) ||
+      !IsVariablePointer(_, variable_pointers, inst)) {
+    return SPV_SUCCESS;
+  }
+
+  const auto result_type = _.FindDef(inst->type_id());
+  const auto untyped =
+      result_type->opcode() == spv::Op::OpTypeUntypedPointerKHR;
+
+  // Pointers must be selected from the same buffer unless the VariablePointers
+  // capability is declared.
+  if (!_.HasCapability(spv::Capability::VariablePointers) &&
+      (inst->opcode() == spv::Op::OpSelect ||
+       inst->opcode() == spv::Op::OpPhi)) {
+    std::unordered_set<const Instruction*> sources;
+    const auto checker = [&sources, &inst](
+                             ValidationState_t& vstate,
+                             const Instruction* check_inst) -> spv_result_t {
+      switch (check_inst->opcode()) {
+        case spv::Op::OpVariable:
+        case spv::Op::OpUntypedVariableKHR:
+          if (check_inst->GetOperandAs<spv::StorageClass>(2) ==
+                  spv::StorageClass::StorageBuffer ||
+              check_inst->GetOperandAs<spv::StorageClass>(2) ==
+                  spv::StorageClass::Workgroup) {
+            sources.insert(check_inst);
+          }
+          if (sources.size() > 1) {
+            return vstate.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << "Variable pointers must point into the same structure "
+                      "(or OpConstantNull)";
+          }
+          break;
+        default:
+          break;
+      }
+      return SPV_SUCCESS;
+    };
+    if (auto error = TraceVariablePointers(_, inst, checker)) {
+      return error;
+    }
+  }
+
+  // Variable pointers must not:
+  // * point to array of Block- or BufferBlock-decorated structs
+  // * point to an object that is or contains a matrix
+  // * point to a column, or component in a column, of a matrix
+  if (untyped) {
+    if (auto error =
+            TraceVariablePointers(_, inst, CheckMatrixElementUntyped)) {
+      return error;
+    }
+
+    // Block arrays can only really appear as the top most type so only look at
+    // unmodified pointers to determine if one is used.
+    const auto num_operands = inst->operands().size();
+    if (!(num_operands == 3 &&
+          (inst->opcode() == spv::Op::OpUntypedAccessChainKHR ||
+           inst->opcode() == spv::Op::OpUntypedInBoundsAccessChainKHR ||
+           inst->opcode() == spv::Op::OpUntypedPtrAccessChainKHR))) {
+      const auto checker = [&inst](
+                               ValidationState_t& vstate,
+                               const Instruction* check_inst) -> spv_result_t {
+        bool fail = false;
+        if (check_inst->opcode() == spv::Op::OpUntypedVariableKHR) {
+          if (check_inst->operands().size() > 3) {
+            const auto type =
+                vstate.FindDef(check_inst->GetOperandAs<uint32_t>(3));
+            fail = IsBlockArray(vstate, type);
+          }
+        } else if (check_inst->opcode() == spv::Op::OpVariable) {
+          const auto res_type = vstate.FindDef(check_inst->type_id());
+          const auto pointee_type =
+              vstate.FindDef(res_type->GetOperandAs<uint32_t>(2));
+          fail = IsBlockArray(vstate, pointee_type);
+        }
+
+        if (fail) {
+          return vstate.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Variable pointer must not point to an array of Block- or "
+                    "BufferBlock-decorated structs";
+        }
+        return SPV_SUCCESS;
+      };
+
+      if (auto error = TraceUnmodifiedVariablePointers(_, inst, checker)) {
+        return error;
+      }
+    }
+  } else {
+    const auto pointee_type = _.FindDef(result_type->GetOperandAs<uint32_t>(2));
+    if (IsBlockArray(_, pointee_type)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Variable pointer must not point to an array of Block- or "
+                "BufferBlock-decorated structs";
+    } else if (_.ContainsType(
+                   pointee_type->id(),
+                   [](const Instruction* type_inst) {
+                     return type_inst->opcode() == spv::Op::OpTypeMatrix;
+                   },
+                   /* traverse_all_types = */ false)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Variable pointer must not point to an object that is or "
+                "contains a matrix";
+    } else if (_.IsFloatScalarOrVectorType(pointee_type->id())) {
+      // Pointing to a column or component in a column is trickier to detect.
+      // Trace backwards and check encountered access chains to determine if
+      // this pointer is pointing into a matrix.
+      if (auto error =
+              TraceVariablePointers(_, inst, CheckMatrixElementTyped)) {
+        return error;
+      }
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+}  // namespace
+
+spv_result_t ValidateLogicalPointers(ValidationState_t& _) {
+  // Only the following addressing models have logical pointers.
+  if (_.addressing_model() != spv::AddressingModel::Logical &&
+      _.addressing_model() != spv::AddressingModel::PhysicalStorageBuffer64) {
+    return SPV_SUCCESS;
+  }
+
+  if (_.options()->relax_logical_pointer) {
+    return SPV_SUCCESS;
+  }
+
+  // Cache all variable pointers
+  std::unordered_map<uint32_t, bool> variable_pointers;
+  for (auto& inst : _.ordered_instructions()) {
+    if (!IsLogicalPointer(_, &inst)) {
+      continue;
+    }
+
+    IsVariablePointer(_, variable_pointers, &inst);
+  }
+
+  for (auto& inst : _.ordered_instructions()) {
+    if (auto error = ValidateLogicalPointerOperands(_, &inst)) {
+      return error;
+    }
+    if (auto error = ValidateLogicalPointerReturns(_, &inst)) {
+      return error;
+    }
+    if (auto error = ValidateVariablePointers(_, variable_pointers, &inst)) {
+      return error;
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+}  // namespace val
+}  // namespace spvtools
diff --git a/source/val/validate_logicals.cpp b/source/val/validate_logicals.cpp
index 8a2e5d8..5f3161a 100644
--- a/source/val/validate_logicals.cpp
+++ b/source/val/validate_logicals.cpp
@@ -22,54 +22,258 @@
 namespace spvtools {
 namespace val {
 
-// Validates correctness of logical instructions.
-spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) {
+spv_result_t ValidateAnyAll(ValidationState_t& _, const Instruction* inst) {
   const spv::Op opcode = inst->opcode();
   const uint32_t result_type = inst->type_id();
+  if (!_.IsBoolScalarType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected bool scalar type as Result Type: "
+           << spvOpcodeString(opcode);
 
-  switch (opcode) {
-    case spv::Op::OpAny:
-    case spv::Op::OpAll: {
-      if (!_.IsBoolScalarType(result_type))
+  const uint32_t vector_type = _.GetOperandTypeId(inst, 2);
+  if (!vector_type || !_.IsBoolVectorType(vector_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected operand to be vector bool: " << spvOpcodeString(opcode);
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateSingleStatus(ValidationState_t& _,
+                                  const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected bool scalar or vector type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t operand_type = _.GetOperandTypeId(inst, 2);
+  if (!operand_type || (!_.IsFloatScalarType(operand_type) &&
+                        !_.IsFloatVectorType(operand_type)))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected operand to be scalar or vector float: "
+           << spvOpcodeString(opcode);
+
+  if (_.GetDimension(result_type) != _.GetDimension(operand_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected vector sizes of Result Type and the operand to be "
+              "equal: "
+           << spvOpcodeString(opcode);
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateFloatCompare(ValidationState_t& _,
+                                  const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected bool scalar or vector type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t left_operand_type = _.GetOperandTypeId(inst, 2);
+  if (!left_operand_type || (!_.IsFloatScalarType(left_operand_type) &&
+                             !_.IsFloatVectorType(left_operand_type)))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected operands to be scalar or vector float: "
+           << spvOpcodeString(opcode);
+
+  if (_.GetDimension(result_type) != _.GetDimension(left_operand_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected vector sizes of Result Type and the operands to be "
+              "equal: "
+           << spvOpcodeString(opcode);
+
+  if (left_operand_type != _.GetOperandTypeId(inst, 3))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected left and right operands to have the same type: "
+           << spvOpcodeString(opcode);
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateLogicalCompare(ValidationState_t& _,
+                                    const Instruction* inst,
+                                    uint32_t operand_index = 2) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected bool scalar or vector type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t operand_1 = _.GetOperandTypeId(inst, operand_index);
+  const uint32_t operand_2 = _.GetOperandTypeId(inst, operand_index + 1);
+  if (result_type != operand_1 || result_type != operand_2)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected both operands to be of Result Type: "
+           << spvOpcodeString(opcode);
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateLogicalNot(ValidationState_t& _, const Instruction* inst,
+                                uint32_t operand_index = 2) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected bool scalar or vector type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  if (result_type != _.GetOperandTypeId(inst, operand_index))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected operand to be of Result Type: "
+           << spvOpcodeString(opcode);
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateSelect(ValidationState_t& _, const Instruction* inst,
+                            uint32_t operand_index = 2) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  uint32_t dimension = 1;
+  const Instruction* type_inst = _.FindDef(result_type);
+  assert(type_inst);
+
+  const auto composites = _.features().select_between_composites;
+  auto fail = [&_, composites, inst, opcode]() -> spv_result_t {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected scalar or " << (composites ? "composite" : "vector")
+           << " type as Result Type: " << spvOpcodeString(opcode);
+  };
+
+  const spv::Op type_opcode = type_inst->opcode();
+  switch (type_opcode) {
+    case spv::Op::OpTypeUntypedPointerKHR:
+    case spv::Op::OpTypePointer: {
+      if (_.addressing_model() == spv::AddressingModel::Logical &&
+          !_.HasCapability(spv::Capability::VariablePointersStorageBuffer))
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected bool scalar type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t vector_type = _.GetOperandTypeId(inst, 2);
-      if (!vector_type || !_.IsBoolVectorType(vector_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected operand to be vector bool: "
-               << spvOpcodeString(opcode);
-
+               << "Using pointers with OpSelect requires capability "
+               << "VariablePointers or VariablePointersStorageBuffer";
       break;
     }
 
+    case spv::Op::OpTypeSampledImage:
+    case spv::Op::OpTypeImage:
+    case spv::Op::OpTypeSampler: {
+      if (!_.HasCapability(spv::Capability::BindlessTextureNV))
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Using image/sampler with OpSelect requires capability "
+               << "BindlessTextureNV";
+      break;
+    }
+
+    case spv::Op::OpTypeVector: {
+      dimension = type_inst->word(3);
+      break;
+    }
+    case spv::Op::OpTypeVectorIdEXT: {
+      dimension = _.GetDimension(result_type);
+      break;
+    }
+
+    case spv::Op::OpTypeBool:
+    case spv::Op::OpTypeInt:
+    case spv::Op::OpTypeFloat: {
+      break;
+    }
+
+    // Not RuntimeArray because of other rules.
+    case spv::Op::OpTypeArray:
+    case spv::Op::OpTypeMatrix:
+    case spv::Op::OpTypeStruct: {
+      if (!composites) return fail();
+      break;
+    }
+
+    default:
+      return fail();
+  }
+
+  const uint32_t condition_type = _.GetOperandTypeId(inst, operand_index);
+  const uint32_t left_type = _.GetOperandTypeId(inst, operand_index + 1);
+  const uint32_t right_type = _.GetOperandTypeId(inst, operand_index + 2);
+
+  if (!condition_type || (!_.IsBoolScalarType(condition_type) &&
+                          !_.IsBoolVectorType(condition_type)))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected bool scalar or vector type as condition: "
+           << spvOpcodeString(opcode);
+
+  if (_.GetDimension(condition_type) != dimension) {
+    // If the condition is a vector type, then the result must also be a
+    // vector with matching dimensions. In SPIR-V 1.4, a scalar condition
+    // can be used to select between vector types. |composites| is a
+    // proxy for SPIR-V 1.4 functionality.
+    if (!composites || _.IsBoolVectorType(condition_type)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Expected vector sizes of Result Type and the condition "
+                "to be equal: "
+             << spvOpcodeString(opcode);
+    }
+  }
+
+  if (result_type != left_type || result_type != right_type)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected both objects to be of Result Type: "
+           << spvOpcodeString(opcode);
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateIntCompare(ValidationState_t& _, const Instruction* inst,
+                                uint32_t operand_index = 2) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected bool scalar or vector type as Result Type: "
+           << spvOpcodeString(opcode);
+
+  const uint32_t left_type = _.GetOperandTypeId(inst, operand_index);
+  const uint32_t right_type = _.GetOperandTypeId(inst, operand_index + 1);
+
+  if (!left_type ||
+      (!_.IsIntScalarType(left_type) && !_.IsIntVectorType(left_type)))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected operands to be scalar or vector int: "
+           << spvOpcodeString(opcode);
+
+  if (_.GetDimension(result_type) != _.GetDimension(left_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected vector sizes of Result Type and the operands to be"
+           << " equal: " << spvOpcodeString(opcode);
+
+  if (!right_type ||
+      (!_.IsIntScalarType(right_type) && !_.IsIntVectorType(right_type)))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected operands to be scalar or vector int: "
+           << spvOpcodeString(opcode);
+
+  if (_.GetDimension(result_type) != _.GetDimension(right_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected vector sizes of Result Type and the operands to be"
+           << " equal: " << spvOpcodeString(opcode);
+
+  if (_.GetBitWidth(left_type) != _.GetBitWidth(right_type))
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected both operands to have the same component bit "
+              "width: "
+           << spvOpcodeString(opcode);
+  return SPV_SUCCESS;
+}
+
+// Validates correctness of logical instructions.
+spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) {
+  switch (inst->opcode()) {
+    case spv::Op::OpAny:
+    case spv::Op::OpAll:
+      return ValidateAnyAll(_, inst);
     case spv::Op::OpIsNan:
     case spv::Op::OpIsInf:
     case spv::Op::OpIsFinite:
     case spv::Op::OpIsNormal:
-    case spv::Op::OpSignBitSet: {
-      if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected bool scalar or vector type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t operand_type = _.GetOperandTypeId(inst, 2);
-      if (!operand_type || (!_.IsFloatScalarType(operand_type) &&
-                            !_.IsFloatVectorType(operand_type)))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected operand to be scalar or vector float: "
-               << spvOpcodeString(opcode);
-
-      if (_.GetDimension(result_type) != _.GetDimension(operand_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected vector sizes of Result Type and the operand to be "
-                  "equal: "
-               << spvOpcodeString(opcode);
-
-      break;
-    }
-
+    case spv::Op::OpSignBitSet:
+      return ValidateSingleStatus(_, inst);
     case spv::Op::OpFOrdEqual:
     case spv::Op::OpFUnordEqual:
     case spv::Op::OpFOrdNotEqual:
@@ -84,157 +288,17 @@
     case spv::Op::OpFUnordGreaterThanEqual:
     case spv::Op::OpLessOrGreater:
     case spv::Op::OpOrdered:
-    case spv::Op::OpUnordered: {
-      if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected bool scalar or vector type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      const uint32_t left_operand_type = _.GetOperandTypeId(inst, 2);
-      if (!left_operand_type || (!_.IsFloatScalarType(left_operand_type) &&
-                                 !_.IsFloatVectorType(left_operand_type)))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected operands to be scalar or vector float: "
-               << spvOpcodeString(opcode);
-
-      if (_.GetDimension(result_type) != _.GetDimension(left_operand_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected vector sizes of Result Type and the operands to be "
-                  "equal: "
-               << spvOpcodeString(opcode);
-
-      if (left_operand_type != _.GetOperandTypeId(inst, 3))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected left and right operands to have the same type: "
-               << spvOpcodeString(opcode);
-
-      break;
-    }
-
+    case spv::Op::OpUnordered:
+      return ValidateFloatCompare(_, inst);
     case spv::Op::OpLogicalEqual:
     case spv::Op::OpLogicalNotEqual:
     case spv::Op::OpLogicalOr:
-    case spv::Op::OpLogicalAnd: {
-      if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected bool scalar or vector type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      if (result_type != _.GetOperandTypeId(inst, 2) ||
-          result_type != _.GetOperandTypeId(inst, 3))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected both operands to be of Result Type: "
-               << spvOpcodeString(opcode);
-
-      break;
-    }
-
-    case spv::Op::OpLogicalNot: {
-      if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected bool scalar or vector type as Result Type: "
-               << spvOpcodeString(opcode);
-
-      if (result_type != _.GetOperandTypeId(inst, 2))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected operand to be of Result Type: "
-               << spvOpcodeString(opcode);
-
-      break;
-    }
-
-    case spv::Op::OpSelect: {
-      uint32_t dimension = 1;
-      {
-        const Instruction* type_inst = _.FindDef(result_type);
-        assert(type_inst);
-
-        const auto composites = _.features().select_between_composites;
-        auto fail = [&_, composites, inst, opcode]() -> spv_result_t {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected scalar or "
-                 << (composites ? "composite" : "vector")
-                 << " type as Result Type: " << spvOpcodeString(opcode);
-        };
-
-        const spv::Op type_opcode = type_inst->opcode();
-        switch (type_opcode) {
-          case spv::Op::OpTypeUntypedPointerKHR:
-          case spv::Op::OpTypePointer: {
-            if (_.addressing_model() == spv::AddressingModel::Logical &&
-                !_.HasCapability(
-                    spv::Capability::VariablePointersStorageBuffer))
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << "Using pointers with OpSelect requires capability "
-                     << "VariablePointers or VariablePointersStorageBuffer";
-            break;
-          }
-
-          case spv::Op::OpTypeSampledImage:
-          case spv::Op::OpTypeImage:
-          case spv::Op::OpTypeSampler: {
-            if (!_.HasCapability(spv::Capability::BindlessTextureNV))
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << "Using image/sampler with OpSelect requires capability "
-                     << "BindlessTextureNV";
-            break;
-          }
-
-          case spv::Op::OpTypeVector: {
-            dimension = type_inst->word(3);
-            break;
-          }
-
-          case spv::Op::OpTypeBool:
-          case spv::Op::OpTypeInt:
-          case spv::Op::OpTypeFloat: {
-            break;
-          }
-
-          // Not RuntimeArray because of other rules.
-          case spv::Op::OpTypeArray:
-          case spv::Op::OpTypeMatrix:
-          case spv::Op::OpTypeStruct: {
-            if (!composites) return fail();
-            break;
-          }
-
-          default:
-            return fail();
-        }
-
-        const uint32_t condition_type = _.GetOperandTypeId(inst, 2);
-        const uint32_t left_type = _.GetOperandTypeId(inst, 3);
-        const uint32_t right_type = _.GetOperandTypeId(inst, 4);
-
-        if (!condition_type || (!_.IsBoolScalarType(condition_type) &&
-                                !_.IsBoolVectorType(condition_type)))
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected bool scalar or vector type as condition: "
-                 << spvOpcodeString(opcode);
-
-        if (_.GetDimension(condition_type) != dimension) {
-          // If the condition is a vector type, then the result must also be a
-          // vector with matching dimensions. In SPIR-V 1.4, a scalar condition
-          // can be used to select between vector types. |composites| is a
-          // proxy for SPIR-V 1.4 functionality.
-          if (!composites || _.IsBoolVectorType(condition_type)) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << "Expected vector sizes of Result Type and the condition "
-                      "to be equal: "
-                   << spvOpcodeString(opcode);
-          }
-        }
-
-        if (result_type != left_type || result_type != right_type)
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Expected both objects to be of Result Type: "
-                 << spvOpcodeString(opcode);
-
-        break;
-      }
-    }
-
+    case spv::Op::OpLogicalAnd:
+      return ValidateLogicalCompare(_, inst);
+    case spv::Op::OpLogicalNot:
+      return ValidateLogicalNot(_, inst);
+    case spv::Op::OpSelect:
+      return ValidateSelect(_, inst);
     case spv::Op::OpIEqual:
     case spv::Op::OpINotEqual:
     case spv::Op::OpUGreaterThan:
@@ -244,43 +308,34 @@
     case spv::Op::OpSGreaterThan:
     case spv::Op::OpSGreaterThanEqual:
     case spv::Op::OpSLessThan:
-    case spv::Op::OpSLessThanEqual: {
-      if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected bool scalar or vector type as Result Type: "
-               << spvOpcodeString(opcode);
+    case spv::Op::OpSLessThanEqual:
+      return ValidateIntCompare(_, inst);
 
-      const uint32_t left_type = _.GetOperandTypeId(inst, 2);
-      const uint32_t right_type = _.GetOperandTypeId(inst, 3);
-
-      if (!left_type ||
-          (!_.IsIntScalarType(left_type) && !_.IsIntVectorType(left_type)))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected operands to be scalar or vector int: "
-               << spvOpcodeString(opcode);
-
-      if (_.GetDimension(result_type) != _.GetDimension(left_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected vector sizes of Result Type and the operands to be"
-               << " equal: " << spvOpcodeString(opcode);
-
-      if (!right_type ||
-          (!_.IsIntScalarType(right_type) && !_.IsIntVectorType(right_type)))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected operands to be scalar or vector int: "
-               << spvOpcodeString(opcode);
-
-      if (_.GetDimension(result_type) != _.GetDimension(right_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected vector sizes of Result Type and the operands to be"
-               << " equal: " << spvOpcodeString(opcode);
-
-      if (_.GetBitWidth(left_type) != _.GetBitWidth(right_type))
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected both operands to have the same component bit "
-                  "width: "
-               << spvOpcodeString(opcode);
-
+    case spv::Op::OpSpecConstantOp: {
+      switch (inst->GetOperandAs<spv::Op>(2u)) {
+        case spv::Op::OpLogicalEqual:
+        case spv::Op::OpLogicalNotEqual:
+        case spv::Op::OpLogicalOr:
+        case spv::Op::OpLogicalAnd:
+          return ValidateLogicalCompare(_, inst, 3);
+        case spv::Op::OpLogicalNot:
+          return ValidateLogicalNot(_, inst, 3);
+        case spv::Op::OpSelect:
+          return ValidateSelect(_, inst, 3);
+        case spv::Op::OpIEqual:
+        case spv::Op::OpINotEqual:
+        case spv::Op::OpUGreaterThan:
+        case spv::Op::OpUGreaterThanEqual:
+        case spv::Op::OpULessThan:
+        case spv::Op::OpULessThanEqual:
+        case spv::Op::OpSGreaterThan:
+        case spv::Op::OpSGreaterThanEqual:
+        case spv::Op::OpSLessThan:
+        case spv::Op::OpSLessThanEqual:
+          return ValidateIntCompare(_, inst, 3);
+        default:
+          break;
+      }
       break;
     }
 
diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp
index 32f6427..47d1a3c 100644
--- a/source/val/validate_memory.cpp
+++ b/source/val/validate_memory.cpp
@@ -1,6 +1,6 @@
 // Copyright (c) 2018 Google LLC.
-// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
-// reserved.
+// Modifications Copyright (C) 2020-2024 Advanced Micro Devices, Inc. All
+// rights reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -15,11 +15,13 @@
 // limitations under the License.
 
 #include <algorithm>
+#include <cstdint>
 #include <string>
 #include <vector>
 
 #include "source/opcode.h"
 #include "source/spirv_target_env.h"
+#include "source/table2.h"
 #include "source/val/instruction.h"
 #include "source/val/validate.h"
 #include "source/val/validate_scopes.h"
@@ -38,15 +40,15 @@
 bool HasConflictingMemberOffsets(const std::set<Decoration>&,
                                  const std::set<Decoration>&);
 
-bool IsAllowedTypeOrArrayOfSame(ValidationState_t& _, const Instruction* type,
+bool IsAllowedTypeOrArrayOfSame(ValidationState_t& _, const Instruction& type,
                                 std::initializer_list<spv::Op> allowed) {
-  if (std::find(allowed.begin(), allowed.end(), type->opcode()) !=
+  if (std::find(allowed.begin(), allowed.end(), type.opcode()) !=
       allowed.end()) {
     return true;
   }
-  if (type->opcode() == spv::Op::OpTypeArray ||
-      type->opcode() == spv::Op::OpTypeRuntimeArray) {
-    auto elem_type = _.FindDef(type->word(2));
+  if (type.opcode() == spv::Op::OpTypeArray ||
+      type.opcode() == spv::Op::OpTypeRuntimeArray) {
+    auto elem_type = _.FindDef(type.word(2));
     return std::find(allowed.begin(), allowed.end(), elem_type->opcode()) !=
            allowed.end();
   }
@@ -196,75 +198,43 @@
   return false;
 }
 
-bool ContainsCooperativeMatrix(ValidationState_t& _,
-                               const Instruction* storage) {
-  const size_t elem_type_index = 1;
-  uint32_t elem_type_id;
-  Instruction* elem_type;
-
-  switch (storage->opcode()) {
-    case spv::Op::OpTypeCooperativeMatrixNV:
-    case spv::Op::OpTypeCooperativeMatrixKHR:
-      return true;
-    case spv::Op::OpTypeArray:
-    case spv::Op::OpTypeRuntimeArray:
-      elem_type_id = storage->GetOperandAs<uint32_t>(elem_type_index);
-      elem_type = _.FindDef(elem_type_id);
-      return ContainsCooperativeMatrix(_, elem_type);
-    case spv::Op::OpTypeStruct:
-      for (size_t member_type_index = 1;
-           member_type_index < storage->operands().size();
-           ++member_type_index) {
-        auto member_type_id =
-            storage->GetOperandAs<uint32_t>(member_type_index);
-        auto member_type = _.FindDef(member_type_id);
-        if (ContainsCooperativeMatrix(_, member_type)) return true;
-      }
-      break;
-    default:
-      break;
-  }
-  return false;
-}
-
-std::pair<spv::StorageClass, spv::StorageClass> GetStorageClass(
-    ValidationState_t& _, const Instruction* inst) {
-  spv::StorageClass dst_sc = spv::StorageClass::Max;
-  spv::StorageClass src_sc = spv::StorageClass::Max;
+std::pair<Instruction*, Instruction*> GetPointerTypes(ValidationState_t& _,
+                                                      const Instruction* inst) {
+  Instruction* dst_pointer_type = nullptr;
+  Instruction* src_pointer_type = nullptr;
   switch (inst->opcode()) {
     case spv::Op::OpCooperativeMatrixLoadNV:
     case spv::Op::OpCooperativeMatrixLoadTensorNV:
     case spv::Op::OpCooperativeMatrixLoadKHR:
+    case spv::Op::OpCooperativeVectorLoadNV:
     case spv::Op::OpLoad: {
       auto load_pointer = _.FindDef(inst->GetOperandAs<uint32_t>(2));
-      auto load_pointer_type = _.FindDef(load_pointer->type_id());
-      dst_sc = load_pointer_type->GetOperandAs<spv::StorageClass>(1);
+      dst_pointer_type = _.FindDef(load_pointer->type_id());
       break;
     }
     case spv::Op::OpCooperativeMatrixStoreNV:
     case spv::Op::OpCooperativeMatrixStoreTensorNV:
     case spv::Op::OpCooperativeMatrixStoreKHR:
+    case spv::Op::OpCooperativeVectorStoreNV:
     case spv::Op::OpStore: {
       auto store_pointer = _.FindDef(inst->GetOperandAs<uint32_t>(0));
-      auto store_pointer_type = _.FindDef(store_pointer->type_id());
-      dst_sc = store_pointer_type->GetOperandAs<spv::StorageClass>(1);
+      dst_pointer_type = _.FindDef(store_pointer->type_id());
       break;
     }
+    // Spec: "Matching Storage Class is not required"
     case spv::Op::OpCopyMemory:
     case spv::Op::OpCopyMemorySized: {
-      auto dst = _.FindDef(inst->GetOperandAs<uint32_t>(0));
-      auto dst_type = _.FindDef(dst->type_id());
-      dst_sc = dst_type->GetOperandAs<spv::StorageClass>(1);
-      auto src = _.FindDef(inst->GetOperandAs<uint32_t>(1));
-      auto src_type = _.FindDef(src->type_id());
-      src_sc = src_type->GetOperandAs<spv::StorageClass>(1);
+      auto dst_pointer = _.FindDef(inst->GetOperandAs<uint32_t>(0));
+      dst_pointer_type = _.FindDef(dst_pointer->type_id());
+      auto src_pointer = _.FindDef(inst->GetOperandAs<uint32_t>(1));
+      src_pointer_type = _.FindDef(src_pointer->type_id());
       break;
     }
     default:
       break;
   }
 
-  return std::make_pair(dst_sc, src_sc);
+  return std::make_pair(dst_pointer_type, src_pointer_type);
 }
 
 // Returns the number of instruction words taken up by a memory access
@@ -280,8 +250,9 @@
 // Returns the scope ID operand for MakeAvailable memory access with mask
 // at the given operand index.
 // This function is only called for OpLoad, OpStore, OpCopyMemory and
-// OpCopyMemorySized, OpCooperativeMatrixLoadNV, and
-// OpCooperativeMatrixStoreNV.
+// OpCopyMemorySized, OpCooperativeMatrixLoadNV,
+// OpCooperativeMatrixStoreNV, OpCooperativeVectorLoadNV,
+// OpCooperativeVectorStoreNV.
 uint32_t GetMakeAvailableScope(const Instruction* inst, uint32_t mask,
                                uint32_t mask_index) {
   assert(mask & uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR));
@@ -292,8 +263,9 @@
 }
 
 // This function is only called for OpLoad, OpStore, OpCopyMemory,
-// OpCopyMemorySized, OpCooperativeMatrixLoadNV, and
-// OpCooperativeMatrixStoreNV.
+// OpCopyMemorySized, OpCooperativeMatrixLoadNV,
+// OpCooperativeMatrixStoreNV, OpCooperativeVectorLoadNV,
+// OpCooperativeVectorStoreNV.
 uint32_t GetMakeVisibleScope(const Instruction* inst, uint32_t mask,
                              uint32_t mask_index) {
   assert(mask & uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR));
@@ -315,8 +287,17 @@
 
 spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
                                uint32_t index) {
-  spv::StorageClass dst_sc, src_sc;
-  std::tie(dst_sc, src_sc) = GetStorageClass(_, inst);
+  Instruction* dst_pointer_type = nullptr;
+  Instruction* src_pointer_type = nullptr;  // only used for OpCopyMemory
+  std::tie(dst_pointer_type, src_pointer_type) = GetPointerTypes(_, inst);
+
+  const spv::StorageClass dst_sc =
+      dst_pointer_type ? dst_pointer_type->GetOperandAs<spv::StorageClass>(1)
+                       : spv::StorageClass::Max;
+  const spv::StorageClass src_sc =
+      src_pointer_type ? src_pointer_type->GetOperandAs<spv::StorageClass>(1)
+                       : spv::StorageClass::Max;
+
   if (inst->operands().size() <= index) {
     // Cases where lack of some operand is invalid
     if (src_sc == spv::StorageClass::PhysicalStorageBuffer ||
@@ -333,7 +314,8 @@
     if (inst->opcode() == spv::Op::OpLoad ||
         inst->opcode() == spv::Op::OpCooperativeMatrixLoadNV ||
         inst->opcode() == spv::Op::OpCooperativeMatrixLoadTensorNV ||
-        inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) {
+        inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR ||
+        inst->opcode() == spv::Op::OpCooperativeVectorLoadNV) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "MakePointerAvailableKHR cannot be used with OpLoad.";
     }
@@ -354,7 +336,8 @@
     if (inst->opcode() == spv::Op::OpStore ||
         inst->opcode() == spv::Op::OpCooperativeMatrixStoreNV ||
         inst->opcode() == spv::Op::OpCooperativeMatrixStoreKHR ||
-        inst->opcode() == spv::Op::OpCooperativeMatrixStoreTensorNV) {
+        inst->opcode() == spv::Op::OpCooperativeMatrixStoreTensorNV ||
+        inst->opcode() == spv::Op::OpCooperativeVectorStoreNV) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "MakePointerVisibleKHR cannot be used with OpStore.";
     }
@@ -405,74 +388,65 @@
              << _.VkErrorID(4708)
              << "Memory accesses with PhysicalStorageBuffer must use Aligned.";
     }
+  } else {
+    // even if there are other masks, the Aligned operand will be next
+    const uint32_t aligned_value = inst->GetOperandAs<uint32_t>(index + 1);
+    const bool is_power_of_two =
+        aligned_value && !(aligned_value & (aligned_value - 1));
+    if (!is_power_of_two) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "Memory accesses Aligned operand value " << aligned_value
+             << " is not a power of two.";
+    }
+
+    uint32_t largest_scalar = 0;
+    if (dst_sc == spv::StorageClass::PhysicalStorageBuffer) {
+      if (dst_pointer_type->opcode() != spv::Op::OpTypeUntypedPointerKHR) {
+        largest_scalar =
+            _.GetLargestScalarType(dst_pointer_type->GetOperandAs<uint32_t>(2));
+      } else if (inst->type_id() != 0) {
+        largest_scalar = _.GetLargestScalarType(inst->type_id());
+      } else {
+        // TODO need to handle cases like OpStore and OpCopyMemorySized which
+        // don't have a result type
+      }
+    }
+    // TODO - Handle Untyped in OpCopyMemory
+    if (src_sc == spv::StorageClass::PhysicalStorageBuffer &&
+        src_pointer_type->opcode() != spv::Op::OpTypeUntypedPointerKHR) {
+      largest_scalar = std::max(
+          largest_scalar,
+          _.GetLargestScalarType(src_pointer_type->GetOperandAs<uint32_t>(2)));
+    }
+    if (aligned_value < largest_scalar) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << _.VkErrorID(6314) << "Memory accesses Aligned operand value "
+             << aligned_value << " is too small, the largest scalar type is "
+             << largest_scalar << " bytes.";
+    }
   }
 
   return SPV_SUCCESS;
 }
 
-spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
+spv_result_t ValidateVariableInitializer(ValidationState_t& _,
+                                         const Instruction* inst,
+                                         spv::StorageClass storage_class,
+                                         uint32_t value_id) {
   const bool untyped_pointer = inst->opcode() == spv::Op::OpUntypedVariableKHR;
-
-  auto result_type = _.FindDef(inst->type_id());
-  if (untyped_pointer) {
-    if (!result_type ||
-        result_type->opcode() != spv::Op::OpTypeUntypedPointerKHR)
-      return _.diag(SPV_ERROR_INVALID_ID, inst)
-             << "Result type must be an untyped pointer";
-  } else {
-    if (!result_type || result_type->opcode() != spv::Op::OpTypePointer) {
-      return _.diag(SPV_ERROR_INVALID_ID, inst)
-             << "OpVariable Result Type <id> " << _.getIdName(inst->type_id())
-             << " is not a pointer type.";
-    }
-  }
-
-  const auto storage_class_index = 2u;
-  auto storage_class =
-      inst->GetOperandAs<spv::StorageClass>(storage_class_index);
-  uint32_t value_id = 0;
-  if (untyped_pointer) {
-    const auto has_data_type = 3u < inst->operands().size();
-    if (has_data_type) {
-      value_id = inst->GetOperandAs<uint32_t>(3u);
-      auto data_type = _.FindDef(value_id);
-      if (!data_type || !spvOpcodeGeneratesType(data_type->opcode())) {
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << "Data type must be a type instruction";
-      }
-    } else {
-      if (storage_class == spv::StorageClass::Function ||
-          storage_class == spv::StorageClass::Private ||
-          storage_class == spv::StorageClass::Workgroup) {
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << "Data type must be specified for Function, Private, and "
-                  "Workgroup storage classes";
-      }
-      if (spvIsVulkanEnv(_.context()->target_env)) {
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << "Vulkan requires that data type be specified";
-      }
-    }
-  }
-
-  // For OpVariable the data type comes from pointee type of the result type,
-  // while for OpUntypedVariableKHR the data type comes from the operand.
-  if (!untyped_pointer) {
-    value_id = result_type->GetOperandAs<uint32_t>(2);
-  }
-  auto value_type = value_id == 0 ? nullptr : _.FindDef(value_id);
-
-  const auto initializer_index = untyped_pointer ? 4u : 3u;
+  const uint32_t initializer_index = untyped_pointer ? 4u : 3u;
   if (initializer_index < inst->operands().size()) {
-    const auto initializer_id = inst->GetOperandAs<uint32_t>(initializer_index);
-    const auto initializer = _.FindDef(initializer_id);
-    const auto is_module_scope_var =
+    const uint32_t initializer_id =
+        inst->GetOperandAs<uint32_t>(initializer_index);
+    const Instruction* initializer = _.FindDef(initializer_id);
+    const uint32_t storage_class_index = 2u;
+    const bool is_module_scope_var =
         initializer &&
         (initializer->opcode() == spv::Op::OpVariable ||
          initializer->opcode() == spv::Op::OpUntypedVariableKHR) &&
         (initializer->GetOperandAs<spv::StorageClass>(storage_class_index) !=
          spv::StorageClass::Function);
-    const auto is_constant =
+    const bool is_constant =
         initializer && spvOpcodeIsConstant(initializer->opcode());
     if (!initializer || !(is_constant || is_module_scope_var)) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
@@ -485,188 +459,6 @@
     }
   }
 
-  if (storage_class != spv::StorageClass::Workgroup &&
-      storage_class != spv::StorageClass::CrossWorkgroup &&
-      storage_class != spv::StorageClass::Private &&
-      storage_class != spv::StorageClass::Function &&
-      storage_class != spv::StorageClass::UniformConstant &&
-      storage_class != spv::StorageClass::RayPayloadKHR &&
-      storage_class != spv::StorageClass::IncomingRayPayloadKHR &&
-      storage_class != spv::StorageClass::HitAttributeKHR &&
-      storage_class != spv::StorageClass::CallableDataKHR &&
-      storage_class != spv::StorageClass::IncomingCallableDataKHR &&
-      storage_class != spv::StorageClass::TaskPayloadWorkgroupEXT &&
-      storage_class != spv::StorageClass::HitObjectAttributeNV) {
-    bool storage_input_or_output = storage_class == spv::StorageClass::Input ||
-                                   storage_class == spv::StorageClass::Output;
-    bool builtin = false;
-    if (storage_input_or_output) {
-      for (const Decoration& decoration : _.id_decorations(inst->id())) {
-        if (decoration.dec_type() == spv::Decoration::BuiltIn) {
-          builtin = true;
-          break;
-        }
-      }
-    }
-    if (!builtin && value_type &&
-        ContainsInvalidBool(_, value_type, storage_input_or_output)) {
-      if (storage_input_or_output) {
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << _.VkErrorID(7290)
-               << "If OpTypeBool is stored in conjunction with OpVariable "
-                  "using Input or Output Storage Classes it requires a BuiltIn "
-                  "decoration";
-
-      } else {
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << "If OpTypeBool is stored in conjunction with OpVariable, it "
-                  "can only be used with non-externally visible shader Storage "
-                  "Classes: Workgroup, CrossWorkgroup, Private, Function, "
-                  "Input, Output, RayPayloadKHR, IncomingRayPayloadKHR, "
-                  "HitAttributeKHR, CallableDataKHR, "
-                  "IncomingCallableDataKHR, or UniformConstant";
-      }
-    }
-  }
-
-  if (!_.IsValidStorageClass(storage_class)) {
-    return _.diag(SPV_ERROR_INVALID_BINARY, inst)
-           << _.VkErrorID(4643)
-           << "Invalid storage class for target environment";
-  }
-
-  if (storage_class == spv::StorageClass::Generic) {
-    return _.diag(SPV_ERROR_INVALID_BINARY, inst)
-           << "Variable storage class cannot be Generic";
-  }
-
-  if (inst->function() && storage_class != spv::StorageClass::Function) {
-    return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
-           << "Variables must have a function[7] storage class inside"
-              " of a function";
-  }
-
-  if (!inst->function() && storage_class == spv::StorageClass::Function) {
-    return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
-           << "Variables can not have a function[7] storage class "
-              "outside of a function";
-  }
-
-  // SPIR-V 3.32.8: Check that pointer type and variable type have the same
-  // storage class.
-  const auto result_storage_class_index = 1;
-  const auto result_storage_class =
-      result_type->GetOperandAs<spv::StorageClass>(result_storage_class_index);
-  if (storage_class != result_storage_class) {
-    return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "Storage class must match result type storage class";
-  }
-
-  // Variable pointer related restrictions.
-  const auto pointee = untyped_pointer
-                           ? value_id == 0 ? nullptr : _.FindDef(value_id)
-                           : _.FindDef(result_type->word(3));
-  if (_.addressing_model() == spv::AddressingModel::Logical &&
-      !_.options()->relax_logical_pointer) {
-    // VariablePointersStorageBuffer is implied by VariablePointers.
-    if (pointee && pointee->opcode() == spv::Op::OpTypePointer) {
-      if (!_.HasCapability(spv::Capability::VariablePointersStorageBuffer)) {
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << "In Logical addressing, variables may not allocate a pointer "
-               << "type";
-      } else if (storage_class != spv::StorageClass::Function &&
-                 storage_class != spv::StorageClass::Private) {
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << "In Logical addressing with variable pointers, variables "
-               << "that allocate pointers must be in Function or Private "
-               << "storage classes";
-      }
-    }
-  }
-
-  if (spvIsVulkanEnv(_.context()->target_env)) {
-    // Vulkan Push Constant Interface section: Check type of PushConstant
-    // variables.
-    if (storage_class == spv::StorageClass::PushConstant) {
-      if (pointee && pointee->opcode() != spv::Op::OpTypeStruct) {
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << _.VkErrorID(6808) << "PushConstant OpVariable <id> "
-               << _.getIdName(inst->id()) << " has illegal type.\n"
-               << "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 == spv::StorageClass::UniformConstant) {
-      if (pointee && !IsAllowedTypeOrArrayOfSame(
-                         _, pointee,
-                         {spv::Op::OpTypeImage, spv::Op::OpTypeSampler,
-                          spv::Op::OpTypeSampledImage,
-                          spv::Op::OpTypeAccelerationStructureKHR})) {
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << _.VkErrorID(4655) << "UniformConstant OpVariable <id> "
-               << _.getIdName(inst->id()) << " has illegal type.\n"
-               << "Variables identified with the UniformConstant storage class "
-               << "are used only as handles to refer to opaque resources. Such "
-               << "variables must be typed as OpTypeImage, OpTypeSampler, "
-               << "OpTypeSampledImage, OpTypeAccelerationStructureKHR, "
-               << "or an array of one of these types.";
-      }
-    }
-
-    if (storage_class == spv::StorageClass::Uniform) {
-      if (pointee &&
-          !IsAllowedTypeOrArrayOfSame(_, pointee, {spv::Op::OpTypeStruct})) {
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << _.VkErrorID(6807) << "Uniform OpVariable <id> "
-               << _.getIdName(inst->id()) << " has illegal type.\n"
-               << "From Vulkan spec:\n"
-               << "Variables identified with the Uniform storage class are "
-               << "used to access transparent buffer backed resources. Such "
-               << "variables must be typed as OpTypeStruct, or an array of "
-               << "this type";
-      }
-    }
-
-    if (storage_class == spv::StorageClass::StorageBuffer) {
-      if (pointee &&
-          !IsAllowedTypeOrArrayOfSame(_, pointee, {spv::Op::OpTypeStruct})) {
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << _.VkErrorID(6807) << "StorageBuffer OpVariable <id> "
-               << _.getIdName(inst->id()) << " has illegal type.\n"
-               << "From Vulkan spec:\n"
-               << "Variables identified with the StorageBuffer storage class "
-                  "are used to access transparent buffer backed resources. "
-                  "Such variables must be typed as OpTypeStruct, or an array "
-                  "of this type";
-      }
-    }
-
-    // Check for invalid use of Invariant
-    if (storage_class != spv::StorageClass::Input &&
-        storage_class != spv::StorageClass::Output) {
-      if (_.HasDecoration(inst->id(), spv::Decoration::Invariant)) {
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << _.VkErrorID(4677)
-               << "Variable decorated with Invariant must only be identified "
-                  "with the Input or Output storage class in Vulkan "
-                  "environment.";
-      }
-      // Need to check if only the members in a struct are decorated
-      if (value_type && value_type->opcode() == spv::Op::OpTypeStruct) {
-        if (_.HasDecoration(value_id, spv::Decoration::Invariant)) {
-          return _.diag(SPV_ERROR_INVALID_ID, inst)
-                 << _.VkErrorID(4677)
-                 << "Variable struct member decorated with Invariant must only "
-                    "be identified with the Input or Output storage class in "
-                    "Vulkan environment.";
-        }
-      }
-    }
-  }
-
   // Vulkan Appendix A: Check that if contains initializer, then
   // storage class is Output, Private, or Function.
   if (inst->operands().size() > initializer_index &&
@@ -718,108 +510,275 @@
              << "OpVariable, <id> " << _.getIdName(inst->id())
              << ", initializer are not allowed for HitObjectAttributeNV";
     }
+    if (storage_class == spv::StorageClass::HitObjectAttributeEXT) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "OpVariable, <id> " << _.getIdName(inst->id())
+             << ", initializer are not allowed for HitObjectAttributeEXT";
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateVariableStorageClass(ValidationState_t& _,
+                                          const Instruction* inst,
+                                          spv::StorageClass storage_class,
+                                          const Instruction* value_type) {
+  if (storage_class != spv::StorageClass::Workgroup &&
+      storage_class != spv::StorageClass::CrossWorkgroup &&
+      storage_class != spv::StorageClass::Private &&
+      storage_class != spv::StorageClass::Function &&
+      storage_class != spv::StorageClass::UniformConstant &&
+      storage_class != spv::StorageClass::RayPayloadKHR &&
+      storage_class != spv::StorageClass::IncomingRayPayloadKHR &&
+      storage_class != spv::StorageClass::HitAttributeKHR &&
+      storage_class != spv::StorageClass::CallableDataKHR &&
+      storage_class != spv::StorageClass::IncomingCallableDataKHR &&
+      storage_class != spv::StorageClass::TaskPayloadWorkgroupEXT &&
+      storage_class != spv::StorageClass::HitObjectAttributeNV &&
+      storage_class != spv::StorageClass::HitObjectAttributeEXT &&
+      storage_class != spv::StorageClass::NodePayloadAMDX) {
+    bool storage_input_or_output = storage_class == spv::StorageClass::Input ||
+                                   storage_class == spv::StorageClass::Output;
+    bool builtin = false;
+    if (storage_input_or_output) {
+      for (const Decoration& decoration : _.id_decorations(inst->id())) {
+        if (decoration.dec_type() == spv::Decoration::BuiltIn) {
+          builtin = true;
+          break;
+        }
+      }
+    }
+    if (!builtin && value_type &&
+        ContainsInvalidBool(_, value_type, storage_input_or_output)) {
+      if (storage_input_or_output) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << _.VkErrorID(7290)
+               << "If OpTypeBool is stored in conjunction with OpVariable "
+                  "using Input or Output Storage Classes it requires a BuiltIn "
+                  "decoration";
+
+      } else {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "If OpTypeBool is stored in conjunction with OpVariable, it "
+                  "can only be used with non-externally visible shader Storage "
+                  "Classes: Workgroup, CrossWorkgroup, Private, Function, "
+                  "Input, Output, RayPayloadKHR, IncomingRayPayloadKHR, "
+                  "HitAttributeKHR, CallableDataKHR, "
+                  "IncomingCallableDataKHR, NodePayloadAMDX, or "
+                  "UniformConstant";
+      }
+    }
+  }
+
+  if (!_.IsValidStorageClass(storage_class)) {
+    return _.diag(SPV_ERROR_INVALID_BINARY, inst)
+           << _.VkErrorID(4643)
+           << "Invalid storage class for target environment";
+  }
+
+  if (storage_class == spv::StorageClass::Generic) {
+    return _.diag(SPV_ERROR_INVALID_BINARY, inst)
+           << "Variable storage class cannot be Generic";
+  }
+
+  if (inst->function() && storage_class != spv::StorageClass::Function) {
+    return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+           << "Variables must have a function[7] storage class inside"
+              " of a function";
+  }
+
+  if (!inst->function() && storage_class == spv::StorageClass::Function) {
+    return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+           << "Variables can not have a function[7] storage class "
+              "outside of a function";
+  }
+
+  // SPIR-V 3.32.8: Check that pointer type and variable type have the same
+  // storage class.
+  auto result_type = _.FindDef(inst->type_id());
+  const auto result_storage_class_index = 1;
+  const auto result_storage_class =
+      result_type->GetOperandAs<spv::StorageClass>(result_storage_class_index);
+  if (storage_class != result_storage_class) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Storage class must match result type storage class";
   }
 
   if (storage_class == spv::StorageClass::PhysicalStorageBuffer) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "PhysicalStorageBuffer must not be used with OpVariable.";
   }
+  return SPV_SUCCESS;
+}
 
-  auto pointee_base = pointee;
-  while (pointee_base && pointee_base->opcode() == spv::Op::OpTypeArray) {
-    pointee_base = _.FindDef(pointee_base->GetOperandAs<uint32_t>(1u));
-  }
-  if (pointee_base && pointee_base->opcode() == spv::Op::OpTypePointer) {
-    if (pointee_base->GetOperandAs<spv::StorageClass>(1u) ==
-        spv::StorageClass::PhysicalStorageBuffer) {
-      // check for AliasedPointer/RestrictPointer
-      bool foundAliased =
-          _.HasDecoration(inst->id(), spv::Decoration::AliasedPointer);
-      bool foundRestrict =
-          _.HasDecoration(inst->id(), spv::Decoration::RestrictPointer);
-      if (!foundAliased && !foundRestrict) {
+spv_result_t ValidateVariablePointer(ValidationState_t& _,
+                                     const Instruction* inst,
+                                     spv::StorageClass storage_class,
+                                     const Instruction& pointee) {
+  if ((_.addressing_model() == spv::AddressingModel::Logical ||
+       _.addressing_model() == spv::AddressingModel::PhysicalStorageBuffer64) &&
+      !_.options()->relax_logical_pointer) {
+    spv_result_t error = SPV_SUCCESS;
+    bool contains_logical_pointer = _.ContainsType(
+        pointee.id(),
+        [&_, inst, &error](const Instruction* type) {
+          if (type->opcode() == spv::Op::OpTypePointer ||
+              type->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
+            const auto sc = type->GetOperandAs<spv::StorageClass>(1u);
+            if (sc != spv::StorageClass::PhysicalStorageBuffer) {
+              if (sc != spv::StorageClass::StorageBuffer &&
+                  sc != spv::StorageClass::Workgroup) {
+                error =
+                    _.diag(SPV_ERROR_INVALID_ID, inst)
+                    << "In Logical addressing, variables can only allocate a "
+                       "pointer to the StorageBuffer or Workgroup storage "
+                       "classes";
+              } else if (!_.HasCapability(
+                             spv::Capability::VariablePointersStorageBuffer) &&
+                         sc == spv::StorageClass::StorageBuffer) {
+                error =
+                    _.diag(SPV_ERROR_INVALID_ID, inst)
+                    << "In Logical addressing, variables can only allocate a "
+                       "storage buffer pointer if the "
+                       "VariablePointersStorageBuffer capability is declared";
+              } else if (!_.HasCapability(spv::Capability::VariablePointers) &&
+                         sc == spv::StorageClass::Workgroup) {
+                error =
+                    _.diag(SPV_ERROR_INVALID_ID, inst)
+                    << "In Logical addressing, variables can only allocate a "
+                       "workgroup pointer if the VariablePointers capability "
+                       "is "
+                       "declared";
+              }
+              return true;
+            }
+          }
+          return false;
+        },
+        /* traverse_all_types = */ false);
+
+    if (error != SPV_SUCCESS) return error;
+
+    if (contains_logical_pointer) {
+      if (storage_class != spv::StorageClass::Function &&
+          storage_class != spv::StorageClass::Private) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << "OpVariable " << inst->id()
-               << ": expected AliasedPointer or RestrictPointer for "
-               << "PhysicalStorageBuffer pointer.";
-      }
-      if (foundAliased && foundRestrict) {
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << "OpVariable " << inst->id()
-               << ": can't specify both AliasedPointer and "
-               << "RestrictPointer for PhysicalStorageBuffer pointer.";
+               << "In Logical addressing with variable pointers, variables "
+               << "that allocate pointers must be in Function or Private "
+               << "storage classes";
       }
     }
   }
+  return SPV_SUCCESS;
+}
 
-  // Vulkan specific validation rules for OpTypeRuntimeArray
-  if (spvIsVulkanEnv(_.context()->target_env)) {
-    // OpTypeRuntimeArray should only ever be in a container like OpTypeStruct,
-    // so should never appear as a bare variable.
-    // Unless the module has the RuntimeDescriptorArrayEXT capability.
-    if (value_type && value_type->opcode() == spv::Op::OpTypeRuntimeArray) {
-      if (!_.HasCapability(spv::Capability::RuntimeDescriptorArrayEXT)) {
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << _.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 "
-               << "be instantiated via OpVariable";
-      } else {
-        // A bare variable OpTypeRuntimeArray is allowed in this context, but
-        // still need to check the storage class.
-        if (storage_class != spv::StorageClass::StorageBuffer &&
-            storage_class != spv::StorageClass::Uniform &&
-            storage_class != spv::StorageClass::UniformConstant) {
-          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.";
-        }
-      }
+spv_result_t ValidateVariableVulkanDescriptor(ValidationState_t& _,
+                                              const Instruction* inst,
+                                              spv::StorageClass storage_class,
+                                              const Instruction& pointee) {
+  // Vulkan Push Constant Interface section: Check type of PushConstant
+  // variables.
+  if (storage_class == spv::StorageClass::PushConstant) {
+    if (pointee.opcode() != spv::Op::OpTypeStruct) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << _.VkErrorID(6808) << "PushConstant OpVariable <id> "
+             << _.getIdName(inst->id()) << " has illegal type.\n"
+             << "From Vulkan spec, Push Constant Interface section:\n"
+             << "Such variables must be typed as OpTypeStruct";
     }
+  }
 
-    // If an OpStruct has an OpTypeRuntimeArray somewhere within it, then it
-    // must either have the storage class StorageBuffer and be decorated
-    // with Block, or it must be in the Uniform storage class and be decorated
-    // as BufferBlock.
+  // Vulkan Descriptor Set Interface: Check type of UniformConstant and
+  // Uniform variables.
+  if (storage_class == spv::StorageClass::UniformConstant) {
+    if (!IsAllowedTypeOrArrayOfSame(
+            _, pointee,
+            {spv::Op::OpTypeImage, spv::Op::OpTypeSampler,
+             spv::Op::OpTypeSampledImage, spv::Op::OpTypeTensorARM,
+             spv::Op::OpTypeAccelerationStructureKHR})) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << _.VkErrorID(4655) << "UniformConstant OpVariable <id> "
+             << _.getIdName(inst->id()) << " has illegal type.\n"
+             << "Variables identified with the UniformConstant storage class "
+             << "are used only as handles to refer to opaque resources. Such "
+             << "variables must be typed as OpTypeImage, OpTypeSampler, "
+             << "OpTypeSampledImage, OpTypeAccelerationStructureKHR, "
+             << "or an array of one of these types.";
+    }
+  }
+
+  if (storage_class == spv::StorageClass::Uniform) {
+    if (!IsAllowedTypeOrArrayOfSame(_, pointee, {spv::Op::OpTypeStruct})) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << _.VkErrorID(6807) << "Uniform OpVariable <id> "
+             << _.getIdName(inst->id()) << " has illegal type.\n"
+             << "From Vulkan spec:\n"
+             << "Variables identified with the Uniform storage class are "
+             << "used to access transparent buffer backed resources. Such "
+             << "variables must be typed as OpTypeStruct, or an array of "
+             << "this type";
+    }
+  }
+
+  if (storage_class == spv::StorageClass::StorageBuffer) {
+    if (!IsAllowedTypeOrArrayOfSame(_, pointee, {spv::Op::OpTypeStruct})) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << _.VkErrorID(6807) << "StorageBuffer OpVariable <id> "
+             << _.getIdName(inst->id()) << " has illegal type.\n"
+             << "From Vulkan spec:\n"
+             << "Variables identified with the StorageBuffer storage class "
+                "are used to access transparent buffer backed resources. "
+                "Such variables must be typed as OpTypeStruct, or an array "
+                "of this type";
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateVariableVulkanInterface(ValidationState_t& _,
+                                             const Instruction* inst,
+                                             spv::StorageClass storage_class,
+                                             const Instruction* value_type,
+                                             uint32_t value_id) {
+  // Check for invalid use of Invariant
+  if (storage_class != spv::StorageClass::Input &&
+      storage_class != spv::StorageClass::Output) {
+    if (_.HasDecoration(inst->id(), spv::Decoration::Invariant)) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << _.VkErrorID(4677)
+             << "Variable decorated with Invariant must only be identified "
+                "with the Input or Output storage class in Vulkan "
+                "environment.";
+    }
+    // Need to check if only the members in a struct are decorated
     if (value_type && value_type->opcode() == spv::Op::OpTypeStruct) {
-      if (DoesStructContainRTA(_, value_type)) {
-        if (storage_class == spv::StorageClass::StorageBuffer ||
-            storage_class == spv::StorageClass::PhysicalStorageBuffer) {
-          if (!_.HasDecoration(value_id, spv::Decoration::Block)) {
-            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 or "
-                      "PhysicalStorageBuffer.";
-          }
-        } else if (storage_class == spv::StorageClass::Uniform) {
-          if (!_.HasDecoration(value_id, spv::Decoration::BufferBlock)) {
-            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, PhysicalStorageBuffer, or Uniform.";
-        }
+      if (_.HasDecoration(value_id, spv::Decoration::Invariant)) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << _.VkErrorID(4677)
+               << "Variable struct member decorated with Invariant must only "
+                  "be identified with the Input or Output storage class in "
+                  "Vulkan environment.";
       }
     }
   }
 
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateVariableCoopMat(ValidationState_t& _,
+                                     const Instruction* inst,
+                                     spv::StorageClass storage_class,
+                                     const Instruction& pointee) {
   // Cooperative matrix types can only be allocated in Function or Private
   if ((storage_class != spv::StorageClass::Function &&
        storage_class != spv::StorageClass::Private) &&
-      pointee && ContainsCooperativeMatrix(_, pointee)) {
+      _.ContainsType(pointee.id(), [](const Instruction* type_inst) {
+        auto opcode = type_inst->opcode();
+        return opcode == spv::Op::OpTypeCooperativeMatrixNV ||
+               opcode == spv::Op::OpTypeCooperativeMatrixKHR;
+      })) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Cooperative matrix types (or types containing them) can only be "
               "allocated "
@@ -827,135 +786,460 @@
               "parameters";
   }
 
+  return SPV_SUCCESS;
+}
+
+// Vulkan specific validation rules for OpTypeRuntimeArray
+spv_result_t ValidateVariableVulkanArray(ValidationState_t& _,
+                                         const Instruction* inst,
+                                         spv::StorageClass storage_class,
+                                         const Instruction& value_type,
+                                         uint32_t value_id) {
+  // OpTypeRuntimeArray should only ever be in a container like OpTypeStruct,
+  // so should never appear as a bare variable.
+  // Unless the module has the RuntimeDescriptorArray capability.
+  if (value_type.opcode() == spv::Op::OpTypeRuntimeArray) {
+    if (!_.HasCapability(spv::Capability::RuntimeDescriptorArray)) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << _.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 "
+             << "be instantiated via OpVariable, unless the "
+                "RuntimeDescriptorArray Capability is declared";
+    } else {
+      // A bare variable OpTypeRuntimeArray is allowed in this context, but
+      // still need to check the storage class.
+      if (storage_class != spv::StorageClass::StorageBuffer &&
+          storage_class != spv::StorageClass::Uniform &&
+          storage_class != spv::StorageClass::UniformConstant) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << _.VkErrorID(4680)
+               << "For Vulkan with RuntimeDescriptorArray, a variable "
+               << "containing OpTypeRuntimeArray must have storage class of "
+               << "StorageBuffer, Uniform, or UniformConstant.";
+      }
+    }
+  }
+
+  // If an OpStruct has an OpTypeRuntimeArray somewhere within it, then it
+  // must either have the storage class StorageBuffer and be decorated
+  // with Block, or it must be in the Uniform storage class
+  if (value_type.opcode() == spv::Op::OpTypeStruct) {
+    if (DoesStructContainRTA(_, &value_type)) {
+      if (storage_class == spv::StorageClass::StorageBuffer ||
+          storage_class == spv::StorageClass::PhysicalStorageBuffer) {
+        if (!_.HasDecoration(value_id, spv::Decoration::Block)) {
+          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 or "
+                    "PhysicalStorageBuffer.";
+        }
+      } else if (storage_class == spv::StorageClass::Uniform) {
+        // BufferBlock Uniform were always allowed.
+        //
+        // Block Uniform use to be invalid, but Vulkan added
+        // VK_EXT_shader_uniform_buffer_unsized_array and now this is
+        // validated at runtime
+        //
+        // The uniform must have either the Block or BufferBlock decoration
+        // (see VUID-StandaloneSpirv-Uniform-06676)
+      } else {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << _.VkErrorID(4680)
+               << "For Vulkan, OpTypeStruct variables containing "
+               << "OpTypeRuntimeArray must have storage class of "
+               << "StorageBuffer, PhysicalStorageBuffer, or Uniform.";
+      }
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+// Vulkan-specific validation for long vectors
+spv_result_t ValidateVariableVulkanLongVector(ValidationState_t& _,
+                                              const Instruction* inst,
+                                              spv::StorageClass storage_class,
+                                              const Instruction& pointee) {
+  if (_.HasCapability(spv::Capability::LongVectorEXT)) {
+    if ((storage_class != spv::StorageClass::Function &&
+         storage_class != spv::StorageClass::Private &&
+         storage_class != spv::StorageClass::StorageBuffer &&
+         storage_class != spv::StorageClass::PhysicalStorageBuffer &&
+         storage_class != spv::StorageClass::Workgroup &&
+         storage_class != spv::StorageClass::Uniform &&
+         storage_class != spv::StorageClass::PushConstant &&
+         storage_class != spv::StorageClass::ShaderRecordBufferKHR) &&
+        _.ContainsType(pointee.id(), [&](const Instruction* type_inst) {
+          auto opcode = type_inst->opcode();
+          if (opcode == spv::Op::OpTypeVector ||
+              opcode == spv::Op::OpTypeVectorIdEXT) {
+            uint32_t dim = _.GetDimension(type_inst->id());
+            return dim > 4;
+          }
+          return false;
+        })) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << _.VkErrorID(12297)
+             << "Long vector types with more than 4 components (or types "
+                "containing them) not supported in storage class "
+             << StorageClassToString(storage_class);
+    }
+
+    if ((storage_class == spv::StorageClass::StorageBuffer ||
+         storage_class == spv::StorageClass::PhysicalStorageBuffer ||
+         storage_class == spv::StorageClass::Uniform ||
+         storage_class == spv::StorageClass::PushConstant ||
+         storage_class == spv::StorageClass::ShaderRecordBufferKHR ||
+         (storage_class == spv::StorageClass::Workgroup &&
+          _.HasDecoration(pointee.id(), spv::Decoration::Block))) &&
+        _.ContainsType(pointee.id(), [&](const Instruction* type_inst) {
+          auto opcode = type_inst->opcode();
+          if (opcode == spv::Op::OpTypeVectorIdEXT) {
+            auto component_count =
+                _.FindDef(type_inst->GetOperandAs<uint32_t>(2u));
+            return (bool)spvOpcodeIsSpecConstant(component_count->opcode());
+          }
+          return false;
+        })) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << _.VkErrorID(12294)
+             << "Long vector types with spec constant component count "
+                "not supported in storage class with explicit layout "
+             << StorageClassToString(storage_class);
+    }
+  } else {
+    if ((storage_class != spv::StorageClass::Function &&
+         storage_class != spv::StorageClass::Private) &&
+        _.ContainsType(pointee.id(), [](const Instruction* type_inst) {
+          auto opcode = type_inst->opcode();
+          return opcode == spv::Op::OpTypeVectorIdEXT;
+        })) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "Cooperative vector types (or types containing them) can "
+                "only be "
+                "allocated "
+             << "in Function or Private storage classes or as function "
+                "parameters";
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateVariableShader(ValidationState_t& _,
+                                    const Instruction* inst,
+                                    spv::StorageClass storage_class,
+                                    const Instruction* value_type,
+                                    uint32_t value_id) {
+  // Don't allow variables containing 16-bit elements without the appropriate
+  // capabilities.
+  if ((!_.HasCapability(spv::Capability::Int16) &&
+       _.ContainsSizedIntOrFloatType(value_id, spv::Op::OpTypeInt, 16)) ||
+      (!_.HasCapability(spv::Capability::Float16) &&
+       _.ContainsSizedIntOrFloatType(value_id, spv::Op::OpTypeFloat, 16))) {
+    auto underlying_type = value_type;
+    while (underlying_type &&
+           underlying_type->opcode() == spv::Op::OpTypePointer) {
+      storage_class = underlying_type->GetOperandAs<spv::StorageClass>(1u);
+      underlying_type = _.FindDef(underlying_type->GetOperandAs<uint32_t>(2u));
+    }
+    bool storage_class_ok = true;
+    std::string sc_name = _.grammar().lookupOperandName(
+        SPV_OPERAND_TYPE_STORAGE_CLASS, uint32_t(storage_class));
+    switch (storage_class) {
+      case spv::StorageClass::StorageBuffer:
+      case spv::StorageClass::PhysicalStorageBuffer:
+        if (!_.HasCapability(spv::Capability::StorageBuffer16BitAccess)) {
+          storage_class_ok = false;
+        }
+        break;
+      case spv::StorageClass::Uniform:
+        if (underlying_type &&
+            !_.HasCapability(
+                spv::Capability::UniformAndStorageBuffer16BitAccess)) {
+          if (underlying_type->opcode() == spv::Op::OpTypeArray ||
+              underlying_type->opcode() == spv::Op::OpTypeRuntimeArray) {
+            underlying_type =
+                _.FindDef(underlying_type->GetOperandAs<uint32_t>(1u));
+          }
+          if (!_.HasCapability(spv::Capability::StorageBuffer16BitAccess) ||
+              !_.HasDecoration(underlying_type->id(),
+                               spv::Decoration::BufferBlock)) {
+            storage_class_ok = false;
+          }
+        }
+        break;
+      case spv::StorageClass::PushConstant:
+        if (!_.HasCapability(spv::Capability::StoragePushConstant16)) {
+          storage_class_ok = false;
+        }
+        break;
+      case spv::StorageClass::Input:
+      case spv::StorageClass::Output:
+        if (!_.HasCapability(spv::Capability::StorageInputOutput16)) {
+          storage_class_ok = false;
+        }
+        break;
+      case spv::StorageClass::Workgroup:
+        if (!_.HasCapability(
+                spv::Capability::WorkgroupMemoryExplicitLayout16BitAccessKHR)) {
+          storage_class_ok = false;
+        }
+        break;
+      default:
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "Cannot allocate a variable containing a 16-bit type in "
+               << sc_name << " storage class";
+    }
+    if (!storage_class_ok) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "Allocating a variable containing a 16-bit element in "
+             << sc_name << " storage class requires an additional capability";
+    }
+  }
+  // Don't allow variables containing 8-bit elements without the appropriate
+  // capabilities.
+  if (!_.HasCapability(spv::Capability::Int8) &&
+      _.ContainsSizedIntOrFloatType(value_id, spv::Op::OpTypeInt, 8)) {
+    auto underlying_type = value_type;
+    while (underlying_type &&
+           underlying_type->opcode() == spv::Op::OpTypePointer) {
+      storage_class = underlying_type->GetOperandAs<spv::StorageClass>(1u);
+      underlying_type = _.FindDef(underlying_type->GetOperandAs<uint32_t>(2u));
+    }
+    bool storage_class_ok = true;
+    std::string sc_name = _.grammar().lookupOperandName(
+        SPV_OPERAND_TYPE_STORAGE_CLASS, uint32_t(storage_class));
+    switch (storage_class) {
+      case spv::StorageClass::StorageBuffer:
+      case spv::StorageClass::PhysicalStorageBuffer:
+        if (!_.HasCapability(spv::Capability::StorageBuffer8BitAccess)) {
+          storage_class_ok = false;
+        }
+        break;
+      case spv::StorageClass::Uniform:
+        if (underlying_type &&
+            !_.HasCapability(
+                spv::Capability::UniformAndStorageBuffer8BitAccess)) {
+          if (underlying_type->opcode() == spv::Op::OpTypeArray ||
+              underlying_type->opcode() == spv::Op::OpTypeRuntimeArray) {
+            underlying_type =
+                _.FindDef(underlying_type->GetOperandAs<uint32_t>(1u));
+          }
+          if (!_.HasCapability(spv::Capability::StorageBuffer8BitAccess) ||
+              !_.HasDecoration(underlying_type->id(),
+                               spv::Decoration::BufferBlock)) {
+            storage_class_ok = false;
+          }
+        }
+        break;
+      case spv::StorageClass::PushConstant:
+        if (!_.HasCapability(spv::Capability::StoragePushConstant8)) {
+          storage_class_ok = false;
+        }
+        break;
+      case spv::StorageClass::Workgroup:
+        if (!_.HasCapability(
+                spv::Capability::WorkgroupMemoryExplicitLayout8BitAccessKHR)) {
+          storage_class_ok = false;
+        }
+        break;
+      default:
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "Cannot allocate a variable containing a 8-bit type in "
+               << sc_name << " storage class";
+    }
+    if (!storage_class_ok) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "Allocating a variable containing a 8-bit element in "
+             << sc_name << " storage class requires an additional capability";
+    }
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateVariableTileShadingQCOM(ValidationState_t& _,
+                                             const Instruction* inst) {
+  auto result_type = _.FindDef(inst->type_id());
+  if (result_type->opcode() == spv::Op::OpTypePointer) {
+    const auto pointee_type = _.FindDef(result_type->GetOperandAs<uint32_t>(2));
+    if (pointee_type && pointee_type->opcode() == spv::Op::OpTypeImage) {
+      spv::Dim dim = static_cast<spv::Dim>(pointee_type->word(3));
+      if (dim != spv::Dim::Dim2D) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Any OpTypeImage variable in the TileAttachmentQCOM "
+                  "Storage Class must "
+                  "have 2D as its dimension";
+      }
+      unsigned sampled = pointee_type->word(7);
+      if (sampled != 1 && sampled != 2) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Any OpyTpeImage variable in the TileAttachmentQCOM "
+                  "Storage Class must "
+                  "have 1 or 2 as Image 'Sampled' parameter";
+      }
+      for (const auto& pair_o : inst->uses()) {
+        const auto* use_inst_o = pair_o.first;
+        if (use_inst_o->opcode() == spv::Op::OpLoad) {
+          for (const auto& pair_i : use_inst_o->uses()) {
+            const auto* use_inst_i = pair_i.first;
+            switch (use_inst_i->opcode()) {
+              case spv::Op::OpImageQueryFormat:
+              case spv::Op::OpImageQueryOrder:
+              case spv::Op::OpImageQuerySizeLod:
+              case spv::Op::OpImageQuerySize:
+              case spv::Op::OpImageQueryLod:
+              case spv::Op::OpImageQueryLevels:
+              case spv::Op::OpImageQuerySamples:
+                return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                       << "Any variable in the TileAttachmentQCOM Storage "
+                          "Class must "
+                          "not be consumed by an OpImageQuery* instruction";
+              default:
+                break;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  if (!(_.HasDecoration(inst->id(), spv::Decoration::DescriptorSet) &&
+        _.HasDecoration(inst->id(), spv::Decoration::Binding))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Any variable in the TileAttachmentQCOM Storage Class must "
+              "be decorated with DescriptorSet and Binding";
+  }
+  if (_.HasDecoration(inst->id(), spv::Decoration::Component)) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Any variable in the TileAttachmentQCOM Storage Class must "
+              "not be decorated with Component decoration";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
+  const bool untyped_pointer = inst->opcode() == spv::Op::OpUntypedVariableKHR;
+
+  auto result_type = _.FindDef(inst->type_id());
+  if (untyped_pointer) {
+    if (!result_type ||
+        result_type->opcode() != spv::Op::OpTypeUntypedPointerKHR)
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "Result type must be an untyped pointer";
+  } else {
+    if (!result_type || result_type->opcode() != spv::Op::OpTypePointer) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "OpVariable Result Type <id> " << _.getIdName(inst->type_id())
+             << " is not a pointer type.";
+    }
+  }
+
+  const auto storage_class_index = 2u;
+  auto storage_class =
+      inst->GetOperandAs<spv::StorageClass>(storage_class_index);
+  uint32_t value_id = 0;
+  if (untyped_pointer) {
+    const bool has_data_type = 3u < inst->operands().size();
+    if (has_data_type) {
+      value_id = inst->GetOperandAs<uint32_t>(3u);
+      auto data_type = _.FindDef(value_id);
+      if (!data_type || !spvOpcodeGeneratesType(data_type->opcode())) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "Data type must be a type instruction";
+      }
+    } else {
+      if (storage_class == spv::StorageClass::Function ||
+          storage_class == spv::StorageClass::Private ||
+          storage_class == spv::StorageClass::Workgroup) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "Data type must be specified for Function, Private, and "
+                  "Workgroup storage classes";
+      }
+      // Added from SPV_EXT_descriptor_heap
+      // Vulkan allows untyped pointer without |Data Type| but only for heap
+      // decorated variable that are in UniformConstant
+      if (spvIsVulkanEnv(_.context()->target_env)) {
+        if (storage_class != spv::StorageClass::UniformConstant) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << _.VkErrorID(11167) << "Storage class is "
+                 << StorageClassToString(storage_class)
+                 << ", but Vulkan requires that Data Type be specified when "
+                    "not using UniformConstant storage class";
+        } else if (!(_.IsDescriptorHeapBaseVariable(inst))) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << _.VkErrorID(11347)
+                 << "Storage class is UniformConstant, but Vulkan requires "
+                    "that Data Type be specified if the variable is not "
+                    "decorated with SamplerHeapEXT or ResourceHeapEXT";
+        }
+      }
+    }
+  }
+
+  // For OpVariable the data type comes from pointee type of the result type,
+  // while for OpUntypedVariableKHR the data type comes from the operand.
+  if (!untyped_pointer) {
+    value_id = result_type->GetOperandAs<uint32_t>(2);
+  }
+  const Instruction* value_type = value_id == 0 ? nullptr : _.FindDef(value_id);
+
+  if (auto error =
+          ValidateVariableInitializer(_, inst, storage_class, value_id))
+    return error;
+
+  if (auto error =
+          ValidateVariableStorageClass(_, inst, storage_class, value_type))
+    return error;
+
+  // Variable pointer related restrictions.
+  const Instruction* pointee =
+      untyped_pointer ? value_id == 0 ? nullptr : _.FindDef(value_id)
+                      : _.FindDef(result_type->word(3));
+
+  if (pointee) {
+    if (auto error = ValidateVariablePointer(_, inst, storage_class, *pointee))
+      return error;
+    if (auto error = ValidateVariableCoopMat(_, inst, storage_class, *pointee))
+      return error;
+  }
+
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    if (pointee) {
+      if (auto error = ValidateVariableVulkanDescriptor(_, inst, storage_class,
+                                                        *pointee))
+        return error;
+      if (auto error = ValidateVariableVulkanLongVector(_, inst, storage_class,
+                                                        *pointee))
+        return error;
+    }
+
+    if (auto error = ValidateVariableVulkanInterface(_, inst, storage_class,
+                                                     value_type, value_id))
+      return error;
+
+    if (value_type) {
+      if (auto error = ValidateVariableVulkanArray(_, inst, storage_class,
+                                                   *value_type, value_id))
+        return error;
+    }
+  }
+
   if (_.HasCapability(spv::Capability::Shader)) {
-    // Don't allow variables containing 16-bit elements without the appropriate
-    // capabilities.
-    if ((!_.HasCapability(spv::Capability::Int16) &&
-         _.ContainsSizedIntOrFloatType(value_id, spv::Op::OpTypeInt, 16)) ||
-        (!_.HasCapability(spv::Capability::Float16) &&
-         _.ContainsSizedIntOrFloatType(value_id, spv::Op::OpTypeFloat, 16))) {
-      auto underlying_type = value_type;
-      while (underlying_type &&
-             underlying_type->opcode() == spv::Op::OpTypePointer) {
-        storage_class = underlying_type->GetOperandAs<spv::StorageClass>(1u);
-        underlying_type =
-            _.FindDef(underlying_type->GetOperandAs<uint32_t>(2u));
-      }
-      bool storage_class_ok = true;
-      std::string sc_name = _.grammar().lookupOperandName(
-          SPV_OPERAND_TYPE_STORAGE_CLASS, uint32_t(storage_class));
-      switch (storage_class) {
-        case spv::StorageClass::StorageBuffer:
-        case spv::StorageClass::PhysicalStorageBuffer:
-          if (!_.HasCapability(spv::Capability::StorageBuffer16BitAccess)) {
-            storage_class_ok = false;
-          }
-          break;
-        case spv::StorageClass::Uniform:
-          if (underlying_type &&
-              !_.HasCapability(
-                  spv::Capability::UniformAndStorageBuffer16BitAccess)) {
-            if (underlying_type->opcode() == spv::Op::OpTypeArray ||
-                underlying_type->opcode() == spv::Op::OpTypeRuntimeArray) {
-              underlying_type =
-                  _.FindDef(underlying_type->GetOperandAs<uint32_t>(1u));
-            }
-            if (!_.HasCapability(spv::Capability::StorageBuffer16BitAccess) ||
-                !_.HasDecoration(underlying_type->id(),
-                                 spv::Decoration::BufferBlock)) {
-              storage_class_ok = false;
-            }
-          }
-          break;
-        case spv::StorageClass::PushConstant:
-          if (!_.HasCapability(spv::Capability::StoragePushConstant16)) {
-            storage_class_ok = false;
-          }
-          break;
-        case spv::StorageClass::Input:
-        case spv::StorageClass::Output:
-          if (!_.HasCapability(spv::Capability::StorageInputOutput16)) {
-            storage_class_ok = false;
-          }
-          break;
-        case spv::StorageClass::Workgroup:
-          if (!_.HasCapability(
-                  spv::Capability::
-                      WorkgroupMemoryExplicitLayout16BitAccessKHR)) {
-            storage_class_ok = false;
-          }
-          break;
-        default:
-          return _.diag(SPV_ERROR_INVALID_ID, inst)
-                 << "Cannot allocate a variable containing a 16-bit type in "
-                 << sc_name << " storage class";
-      }
-      if (!storage_class_ok) {
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << "Allocating a variable containing a 16-bit element in "
-               << sc_name << " storage class requires an additional capability";
-      }
-    }
-    // Don't allow variables containing 8-bit elements without the appropriate
-    // capabilities.
-    if (!_.HasCapability(spv::Capability::Int8) &&
-        _.ContainsSizedIntOrFloatType(value_id, spv::Op::OpTypeInt, 8)) {
-      auto underlying_type = value_type;
-      while (underlying_type &&
-             underlying_type->opcode() == spv::Op::OpTypePointer) {
-        storage_class = underlying_type->GetOperandAs<spv::StorageClass>(1u);
-        underlying_type =
-            _.FindDef(underlying_type->GetOperandAs<uint32_t>(2u));
-      }
-      bool storage_class_ok = true;
-      std::string sc_name = _.grammar().lookupOperandName(
-          SPV_OPERAND_TYPE_STORAGE_CLASS, uint32_t(storage_class));
-      switch (storage_class) {
-        case spv::StorageClass::StorageBuffer:
-        case spv::StorageClass::PhysicalStorageBuffer:
-          if (!_.HasCapability(spv::Capability::StorageBuffer8BitAccess)) {
-            storage_class_ok = false;
-          }
-          break;
-        case spv::StorageClass::Uniform:
-          if (underlying_type &&
-              !_.HasCapability(
-                  spv::Capability::UniformAndStorageBuffer8BitAccess)) {
-            if (underlying_type->opcode() == spv::Op::OpTypeArray ||
-                underlying_type->opcode() == spv::Op::OpTypeRuntimeArray) {
-              underlying_type =
-                  _.FindDef(underlying_type->GetOperandAs<uint32_t>(1u));
-            }
-            if (!_.HasCapability(spv::Capability::StorageBuffer8BitAccess) ||
-                !_.HasDecoration(underlying_type->id(),
-                                 spv::Decoration::BufferBlock)) {
-              storage_class_ok = false;
-            }
-          }
-          break;
-        case spv::StorageClass::PushConstant:
-          if (!_.HasCapability(spv::Capability::StoragePushConstant8)) {
-            storage_class_ok = false;
-          }
-          break;
-        case spv::StorageClass::Workgroup:
-          if (!_.HasCapability(
-                  spv::Capability::
-                      WorkgroupMemoryExplicitLayout8BitAccessKHR)) {
-            storage_class_ok = false;
-          }
-          break;
-        default:
-          return _.diag(SPV_ERROR_INVALID_ID, inst)
-                 << "Cannot allocate a variable containing a 8-bit type in "
-                 << sc_name << " storage class";
-      }
-      if (!storage_class_ok) {
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << "Allocating a variable containing a 8-bit element in "
-               << sc_name << " storage class requires an additional capability";
-      }
-    }
+    if (auto error = ValidateVariableShader(_, inst, storage_class, value_type,
+                                            value_id))
+      return error;
+  }
+
+  if (_.HasCapability(spv::Capability::TileShadingQCOM) &&
+      storage_class == spv::StorageClass::TileAttachmentQCOM) {
+    if (auto error = ValidateVariableTileShadingQCOM(_, inst)) return error;
   }
 
   return SPV_SUCCESS;
@@ -1023,8 +1307,81 @@
     }
   }
 
+  // Skip checking if there is zero chance for this having a mesh shader
+  // entrypoint
+  if (_.HasCapability(spv::Capability::MeshShadingEXT) &&
+      pointer_type->GetOperandAs<spv::StorageClass>(1) ==
+          spv::StorageClass::Output) {
+    std::string errorVUID = _.VkErrorID(7107);
+    _.function(inst->function()->id())
+        ->RegisterExecutionModelLimitation(
+            [errorVUID](spv::ExecutionModel model, std::string* message) {
+              // Seems the NV Mesh extension was less strict and allowed
+              // writting to outputs
+              if (model == spv::ExecutionModel::MeshEXT) {
+                if (message) {
+                  *message = errorVUID +
+                             "The Output Storage Class in a Mesh Execution "
+                             "Model must not be read from";
+                }
+                return false;
+              }
+              return true;
+            });
+  }
+
   _.RegisterQCOMImageProcessingTextureConsumer(pointer_id, inst, nullptr);
 
+  // EXT_descriptor_heap
+  if (spvIsVulkanEnv(_.context()->target_env) &&
+      _.IsDescriptorHeapBaseVariable(_.FindDef(pointer_id))) {
+    auto descBaseVariable = _.FindUntypedBaseVariable(_.FindDef(pointer_id));
+    auto descBaseVariableId = descBaseVariable->id();
+    if (!_.HasDecoration(descBaseVariableId, spv::Decoration::DescriptorSet) &&
+        !_.HasDecoration(descBaseVariableId, spv::Decoration::Binding)) {
+      switch (result_type->opcode()) {
+        case spv::Op::OpTypeSampler:
+          if (!_.IsBuiltin(descBaseVariableId, spv::BuiltIn::SamplerHeapEXT)) {
+            return _.diag(SPV_ERROR_INVALID_ID, inst)
+                   << _.VkErrorID(11336)
+                   << "OpTypeSampler pointer instruction has no descriptor set "
+                   << "or binding and is not derived from a variable decorated "
+                      "with "
+                      "SamplerHeapEXT";
+          }
+          break;
+        case spv::Op::OpTypeImage:
+          if (!_.IsBuiltin(descBaseVariableId, spv::BuiltIn::ResourceHeapEXT)) {
+            return _.diag(SPV_ERROR_INVALID_ID, inst)
+                   << _.VkErrorID(11337)
+                   << "OpTypeImage pointer instruction has no descriptor set "
+                   << "or binding and is not derived from a variable decorated "
+                      "with "
+                      "ResourceHeapEXT";
+          }
+          break;
+        case spv::Op::OpTypeAccelerationStructureKHR:
+          uint32_t data_type;
+          spv::StorageClass sc;
+          if (_.GetPointerTypeInfo(descBaseVariable->type_id(), &data_type,
+                                   &sc) &&
+              sc != spv::StorageClass::Private &&
+              sc != spv::StorageClass::Function &&
+              !_.IsBuiltin(descBaseVariableId, spv::BuiltIn::ResourceHeapEXT)) {
+            return _.diag(SPV_ERROR_INVALID_ID, inst)
+                   << _.VkErrorID(11339)
+                   << "OpTypeAccelerationStructureKHR pointer instruction has "
+                      "no "
+                   << "descriptor set or binding and is not derived from a "
+                      "variable decorated with ResourceHeapEXT";
+          }
+          break;
+        default:
+          break;
+      }
+    }
+  }
+
   return SPV_SUCCESS;
 }
 
@@ -1228,7 +1585,7 @@
         }
       } else {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << spvOpcodeString(static_cast<spv::Op>(inst->opcode()))
+               << spvOpcodeString(inst->opcode())
                << " with two memory access operands requires SPIR-V 1.4 or "
                   "later";
       }
@@ -1446,12 +1803,21 @@
           }
           if (!int8 && !int16 && !(source_int16_match && target_int16_match)) {
             return _.diag(SPV_ERROR_INVALID_ID, inst)
-                   << "Size must be a multiple of 4";
+                   << _.VkErrorID(11165)
+                   << "Size must be a multiple of 4. This is valid if Source ("
+                   << StorageClassToString(source_sc) << ") and Target ("
+                   << StorageClassToString(source_sc)
+                   << ") storage classes both support either 8-bit or 16-bit";
           }
           if (value % 2 != 0) {
             if (!int8 && !(source_int8_match && target_int8_match)) {
               return _.diag(SPV_ERROR_INVALID_ID, inst)
-                     << "Size must be a multiple of 2";
+                     << _.VkErrorID(11165)
+                     << "Size must be a multiple of 2. This is valid if Source "
+                        "("
+                     << StorageClassToString(source_sc) << ") and Target ("
+                     << StorageClassToString(source_sc)
+                     << ") storage classes both support 8-bit";
             }
           }
         }
@@ -1480,9 +1846,7 @@
 
 spv_result_t ValidateAccessChain(ValidationState_t& _,
                                  const Instruction* inst) {
-  std::string instr_name =
-      "Op" + std::string(spvOpcodeString(static_cast<spv::Op>(inst->opcode())));
-
+  const spv::Op opcode = inst->opcode();
   const bool untyped_pointer = spvOpcodeGeneratesUntypedPointer(inst->opcode());
 
   // The result type must be OpTypePointer for regular access chains and an
@@ -1492,19 +1856,17 @@
     if (!result_type ||
         spv::Op::OpTypeUntypedPointerKHR != result_type->opcode()) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
-             << "The Result Type of " << instr_name << " <id> "
+             << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> "
              << _.getIdName(inst->id())
              << " must be OpTypeUntypedPointerKHR. Found Op"
-             << spvOpcodeString(static_cast<spv::Op>(result_type->opcode()))
-             << ".";
+             << spvOpcodeString(result_type->opcode()) << ".";
     }
   } else {
     if (!result_type || spv::Op::OpTypePointer != result_type->opcode()) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
-             << "The Result Type of " << instr_name << " <id> "
+             << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> "
              << _.getIdName(inst->id()) << " must be OpTypePointer. Found Op"
-             << spvOpcodeString(static_cast<spv::Op>(result_type->opcode()))
-             << ".";
+             << spvOpcodeString(result_type->opcode()) << ".";
     }
   }
 
@@ -1517,6 +1879,60 @@
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "Base type must be a non-pointer type";
     }
+
+    const auto ContainsBlock = [&_](const Instruction* type_inst) {
+      if (type_inst->opcode() == spv::Op::OpTypeStruct) {
+        if (_.HasDecoration(type_inst->id(), spv::Decoration::Block) ||
+            _.HasDecoration(type_inst->id(), spv::Decoration::BufferBlock)) {
+          return true;
+        }
+      }
+      return false;
+    };
+
+    // Block (and BufferBlock) arrays cannot be reinterpreted via untyped access
+    // chains.
+    const bool base_type_block_array =
+        base_type->opcode() == spv::Op::OpTypeArray &&
+        _.ContainsType(base_type->id(), ContainsBlock,
+                       /* traverse_all_types = */ false);
+
+    const auto base_index = untyped_pointer ? 3 : 2;
+    const auto base_id = inst->GetOperandAs<uint32_t>(base_index);
+    auto base = _.FindDef(base_id);
+    // Strictly speaking this misses trivial access chains and function
+    // parameter chasing, but that would be a significant complication in the
+    // traversal.
+    while (base->opcode() == spv::Op::OpCopyObject) {
+      base = _.FindDef(base->GetOperandAs<uint32_t>(2));
+    }
+    const Instruction* base_data_type = nullptr;
+    if (base->opcode() == spv::Op::OpVariable) {
+      const auto ptr_type = _.FindDef(base->type_id());
+      base_data_type = _.FindDef(ptr_type->GetOperandAs<uint32_t>(2));
+    } else if (base->opcode() == spv::Op::OpUntypedVariableKHR) {
+      if (base->operands().size() > 3) {
+        base_data_type = _.FindDef(base->GetOperandAs<uint32_t>(3));
+      }
+    }
+
+    if (base_data_type) {
+      const bool base_block_array =
+          base_data_type->opcode() == spv::Op::OpTypeArray &&
+          _.ContainsType(base_data_type->id(), ContainsBlock,
+                         /* traverse_all_types = */ false);
+
+      if (base_type_block_array != base_block_array) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "Both Base Type and Base must be Block or BufferBlock arrays "
+                  "or neither can be";
+      } else if (base_type_block_array && base_block_array &&
+                 base_type->id() != base_data_type->id()) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "If Base or Base Type is a Block or BufferBlock array, the "
+                  "other must also be the same array";
+      }
+    }
   }
 
   // Base must be a pointer, pointing to the base of a composite object.
@@ -1528,8 +1944,8 @@
                       (untyped_pointer && spv::Op::OpTypeUntypedPointerKHR ==
                                               base_type->opcode()))) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The Base <id> " << _.getIdName(base_id) << " in " << instr_name
-           << " instruction must be a pointer.";
+           << "The Base <id> " << _.getIdName(base_id) << " in Op"
+           << spvOpcodeString(opcode) << " instruction must be a pointer.";
   }
 
   // The result pointer storage class and base pointer storage class must match.
@@ -1539,8 +1955,8 @@
   if (result_type_storage_class != base_type_storage_class) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "The result pointer storage class and base "
-              "pointer storage class in "
-           << instr_name << " do not match.";
+              "pointer storage class in Op"
+           << spvOpcodeString(opcode) << " do not match.";
   }
 
   // The type pointed to by OpTypePointer (word 3) must be a composite type.
@@ -1564,8 +1980,9 @@
       _.options()->universal_limits_.max_access_chain_indexes;
   if (num_indexes > num_indexes_limit) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The number of indexes in " << instr_name << " may not exceed "
-           << num_indexes_limit << ". Found " << num_indexes << " indexes.";
+           << "The number of indexes in Op" << spvOpcodeString(opcode)
+           << " may not exceed " << num_indexes_limit << ". Found "
+           << num_indexes << " indexes.";
   }
   // Indexes walk the type hierarchy to the desired depth, potentially down to
   // scalar granularity. The first index in Indexes will select the top-level
@@ -1589,18 +2006,41 @@
     auto index_type = _.FindDef(cur_word_instr->type_id());
     if (!index_type || spv::Op::OpTypeInt != index_type->opcode()) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
-             << "Indexes passed to " << instr_name
+             << "Indexes passed to Op" << spvOpcodeString(opcode)
              << " must be of type integer.";
     }
+
+    // Logical pointer restrictions: any constant index with a signed integer
+    // type must not have its sign bit set.
+    if (!_.options()->relax_logical_pointer &&
+        (_.addressing_model() == spv::AddressingModel::Logical ||
+         _.addressing_model() ==
+             spv::AddressingModel::PhysicalStorageBuffer64) &&
+        result_type_storage_class !=
+            static_cast<uint32_t>(spv::StorageClass::PhysicalStorageBuffer)) {
+      if (index_type->GetOperandAs<uint32_t>(2) == 1) {
+        int64_t val = 0;
+        if (_.EvalConstantValInt64(cur_word, &val)) {
+          if (val < 0) {
+            return _.diag(SPV_ERROR_INVALID_ID, inst)
+                   << "Index at word " << i << " may not have a negative value";
+          }
+        }
+      }
+    }
+
     switch (type_pointee->opcode()) {
       case spv::Op::OpTypeMatrix:
       case spv::Op::OpTypeVector:
+      case spv::Op::OpTypeVectorIdEXT:
       case spv::Op::OpTypeCooperativeMatrixNV:
       case spv::Op::OpTypeCooperativeMatrixKHR:
       case spv::Op::OpTypeArray:
-      case spv::Op::OpTypeRuntimeArray: {
+      case spv::Op::OpTypeRuntimeArray:
+      case spv::Op::OpTypeNodePayloadArrayAMDX: {
         // In OpTypeMatrix, OpTypeVector, spv::Op::OpTypeCooperativeMatrixNV,
-        // OpTypeArray, and OpTypeRuntimeArray, word 2 is the Element Type.
+        // OpTypeVectorIdEXT, OpTypeArray, and OpTypeRuntimeArray, word
+        // 2 is the Element Type.
         type_pointee = _.FindDef(type_pointee->word(2));
         break;
       }
@@ -1609,9 +2049,10 @@
         // index: the index must be an OpConstant.
         int64_t cur_index;
         if (!_.EvalConstantValInt64(cur_word, &cur_index)) {
-          return _.diag(SPV_ERROR_INVALID_ID, cur_word_instr)
-                 << "The <id> passed to " << instr_name
-                 << " to index into a "
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "The <id> passed to Op" << spvOpcodeString(opcode)
+                 << " to index " << _.getIdName(cur_word)
+                 << " into a "
                     "structure must be an OpConstant.";
         }
 
@@ -1620,10 +2061,10 @@
         const int64_t num_struct_members =
             static_cast<int64_t>(type_pointee->words().size() - 2);
         if (cur_index >= num_struct_members || cur_index < 0) {
-          return _.diag(SPV_ERROR_INVALID_ID, cur_word_instr)
-                 << "Index is out of bounds: " << instr_name
-                 << " cannot find index " << cur_index
-                 << " into the structure <id> "
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "Index " << _.getIdName(cur_word) << " is out of bounds: Op"
+                 << spvOpcodeString(opcode) << " cannot find index "
+                 << cur_index << " into the structure <id> "
                  << _.getIdName(type_pointee->id()) << ". This structure has "
                  << num_struct_members << " members. Largest valid index is "
                  << num_struct_members - 1 << ".";
@@ -1637,7 +2078,7 @@
       default: {
         // Give an error. reached non-composite type while indexes still remain.
         return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << instr_name
+               << "Op" << spvOpcodeString(opcode)
                << " reached non-composite type while indexes "
                   "still remain to be traversed.";
       }
@@ -1652,15 +2093,19 @@
     // At this point, we have fully walked down from the base using the indeces.
     // The type being pointed to should be the same as the result type.
     if (type_pointee->id() != result_type_pointee->id()) {
+      bool same_type = result_type_pointee->opcode() == type_pointee->opcode();
       return _.diag(SPV_ERROR_INVALID_ID, inst)
-             << instr_name << " result type (Op"
-             << spvOpcodeString(
-                    static_cast<spv::Op>(result_type_pointee->opcode()))
+             << "Op" << spvOpcodeString(opcode) << " result type <id> "
+             << _.getIdName(result_type_pointee->id()) << " (Op"
+             << spvOpcodeString(result_type_pointee->opcode())
              << ") does not match the type that results from indexing into the "
                 "base "
-                "<id> (Op"
-             << spvOpcodeString(static_cast<spv::Op>(type_pointee->opcode()))
-             << ").";
+                "<id> "
+             << _.getIdName(type_pointee->id()) << " (Op"
+             << spvOpcodeString(type_pointee->opcode()) << ")."
+             << (same_type ? " (The types must be the exact same Id, so the "
+                             "two types referenced are slighlty different)"
+                           : "");
     }
   }
 
@@ -1669,13 +2114,12 @@
 
 spv_result_t ValidateRawAccessChain(ValidationState_t& _,
                                     const Instruction* inst) {
-  std::string instr_name = "Op" + std::string(spvOpcodeString(inst->opcode()));
-
+  const spv::Op opcode = inst->opcode();
   // The result type must be OpTypePointer.
   const auto result_type = _.FindDef(inst->type_id());
   if (spv::Op::OpTypePointer != result_type->opcode()) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "The Result Type of " << instr_name << " <id> "
+           << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> "
            << _.getIdName(inst->id()) << " must be OpTypePointer. Found Op"
            << spvOpcodeString(result_type->opcode()) << '.';
   }
@@ -1686,7 +2130,7 @@
       storage_class != spv::StorageClass::PhysicalStorageBuffer &&
       storage_class != spv::StorageClass::Uniform) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "The Result Type of " << instr_name << " <id> "
+           << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> "
            << _.getIdName(inst->id())
            << " must point to a storage class of "
               "StorageBuffer, PhysicalStorageBuffer, or Uniform.";
@@ -1699,7 +2143,7 @@
       result_type_pointee->opcode() == spv::Op::OpTypeMatrix ||
       result_type_pointee->opcode() == spv::Op::OpTypeStruct) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "The Result Type of " << instr_name << " <id> "
+           << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> "
            << _.getIdName(inst->id())
            << " must not point to "
               "OpTypeArray, OpTypeMatrix, or OpTypeStruct.";
@@ -1709,7 +2153,7 @@
   const auto stride = _.FindDef(inst->GetOperandAs<uint32_t>(3));
   if (stride->opcode() != spv::Op::OpConstant) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "The Stride of " << instr_name << " <id> "
+           << "The Stride of Op" << spvOpcodeString(opcode) << " <id> "
            << _.getIdName(inst->id()) << " must be OpConstant. Found Op"
            << spvOpcodeString(stride->opcode()) << '.';
   }
@@ -1717,7 +2161,7 @@
   const auto stride_type = _.FindDef(stride->type_id());
   if (stride_type->opcode() != spv::Op::OpTypeInt) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "The type of Stride of " << instr_name << " <id> "
+           << "The type of Stride of Op" << spvOpcodeString(opcode) << " <id> "
            << _.getIdName(inst->id()) << " must be OpTypeInt. Found Op"
            << spvOpcodeString(stride_type->opcode()) << '.';
   }
@@ -1729,16 +2173,17 @@
     const auto value_type = _.FindDef(value->type_id());
     if (value_type->opcode() != spv::Op::OpTypeInt) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "The type of " << name << " of " << instr_name << " <id> "
-             << _.getIdName(inst->id()) << " must be OpTypeInt. Found Op"
+             << "The type of " << name << " of Op" << spvOpcodeString(opcode)
+             << " <id> " << _.getIdName(inst->id())
+             << " must be OpTypeInt. Found Op"
              << spvOpcodeString(value_type->opcode()) << '.';
     }
     const auto width = value_type->GetOperandAs<uint32_t>(1);
     if (width != 32) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "The integer width of " << name << " of " << instr_name
-             << " <id> " << _.getIdName(inst->id()) << " must be 32. Found "
-             << width << '.';
+             << "The integer width of " << name << " of Op"
+             << spvOpcodeString(opcode) << " <id> " << _.getIdName(inst->id())
+             << " must be 32. Found " << width << '.';
     }
     return SPV_SUCCESS;
   };
@@ -1789,28 +2234,39 @@
 
 spv_result_t ValidatePtrAccessChain(ValidationState_t& _,
                                     const Instruction* inst) {
-  if (_.addressing_model() == spv::AddressingModel::Logical &&
-      inst->opcode() == spv::Op::OpPtrAccessChain) {
-    if (!_.features().variable_pointers) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Generating variable pointers requires capability "
-             << "VariablePointers or VariablePointersStorageBuffer";
-    }
-  }
-
   // Need to call first, will make sure Base is a valid ID
   if (auto error = ValidateAccessChain(_, inst)) return error;
 
   const bool untyped_pointer = spvOpcodeGeneratesUntypedPointer(inst->opcode());
 
-  const auto base_id = inst->GetOperandAs<uint32_t>(2);
-  const auto base = _.FindDef(base_id);
-  const auto base_type = untyped_pointer
-                             ? _.FindDef(inst->GetOperandAs<uint32_t>(2))
-                             : _.FindDef(base->type_id());
+  const auto base_idx = untyped_pointer ? 3 : 2;
+  const auto base = _.FindDef(inst->GetOperandAs<uint32_t>(base_idx));
+  const auto base_type = _.FindDef(base->type_id());
   const auto base_type_storage_class =
       base_type->GetOperandAs<spv::StorageClass>(1);
 
+  const auto element_idx = untyped_pointer ? 4 : 3;
+  const auto element = _.FindDef(inst->GetOperandAs<uint32_t>(element_idx));
+  const auto element_type = _.FindDef(element->type_id());
+  if (!element_type || element_type->opcode() != spv::Op::OpTypeInt) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Element must be an integer";
+  }
+  uint64_t element_val = 0;
+  if (_.EvalConstantValUint64(element->id(), &element_val)) {
+    if (element_val != 0) {
+      const auto interp_type =
+          untyped_pointer ? _.FindDef(inst->GetOperandAs<uint32_t>(2))
+                          : _.FindDef(base_type->GetOperandAs<uint32_t>(2));
+      if (interp_type->opcode() == spv::Op::OpTypeStruct &&
+          (_.HasDecoration(interp_type->id(), spv::Decoration::Block) ||
+           _.HasDecoration(interp_type->id(), spv::Decoration::BufferBlock))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Element must be 0 if the interpretation type is a Block- or "
+                  "BufferBlock-decorated structure";
+      }
+    }
+  }
+
   if (_.HasCapability(spv::Capability::Shader) &&
       (base_type_storage_class == spv::StorageClass::Uniform ||
        base_type_storage_class == spv::StorageClass::StorageBuffer ||
@@ -1818,10 +2274,11 @@
        base_type_storage_class == spv::StorageClass::PushConstant ||
        (_.HasCapability(spv::Capability::WorkgroupMemoryExplicitLayoutKHR) &&
         base_type_storage_class == spv::StorageClass::Workgroup)) &&
-      !_.HasDecoration(base_type->id(), spv::Decoration::ArrayStride)) {
+      (!_.HasDecoration(base_type->id(), spv::Decoration::ArrayStride) &&
+       !_.HasDecoration(base_type->id(), spv::Decoration::ArrayStrideIdEXT))) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "OpPtrAccessChain must have a Base whose type is decorated "
-              "with ArrayStride";
+              "with ArrayStride or ArrayStrideIdEXT";
   }
 
   if (spvIsVulkanEnv(_.context()->target_env)) {
@@ -1857,32 +2314,39 @@
 
 spv_result_t ValidateArrayLength(ValidationState_t& state,
                                  const Instruction* inst) {
-  std::string instr_name =
-      "Op" + std::string(spvOpcodeString(static_cast<spv::Op>(inst->opcode())));
+  const spv::Op opcode = inst->opcode();
 
-  // Result type must be a 32-bit unsigned int.
-  auto result_type = state.FindDef(inst->type_id());
-  if (result_type->opcode() != spv::Op::OpTypeInt ||
-      result_type->GetOperandAs<uint32_t>(1) != 32 ||
-      result_type->GetOperandAs<uint32_t>(2) != 0) {
+  // Result type must be a 32- or 64-bit unsigned int.
+  // 64-bit requires CapabilityShader64BitIndexingEXT or a pipeline/shader
+  // flag and is validated in VVL.
+  const uint32_t result_type_id = inst->type_id();
+  if (!state.IsIntScalarTypeWithSignedness(result_type_id, 0)) {
     return state.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The Result Type of " << instr_name << " <id> "
+           << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> "
            << state.getIdName(inst->id())
-           << " must be OpTypeInt with width 32 and signedness 0.";
+           << " must be OpTypeInt with width 32 or 64 and signedness 0.";
+  }
+  const uint32_t result_type_width = state.GetBitWidth(inst->type_id());
+  if (result_type_width != 32 && result_type_width != 64) {
+    return state.diag(SPV_ERROR_INVALID_ID, inst)
+           << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> "
+           << state.getIdName(inst->id())
+           << " must be OpTypeInt with width 32 or 64 and signedness 0.";
   }
 
   const bool untyped = inst->opcode() == spv::Op::OpUntypedArrayLengthKHR;
   auto pointer_ty_id = state.GetOperandTypeId(inst, (untyped ? 3 : 2));
   auto pointer_ty = state.FindDef(pointer_ty_id);
   if (untyped) {
-    if (pointer_ty->opcode() != spv::Op::OpTypeUntypedPointerKHR) {
+    if (!pointer_ty ||
+        pointer_ty->opcode() != spv::Op::OpTypeUntypedPointerKHR) {
       return state.diag(SPV_ERROR_INVALID_ID, inst)
-             << "Pointer must be an untyped pointer";
+             << "Pointer must be an untyped pointer object";
     }
   } else if (pointer_ty->opcode() != spv::Op::OpTypePointer) {
     return state.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The Structure's type in " << instr_name << " <id> "
-           << state.getIdName(inst->id())
+           << "The Structure's type in Op" << spvOpcodeString(opcode)
+           << " <id> " << state.getIdName(inst->id())
            << " must be a pointer to an OpTypeStruct.";
   }
 
@@ -1895,8 +2359,8 @@
 
   if (structure_type->opcode() != spv::Op::OpTypeStruct) {
     return state.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The Structure's type in " << instr_name << " <id> "
-           << state.getIdName(inst->id())
+           << "The Structure's type in Op" << spvOpcodeString(opcode)
+           << " <id> " << state.getIdName(inst->id())
            << " must be a pointer to an OpTypeStruct.";
   }
 
@@ -1905,8 +2369,9 @@
       state.FindDef(structure_type->GetOperandAs<uint32_t>(num_of_members));
   if (last_member->opcode() != spv::Op::OpTypeRuntimeArray) {
     return state.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The Structure's last member in " << instr_name << " <id> "
-           << state.getIdName(inst->id()) << " must be an OpTypeRuntimeArray.";
+           << "The Structure's last member in Op" << spvOpcodeString(opcode)
+           << " <id> " << state.getIdName(inst->id())
+           << " must be an OpTypeRuntimeArray.";
   }
 
   // The array member must the index of the last element (the run time
@@ -1914,40 +2379,50 @@
   const auto index = untyped ? 4 : 3;
   if (inst->GetOperandAs<uint32_t>(index) != num_of_members - 1) {
     return state.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The array member in " << instr_name << " <id> "
+           << "The array member in Op" << spvOpcodeString(opcode) << " <id> "
            << state.getIdName(inst->id())
            << " must be the last member of the struct.";
   }
+
+  if (spvIsVulkanEnv(state.context()->target_env)) {
+    const auto storage_class = pointer_ty->GetOperandAs<spv::StorageClass>(1);
+    if (storage_class == spv::StorageClass::Uniform &&
+        state.HasDecoration(structure_type->id(), spv::Decoration::Block)) {
+      return state.diag(SPV_ERROR_INVALID_ID, inst)
+             << state.VkErrorID(11805) << "Op" << spvOpcodeString(opcode)
+             << " must not be used on the OpTypeRuntimeArray inside a Uniform "
+                "block";
+    }
+  }
+
   return SPV_SUCCESS;
 }
 
-spv_result_t ValidateCooperativeMatrixLengthNV(ValidationState_t& state,
-                                               const Instruction* inst) {
-  std::string instr_name =
-      "Op" + std::string(spvOpcodeString(static_cast<spv::Op>(inst->opcode())));
-
+spv_result_t ValidateCooperativeMatrixLength(ValidationState_t& state,
+                                             const Instruction* inst,
+                                             bool is_khr,
+                                             uint32_t operand_index = 2) {
+  const spv::Op opcode = inst->opcode();
   // Result type must be a 32-bit unsigned int.
-  auto result_type = state.FindDef(inst->type_id());
-  if (result_type->opcode() != spv::Op::OpTypeInt ||
-      result_type->GetOperandAs<uint32_t>(1) != 32 ||
-      result_type->GetOperandAs<uint32_t>(2) != 0) {
+  const uint32_t result_type_id = inst->type_id();
+  if (!state.IsIntScalarTypeWithSignedness(result_type_id, 0) ||
+      state.GetBitWidth(inst->type_id()) != 32) {
     return state.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The Result Type of " << instr_name << " <id> "
+           << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> "
            << state.getIdName(inst->id())
            << " must be OpTypeInt with width 32 and signedness 0.";
   }
 
-  bool isKhr = inst->opcode() == spv::Op::OpCooperativeMatrixLengthKHR;
-  auto type_id = inst->GetOperandAs<uint32_t>(2);
+  auto type_id = inst->GetOperandAs<uint32_t>(operand_index);
   auto type = state.FindDef(type_id);
-  if (isKhr && type->opcode() != spv::Op::OpTypeCooperativeMatrixKHR) {
+  if (is_khr && type->opcode() != spv::Op::OpTypeCooperativeMatrixKHR) {
     return state.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The type in " << instr_name << " <id> "
+           << "The type in Op" << spvOpcodeString(opcode) << " <id> "
            << state.getIdName(type_id)
            << " must be OpTypeCooperativeMatrixKHR.";
-  } else if (!isKhr && type->opcode() != spv::Op::OpTypeCooperativeMatrixNV) {
+  } else if (!is_khr && type->opcode() != spv::Op::OpTypeCooperativeMatrixNV) {
     return state.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The type in " << instr_name << " <id> "
+           << "The type in Op" << spvOpcodeString(opcode) << " <id> "
            << state.getIdName(type_id) << " must be OpTypeCooperativeMatrixNV.";
   }
   return SPV_SUCCESS;
@@ -2150,23 +2625,96 @@
   }
 
   bool stride_required = false;
+  bool layout_requires_constant_stride = false;
   uint64_t layout;
   if (_.EvalConstantValUint64(layout_id, &layout)) {
+    const bool is_arm_layout =
+        (layout ==
+         (uint64_t)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM) ||
+        (layout ==
+         (uint64_t)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM);
+
+    if (is_arm_layout) {
+      if (!_.HasCapability(spv::Capability::CooperativeMatrixLayoutsARM)) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "Using the RowBlockedInterleavedARM or "
+                  "ColumnBlockedInterleavedARM MemoryLayout requires the "
+                  "CooperativeMatrixLayoutsARM capability be declared";
+      }
+    }
+
     stride_required =
         (layout == (uint64_t)spv::CooperativeMatrixLayout::RowMajorKHR) ||
-        (layout == (uint64_t)spv::CooperativeMatrixLayout::ColumnMajorKHR);
+        (layout == (uint64_t)spv::CooperativeMatrixLayout::ColumnMajorKHR) ||
+        is_arm_layout;
+    layout_requires_constant_stride = is_arm_layout;
   }
 
   const auto stride_index =
       (inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) ? 4u : 3u;
   if (inst->operands().size() > stride_index) {
     const auto stride_id = inst->GetOperandAs<uint32_t>(stride_index);
-    const auto stride = _.FindDef(stride_id);
-    if (!stride || !_.IsIntScalarType(stride->type_id())) {
+    const auto stride_inst = _.FindDef(stride_id);
+    if (!stride_inst || !_.IsIntScalarType(stride_inst->type_id())) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "Stride operand <id> " << _.getIdName(stride_id)
              << " must be a scalar integer type.";
     }
+    // Check SPV_ARM_cooperative_matrix_layouts constraints
+    if (layout_requires_constant_stride &&
+        !spvOpcodeIsConstant(stride_inst->opcode())) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "MemoryLayout " << layout
+             << " requires Stride come from a constant instruction.";
+    }
+    if (layout_requires_constant_stride) {
+      uint64_t stride;
+      if (_.EvalConstantValUint64(stride_id, &stride)) {
+        if ((layout ==
+             (uint64_t)
+                 spv::CooperativeMatrixLayout::RowBlockedInterleavedARM) ||
+            (layout ==
+             (uint64_t)
+                 spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM)) {
+          if ((stride != 1) && (stride != 2) && (stride != 4)) {
+            return _.diag(SPV_ERROR_INVALID_ID, inst)
+                   << "MemoryLayout " << layout
+                   << " requires Stride be 1, 2, or 4.";
+          }
+        }
+        const uint32_t elty_id = matrix_type->GetOperandAs<uint32_t>(1);
+        const uint32_t rows_id = matrix_type->GetOperandAs<uint32_t>(3);
+        const uint32_t cols_id = matrix_type->GetOperandAs<uint32_t>(4);
+        uint64_t rows = 0, cols = 0;
+        _.EvalConstantValUint64(rows_id, &rows);
+        _.EvalConstantValUint64(cols_id, &cols);
+        uint32_t sizeof_component_in_bytes = _.GetBitWidth(elty_id) / 8;
+        uint64_t rows_required_multiple = 4;
+        uint64_t cols_required_multiple = 16 / sizeof_component_in_bytes;
+
+        if (layout ==
+            (uint64_t)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM) {
+          cols_required_multiple *= stride;
+        }
+        if (layout ==
+            (uint64_t)
+                spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM) {
+          rows_required_multiple *= stride;
+        }
+        if ((rows != 0) && (rows % rows_required_multiple != 0)) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "MemoryLayout " << layout << " with a Stride of " << stride
+                 << " requires that the number of rows be a multiple of "
+                 << rows_required_multiple;
+        }
+        if ((cols != 0) && (cols % cols_required_multiple != 0)) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "MemoryLayout " << layout << " with a Stride of " << stride
+                 << " requires that the number of columns be a multiple of "
+                 << cols_required_multiple;
+        }
+      }
+    }
   } else if (stride_required) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "MemoryLayout " << layout << " requires a Stride.";
@@ -2182,6 +2730,27 @@
   return SPV_SUCCESS;
 }
 
+spv_result_t ValidateBufferPointerEXT(ValidationState_t& _,
+                                      const Instruction* inst) {
+  const auto storage_class_ptr = _.FindDef(inst->GetOperandAs<uint32_t>(0));
+  if (storage_class_ptr->opcode() != spv::Op::OpTypeUntypedPointerKHR &&
+      storage_class_ptr->opcode() != spv::Op::OpTypePointer) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "OpBufferPointerEXT's Result Type should be "
+           << "a pointer type.";
+  } else {
+    // Buffer operand
+    auto buffer =
+        _.FindUntypedBaseVariable(_.FindDef(inst->GetOperandAs<uint32_t>(2)));
+    if (!_.IsBuiltin(buffer->id(), spv::BuiltIn::ResourceHeapEXT)) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "OpBufferPointerEXT's buffer must be an untyped pointer"
+             << " into a variable declared with the ResourceHeapEXT built-in";
+    }
+  }
+  return SPV_SUCCESS;
+}
+
 // Returns the number of instruction words taken up by a tensor addressing
 // operands argument and its implied operands.
 int TensorAddressingOperandsNumWords(spv::TensorAddressingOperandsMask mask) {
@@ -2398,12 +2967,443 @@
   return SPV_SUCCESS;
 }
 
+spv_result_t ValidateInt32Operand(ValidationState_t& _, const Instruction* inst,
+                                  uint32_t operand_index,
+                                  const char* opcode_name,
+                                  const char* operand_name) {
+  const auto type_id =
+      _.FindDef(inst->GetOperandAs<uint32_t>(operand_index))->type_id();
+  if (!_.IsIntScalarType(type_id, 32)) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << opcode_name << " " << operand_name << " type <id> "
+           << _.getIdName(type_id) << " is not a 32 bit integer.";
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateInt32Or64Operand(ValidationState_t& _,
+                                      const Instruction* inst,
+                                      uint32_t operand_index,
+                                      const char* opcode_name,
+                                      const char* operand_name) {
+  const auto type_id =
+      _.FindDef(inst->GetOperandAs<uint32_t>(operand_index))->type_id();
+  if (!_.IsIntScalarType(type_id) ||
+      !(_.GetBitWidth(type_id) == 32 || _.GetBitWidth(type_id) == 64)) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << opcode_name << " " << operand_name << " type <id> "
+           << _.getIdName(type_id) << " is not a 32 or 64 bit integer.";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateCooperativeVectorPointer(ValidationState_t& _,
+                                              const Instruction* inst,
+                                              const char* opname,
+                                              uint32_t pointer_index) {
+  const auto pointer_id = inst->GetOperandAs<uint32_t>(pointer_index);
+  const auto pointer = _.FindDef(pointer_id);
+  if (!pointer ||
+      ((_.addressing_model() == spv::AddressingModel::Logical) &&
+       ((!_.features().variable_pointers &&
+         !spvOpcodeReturnsLogicalPointer(pointer->opcode())) ||
+        (_.features().variable_pointers &&
+         !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << opname << " Pointer <id> " << _.getIdName(pointer_id)
+           << " is not a logical pointer.";
+  }
+
+  const auto pointer_type_id = pointer->type_id();
+  const auto pointer_type = _.FindDef(pointer_type_id);
+  if (!pointer_type || pointer_type->opcode() != spv::Op::OpTypePointer) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << opname << " type for pointer <id> " << _.getIdName(pointer_id)
+           << " is not a pointer type.";
+  }
+
+  const auto storage_class_index = 1u;
+  const auto storage_class =
+      pointer_type->GetOperandAs<spv::StorageClass>(storage_class_index);
+
+  if (storage_class != spv::StorageClass::Workgroup &&
+      storage_class != spv::StorageClass::StorageBuffer &&
+      storage_class != spv::StorageClass::PhysicalStorageBuffer) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << opname << " storage class for pointer type <id> "
+           << _.getIdName(pointer_type_id)
+           << " is not Workgroup or StorageBuffer.";
+  }
+
+  const auto pointee_id = pointer_type->GetOperandAs<uint32_t>(2);
+  const auto pointee_type = _.FindDef(pointee_id);
+  if (!pointee_type ||
+      (pointee_type->opcode() != spv::Op::OpTypeArray &&
+       pointee_type->opcode() != spv::Op::OpTypeRuntimeArray)) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << opname << " Pointer <id> " << _.getIdName(pointer->id())
+           << "s Type must be an array type.";
+  }
+
+  const auto array_elem_type_id = pointee_type->GetOperandAs<uint32_t>(1);
+  auto array_elem_type = _.FindDef(array_elem_type_id);
+  if (!array_elem_type || !(_.IsIntScalarOrVectorType(array_elem_type_id) ||
+                            _.IsFloatScalarOrVectorType(array_elem_type_id))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << opname << " Pointer <id> " << _.getIdName(pointer->id())
+           << "s Type must be an array of scalar or vector type.";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateCooperativeVectorLoadStoreNV(ValidationState_t& _,
+                                                  const Instruction* inst) {
+  uint32_t type_id;
+  const char* opname;
+  if (inst->opcode() == spv::Op::OpCooperativeVectorLoadNV) {
+    type_id = inst->type_id();
+    opname = "spv::Op::OpCooperativeVectorLoadNV";
+  } else {
+    // get Object operand's type
+    type_id = _.FindDef(inst->GetOperandAs<uint32_t>(2))->type_id();
+    opname = "spv::Op::OpCooperativeVectorStoreNV";
+  }
+
+  auto vector_type = _.FindDef(type_id);
+
+  if (vector_type->opcode() != spv::Op::OpTypeVectorIdEXT) {
+    if (inst->opcode() == spv::Op::OpCooperativeVectorLoadNV) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "spv::Op::OpCooperativeVectorLoadNV Result Type <id> "
+             << _.getIdName(type_id) << " is not a cooperative vector type.";
+    } else {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "spv::Op::OpCooperativeVectorStoreNV Object type <id> "
+             << _.getIdName(type_id) << " is not a cooperative vector type.";
+    }
+  }
+
+  const auto pointer_index =
+      (inst->opcode() == spv::Op::OpCooperativeVectorLoadNV) ? 2u : 0u;
+
+  const auto offset_index =
+      (inst->opcode() == spv::Op::OpCooperativeVectorLoadNV) ? 3u : 1u;
+
+  if (auto error =
+          ValidateCooperativeVectorPointer(_, inst, opname, pointer_index)) {
+    return error;
+  }
+
+  if (auto error =
+          ValidateInt32Or64Operand(_, inst, offset_index, opname, "Offset")) {
+    return error;
+  }
+
+  const auto memory_access_index =
+      (inst->opcode() == spv::Op::OpCooperativeVectorLoadNV) ? 4u : 3u;
+  if (inst->operands().size() > memory_access_index) {
+    if (auto error = CheckMemoryAccess(_, inst, memory_access_index))
+      return error;
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateCooperativeVectorOuterProductNV(ValidationState_t& _,
+                                                     const Instruction* inst) {
+  const auto pointer_index = 0u;
+  const auto opcode_name =
+      "spv::Op::OpCooperativeVectorOuterProductAccumulateNV";
+
+  if (auto error = ValidateCooperativeVectorPointer(_, inst, opcode_name,
+                                                    pointer_index)) {
+    return error;
+  }
+
+  auto type_id = _.FindDef(inst->GetOperandAs<uint32_t>(2))->type_id();
+  auto a_type = _.FindDef(type_id);
+
+  if (a_type->opcode() != spv::Op::OpTypeVectorIdEXT) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << opcode_name << " A type <id> " << _.getIdName(type_id)
+           << " is not a cooperative vector type.";
+  }
+
+  type_id = _.FindDef(inst->GetOperandAs<uint32_t>(3))->type_id();
+  auto b_type = _.FindDef(type_id);
+
+  if (b_type->opcode() != spv::Op::OpTypeVectorIdEXT) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << opcode_name << " B type <id> " << _.getIdName(type_id)
+           << " is not a cooperative vector type.";
+  }
+
+  const auto a_component_type_id = a_type->GetOperandAs<uint32_t>(1);
+  const auto b_component_type_id = b_type->GetOperandAs<uint32_t>(1);
+
+  if (a_component_type_id != b_component_type_id) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << opcode_name << " A and B component types "
+           << _.getIdName(a_component_type_id) << " and "
+           << _.getIdName(b_component_type_id) << " do not match.";
+  }
+
+  if (auto error =
+          ValidateInt32Or64Operand(_, inst, 1, opcode_name, "Offset")) {
+    return error;
+  }
+
+  if (auto error =
+          ValidateInt32Operand(_, inst, 4, opcode_name, "MemoryLayout")) {
+    return error;
+  }
+
+  if (auto error = ValidateInt32Operand(_, inst, 5, opcode_name,
+                                        "MatrixInterpretation")) {
+    return error;
+  }
+
+  if (inst->operands().size() > 6) {
+    if (auto error =
+            ValidateInt32Operand(_, inst, 6, opcode_name, "MatrixStride")) {
+      return error;
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateCooperativeVectorReduceSumNV(ValidationState_t& _,
+                                                  const Instruction* inst) {
+  const auto opcode_name = "spv::Op::OpCooperativeVectorReduceSumAccumulateNV";
+  const auto pointer_index = 0u;
+
+  if (auto error = ValidateCooperativeVectorPointer(_, inst, opcode_name,
+                                                    pointer_index)) {
+    return error;
+  }
+
+  auto type_id = _.FindDef(inst->GetOperandAs<uint32_t>(2))->type_id();
+  auto v_type = _.FindDef(type_id);
+
+  if (v_type->opcode() != spv::Op::OpTypeVectorIdEXT) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << opcode_name << " V type <id> " << _.getIdName(type_id)
+           << " is not a cooperative vector type.";
+  }
+
+  if (auto error =
+          ValidateInt32Or64Operand(_, inst, 1, opcode_name, "Offset")) {
+    return error;
+  }
+
+  return SPV_SUCCESS;
+}
+
+bool InterpretationIsPacked(spv::ComponentType interp) {
+  switch (interp) {
+    case spv::ComponentType::SignedInt8PackedNV:
+    case spv::ComponentType::UnsignedInt8PackedNV:
+      return true;
+    default:
+      return false;
+  }
+}
+
+using std::get;
+
+spv_result_t ValidateCooperativeVectorMatrixMulNV(ValidationState_t& _,
+                                                  const Instruction* inst) {
+  const bool has_bias =
+      inst->opcode() == spv::Op::OpCooperativeVectorMatrixMulAddNV;
+  const auto opcode_name = has_bias
+                               ? "spv::Op::OpCooperativeVectorMatrixMulAddNV"
+                               : "spv::Op::OpCooperativeVectorMatrixMulNV";
+
+  const auto bias_offset = has_bias ? 3 : 0;
+
+  const auto result_type_index = 0u;
+  const auto input_index = 2u;
+  const auto input_interpretation_index = 3u;
+  const auto matrix_index = 4u;
+  const auto matrix_offset_index = 5u;
+  const auto matrix_interpretation_index = 6u;
+  const auto bias_index = 7u;
+  const auto bias_offset_index = 8u;
+  const auto bias_interpretation_index = 9u;
+  const auto m_index = 7u + bias_offset;
+  const auto k_index = 8u + bias_offset;
+  const auto memory_layout_index = 9u + bias_offset;
+  const auto transpose_index = 10u + bias_offset;
+
+  const auto result_type_id = inst->GetOperandAs<uint32_t>(result_type_index);
+  const auto input_id = inst->GetOperandAs<uint32_t>(input_index);
+  const auto input_interpretation_id =
+      inst->GetOperandAs<uint32_t>(input_interpretation_index);
+  const auto matrix_interpretation_id =
+      inst->GetOperandAs<uint32_t>(matrix_interpretation_index);
+  const auto bias_interpretation_id =
+      inst->GetOperandAs<uint32_t>(bias_interpretation_index);
+  const auto m_id = inst->GetOperandAs<uint32_t>(m_index);
+  const auto k_id = inst->GetOperandAs<uint32_t>(k_index);
+  const auto memory_layout_id =
+      inst->GetOperandAs<uint32_t>(memory_layout_index);
+  const auto transpose_id = inst->GetOperandAs<uint32_t>(transpose_index);
+
+  if (auto error = ValidateCooperativeVectorPointer(_, inst, opcode_name,
+                                                    matrix_index)) {
+    return error;
+  }
+
+  if (inst->opcode() == spv::Op::OpCooperativeVectorMatrixMulAddNV) {
+    if (auto error = ValidateCooperativeVectorPointer(_, inst, opcode_name,
+                                                      bias_index)) {
+      return error;
+    }
+  }
+
+  const auto result_type = _.FindDef(result_type_id);
+
+  if (result_type->opcode() != spv::Op::OpTypeVectorIdEXT) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << opcode_name << " result type <id> " << _.getIdName(result_type_id)
+           << " is not a cooperative vector type.";
+  }
+
+  const auto result_component_type_id = result_type->GetOperandAs<uint32_t>(1u);
+  if (!_.IsIntScalarType(result_component_type_id, 32) &&
+      !_.IsFloatScalarType(result_component_type_id, 32) &&
+      !_.IsFloatScalarType(result_component_type_id, 16)) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << opcode_name << " result component type <id> "
+           << _.getIdName(result_component_type_id)
+           << " is not a 32 bit int or 16/32 bit float.";
+  }
+
+  const auto m_eval = _.EvalInt32IfConst(m_id);
+  const auto rc_eval =
+      _.EvalInt32IfConst(result_type->GetOperandAs<uint32_t>(2u));
+  if (get<1>(m_eval) && get<1>(rc_eval) && get<2>(m_eval) != get<2>(rc_eval)) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << opcode_name << " result type number of components "
+           << get<2>(rc_eval) << " does not match M " << get<2>(m_eval);
+  }
+
+  const auto k_eval = _.EvalInt32IfConst(k_id);
+
+  const auto input = _.FindDef(input_id);
+  const auto input_type = _.FindDef(input->type_id());
+  const auto input_num_components_id = input_type->GetOperandAs<uint32_t>(2u);
+
+  auto input_interp_eval = _.EvalInt32IfConst(input_interpretation_id);
+  if (get<1>(input_interp_eval) &&
+      !InterpretationIsPacked(spv::ComponentType{get<2>(input_interp_eval)})) {
+    const auto inc_eval = _.EvalInt32IfConst(input_num_components_id);
+    if (get<1>(inc_eval) && get<1>(k_eval) &&
+        get<2>(inc_eval) != get<2>(k_eval)) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << opcode_name << " input number of components "
+             << get<2>(inc_eval) << " does not match K " << get<2>(k_eval);
+    }
+  }
+
+  if (!_.IsBoolScalarType(_.FindDef(transpose_id)->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << opcode_name << " Transpose <id> " << _.getIdName(transpose_id)
+           << " is not a scalar boolean.";
+  }
+
+  const auto check_constant = [&](uint32_t id,
+                                  const char* operand_name) -> spv_result_t {
+    if (!spvOpcodeIsConstant(_.GetIdOpcode(id))) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << opcode_name << " " << operand_name << " <id> "
+             << _.getIdName(id) << " is not a constant instruction.";
+    }
+    return SPV_SUCCESS;
+  };
+
+  if (auto error =
+          check_constant(input_interpretation_id, "InputInterpretation")) {
+    return error;
+  }
+  if (auto error =
+          check_constant(matrix_interpretation_id, "MatrixInterpretation")) {
+    return error;
+  }
+  if (has_bias) {
+    if (auto error =
+            check_constant(bias_interpretation_id, "BiasInterpretation")) {
+      return error;
+    }
+  }
+  if (auto error = check_constant(m_id, "M")) {
+    return error;
+  }
+  if (auto error = check_constant(k_id, "K")) {
+    return error;
+  }
+  if (auto error = check_constant(memory_layout_id, "MemoryLayout")) {
+    return error;
+  }
+  if (auto error = check_constant(transpose_id, "Transpose")) {
+    return error;
+  }
+
+  if (auto error = ValidateInt32Operand(_, inst, input_interpretation_index,
+                                        opcode_name, "InputInterpretation")) {
+    return error;
+  }
+  if (auto error = ValidateInt32Operand(_, inst, matrix_interpretation_index,
+                                        opcode_name, "MatrixInterpretation")) {
+    return error;
+  }
+  if (has_bias) {
+    if (auto error = ValidateInt32Operand(_, inst, bias_interpretation_index,
+                                          opcode_name, "BiasInterpretation")) {
+      return error;
+    }
+  }
+  if (auto error = ValidateInt32Operand(_, inst, m_index, opcode_name, "M")) {
+    return error;
+  }
+  if (auto error = ValidateInt32Operand(_, inst, k_index, opcode_name, "K")) {
+    return error;
+  }
+  if (auto error = ValidateInt32Operand(_, inst, memory_layout_index,
+                                        opcode_name, "MemoryLayout")) {
+    return error;
+  }
+
+  if (auto error = ValidateInt32Or64Operand(_, inst, matrix_offset_index,
+                                            opcode_name, "MatrixOffset")) {
+    return error;
+  }
+  if (has_bias) {
+    if (auto error = ValidateInt32Or64Operand(_, inst, bias_offset_index,
+                                              opcode_name, "BiasOffset")) {
+      return error;
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
 spv_result_t ValidatePtrComparison(ValidationState_t& _,
                                    const Instruction* inst) {
-  if (_.addressing_model() == spv::AddressingModel::Logical &&
+  const auto op1 = _.FindDef(inst->GetOperandAs<uint32_t>(2u));
+  const auto op2 = _.FindDef(inst->GetOperandAs<uint32_t>(3u));
+  const auto op1_type = _.FindDef(op1->type_id());
+  const auto op2_type = _.FindDef(op2->type_id());
+  spv::StorageClass sc = op1_type->GetOperandAs<spv::StorageClass>(1u);
+  if ((_.addressing_model() == spv::AddressingModel::Logical ||
+       _.addressing_model() == spv::AddressingModel::PhysicalStorageBuffer64) &&
+      sc != spv::StorageClass::PhysicalStorageBuffer &&
       !_.features().variable_pointers) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "Instruction cannot for logical addressing model be used without "
+           << "Instruction on logical pointers cannot be used without "
               "a variable pointers capability";
   }
 
@@ -2420,20 +3420,40 @@
     }
   }
 
-  const auto op1 = _.FindDef(inst->GetOperandAs<uint32_t>(2u));
-  const auto op2 = _.FindDef(inst->GetOperandAs<uint32_t>(3u));
-  if (!op1 || !op2 || op1->type_id() != op2->type_id()) {
-    return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The types of Operand 1 and Operand 2 must match";
-  }
-  const auto op1_type = _.FindDef(op1->type_id());
   if (!op1_type || (op1_type->opcode() != spv::Op::OpTypePointer &&
                     op1_type->opcode() != spv::Op::OpTypeUntypedPointerKHR)) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Operand type must be a pointer";
   }
 
-  spv::StorageClass sc = op1_type->GetOperandAs<spv::StorageClass>(1u);
+  if (!op2_type || (op2_type->opcode() != spv::Op::OpTypePointer &&
+                    op2_type->opcode() != spv::Op::OpTypeUntypedPointerKHR)) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Operand type must be a pointer";
+  }
+
+  if (inst->opcode() == spv::Op::OpPtrDiff) {
+    if (op1->type_id() != op2->type_id()) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "The types of Operand 1 and Operand 2 must match";
+    }
+  } else {
+    const auto either_untyped =
+        op1_type->opcode() == spv::Op::OpTypeUntypedPointerKHR ||
+        op2_type->opcode() == spv::Op::OpTypeUntypedPointerKHR;
+    if (either_untyped) {
+      const auto sc1 = op1_type->GetOperandAs<spv::StorageClass>(1);
+      const auto sc2 = op2_type->GetOperandAs<spv::StorageClass>(1);
+      if (sc1 != sc2) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "Pointer storage classes must match";
+      }
+    } else if (op1->type_id() != op2->type_id()) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "The types of Operand 1 and Operand 2 must match";
+    }
+  }
+
   if (_.addressing_model() == spv::AddressingModel::Logical) {
     if (sc != spv::StorageClass::Workgroup &&
         sc != spv::StorageClass::StorageBuffer) {
@@ -2461,63 +3481,73 @@
   switch (inst->opcode()) {
     case spv::Op::OpVariable:
     case spv::Op::OpUntypedVariableKHR:
-      if (auto error = ValidateVariable(_, inst)) return error;
-      break;
+      return ValidateVariable(_, inst);
+    case spv::Op::OpBufferPointerEXT:
+      return ValidateBufferPointerEXT(_, inst);
     case spv::Op::OpLoad:
-      if (auto error = ValidateLoad(_, inst)) return error;
-      break;
+      return ValidateLoad(_, inst);
     case spv::Op::OpStore:
-      if (auto error = ValidateStore(_, inst)) return error;
-      break;
+      return ValidateStore(_, inst);
     case spv::Op::OpCopyMemory:
     case spv::Op::OpCopyMemorySized:
-      if (auto error = ValidateCopyMemory(_, inst)) return error;
-      break;
+      return ValidateCopyMemory(_, inst);
     case spv::Op::OpPtrAccessChain:
     case spv::Op::OpUntypedPtrAccessChainKHR:
     case spv::Op::OpUntypedInBoundsPtrAccessChainKHR:
-      if (auto error = ValidatePtrAccessChain(_, inst)) return error;
-      break;
+      return ValidatePtrAccessChain(_, inst);
     case spv::Op::OpAccessChain:
     case spv::Op::OpInBoundsAccessChain:
     case spv::Op::OpInBoundsPtrAccessChain:
     case spv::Op::OpUntypedAccessChainKHR:
     case spv::Op::OpUntypedInBoundsAccessChainKHR:
-      if (auto error = ValidateAccessChain(_, inst)) return error;
-      break;
+      return ValidateAccessChain(_, inst);
     case spv::Op::OpRawAccessChainNV:
-      if (auto error = ValidateRawAccessChain(_, inst)) return error;
-      break;
+      return ValidateRawAccessChain(_, inst);
     case spv::Op::OpArrayLength:
     case spv::Op::OpUntypedArrayLengthKHR:
-      if (auto error = ValidateArrayLength(_, inst)) return error;
-      break;
+      return ValidateArrayLength(_, inst);
     case spv::Op::OpCooperativeMatrixLoadNV:
     case spv::Op::OpCooperativeMatrixStoreNV:
-      if (auto error = ValidateCooperativeMatrixLoadStoreNV(_, inst))
-        return error;
-      break;
+      return ValidateCooperativeMatrixLoadStoreNV(_, inst);
     case spv::Op::OpCooperativeMatrixLengthKHR:
+      return ValidateCooperativeMatrixLength(_, inst, true);
     case spv::Op::OpCooperativeMatrixLengthNV:
-      if (auto error = ValidateCooperativeMatrixLengthNV(_, inst)) return error;
-      break;
+      return ValidateCooperativeMatrixLength(_, inst, false);
     case spv::Op::OpCooperativeMatrixLoadKHR:
     case spv::Op::OpCooperativeMatrixStoreKHR:
-      if (auto error = ValidateCooperativeMatrixLoadStoreKHR(_, inst))
-        return error;
-      break;
+      return ValidateCooperativeMatrixLoadStoreKHR(_, inst);
     case spv::Op::OpCooperativeMatrixLoadTensorNV:
     case spv::Op::OpCooperativeMatrixStoreTensorNV:
-      if (auto error = ValidateCooperativeMatrixLoadStoreTensorNV(_, inst))
-        return error;
-      break;
+      return ValidateCooperativeMatrixLoadStoreTensorNV(_, inst);
+    case spv::Op::OpCooperativeVectorLoadNV:
+    case spv::Op::OpCooperativeVectorStoreNV:
+      return ValidateCooperativeVectorLoadStoreNV(_, inst);
+    case spv::Op::OpCooperativeVectorOuterProductAccumulateNV:
+      return ValidateCooperativeVectorOuterProductNV(_, inst);
+    case spv::Op::OpCooperativeVectorReduceSumAccumulateNV:
+      return ValidateCooperativeVectorReduceSumNV(_, inst);
+    case spv::Op::OpCooperativeVectorMatrixMulNV:
+    case spv::Op::OpCooperativeVectorMatrixMulAddNV:
+      return ValidateCooperativeVectorMatrixMulNV(_, inst);
     case spv::Op::OpPtrEqual:
     case spv::Op::OpPtrNotEqual:
     case spv::Op::OpPtrDiff:
-      if (auto error = ValidatePtrComparison(_, inst)) return error;
-      break;
+      return ValidatePtrComparison(_, inst);
     case spv::Op::OpImageTexelPointer:
     case spv::Op::OpGenericPtrMemSemantics:
+      break;  // no validation currently
+    case spv::Op::OpSpecConstantOp: {
+      switch (inst->GetOperandAs<spv::Op>(2u)) {
+        case spv::Op::OpCooperativeMatrixLengthKHR:
+          return ValidateCooperativeMatrixLength(_, inst, true, 3);
+        case spv::Op::OpCooperativeMatrixLengthNV:
+          return ValidateCooperativeMatrixLength(_, inst, false, 3);
+        // TODO - Add AccesChains
+        default:
+          break;
+      }
+    }
+
     default:
       break;
   }
diff --git a/source/val/validate_memory_semantics.cpp b/source/val/validate_memory_semantics.cpp
index dab7b5a..1cee9f1 100644
--- a/source/val/validate_memory_semantics.cpp
+++ b/source/val/validate_memory_semantics.cpp
@@ -32,6 +32,9 @@
   uint32_t value = 0;
   std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(id);
 
+  const bool is_vulkan = spvIsVulkanEnv(_.context()->target_env) ||
+                         _.memory_model() == spv::MemoryModel::VulkanKHR;
+
   if (!is_int32) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << spvOpcodeString(opcode)
@@ -56,6 +59,21 @@
     return SPV_SUCCESS;
   }
 
+  if (value & uint32_t(spv::MemorySemanticsMask::UniformMemory) &&
+      !_.HasCapability(spv::Capability::Shader)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << spvOpcodeString(opcode)
+           << ": Memory Semantics UniformMemory requires capability Shader";
+  }
+
+  if (value & uint32_t(spv::MemorySemanticsMask::OutputMemoryKHR) &&
+      !_.HasCapability(spv::Capability::VulkanMemoryModel)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << spvOpcodeString(opcode)
+           << ": Memory Semantics OutputMemoryKHR requires capability "
+           << "VulkanMemoryModelKHR";
+  }
+
   const size_t num_memory_order_set_bits = spvtools::utils::CountSetBits(
       value & uint32_t(spv::MemorySemanticsMask::Acquire |
                        spv::MemorySemanticsMask::Release |
@@ -64,191 +82,207 @@
 
   if (num_memory_order_set_bits > 1) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << spvOpcodeString(opcode)
-           << ": Memory Semantics can have at most one of the following "
-              "bits "
-              "set: Acquire, Release, AcquireRelease or "
-              "SequentiallyConsistent";
+           << _.VkErrorID(10865) << spvOpcodeString(opcode)
+           << ": Memory Semantics must have at most one non-relaxed "
+              "memory order bit set";
   }
 
-  if (_.memory_model() == spv::MemoryModel::VulkanKHR &&
-      value & uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent)) {
+  if (is_vulkan &&
+      (value & uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent))) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "SequentiallyConsistent memory "
-              "semantics cannot be used with "
-              "the VulkanKHR memory model.";
+           << _.VkErrorID(10866) << spvOpcodeString(opcode)
+           << ": Memory Semantics with SequentiallyConsistent memory order "
+              "must not be used in the Vulkan API";
   }
 
-  if (value & uint32_t(spv::MemorySemanticsMask::MakeAvailableKHR) &&
-      !_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
+  if ((opcode == spv::Op::OpAtomicStore ||
+       opcode == spv::Op::OpAtomicFlagClear) &&
+      (value & uint32_t(spv::MemorySemanticsMask::Acquire) ||
+       value & uint32_t(spv::MemorySemanticsMask::AcquireRelease))) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << spvOpcodeString(opcode)
-           << ": Memory Semantics MakeAvailableKHR requires capability "
-           << "VulkanMemoryModelKHR";
+           << _.VkErrorID(10867) << spvOpcodeString(opcode)
+           << ": MemorySemantics must not use Acquire or AcquireRelease "
+              "memory order with "
+           << spvOpcodeString(opcode);
   }
 
-  if (value & uint32_t(spv::MemorySemanticsMask::MakeVisibleKHR) &&
-      !_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
+  if (opcode == spv::Op::OpAtomicLoad &&
+      (value & uint32_t(spv::MemorySemanticsMask::Release) ||
+       value & uint32_t(spv::MemorySemanticsMask::AcquireRelease))) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << spvOpcodeString(opcode)
-           << ": Memory Semantics MakeVisibleKHR requires capability "
-           << "VulkanMemoryModelKHR";
+           << _.VkErrorID(10868) << spvOpcodeString(opcode)
+           << ": MemorySemantics must not use Release or AcquireRelease "
+              "memory order with "
+           << spvOpcodeString(opcode);
   }
 
-  if (value & uint32_t(spv::MemorySemanticsMask::OutputMemoryKHR) &&
-      !_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
+  // In OpenCL, a relaxed fence has no effect but is not explicitly forbidden
+  if (is_vulkan && opcode == spv::Op::OpMemoryBarrier &&
+      !num_memory_order_set_bits) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << spvOpcodeString(opcode)
-           << ": Memory Semantics OutputMemoryKHR requires capability "
-           << "VulkanMemoryModelKHR";
+           << _.VkErrorID(10869) << spvOpcodeString(opcode)
+           << ": MemorySemantics must not use Relaxed memory order with "
+           << spvOpcodeString(opcode);
+  }
+
+  if (is_vulkan) {
+    const bool includes_storage_class =
+        value & uint32_t(spv::MemorySemanticsMask::UniformMemory |
+                         spv::MemorySemanticsMask::WorkgroupMemory |
+                         spv::MemorySemanticsMask::ImageMemory |
+                         spv::MemorySemanticsMask::OutputMemoryKHR);
+
+    if (num_memory_order_set_bits && !includes_storage_class) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << _.VkErrorID(10870) << spvOpcodeString(opcode)
+             << ": Memory Semantics with a non-relaxed memory order (Acquire, "
+                "Release, or AcquireRelease) must have at least one "
+                "Vulkan-supported storage class semantics bit set "
+                "(UniformMemory, WorkgroupMemory, ImageMemory, or "
+                "OutputMemory)";
+    }
+
+    if (!num_memory_order_set_bits && includes_storage_class) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << _.VkErrorID(10871) << spvOpcodeString(opcode)
+             << ": Memory Semantics with at least one Vulkan-supported "
+                "storage class semantics bit set (UniformMemory, "
+                "WorkgroupMemory, ImageMemory, or OutputMemory) must use "
+                "a non-relaxed memory order (Acquire, Release, or "
+                "AcquireRelease)";
+    }
+  }
+
+  if (value & uint32_t(spv::MemorySemanticsMask::MakeAvailableKHR)) {
+    if (!_.HasCapability(spv::Capability::VulkanMemoryModel)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << spvOpcodeString(opcode)
+             << ": Memory Semantics MakeAvailableKHR requires capability "
+             << "VulkanMemoryModelKHR";
+    }
+    if (!(value & uint32_t(spv::MemorySemanticsMask::Release |
+                           spv::MemorySemanticsMask::AcquireRelease))) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << _.VkErrorID(10872) << spvOpcodeString(opcode)
+             << ": Memory Semantics with MakeAvailable bit set must use "
+                "Release or AcquireRelease memory order";
+    }
+  }
+
+  if (value & uint32_t(spv::MemorySemanticsMask::MakeVisibleKHR)) {
+    if (!_.HasCapability(spv::Capability::VulkanMemoryModel)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << spvOpcodeString(opcode)
+             << ": Memory Semantics MakeVisibleKHR requires capability "
+             << "VulkanMemoryModelKHR";
+    }
+    if (!(value & uint32_t(spv::MemorySemanticsMask::Acquire |
+                           spv::MemorySemanticsMask::AcquireRelease))) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << _.VkErrorID(10873) << spvOpcodeString(opcode)
+             << ": Memory Semantics with MakeVisible bit set must use Acquire "
+                "or AcquireRelease memory order";
+    }
   }
 
   if (value & uint32_t(spv::MemorySemanticsMask::Volatile)) {
-    if (!_.HasCapability(spv::Capability::VulkanMemoryModelKHR)) {
+    if (!_.HasCapability(spv::Capability::VulkanMemoryModel)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << spvOpcodeString(opcode)
              << ": Memory Semantics Volatile requires capability "
                 "VulkanMemoryModelKHR";
     }
-
     if (!spvOpcodeIsAtomicOp(inst->opcode())) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Memory Semantics Volatile can only be used with atomic "
-                "instructions";
+             << _.VkErrorID(10874) << spvOpcodeString(opcode)
+             << ": Memory Semantics with Volatile bit set must not be used "
+                "with barrier instructions";
     }
   }
 
-  if (value & uint32_t(spv::MemorySemanticsMask::UniformMemory) &&
-      !_.HasCapability(spv::Capability::Shader)) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << spvOpcodeString(opcode)
-           << ": Memory Semantics UniformMemory requires capability Shader";
-  }
-
-  // Checking for spv::Capability::AtomicStorage is intentionally not done here.
-  // See https://github.com/KhronosGroup/glslang/issues/1618 for the reasoning
-  // why.
-
-  if (value & uint32_t(spv::MemorySemanticsMask::MakeAvailableKHR |
-                       spv::MemorySemanticsMask::MakeVisibleKHR)) {
-    const bool includes_storage_class =
-        value & uint32_t(spv::MemorySemanticsMask::UniformMemory |
-                         spv::MemorySemanticsMask::SubgroupMemory |
-                         spv::MemorySemanticsMask::WorkgroupMemory |
-                         spv::MemorySemanticsMask::CrossWorkgroupMemory |
-                         spv::MemorySemanticsMask::AtomicCounterMemory |
-                         spv::MemorySemanticsMask::ImageMemory |
-                         spv::MemorySemanticsMask::OutputMemoryKHR);
-
-    if (!includes_storage_class) {
+  if ((opcode == spv::Op::OpAtomicCompareExchange ||
+       opcode == spv::Op::OpAtomicCompareExchangeWeak) &&
+      operand_index == 5) {
+    if (value & uint32_t(spv::MemorySemanticsMask::Release) ||
+        value & uint32_t(spv::MemorySemanticsMask::AcquireRelease)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << spvOpcodeString(opcode)
-             << ": expected Memory Semantics to include a storage class";
+             << _.VkErrorID(10875) << spvOpcodeString(opcode)
+             << " Unequal Memory Semantics must not use Release or "
+                "AcquireRelease memory order";
     }
-  }
 
-  if (value & uint32_t(spv::MemorySemanticsMask::MakeVisibleKHR) &&
-      !(value & uint32_t(spv::MemorySemanticsMask::Acquire |
-                         spv::MemorySemanticsMask::AcquireRelease))) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << spvOpcodeString(opcode)
-           << ": MakeVisibleKHR Memory Semantics also requires either Acquire "
-              "or AcquireRelease Memory Semantics";
-  }
+    bool is_equal_int32 = false;
+    bool is_equal_const = false;
+    uint32_t equal_value = 0;
+    std::tie(is_equal_int32, is_equal_const, equal_value) =
+        _.EvalInt32IfConst(inst->GetOperandAs<uint32_t>(4));
 
-  if (value & uint32_t(spv::MemorySemanticsMask::MakeAvailableKHR) &&
-      !(value & uint32_t(spv::MemorySemanticsMask::Release |
-                         spv::MemorySemanticsMask::AcquireRelease))) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << spvOpcodeString(opcode)
-           << ": MakeAvailableKHR Memory Semantics also requires either "
-              "Release or AcquireRelease Memory Semantics";
-  }
+    const auto equal_mask_seq_cst =
+        uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent);
+    const auto equal_mask_acquire = uint32_t(
+        // Allow EqualMemorySemantics Release with UnequalMemorySemantics
+        // Acquire, since the C standard doesn't clearly forbid it.
+        spv::MemorySemanticsMask::SequentiallyConsistent |
+        spv::MemorySemanticsMask::AcquireRelease |
+        spv::MemorySemanticsMask::Release | spv::MemorySemanticsMask::Acquire);
 
-  if (spvIsVulkanEnv(_.context()->target_env)) {
-    const bool includes_storage_class =
-        value & uint32_t(spv::MemorySemanticsMask::UniformMemory |
-                         spv::MemorySemanticsMask::WorkgroupMemory |
-                         spv::MemorySemanticsMask::ImageMemory |
-                         spv::MemorySemanticsMask::OutputMemoryKHR);
-
-    if (opcode == spv::Op::OpMemoryBarrier && !num_memory_order_set_bits) {
+    if (((value & uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent)) &&
+         !(equal_value & equal_mask_seq_cst)) ||
+        ((value & uint32_t(spv::MemorySemanticsMask::Acquire)) &&
+         !(equal_value & equal_mask_acquire))) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << _.VkErrorID(4732) << spvOpcodeString(opcode)
-             << ": Vulkan specification requires Memory Semantics to have "
-                "one "
-                "of the following bits set: Acquire, Release, "
-                "AcquireRelease "
-                "or SequentiallyConsistent";
-    } else if (opcode != spv::Op::OpMemoryBarrier &&
-               num_memory_order_set_bits) {
-      // should leave only atomics and control barriers for Vulkan env
-      bool memory_is_int32 = false, memory_is_const_int32 = false;
-      uint32_t memory_value = 0;
-      std::tie(memory_is_int32, memory_is_const_int32, memory_value) =
-          _.EvalInt32IfConst(memory_scope);
-      if (memory_is_int32 &&
-          spv::Scope(memory_value) == spv::Scope::Invocation) {
+             << _.VkErrorID(10876) << spvOpcodeString(opcode)
+             << " Unequal Memory Semantics must not use a stronger memory "
+                "order than the corresponding Equal Memory Semantics";
+    }
+
+    if (is_vulkan) {
+      auto storage_class_semantics_mask =
+          uint32_t(spv::MemorySemanticsMask::UniformMemory |
+                   spv::MemorySemanticsMask::WorkgroupMemory |
+                   spv::MemorySemanticsMask::ImageMemory |
+                   spv::MemorySemanticsMask::OutputMemoryKHR);
+
+      if (value & ~equal_value & storage_class_semantics_mask) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << _.VkErrorID(4641) << spvOpcodeString(opcode)
-               << ": Vulkan specification requires Memory Semantics to be None "
-                  "if used with Invocation Memory Scope";
+               << _.VkErrorID(10877) << spvOpcodeString(opcode)
+               << " Unequal Memory Semantics must not have any "
+                  "Vulkan-supported storage class semantics bit set "
+                  "(UniformMemory, WorkgroupMemory, ImageMemory, or "
+                  "OutputMemory) unless this bit is also set in the "
+                  "corresponding Equal Memory Semantics";
+      }
+
+      if (value & ~equal_value &
+          uint32_t(spv::MemorySemanticsMask::MakeVisibleKHR)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << _.VkErrorID(10878) << spvOpcodeString(opcode)
+               << " Unequal Memory Semantics must not have MakeVisible bit set "
+                  "unless this bit is also set in the corresponding Equal "
+                  "Memory Semantics";
+      }
+
+      if ((equal_value & uint32_t(spv::MemorySemanticsMask::Volatile)) ^
+          (value & uint32_t(spv::MemorySemanticsMask::Volatile))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << _.VkErrorID(10879) << spvOpcodeString(opcode)
+               << " Unequal Memory Semantics must have Volatile bit set if and "
+                  "only if this bit is also set in the corresponding Equal "
+                  "Memory Semantics";
       }
     }
-
-    if (opcode == spv::Op::OpMemoryBarrier && !includes_storage_class) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << _.VkErrorID(4733) << spvOpcodeString(opcode)
-             << ": expected Memory Semantics to include a Vulkan-supported "
-                "storage class";
-    }
-
-    if (opcode == spv::Op::OpControlBarrier && value && !includes_storage_class) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << _.VkErrorID(4650) << spvOpcodeString(opcode)
-             << ": expected Memory Semantics to include a Vulkan-supported "
-                "storage class if Memory Semantics is not None";
-    }
   }
 
-  if (opcode == spv::Op::OpAtomicFlagClear &&
-      (value & uint32_t(spv::MemorySemanticsMask::Acquire) ||
-       value & uint32_t(spv::MemorySemanticsMask::AcquireRelease))) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Memory Semantics Acquire and AcquireRelease cannot be used "
-              "with "
-           << spvOpcodeString(opcode);
-  }
-
-  if (opcode == spv::Op::OpAtomicCompareExchange && operand_index == 5 &&
-      (value & uint32_t(spv::MemorySemanticsMask::Release) ||
-       value & uint32_t(spv::MemorySemanticsMask::AcquireRelease))) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << spvOpcodeString(opcode)
-           << ": Memory Semantics Release and AcquireRelease cannot be "
-              "used "
-              "for operand Unequal";
-  }
-
-  if (spvIsVulkanEnv(_.context()->target_env)) {
-    if (opcode == spv::Op::OpAtomicLoad &&
-        (value & uint32_t(spv::MemorySemanticsMask::Release) ||
-         value & uint32_t(spv::MemorySemanticsMask::AcquireRelease) ||
-         value & uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent))) {
+  if (is_vulkan && num_memory_order_set_bits) {
+    bool memory_is_int32 = false, memory_is_const_int32 = false;
+    uint32_t memory_value = 0;
+    std::tie(memory_is_int32, memory_is_const_int32, memory_value) =
+        _.EvalInt32IfConst(memory_scope);
+    if (memory_is_int32 && spv::Scope(memory_value) == spv::Scope::Invocation) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << _.VkErrorID(4731)
-             << "Vulkan spec disallows OpAtomicLoad with Memory Semantics "
-                "Release, AcquireRelease and SequentiallyConsistent";
-    }
-
-    if (opcode == spv::Op::OpAtomicStore &&
-        (value & uint32_t(spv::MemorySemanticsMask::Acquire) ||
-         value & uint32_t(spv::MemorySemanticsMask::AcquireRelease) ||
-         value & uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent))) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << _.VkErrorID(4730)
-             << "Vulkan spec disallows OpAtomicStore with Memory Semantics "
-                "Acquire, AcquireRelease and SequentiallyConsistent";
+             << _.VkErrorID(4641) << spvOpcodeString(opcode)
+             << ": Vulkan specification requires Memory Semantics to be "
+                "Relaxed if used with Invocation Memory Scope";
     }
   }
 
diff --git a/source/val/validate_mesh_shading.cpp b/source/val/validate_mesh_shading.cpp
index e569e25..dd9bb4a 100644
--- a/source/val/validate_mesh_shading.cpp
+++ b/source/val/validate_mesh_shading.cpp
@@ -14,7 +14,8 @@
 
 // Validates ray query instructions from SPV_KHR_ray_query
 
-#include "source/opcode.h"
+#include <string>
+
 #include "source/val/instruction.h"
 #include "source/val/validate.h"
 #include "source/val/validation_state.h"
@@ -22,96 +23,210 @@
 namespace spvtools {
 namespace val {
 
+bool IsInterfaceVariable(ValidationState_t& _, const Instruction* inst,
+                         spv::ExecutionModel model) {
+  bool foundInterface = false;
+  for (auto entry_point : _.entry_points()) {
+    const auto* models = _.GetExecutionModels(entry_point);
+    if (models->find(model) == models->end()) return false;
+    for (const auto& desc : _.entry_point_descriptions(entry_point)) {
+      for (auto interface : desc.interfaces) {
+        if (inst->id() == interface) {
+          foundInterface = true;
+          break;
+        }
+      }
+    }
+  }
+  return foundInterface;
+}
+
+spv_result_t ValidateEmitMeshTasks(ValidationState_t& _,
+                                   const Instruction* inst) {
+  _.function(inst->function()->id())
+      ->RegisterExecutionModelLimitation([](spv::ExecutionModel model,
+                                            std::string* message) {
+        if (model != spv::ExecutionModel::TaskEXT) {
+          if (message) {
+            *message = "OpEmitMeshTasksEXT requires TaskEXT execution model";
+          }
+          return false;
+        }
+        return true;
+      });
+
+  const uint32_t group_count_x = _.GetOperandTypeId(inst, 0);
+  if (!_.IsUnsignedIntScalarType(group_count_x) ||
+      _.GetBitWidth(group_count_x) != 32) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Group Count X must be a 32-bit unsigned int scalar";
+  }
+
+  const uint32_t group_count_y = _.GetOperandTypeId(inst, 1);
+  if (!_.IsUnsignedIntScalarType(group_count_y) ||
+      _.GetBitWidth(group_count_y) != 32) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Group Count Y must be a 32-bit unsigned int scalar";
+  }
+
+  const uint32_t group_count_z = _.GetOperandTypeId(inst, 2);
+  if (!_.IsUnsignedIntScalarType(group_count_z) ||
+      _.GetBitWidth(group_count_z) != 32) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Group Count Z must be a 32-bit unsigned int scalar";
+  }
+
+  if (inst->operands().size() == 4) {
+    const auto payload = _.FindDef(inst->GetOperandAs<uint32_t>(3));
+    if (payload->opcode() != spv::Op::OpVariable) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Payload must be the result of a OpVariable";
+    }
+    if (payload->GetOperandAs<spv::StorageClass>(2) !=
+        spv::StorageClass::TaskPayloadWorkgroupEXT) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Payload OpVariable must have a storage class of "
+                "TaskPayloadWorkgroupEXT";
+    }
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateSetMeshOutputs(ValidationState_t& _,
+                                    const Instruction* inst) {
+  _.function(inst->function()->id())
+      ->RegisterExecutionModelLimitation([](spv::ExecutionModel model,
+                                            std::string* message) {
+        if (model != spv::ExecutionModel::MeshEXT) {
+          if (message) {
+            *message = "OpSetMeshOutputsEXT requires MeshEXT execution model";
+          }
+          return false;
+        }
+        return true;
+      });
+
+  const uint32_t vertex_count = _.GetOperandTypeId(inst, 0);
+  if (!_.IsUnsignedIntScalarType(vertex_count) ||
+      _.GetBitWidth(vertex_count) != 32) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Vertex Count must be a 32-bit unsigned int scalar";
+  }
+
+  const uint32_t primitive_count = _.GetOperandTypeId(inst, 1);
+  if (!_.IsUnsignedIntScalarType(primitive_count) ||
+      _.GetBitWidth(primitive_count) != 32) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Primitive Count must be a 32-bit unsigned int scalar";
+  }
+
+  // Will only validate if constants are used (or spec constant frozen)
+  uint64_t vertex_count_value = 0;
+  if (_.EvalConstantValUint64(inst->GetOperandAs<uint32_t>(0),
+                              &vertex_count_value)) {
+    _.function(inst->function()->id())
+        ->RegisterLimitation(
+            [vertex_count_value](const ValidationState_t& state,
+                                 const Function* entry_point,
+                                 std::string* message) {
+              const uint32_t output_vertices =
+                  state.GetOutputVertices(entry_point->id());
+              if (vertex_count_value > output_vertices) {
+                *message =
+                    "OpSetMeshOutputsEXT Vertex Count (" +
+                    std::to_string(vertex_count_value) +
+                    ") is larger than the OutputVertices in OpExecutionMode (" +
+                    std::to_string(output_vertices) + ").";
+                return false;
+              }
+              return true;
+            });
+  }
+  uint64_t primitive_count_value = 0;
+  if (_.EvalConstantValUint64(inst->GetOperandAs<uint32_t>(1),
+                              &primitive_count_value)) {
+    _.function(inst->function()->id())
+        ->RegisterLimitation(
+            [primitive_count_value](const ValidationState_t& state,
+                                    const Function* entry_point,
+                                    std::string* message) {
+              const uint32_t output_primitives =
+                  state.GetOutputPrimitivesEXT(entry_point->id());
+              if (primitive_count_value > output_primitives) {
+                *message = "OpSetMeshOutputsEXT Primitive Count (" +
+                           std::to_string(primitive_count_value) +
+                           ") is larger than the OutputPrimitivesEXT in "
+                           "OpExecutionMode (" +
+                           std::to_string(output_primitives) + ").";
+                return false;
+              }
+              return true;
+            });
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateMeshVariable(ValidationState_t& _,
+                                  const Instruction* inst) {
+  if (!_.HasCapability(spv::Capability::MeshShadingEXT)) {
+    return SPV_SUCCESS;
+  }
+  bool is_mesh_interface_var =
+      IsInterfaceVariable(_, inst, spv::ExecutionModel::MeshEXT);
+  bool is_frag_interface_var =
+      IsInterfaceVariable(_, inst, spv::ExecutionModel::Fragment);
+
+  const spv::StorageClass storage_class =
+      inst->GetOperandAs<spv::StorageClass>(2);
+  bool storage_output = (storage_class == spv::StorageClass::Output);
+  bool storage_input = (storage_class == spv::StorageClass::Input);
+
+  if (_.HasDecoration(inst->id(), spv::Decoration::PerPrimitiveEXT)) {
+    if (is_frag_interface_var && !storage_input) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "PerPrimitiveEXT decoration must be applied only to "
+                "variables in the Input Storage Class in the Fragment "
+                "Execution Model.";
+    }
+
+    if (is_mesh_interface_var && !storage_output) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << _.VkErrorID(4336)
+             << "PerPrimitiveEXT decoration must be applied only to "
+                "variables in the Output Storage Class in the "
+                "Storage Class in the MeshEXT Execution Model.";
+    }
+  }
+
+  // This only applies to user interface variables, not built-ins (they
+  // are validated with the rest of the builtin)
+  if (is_mesh_interface_var && storage_output &&
+      !_.HasDecoration(inst->id(), spv::Decoration::BuiltIn)) {
+    const Instruction* pointer_inst = _.FindDef(inst->type_id());
+    if (pointer_inst->opcode() == spv::Op::OpTypePointer) {
+      if (!_.IsArrayType(pointer_inst->word(3))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "In the MeshEXT Execution Mode, all Output Variables "
+                  "must contain an Array.";
+      }
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
 spv_result_t MeshShadingPass(ValidationState_t& _, const Instruction* inst) {
   const spv::Op opcode = inst->opcode();
   switch (opcode) {
-    case spv::Op::OpEmitMeshTasksEXT: {
-      _.function(inst->function()->id())
-          ->RegisterExecutionModelLimitation(
-              [](spv::ExecutionModel model, std::string* message) {
-                if (model != spv::ExecutionModel::TaskEXT) {
-                  if (message) {
-                    *message =
-                        "OpEmitMeshTasksEXT requires TaskEXT execution model";
-                  }
-                  return false;
-                }
-                return true;
-              });
-
-      const uint32_t group_count_x = _.GetOperandTypeId(inst, 0);
-      if (!_.IsUnsignedIntScalarType(group_count_x) ||
-          _.GetBitWidth(group_count_x) != 32) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Group Count X must be a 32-bit unsigned int scalar";
-      }
-
-      const uint32_t group_count_y = _.GetOperandTypeId(inst, 1);
-      if (!_.IsUnsignedIntScalarType(group_count_y) ||
-          _.GetBitWidth(group_count_y) != 32) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Group Count Y must be a 32-bit unsigned int scalar";
-      }
-
-      const uint32_t group_count_z = _.GetOperandTypeId(inst, 2);
-      if (!_.IsUnsignedIntScalarType(group_count_z) ||
-          _.GetBitWidth(group_count_z) != 32) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Group Count Z must be a 32-bit unsigned int scalar";
-      }
-
-      if (inst->operands().size() == 4) {
-        const auto payload = _.FindDef(inst->GetOperandAs<uint32_t>(3));
-        if (payload->opcode() != spv::Op::OpVariable) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Payload must be the result of a OpVariable";
-        }
-        if (payload->GetOperandAs<spv::StorageClass>(2) !=
-            spv::StorageClass::TaskPayloadWorkgroupEXT) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << "Payload OpVariable must have a storage class of "
-                    "TaskPayloadWorkgroupEXT";
-        }
-      }
-      break;
-    }
-
-    case spv::Op::OpSetMeshOutputsEXT: {
-      _.function(inst->function()->id())
-          ->RegisterExecutionModelLimitation(
-              [](spv::ExecutionModel model, std::string* message) {
-                if (model != spv::ExecutionModel::MeshEXT) {
-                  if (message) {
-                    *message =
-                        "OpSetMeshOutputsEXT requires MeshEXT execution model";
-                  }
-                  return false;
-                }
-                return true;
-              });
-
-      const uint32_t vertex_count = _.GetOperandTypeId(inst, 0);
-      if (!_.IsUnsignedIntScalarType(vertex_count) ||
-          _.GetBitWidth(vertex_count) != 32) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Vertex Count must be a 32-bit unsigned int scalar";
-      }
-
-      const uint32_t primitive_count = _.GetOperandTypeId(inst, 1);
-      if (!_.IsUnsignedIntScalarType(primitive_count) ||
-          _.GetBitWidth(primitive_count) != 32) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Primitive Count must be a 32-bit unsigned int scalar";
-      }
-
-      break;
-    }
-
-    case spv::Op::OpWritePackedPrimitiveIndices4x8NV: {
-      // No validation rules (for the moment).
-      break;
-    }
-
+    case spv::Op::OpEmitMeshTasksEXT:
+      return ValidateEmitMeshTasks(_, inst);
+    case spv::Op::OpSetMeshOutputsEXT:
+      return ValidateSetMeshOutputs(_, inst);
+    case spv::Op::OpVariable:
+      return ValidateMeshVariable(_, inst);
+    // No validation rules (for the moment).
+    case spv::Op::OpWritePackedPrimitiveIndices4x8NV:
     default:
       break;
   }
diff --git a/source/val/validate_misc.cpp b/source/val/validate_misc.cpp
index a404134..99087d6 100644
--- a/source/val/validate_misc.cpp
+++ b/source/val/validate_misc.cpp
@@ -13,11 +13,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "source/val/validate.h"
-
 #include "source/opcode.h"
 #include "source/spirv_target_env.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validate_scopes.h"
 #include "source/val/validation_state.h"
 
@@ -113,6 +112,22 @@
   return SPV_SUCCESS;
 }
 
+spv_result_t ValidateAbort(ValidationState_t& _, const Instruction* inst) {
+  const auto message_type = _.FindDef(inst->GetOperandAs<uint32_t>(0u));
+  const auto source = _.FindDef(inst->GetOperandAs<uint32_t>(1u));
+  const auto source_type = _.FindDef(source->type_id());
+
+  if (source_type == message_type) return SPV_SUCCESS;
+
+  if (!_.LogicallyMatch(source_type, message_type, false)) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Type of Message operand does not logically match the type of "
+              "the Message Type operand";
+  }
+
+  return SPV_SUCCESS;
+}
+
 }  // namespace
 
 spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst) {
@@ -203,6 +218,11 @@
         return error;
       }
       break;
+    case spv::Op::OpAbortKHR:
+      if (auto error = ValidateAbort(_, inst)) {
+        return error;
+      }
+      break;
     default:
       break;
   }
diff --git a/source/val/validate_mode_setting.cpp b/source/val/validate_mode_setting.cpp
index 8502fda..88392d2 100644
--- a/source/val/validate_mode_setting.cpp
+++ b/source/val/validate_mode_setting.cpp
@@ -1,4 +1,6 @@
 // Copyright (c) 2018 Google LLC.
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -16,6 +18,7 @@
 
 #include "source/opcode.h"
 #include "source/spirv_target_env.h"
+#include "source/table2.h"
 #include "source/val/instruction.h"
 #include "source/val/validate.h"
 #include "source/val/validation_state.h"
@@ -24,6 +27,48 @@
 namespace val {
 namespace {
 
+// TODO - Make a common util if someone else needs it too outside this file
+const char* ExecutionModelToString(spv::ExecutionModel value) {
+  switch (value) {
+    case spv::ExecutionModel::Vertex:
+      return "Vertex";
+    case spv::ExecutionModel::TessellationControl:
+      return "TessellationControl";
+    case spv::ExecutionModel::TessellationEvaluation:
+      return "TessellationEvaluation";
+    case spv::ExecutionModel::Geometry:
+      return "Geometry";
+    case spv::ExecutionModel::Fragment:
+      return "Fragment";
+    case spv::ExecutionModel::GLCompute:
+      return "GLCompute";
+    case spv::ExecutionModel::Kernel:
+      return "Kernel";
+    case spv::ExecutionModel::TaskNV:
+      return "TaskNV";
+    case spv::ExecutionModel::MeshNV:
+      return "MeshNV";
+    case spv::ExecutionModel::RayGenerationKHR:
+      return "RayGenerationKHR";
+    case spv::ExecutionModel::IntersectionKHR:
+      return "IntersectionKHR";
+    case spv::ExecutionModel::AnyHitKHR:
+      return "AnyHitKHR";
+    case spv::ExecutionModel::ClosestHitKHR:
+      return "ClosestHitKHR";
+    case spv::ExecutionModel::MissKHR:
+      return "MissKHR";
+    case spv::ExecutionModel::CallableKHR:
+      return "CallableKHR";
+    case spv::ExecutionModel::TaskEXT:
+      return "TaskEXT";
+    case spv::ExecutionModel::MeshEXT:
+      return "MeshEXT";
+    default:
+      return "Unknown";
+  }
+}
+
 spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) {
   const auto entry_point_id = inst->GetOperandAs<uint32_t>(1);
   auto entry_point = _.FindDef(entry_point_id);
@@ -56,20 +101,22 @@
   }
 
   const auto* execution_modes = _.GetExecutionModes(entry_point_id);
+  auto has_mode = [&execution_modes](spv::ExecutionMode mode) {
+    return execution_modes && execution_modes->count(mode);
+  };
+
   if (_.HasCapability(spv::Capability::Shader)) {
     switch (execution_model) {
       case spv::ExecutionModel::Fragment:
-        if (execution_modes &&
-            execution_modes->count(spv::ExecutionMode::OriginUpperLeft) &&
-            execution_modes->count(spv::ExecutionMode::OriginLowerLeft)) {
+        if (has_mode(spv::ExecutionMode::OriginUpperLeft) &&
+            has_mode(spv::ExecutionMode::OriginLowerLeft)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << "Fragment execution model entry points can only specify "
                     "one of OriginUpperLeft or OriginLowerLeft execution "
                     "modes.";
         }
-        if (!execution_modes ||
-            (!execution_modes->count(spv::ExecutionMode::OriginUpperLeft) &&
-             !execution_modes->count(spv::ExecutionMode::OriginLowerLeft))) {
+        if (!has_mode(spv::ExecutionMode::OriginUpperLeft) &&
+            !has_mode(spv::ExecutionMode::OriginLowerLeft)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << "Fragment execution model entry points require either an "
                     "OriginUpperLeft or OriginLowerLeft execution mode.";
@@ -282,40 +329,95 @@
     }
   }
 
+  bool has_workgroup_size = false;
+  bool has_local_size_id = false;
+  for (auto& i : _.ordered_instructions()) {
+    if (i.opcode() == spv::Op::OpFunction) break;
+    if (i.opcode() == spv::Op::OpDecorate && i.operands().size() > 2) {
+      if (i.GetOperandAs<spv::Decoration>(1) == spv::Decoration::BuiltIn &&
+          i.GetOperandAs<spv::BuiltIn>(2) == spv::BuiltIn::WorkgroupSize) {
+        has_workgroup_size = true;
+      }
+    }
+    if (i.opcode() == spv::Op::OpExecutionModeId) {
+      if (i.GetOperandAs<spv::ExecutionMode>(1) ==
+          spv::ExecutionMode::LocalSizeId) {
+        has_local_size_id = true;
+      }
+    }
+  }
+
   if (spvIsVulkanEnv(_.context()->target_env)) {
+    // SPV_QCOM_tile_shading checks
+    if (execution_model == spv::ExecutionModel::GLCompute) {
+      if (_.HasCapability(spv::Capability::TileShadingQCOM)) {
+        if (has_mode(spv::ExecutionMode::TileShadingRateQCOM) &&
+            (has_mode(spv::ExecutionMode::LocalSize) ||
+             has_mode(spv::ExecutionMode::LocalSizeId))) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "If the TileShadingRateQCOM execution mode is used, "
+                 << "LocalSize and LocalSizeId must not be specified.";
+        }
+        if (has_mode(spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "The NonCoherentTileAttachmentQCOM execution mode must "
+                    "not be used in any stage other than fragment.";
+        }
+      } else {
+        if (has_mode(spv::ExecutionMode::TileShadingRateQCOM)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "If the TileShadingRateQCOM execution mode is used, the "
+                    "TileShadingQCOM capability must be enabled.";
+        }
+      }
+    } else {
+      if (has_mode(spv::ExecutionMode::TileShadingRateQCOM)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "The TileShadingRateQCOM execution mode must not be used "
+                  "in any stage other than compute.";
+      }
+      if (execution_model != spv::ExecutionModel::Fragment) {
+        if (has_mode(spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "The NonCoherentTileAttachmentQCOM execution mode must "
+                    "not be used in any stage other than fragment.";
+        }
+        if (_.HasCapability(spv::Capability::TileShadingQCOM)) {
+          return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
+                 << "The TileShadingQCOM capability must not be enabled in "
+                    "any stage other than compute or fragment.";
+        }
+      } else {
+        if (has_mode(spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) {
+          if (!_.HasCapability(spv::Capability::TileShadingQCOM)) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << "If the NonCoherentTileAttachmentReadQCOM execution "
+                      "mode is used, the TileShadingQCOM capability must be "
+                      "enabled.";
+          }
+        }
+      }
+    }
+
     switch (execution_model) {
       case spv::ExecutionModel::GLCompute:
-        if (!execution_modes ||
-            !execution_modes->count(spv::ExecutionMode::LocalSize)) {
-          bool ok = false;
-          for (auto& i : _.ordered_instructions()) {
-            if (i.opcode() == spv::Op::OpDecorate) {
-              if (i.operands().size() > 2) {
-                if (i.GetOperandAs<spv::Decoration>(1) ==
-                        spv::Decoration::BuiltIn &&
-                    i.GetOperandAs<spv::BuiltIn>(2) ==
-                        spv::BuiltIn::WorkgroupSize) {
-                  ok = true;
-                  break;
-                }
-              }
-            }
-            if (i.opcode() == spv::Op::OpExecutionModeId) {
-              const auto mode = i.GetOperandAs<spv::ExecutionMode>(1);
-              if (mode == spv::ExecutionMode::LocalSizeId) {
-                ok = true;
-                break;
-              }
-            }
-          }
-          if (!ok) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << _.VkErrorID(6426)
-                   << "In the Vulkan environment, GLCompute execution model "
-                      "entry points require either the LocalSize or "
-                      "LocalSizeId execution mode or an object decorated with "
-                      "WorkgroupSize must be specified.";
-          }
+      case spv::ExecutionModel::MeshEXT:
+      case spv::ExecutionModel::MeshNV:
+      case spv::ExecutionModel::TaskEXT:
+      case spv::ExecutionModel::TaskNV:
+        if (!has_mode(spv::ExecutionMode::LocalSize) && !has_workgroup_size &&
+            !has_local_size_id &&
+            !has_mode(spv::ExecutionMode::TileShadingRateQCOM)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << _.VkErrorID(10685) << "In the Vulkan environment, "
+                 << ExecutionModelToString(execution_model)
+                 << " execution model "
+                    "entry points require either the "
+                 << (_.HasCapability(spv::Capability::TileShadingQCOM)
+                         ? "TileShadingRateQCOM, "
+                         : "")
+                 << "LocalSize or LocalSizeId execution mode or an object "
+                    "decorated with WorkgroupSize must be specified.";
         }
         break;
       default:
@@ -323,6 +425,100 @@
     }
   }
 
+  // WorkgroupSize decoration takes precedence over any LocalSize or LocalSizeId
+  // execution mode, so the values can be ignored
+  if (_.EntryPointHasLocalSizeOrId(entry_point_id) && !has_workgroup_size) {
+    const Instruction* local_size_inst =
+        _.EntryPointLocalSizeOrId(entry_point_id);
+    if (local_size_inst) {
+      const auto mode = local_size_inst->GetOperandAs<spv::ExecutionMode>(1);
+      const uint32_t operand_x = local_size_inst->GetOperandAs<uint32_t>(2);
+      const uint32_t operand_y = local_size_inst->GetOperandAs<uint32_t>(3);
+      const uint32_t operand_z = local_size_inst->GetOperandAs<uint32_t>(4);
+      if (mode == spv::ExecutionMode::LocalSize) {
+        const uint64_t product_size = operand_x * operand_y * operand_z;
+        if (product_size == 0) {
+          return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst)
+                 << "Local Size execution mode must not have a product of zero "
+                    "(X "
+                    "= "
+                 << operand_x << ", Y = " << operand_y << ", Z = " << operand_z
+                 << ").";
+        }
+        if (has_mode(spv::ExecutionMode::DerivativeGroupQuadsKHR)) {
+          if (operand_x % 2 != 0 || operand_y % 2 != 0) {
+            return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst)
+                   << _.VkErrorID(10151)
+                   << "Local Size execution mode dimensions is "
+                      "(X = "
+                   << operand_x << ", Y = " << operand_y
+                   << ") but Entry Point id " << entry_point_id
+                   << " also has an DerivativeGroupQuadsKHR execution mode, so "
+                      "both dimensions must be a multiple of 2";
+          }
+        }
+        if (has_mode(spv::ExecutionMode::DerivativeGroupLinearKHR)) {
+          if (product_size % 4 != 0) {
+            return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst)
+                   << _.VkErrorID(10152)
+                   << "Local Size execution mode dimensions is (X = "
+                   << operand_x << ", Y = " << operand_y
+                   << ", Z = " << operand_z << ") but Entry Point id "
+                   << entry_point_id
+                   << " also has an DerivativeGroupLinearKHR execution mode, "
+                      "so "
+                      "the product ("
+                   << product_size << ") must be a multiple of 4";
+          }
+        }
+      } else if (mode == spv::ExecutionMode::LocalSizeId) {
+        // can only validate product if static and not spec constant
+        // (This is done for us in EvalConstantValUint64)
+        uint64_t x_size, y_size, z_size;
+        bool static_x = _.EvalConstantValUint64(operand_x, &x_size);
+        bool static_y = _.EvalConstantValUint64(operand_y, &y_size);
+        bool static_z = _.EvalConstantValUint64(operand_z, &z_size);
+        if (static_x && static_y && static_z) {
+          const uint64_t product_size = x_size * y_size * z_size;
+          if (product_size == 0) {
+            return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst)
+                   << "LocalSizeId execution mode must not have a product of "
+                      "zero "
+                      "(X = "
+                   << x_size << ", Y = " << y_size << ", Z = " << z_size
+                   << ").";
+          }
+          if (has_mode(spv::ExecutionMode::DerivativeGroupQuadsKHR)) {
+            if (x_size % 2 != 0 || y_size % 2 != 0) {
+              return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst)
+                     << _.VkErrorID(10151)
+                     << "LocalSizeId execution mode dimensions is "
+                        "(X = "
+                     << x_size << ", Y = " << y_size << ") but Entry Point id "
+                     << entry_point_id
+                     << " also has an DerivativeGroupQuadsKHR execution mode, "
+                        "so "
+                        "both dimensions must be a multiple of 2";
+            }
+          }
+          if (has_mode(spv::ExecutionMode::DerivativeGroupLinearKHR)) {
+            if (product_size % 4 != 0) {
+              return _.diag(SPV_ERROR_INVALID_DATA, local_size_inst)
+                     << _.VkErrorID(10152)
+                     << "LocalSizeId execution mode dimensions is (X = "
+                     << x_size << ", Y = " << y_size << ", Z = " << z_size
+                     << ") but Entry Point id " << entry_point_id
+                     << " also has an DerivativeGroupLinearKHR execution mode, "
+                        "so "
+                        "the product ("
+                     << product_size << ") must be a multiple of 4";
+            }
+          }
+        }
+      }
+    }
+  }
+
   return SPV_SUCCESS;
 }
 
@@ -347,6 +543,12 @@
       case spv::ExecutionMode::LocalSizeId:
       case spv::ExecutionMode::FPFastMathDefault:
       case spv::ExecutionMode::MaximumRegistersIdINTEL:
+      case spv::ExecutionMode::IsApiEntryAMDX:
+      case spv::ExecutionMode::MaxNodeRecursionAMDX:
+      case spv::ExecutionMode::MaxNumWorkgroupsAMDX:
+      case spv::ExecutionMode::ShaderIndexAMDX:
+      case spv::ExecutionMode::SharesInputWithAMDX:
+      case spv::ExecutionMode::StaticNumWorkgroupsAMDX:
         valid_mode = true;
         break;
       default:
@@ -368,6 +570,12 @@
         case spv::ExecutionMode::SubgroupsPerWorkgroupId:
         case spv::ExecutionMode::LocalSizeHintId:
         case spv::ExecutionMode::LocalSizeId:
+        case spv::ExecutionMode::IsApiEntryAMDX:
+        case spv::ExecutionMode::MaxNodeRecursionAMDX:
+        case spv::ExecutionMode::MaxNumWorkgroupsAMDX:
+        case spv::ExecutionMode::ShaderIndexAMDX:
+        case spv::ExecutionMode::SharesInputWithAMDX:
+        case spv::ExecutionMode::StaticNumWorkgroupsAMDX:
           if (!spvOpcodeIsConstant(operand_inst->opcode())) {
             return _.diag(SPV_ERROR_INVALID_ID, inst)
                    << "For OpExecutionModeId all Extra Operand ids must be "
@@ -426,13 +634,20 @@
   } else if (mode == spv::ExecutionMode::SubgroupsPerWorkgroupId ||
              mode == spv::ExecutionMode::LocalSizeHintId ||
              mode == spv::ExecutionMode::LocalSizeId ||
-             mode == spv::ExecutionMode::FPFastMathDefault) {
+             mode == spv::ExecutionMode::FPFastMathDefault ||
+             mode == spv::ExecutionMode::IsApiEntryAMDX ||
+             mode == spv::ExecutionMode::MaxNodeRecursionAMDX ||
+             mode == spv::ExecutionMode::MaxNumWorkgroupsAMDX ||
+             mode == spv::ExecutionMode::ShaderIndexAMDX ||
+             mode == spv::ExecutionMode::SharesInputWithAMDX ||
+             mode == spv::ExecutionMode::StaticNumWorkgroupsAMDX) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "OpExecutionMode is only valid when the Mode operand is an "
               "execution mode that takes no Extra Operands, or takes Extra "
               "Operands that are not id operands.";
   }
 
+  const bool is_vulkan_env = (spvIsVulkanEnv(_.context()->target_env));
   const auto* models = _.GetExecutionModels(entry_point_id);
   switch (mode) {
     case spv::ExecutionMode::Invocations:
@@ -543,6 +758,15 @@
                     "tessellation execution model.";
         }
       }
+      if (is_vulkan_env) {
+        if (_.HasCapability(spv::Capability::MeshShadingEXT) &&
+            inst->GetOperandAs<uint32_t>(2) == 0) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << _.VkErrorID(7330)
+                 << "In mesh shaders using the MeshEXT Execution Model the "
+                    "OutputVertices Execution Mode must be greater than 0";
+        }
+      }
       break;
     case spv::ExecutionMode::OutputLinesEXT:
     case spv::ExecutionMode::OutputTrianglesEXT:
@@ -557,6 +781,15 @@
                   "execution "
                   "model.";
       }
+      if (mode == spv::ExecutionMode::OutputPrimitivesEXT && is_vulkan_env) {
+        if (_.HasCapability(spv::Capability::MeshShadingEXT) &&
+            inst->GetOperandAs<uint32_t>(2) == 0) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << _.VkErrorID(7331)
+                 << "In mesh shaders using the MeshEXT Execution Model the "
+                    "OutputPrimitivesEXT Execution Mode must be greater than 0";
+        }
+      }
       break;
     case spv::ExecutionMode::QuadDerivativesKHR:
       if (!std::all_of(models->begin(), models->end(),
@@ -577,6 +810,7 @@
     case spv::ExecutionMode::DepthGreater:
     case spv::ExecutionMode::DepthLess:
     case spv::ExecutionMode::DepthUnchanged:
+    case spv::ExecutionMode::StencilRefReplacingEXT:
     case spv::ExecutionMode::NonCoherentColorAttachmentReadEXT:
     case spv::ExecutionMode::NonCoherentDepthAttachmentReadEXT:
     case spv::ExecutionMode::NonCoherentStencilAttachmentReadEXT:
@@ -586,6 +820,7 @@
     case spv::ExecutionMode::SampleInterlockUnorderedEXT:
     case spv::ExecutionMode::ShadingRateInterlockOrderedEXT:
     case spv::ExecutionMode::ShadingRateInterlockUnorderedEXT:
+    case spv::ExecutionMode::PostDepthCoverage:
     case spv::ExecutionMode::EarlyAndLateFragmentTestsAMD:
     case spv::ExecutionMode::StencilRefUnchangedFrontAMD:
     case spv::ExecutionMode::StencilRefGreaterFrontAMD:
@@ -618,9 +853,15 @@
       break;
     case spv::ExecutionMode::LocalSize:
     case spv::ExecutionMode::LocalSizeId:
-      if (mode == spv::ExecutionMode::LocalSizeId && !_.IsLocalSizeIdAllowed())
+      if (mode == spv::ExecutionMode::LocalSizeId &&
+          !_.IsLocalSizeIdAllowed()) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "LocalSizeId mode is not allowed by the current environment.";
+               << "LocalSizeId mode is not allowed by the current environment."
+               << (is_vulkan_env
+                       ? _.MissingFeature("maintenance4 feature",
+                                          "--allow-localsizeid", false)
+                       : "");
+      }
 
       if (!std::all_of(
               models->begin(), models->end(),
@@ -669,7 +910,7 @@
     }
   }
 
-  if (spvIsVulkanEnv(_.context()->target_env)) {
+  if (is_vulkan_env) {
     if (mode == spv::ExecutionMode::OriginLowerLeft) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << _.VkErrorID(4653)
@@ -682,6 +923,14 @@
              << "In the Vulkan environment, the PixelCenterInteger execution "
                 "mode must not be used.";
     }
+    if (mode == spv::ExecutionMode::TileShadingRateQCOM) {
+      const auto rateX = inst->GetOperandAs<int>(2);
+      const auto rateY = inst->GetOperandAs<int>(3);
+      if ((rateX & (rateX - 1)) != 0 || (rateY & (rateY - 1)) != 0)
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "The TileShadingRateQCOM execution mode's x and y values "
+                  "must be powers of 2.";
+    }
   }
 
   return SPV_SUCCESS;
@@ -743,6 +992,20 @@
   }
 }
 
+spv_result_t ValidateCapability(ValidationState_t& _, const Instruction* inst) {
+  auto cap = inst->GetOperandAs<spv::Capability>(0);
+  if (cap == spv::Capability::CooperativeMatrixKHR) {
+    if (_.HasCapability(spv::Capability::Shader) &&
+        !_.HasCapability(spv::Capability::VulkanMemoryModel)) {
+      return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
+             << "If the Shader and CooperativeMatrixKHR capabilities are "
+                "declared, the VulkanMemoryModel capability must also be "
+                "declared";
+    }
+  }
+  return SPV_SUCCESS;
+}
+
 }  // namespace
 
 spv_result_t ValidateFloatControls2(ValidationState_t& _) {
@@ -821,6 +1084,9 @@
     case spv::Op::OpMemoryModel:
       if (auto error = ValidateMemoryModel(_, inst)) return error;
       break;
+    case spv::Op::OpCapability:
+      if (auto error = ValidateCapability(_, inst)) return error;
+      break;
     default:
       break;
   }
@@ -833,12 +1099,12 @@
   std::set<PerEntryKey> seen_per_entry;
   std::set<PerOperandKey> seen_per_operand;
 
-  const auto lookupMode = [&_](spv::ExecutionMode mode) -> std::string {
-    spv_operand_desc desc = nullptr;
-    if (_.grammar().lookupOperand(SPV_OPERAND_TYPE_EXECUTION_MODE,
-                                  static_cast<uint32_t>(mode),
-                                  &desc) == SPV_SUCCESS) {
-      return std::string(desc->name);
+  const auto lookupMode = [](spv::ExecutionMode mode) -> std::string {
+    const spvtools::OperandDesc* desc = nullptr;
+    if (spvtools::LookupOperand(SPV_OPERAND_TYPE_EXECUTION_MODE,
+                                static_cast<uint32_t>(mode),
+                                &desc) == SPV_SUCCESS) {
+      return std::string(desc->name().data());
     }
     return "Unknown";
   };
diff --git a/source/val/validate_non_uniform.cpp b/source/val/validate_non_uniform.cpp
index fd422a5..9711865 100644
--- a/source/val/validate_non_uniform.cpp
+++ b/source/val/validate_non_uniform.cpp
@@ -130,7 +130,7 @@
     if (!spvOpcodeIsConstant(id_op)) {
       std::string operand = GetOperandName(inst->opcode());
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Before SPIR-V 1.5, " << operand
+             << "In SPIR-V 1.4 or earlier, " << operand
              << " must be a constant instruction";
     }
   }
diff --git a/source/val/validate_pipe.cpp b/source/val/validate_pipe.cpp
new file mode 100644
index 0000000..2890272
--- /dev/null
+++ b/source/val/validate_pipe.cpp
@@ -0,0 +1,353 @@
+// Copyright (c) 2026 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.
+
+// Validates correctness of Pipe SPIR-V instructions.
+
+#include "source/val/instruction.h"
+#include "source/val/validate.h"
+#include "source/val/validate_scopes.h"
+#include "source/val/validation_state.h"
+#include "spirv/unified1/spirv.hpp11"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+enum class ValidPipeType {
+  READ_ONLY,
+  WRITE_ONLY,
+  READ_OR_WRITE,  // still excludes Read AND Write
+};
+
+spv_result_t ValidatePipeType(ValidationState_t& _, const Instruction* inst,
+                              uint32_t operand, ValidPipeType valid_pt) {
+  const Instruction* pipe_type = _.FindDef(_.GetOperandTypeId(inst, operand));
+  if (pipe_type->opcode() != spv::Op::OpTypePipe) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Pipe must be a type of OpTypePipe.";
+  }
+
+  const auto access_qualifier =
+      pipe_type->GetOperandAs<spv::AccessQualifier>(1);
+  if (valid_pt == ValidPipeType::READ_ONLY) {
+    if (access_qualifier != spv::AccessQualifier::ReadOnly) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Pipe must have a OpTypePipe with ReadOnly access qualifier.";
+    }
+  } else if (valid_pt == ValidPipeType::WRITE_ONLY) {
+    if (access_qualifier != spv::AccessQualifier::WriteOnly) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Pipe must have a OpTypePipe with WriteOnly access qualifier.";
+    }
+  } else if (valid_pt == ValidPipeType::READ_OR_WRITE) {
+    if (access_qualifier != spv::AccessQualifier::ReadOnly &&
+        access_qualifier != spv::AccessQualifier::WriteOnly) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Pipe must have a OpTypePipe with ReadOnly or WriteOnly access "
+                "qualifier.";
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidatePacketSizeAlign(ValidationState_t& _,
+                                     const Instruction* inst,
+                                     uint32_t size_operand,
+                                     uint32_t alignment_operand) {
+  const uint32_t packet_size_id = _.GetOperandTypeId(inst, size_operand);
+  if (!_.IsIntScalarType(packet_size_id, 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Packet Size must be a 32-bit scalar integer.";
+  }
+
+  const uint32_t packet_alignment_id =
+      _.GetOperandTypeId(inst, alignment_operand);
+  if (!_.IsIntScalarType(packet_alignment_id, 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Packet Alignment must be a 32-bit scalar integer.";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateReadWritePipe(ValidationState_t& _,
+                                   const Instruction* inst) {
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsIntScalarType(result_type, 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result Type must be a 32-bit int scalar.";
+  }
+
+  if (inst->opcode() == spv::Op::OpReadPipe) {
+    if (auto error = ValidatePipeType(_, inst, 2, ValidPipeType::READ_ONLY))
+      return error;
+  } else if (inst->opcode() == spv::Op::OpWritePipe) {
+    if (auto error = ValidatePipeType(_, inst, 2, ValidPipeType::WRITE_ONLY))
+      return error;
+  }
+
+  const Instruction* pointer_type = _.FindDef(_.GetOperandTypeId(inst, 3));
+  if (pointer_type->opcode() != spv::Op::OpTypePointer) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Pointer must be a type of OpTypePointer.";
+  }
+  if (pointer_type->GetOperandAs<spv::StorageClass>(1) !=
+      spv::StorageClass::Generic) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Pointer must be a OpTypePointer with a Generic storage class.";
+  }
+
+  if (auto error = ValidatePacketSizeAlign(_, inst, 4, 5)) return error;
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateReservedReadWritePipe(ValidationState_t& _,
+                                           const Instruction* inst) {
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsIntScalarType(result_type, 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result Type must be a 32-bit int scalar.";
+  }
+
+  if (inst->opcode() == spv::Op::OpReservedReadPipe) {
+    if (auto error = ValidatePipeType(_, inst, 2, ValidPipeType::READ_ONLY))
+      return error;
+  } else if (inst->opcode() == spv::Op::OpReservedWritePipe) {
+    if (auto error = ValidatePipeType(_, inst, 2, ValidPipeType::WRITE_ONLY))
+      return error;
+  }
+
+  const Instruction* reserve_id = _.FindDef(_.GetOperandTypeId(inst, 3));
+  if (reserve_id->opcode() != spv::Op::OpTypeReserveId) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Reserve Id type must be OpTypeReserveId.";
+  }
+
+  const uint32_t index_id = _.GetOperandTypeId(inst, 4);
+  if (!_.IsIntScalarType(index_id, 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Index must be a 32-bit scalar integer.";
+  }
+
+  const Instruction* pointer_type = _.FindDef(_.GetOperandTypeId(inst, 5));
+  if (pointer_type->opcode() != spv::Op::OpTypePointer) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Pointer must be a type of OpTypePointer.";
+  }
+  if (pointer_type->GetOperandAs<spv::StorageClass>(1) !=
+      spv::StorageClass::Generic) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Pointer must be a OpTypePointer with a Generic storage class.";
+  }
+
+  if (auto error = ValidatePacketSizeAlign(_, inst, 6, 7)) return error;
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateReservePackets(ValidationState_t& _,
+                                    const Instruction* inst) {
+  const Instruction* result_type = _.FindDef(inst->type_id());
+  if (result_type->opcode() != spv::Op::OpTypeReserveId) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result Type must be OpTypeReserveId.";
+  }
+
+  if (inst->opcode() == spv::Op::OpReserveReadPipePackets) {
+    if (auto error = ValidatePipeType(_, inst, 2, ValidPipeType::READ_ONLY))
+      return error;
+  } else if (inst->opcode() == spv::Op::OpReserveWritePipePackets) {
+    if (auto error = ValidatePipeType(_, inst, 2, ValidPipeType::WRITE_ONLY))
+      return error;
+  }
+
+  const uint32_t num_packets_id = _.GetOperandTypeId(inst, 3);
+  if (!_.IsIntScalarType(num_packets_id, 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Num Packets must be a 32-bit scalar integer.";
+  }
+
+  if (auto error = ValidatePacketSizeAlign(_, inst, 4, 5)) return error;
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateGroupReservePackets(ValidationState_t& _,
+                                         const Instruction* inst) {
+  const Instruction* result_type = _.FindDef(inst->type_id());
+  if (result_type->opcode() != spv::Op::OpTypeReserveId) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result Type must be OpTypeReserveId.";
+  }
+
+  if (inst->opcode() == spv::Op::OpGroupReserveReadPipePackets) {
+    if (auto error = ValidatePipeType(_, inst, 3, ValidPipeType::READ_ONLY))
+      return error;
+  } else if (inst->opcode() == spv::Op::OpGroupReserveWritePipePackets) {
+    if (auto error = ValidatePipeType(_, inst, 3, ValidPipeType::WRITE_ONLY))
+      return error;
+  }
+
+  const uint32_t num_packets_id = _.GetOperandTypeId(inst, 4);
+  if (!_.IsIntScalarType(num_packets_id, 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Num Packets must be a 32-bit scalar integer.";
+  }
+
+  if (auto error = ValidatePacketSizeAlign(_, inst, 5, 6)) return error;
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateCommitPipe(ValidationState_t& _, const Instruction* inst) {
+  if (inst->opcode() == spv::Op::OpCommitReadPipe) {
+    if (auto error = ValidatePipeType(_, inst, 0, ValidPipeType::READ_ONLY))
+      return error;
+  } else if (inst->opcode() == spv::Op::OpCommitWritePipe) {
+    if (auto error = ValidatePipeType(_, inst, 0, ValidPipeType::WRITE_ONLY))
+      return error;
+  }
+
+  const Instruction* reserve_id = _.FindDef(_.GetOperandTypeId(inst, 1));
+  if (reserve_id->opcode() != spv::Op::OpTypeReserveId) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Reserve Id type must be OpTypeReserveId.";
+  }
+
+  if (auto error = ValidatePacketSizeAlign(_, inst, 2, 3)) return error;
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateGroupCommitPipe(ValidationState_t& _,
+                                     const Instruction* inst) {
+  if (inst->opcode() == spv::Op::OpGroupCommitReadPipe) {
+    if (auto error = ValidatePipeType(_, inst, 1, ValidPipeType::READ_ONLY))
+      return error;
+  } else if (inst->opcode() == spv::Op::OpGroupCommitWritePipe) {
+    if (auto error = ValidatePipeType(_, inst, 1, ValidPipeType::WRITE_ONLY))
+      return error;
+  }
+
+  const Instruction* reserve_id = _.FindDef(_.GetOperandTypeId(inst, 2));
+  if (reserve_id->opcode() != spv::Op::OpTypeReserveId) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Reserve Id type must be OpTypeReserveId.";
+  }
+
+  if (auto error = ValidatePacketSizeAlign(_, inst, 3, 4)) return error;
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidatePipePacketsQuery(ValidationState_t& _,
+                                      const Instruction* inst) {
+  const uint32_t result_type = inst->type_id();
+  if (!_.IsIntScalarType(result_type, 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result Type must be a 32-bit int scalar.";
+  }
+
+  if (auto error = ValidatePipeType(_, inst, 2, ValidPipeType::READ_OR_WRITE))
+    return error;
+
+  if (auto error = ValidatePacketSizeAlign(_, inst, 3, 4)) return error;
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateIsValidReserveId(ValidationState_t& _,
+                                      const Instruction* inst) {
+  if (!_.IsBoolScalarType(inst->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result Type must be a bool scalar";
+  }
+
+  const Instruction* reserve_id = _.FindDef(_.GetOperandTypeId(inst, 2));
+  if (reserve_id->opcode() != spv::Op::OpTypeReserveId) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Reserve Id type must be OpTypeReserveId.";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateCreatePipeFromPipeStorage(ValidationState_t& _,
+                                               const Instruction* inst) {
+  const Instruction* result_type = _.FindDef(inst->type_id());
+  if (result_type->opcode() != spv::Op::OpTypePipe) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result Type must be OpTypePipe.";
+  }
+
+  // TODO - Need to check OpTypeStorage is from OpConstantPipeStorage
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateConstantPipeStorage(ValidationState_t& _,
+                                         const Instruction* inst) {
+  const Instruction* result_type = _.FindDef(inst->type_id());
+  if (result_type->opcode() != spv::Op::OpTypePipeStorage) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Result Type must be OpTypePipeStorage.";
+  }
+
+  // TODO - Should we validate the literal values?
+  // https://gitlab.khronos.org/spirv/SPIR-V/-/issues/914
+  return SPV_SUCCESS;
+}
+
+}  // namespace
+
+// Validates correctness of pipe instructions.
+spv_result_t PipePass(ValidationState_t& _, const Instruction* inst) {
+  switch (inst->opcode()) {
+    case spv::Op::OpReadPipe:
+    case spv::Op::OpWritePipe:
+      return ValidateReadWritePipe(_, inst);
+    case spv::Op::OpReservedReadPipe:
+    case spv::Op::OpReservedWritePipe:
+      return ValidateReservedReadWritePipe(_, inst);
+    case spv::Op::OpReserveReadPipePackets:
+    case spv::Op::OpReserveWritePipePackets:
+      return ValidateReservePackets(_, inst);
+    case spv::Op::OpGroupReserveReadPipePackets:
+    case spv::Op::OpGroupReserveWritePipePackets:
+      return ValidateGroupReservePackets(_, inst);
+    case spv::Op::OpCommitReadPipe:
+    case spv::Op::OpCommitWritePipe:
+      return ValidateCommitPipe(_, inst);
+    case spv::Op::OpGroupCommitReadPipe:
+    case spv::Op::OpGroupCommitWritePipe:
+      return ValidateGroupCommitPipe(_, inst);
+    case spv::Op::OpGetNumPipePackets:
+    case spv::Op::OpGetMaxPipePackets:
+      return ValidatePipePacketsQuery(_, inst);
+    case spv::Op::OpIsValidReserveId:
+      return ValidateIsValidReserveId(_, inst);
+    case spv::Op::OpCreatePipeFromPipeStorage:
+      return ValidateCreatePipeFromPipeStorage(_, inst);
+    case spv::Op::OpConstantPipeStorage:
+      return ValidateConstantPipeStorage(_, inst);
+    default:
+      break;
+  }
+
+  return SPV_SUCCESS;
+}
+
+}  // namespace val
+}  // namespace spvtools
diff --git a/source/val/validate_ray_query.cpp b/source/val/validate_ray_query.cpp
index 9b67fc9..432cad8 100644
--- a/source/val/validate_ray_query.cpp
+++ b/source/val/validate_ray_query.cpp
@@ -23,6 +23,17 @@
 namespace val {
 namespace {
 
+uint32_t GetArrayLength(ValidationState_t& _, const Instruction* array_type) {
+  assert(array_type->opcode() == spv::Op::OpTypeArray);
+  uint32_t const_int_id = array_type->GetOperandAs<uint32_t>(2U);
+  Instruction* array_length_inst = _.FindDef(const_int_id);
+  uint32_t array_length = 0;
+  if (array_length_inst->opcode() == spv::Op::OpConstant) {
+    array_length = array_length_inst->GetOperandAs<uint32_t>(2);
+  }
+  return array_length;
+}
+
 spv_result_t ValidateRayQueryPointer(ValidationState_t& _,
                                      const Instruction* inst,
                                      uint32_t ray_query_index) {
@@ -55,8 +66,7 @@
       inst->GetOperandAs<uint32_t>(intersection_index);
   const uint32_t intersection_type = _.GetTypeId(intersection_id);
   const spv::Op intersection_opcode = _.GetIdOpcode(intersection_id);
-  if (!_.IsIntScalarType(intersection_type) ||
-      _.GetBitWidth(intersection_type) != 32 ||
+  if (!_.IsIntScalarType(intersection_type, 32) ||
       !spvOpcodeIsConstant(intersection_opcode)) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "expected Intersection ID to be a constant 32-bit int scalar";
@@ -83,13 +93,13 @@
       }
 
       const uint32_t ray_flags = _.GetOperandTypeId(inst, 2);
-      if (!_.IsIntScalarType(ray_flags) || _.GetBitWidth(ray_flags) != 32) {
+      if (!_.IsIntScalarType(ray_flags, 32)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Ray Flags must be a 32-bit int scalar";
       }
 
       const uint32_t cull_mask = _.GetOperandTypeId(inst, 3);
-      if (!_.IsIntScalarType(cull_mask) || _.GetBitWidth(cull_mask) != 32) {
+      if (!_.IsIntScalarType(cull_mask, 32)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Cull Mask must be a 32-bit int scalar";
       }
@@ -102,7 +112,7 @@
       }
 
       const uint32_t ray_tmin = _.GetOperandTypeId(inst, 5);
-      if (!_.IsFloatScalarType(ray_tmin) || _.GetBitWidth(ray_tmin) != 32) {
+      if (!_.IsFloatScalarType(ray_tmin, 32)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Ray TMin must be a 32-bit float scalar";
       }
@@ -116,7 +126,7 @@
       }
 
       const uint32_t ray_tmax = _.GetOperandTypeId(inst, 7);
-      if (!_.IsFloatScalarType(ray_tmax) || _.GetBitWidth(ray_tmax) != 32) {
+      if (!_.IsFloatScalarType(ray_tmax, 32)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Ray TMax must be a 32-bit float scalar";
       }
@@ -133,7 +143,7 @@
       if (auto error = ValidateRayQueryPointer(_, inst, 0)) return error;
 
       const uint32_t hit_t_id = _.GetOperandTypeId(inst, 1);
-      if (!_.IsFloatScalarType(hit_t_id) || _.GetBitWidth(hit_t_id) != 32) {
+      if (!_.IsFloatScalarType(hit_t_id, 32)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Hit T must be a 32-bit float scalar";
       }
@@ -162,8 +172,7 @@
     case spv::Op::OpRayQueryGetRayTMinKHR: {
       if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
 
-      if (!_.IsFloatScalarType(result_type) ||
-          _.GetBitWidth(result_type) != 32) {
+      if (!_.IsFloatScalarType(result_type, 32)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "expected Result Type to be 32-bit float scalar type";
       }
@@ -185,7 +194,7 @@
     case spv::Op::OpRayQueryGetRayFlagsKHR: {
       if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
 
-      if (!_.IsIntScalarType(result_type) || _.GetBitWidth(result_type) != 32) {
+      if (!_.IsIntScalarType(result_type, 32)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "expected Result Type to be 32-bit int scalar type";
       }
@@ -263,6 +272,108 @@
       break;
     }
 
+    case spv::Op::OpRayQueryGetClusterIdNV: {
+      if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
+      if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
+
+      if (!_.IsIntScalarType(result_type, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "expected Result Type to be 32-bit int scalar type";
+      }
+      break;
+    }
+
+    case spv::Op::OpRayQueryGetIntersectionSpherePositionNV: {
+      if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
+      if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
+
+      if (!_.IsFloatVectorType(result_type) ||
+          _.GetDimension(result_type) != 3 ||
+          _.GetBitWidth(result_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "expected Result Type to be 32-bit float 3-component "
+                  "vector type";
+      }
+      break;
+    }
+
+    case spv::Op::OpRayQueryGetIntersectionLSSPositionsNV: {
+      if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
+      if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
+
+      auto result_id = _.FindDef(result_type);
+      if ((result_id->opcode() != spv::Op::OpTypeArray) ||
+          (GetArrayLength(_, result_id) != 2) ||
+          !_.IsFloatVectorType(_.GetComponentType(result_type)) ||
+          _.GetDimension(_.GetComponentType(result_type)) != 3) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 2 element array of 32-bit 3 component float point "
+                  "vector as Result Type: "
+               << spvOpcodeString(opcode);
+      }
+      break;
+    }
+
+    case spv::Op::OpRayQueryGetIntersectionLSSRadiiNV: {
+      if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
+      if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
+
+      if (!_.IsFloatArrayType(result_type) ||
+          (GetArrayLength(_, _.FindDef(result_type)) != 2) ||
+          !_.IsFloatScalarType(_.GetComponentType(result_type))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 32-bit floating point scalar as Result Type: "
+               << spvOpcodeString(opcode);
+      }
+      break;
+    }
+
+    case spv::Op::OpRayQueryGetIntersectionSphereRadiusNV:
+    case spv::Op::OpRayQueryGetIntersectionLSSHitValueNV: {
+      if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
+      if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
+
+      if (!_.IsFloatScalarType(result_type, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "expected Result Type to be 32-bit floating point "
+                  "scalar type";
+      }
+      break;
+    }
+
+    case spv::Op::OpRayQueryIsSphereHitNV:
+    case spv::Op::OpRayQueryIsLSSHitNV: {
+      if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
+      if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
+
+      if (!_.IsBoolScalarType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "expected Result Type to be Boolean "
+                  "scalar type";
+      }
+
+      break;
+    }
+
+    // SPV_KHR_ray_tracing_position_fetch
+    case spv::Op::OpRayQueryGetIntersectionTriangleVertexPositionsKHR: {
+      if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
+      if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
+
+      auto result_id = _.FindDef(result_type);
+      if ((result_id->opcode() != spv::Op::OpTypeArray) ||
+          (GetArrayLength(_, result_id) != 3) ||
+          !_.IsFloatVectorType(_.GetComponentType(result_type)) ||
+          _.GetDimension(_.GetComponentType(result_type)) != 3 ||
+          _.GetBitWidth(_.GetComponentType(result_type)) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 3 element array of 32-bit 3 component float point "
+                  "vector as Result Type: "
+               << spvOpcodeString(opcode);
+      }
+
+      break;
+    }
     default:
       break;
   }
diff --git a/source/val/validate_ray_tracing.cpp b/source/val/validate_ray_tracing.cpp
index f74e9d4..5fa14cc 100644
--- a/source/val/validate_ray_tracing.cpp
+++ b/source/val/validate_ray_tracing.cpp
@@ -22,146 +22,143 @@
 namespace spvtools {
 namespace val {
 
-spv_result_t RayTracingPass(ValidationState_t& _, const Instruction* inst) {
-  const spv::Op opcode = inst->opcode();
+spv_result_t ValidateTraceRay(ValidationState_t& _, const Instruction* inst) {
+  _.function(inst->function()->id())
+      ->RegisterExecutionModelLimitation(
+          [](spv::ExecutionModel model, std::string* message) {
+            if (model != spv::ExecutionModel::RayGenerationKHR &&
+                model != spv::ExecutionModel::ClosestHitKHR &&
+                model != spv::ExecutionModel::MissKHR) {
+              if (message) {
+                *message =
+                    "OpTraceRayKHR requires RayGenerationKHR, "
+                    "ClosestHitKHR and MissKHR execution models";
+              }
+              return false;
+            }
+            return true;
+          });
+
+  if (_.GetIdOpcode(_.GetOperandTypeId(inst, 0)) !=
+      spv::Op::OpTypeAccelerationStructureKHR) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Acceleration Structure to be of type "
+              "OpTypeAccelerationStructureKHR";
+  }
+
+  const uint32_t ray_flags = _.GetOperandTypeId(inst, 1);
+  if (!_.IsIntScalarType(ray_flags, 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Ray Flags must be a 32-bit int scalar";
+  }
+
+  const uint32_t cull_mask = _.GetOperandTypeId(inst, 2);
+  if (!_.IsIntScalarType(cull_mask, 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Cull Mask must be a 32-bit int scalar";
+  }
+
+  const uint32_t sbt_offset = _.GetOperandTypeId(inst, 3);
+  if (!_.IsIntScalarType(sbt_offset, 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "SBT Offset must be a 32-bit int scalar";
+  }
+
+  const uint32_t sbt_stride = _.GetOperandTypeId(inst, 4);
+  if (!_.IsIntScalarType(sbt_stride, 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "SBT Stride must be a 32-bit int scalar";
+  }
+
+  const uint32_t miss_index = _.GetOperandTypeId(inst, 5);
+  if (!_.IsIntScalarType(miss_index, 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Miss Index must be a 32-bit int scalar";
+  }
+
+  const uint32_t ray_origin = _.GetOperandTypeId(inst, 6);
+  if (!_.IsFloatVectorType(ray_origin) || _.GetDimension(ray_origin) != 3 ||
+      _.GetBitWidth(ray_origin) != 32) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Ray Origin must be a 32-bit float 3-component vector";
+  }
+
+  const uint32_t ray_tmin = _.GetOperandTypeId(inst, 7);
+  if (!_.IsFloatScalarType(ray_tmin, 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Ray TMin must be a 32-bit float scalar";
+  }
+
+  const uint32_t ray_direction = _.GetOperandTypeId(inst, 8);
+  if (!_.IsFloatVectorType(ray_direction) ||
+      _.GetDimension(ray_direction) != 3 ||
+      _.GetBitWidth(ray_direction) != 32) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Ray Direction must be a 32-bit float 3-component vector";
+  }
+
+  const uint32_t ray_tmax = _.GetOperandTypeId(inst, 9);
+  if (!_.IsFloatScalarType(ray_tmax, 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Ray TMax must be a 32-bit float scalar";
+  }
+
+  const Instruction* payload = _.FindDef(inst->GetOperandAs<uint32_t>(10));
+  if (payload->opcode() != spv::Op::OpVariable) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Payload must be the result of a OpVariable";
+  } else if (payload->GetOperandAs<spv::StorageClass>(2) !=
+                 spv::StorageClass::RayPayloadKHR &&
+             payload->GetOperandAs<spv::StorageClass>(2) !=
+                 spv::StorageClass::IncomingRayPayloadKHR) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Payload must have storage class RayPayloadKHR or "
+              "IncomingRayPayloadKHR";
+  }
+
+  return SPV_SUCCESS;
+}
+spv_result_t ValidateReportIntersection(ValidationState_t& _,
+                                        const Instruction* inst) {
+  _.function(inst->function()->id())
+      ->RegisterExecutionModelLimitation(
+          [](spv::ExecutionModel model, std::string* message) {
+            if (model != spv::ExecutionModel::IntersectionKHR) {
+              if (message) {
+                *message =
+                    "OpReportIntersectionKHR requires IntersectionKHR "
+                    "execution model";
+              }
+              return false;
+            }
+            return true;
+          });
+
   const uint32_t result_type = inst->type_id();
+  if (!_.IsBoolScalarType(result_type)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "expected Result Type to be bool scalar type";
+  }
 
-  switch (opcode) {
-    case spv::Op::OpTraceRayKHR: {
-      _.function(inst->function()->id())
-          ->RegisterExecutionModelLimitation(
-              [](spv::ExecutionModel model, std::string* message) {
-                if (model != spv::ExecutionModel::RayGenerationKHR &&
-                    model != spv::ExecutionModel::ClosestHitKHR &&
-                    model != spv::ExecutionModel::MissKHR) {
-                  if (message) {
-                    *message =
-                        "OpTraceRayKHR requires RayGenerationKHR, "
-                        "ClosestHitKHR and MissKHR execution models";
-                  }
-                  return false;
-                }
-                return true;
-              });
+  const uint32_t hit = _.GetOperandTypeId(inst, 2);
+  if (!_.IsFloatScalarType(hit, 32)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Hit must be a 32-bit int scalar";
+  }
 
-      if (_.GetIdOpcode(_.GetOperandTypeId(inst, 0)) !=
-          spv::Op::OpTypeAccelerationStructureKHR) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Expected Acceleration Structure to be of type "
-                  "OpTypeAccelerationStructureKHR";
-      }
+  const uint32_t hit_kind = _.GetOperandTypeId(inst, 3);
+  if (!_.IsUnsignedIntScalarType(hit_kind) || _.GetBitWidth(hit_kind) != 32) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Hit Kind must be a 32-bit unsigned int scalar";
+  }
+  return SPV_SUCCESS;
+}
 
-      const uint32_t ray_flags = _.GetOperandTypeId(inst, 1);
-      if (!_.IsIntScalarType(ray_flags) || _.GetBitWidth(ray_flags) != 32) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Ray Flags must be a 32-bit int scalar";
-      }
-
-      const uint32_t cull_mask = _.GetOperandTypeId(inst, 2);
-      if (!_.IsIntScalarType(cull_mask) || _.GetBitWidth(cull_mask) != 32) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Cull Mask must be a 32-bit int scalar";
-      }
-
-      const uint32_t sbt_offset = _.GetOperandTypeId(inst, 3);
-      if (!_.IsIntScalarType(sbt_offset) || _.GetBitWidth(sbt_offset) != 32) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "SBT Offset must be a 32-bit int scalar";
-      }
-
-      const uint32_t sbt_stride = _.GetOperandTypeId(inst, 4);
-      if (!_.IsIntScalarType(sbt_stride) || _.GetBitWidth(sbt_stride) != 32) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "SBT Stride must be a 32-bit int scalar";
-      }
-
-      const uint32_t miss_index = _.GetOperandTypeId(inst, 5);
-      if (!_.IsIntScalarType(miss_index) || _.GetBitWidth(miss_index) != 32) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Miss Index must be a 32-bit int scalar";
-      }
-
-      const uint32_t ray_origin = _.GetOperandTypeId(inst, 6);
-      if (!_.IsFloatVectorType(ray_origin) || _.GetDimension(ray_origin) != 3 ||
-          _.GetBitWidth(ray_origin) != 32) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Ray Origin must be a 32-bit float 3-component vector";
-      }
-
-      const uint32_t ray_tmin = _.GetOperandTypeId(inst, 7);
-      if (!_.IsFloatScalarType(ray_tmin) || _.GetBitWidth(ray_tmin) != 32) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Ray TMin must be a 32-bit float scalar";
-      }
-
-      const uint32_t ray_direction = _.GetOperandTypeId(inst, 8);
-      if (!_.IsFloatVectorType(ray_direction) ||
-          _.GetDimension(ray_direction) != 3 ||
-          _.GetBitWidth(ray_direction) != 32) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Ray Direction must be a 32-bit float 3-component vector";
-      }
-
-      const uint32_t ray_tmax = _.GetOperandTypeId(inst, 9);
-      if (!_.IsFloatScalarType(ray_tmax) || _.GetBitWidth(ray_tmax) != 32) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Ray TMax must be a 32-bit float scalar";
-      }
-
-      const Instruction* payload = _.FindDef(inst->GetOperandAs<uint32_t>(10));
-      if (payload->opcode() != spv::Op::OpVariable) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Payload must be the result of a OpVariable";
-      } else if (payload->GetOperandAs<spv::StorageClass>(2) !=
-                     spv::StorageClass::RayPayloadKHR &&
-                 payload->GetOperandAs<spv::StorageClass>(2) !=
-                     spv::StorageClass::IncomingRayPayloadKHR) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Payload must have storage class RayPayloadKHR or "
-                  "IncomingRayPayloadKHR";
-      }
-      break;
-    }
-
-    case spv::Op::OpReportIntersectionKHR: {
-      _.function(inst->function()->id())
-          ->RegisterExecutionModelLimitation(
-              [](spv::ExecutionModel model, std::string* message) {
-                if (model != spv::ExecutionModel::IntersectionKHR) {
-                  if (message) {
-                    *message =
-                        "OpReportIntersectionKHR requires IntersectionKHR "
-                        "execution model";
-                  }
-                  return false;
-                }
-                return true;
-              });
-
-      if (!_.IsBoolScalarType(result_type)) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "expected Result Type to be bool scalar type";
-      }
-
-      const uint32_t hit = _.GetOperandTypeId(inst, 2);
-      if (!_.IsFloatScalarType(hit) || _.GetBitWidth(hit) != 32) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Hit must be a 32-bit int scalar";
-      }
-
-      const uint32_t hit_kind = _.GetOperandTypeId(inst, 3);
-      if (!_.IsUnsignedIntScalarType(hit_kind) ||
-          _.GetBitWidth(hit_kind) != 32) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Hit Kind must be a 32-bit unsigned int scalar";
-      }
-      break;
-    }
-
-    case spv::Op::OpExecuteCallableKHR: {
-      _.function(inst->function()->id())
-          ->RegisterExecutionModelLimitation([](spv::ExecutionModel model,
-                                                std::string* message) {
+spv_result_t ValidateExecuteCallable(ValidationState_t& _,
+                                     const Instruction* inst) {
+  _.function(inst->function()->id())
+      ->RegisterExecutionModelLimitation(
+          [](spv::ExecutionModel model, std::string* message) {
             if (model != spv::ExecutionModel::RayGenerationKHR &&
                 model != spv::ExecutionModel::ClosestHitKHR &&
                 model != spv::ExecutionModel::MissKHR &&
@@ -176,29 +173,37 @@
             return true;
           });
 
-      const uint32_t sbt_index = _.GetOperandTypeId(inst, 0);
-      if (!_.IsUnsignedIntScalarType(sbt_index) ||
-          _.GetBitWidth(sbt_index) != 32) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "SBT Index must be a 32-bit unsigned int scalar";
-      }
+  const uint32_t sbt_index = _.GetOperandTypeId(inst, 0);
+  if (!_.IsUnsignedIntScalarType(sbt_index) || _.GetBitWidth(sbt_index) != 32) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "SBT Index must be a 32-bit unsigned int scalar";
+  }
 
-      const auto callable_data = _.FindDef(inst->GetOperandAs<uint32_t>(1));
-      if (callable_data->opcode() != spv::Op::OpVariable) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Callable Data must be the result of a OpVariable";
-      } else if (callable_data->GetOperandAs<spv::StorageClass>(2) !=
-                     spv::StorageClass::CallableDataKHR &&
-                 callable_data->GetOperandAs<spv::StorageClass>(2) !=
-                     spv::StorageClass::IncomingCallableDataKHR) {
-        return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Callable Data must have storage class CallableDataKHR or "
-                  "IncomingCallableDataKHR";
-      }
+  const auto callable_data = _.FindDef(inst->GetOperandAs<uint32_t>(1));
+  if (callable_data->opcode() != spv::Op::OpVariable) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Callable Data must be the result of a OpVariable";
+  } else if (callable_data->GetOperandAs<spv::StorageClass>(2) !=
+                 spv::StorageClass::CallableDataKHR &&
+             callable_data->GetOperandAs<spv::StorageClass>(2) !=
+                 spv::StorageClass::IncomingCallableDataKHR) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Callable Data must have storage class CallableDataKHR or "
+              "IncomingCallableDataKHR";
+  }
+  return SPV_SUCCESS;
+}
 
-      break;
-    }
+spv_result_t RayTracingPass(ValidationState_t& _, const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
 
+  switch (opcode) {
+    case spv::Op::OpTraceRayKHR:
+      return ValidateTraceRay(_, inst);
+    case spv::Op::OpReportIntersectionKHR:
+      return ValidateReportIntersection(_, inst);
+    case spv::Op::OpExecuteCallableKHR:
+      return ValidateExecuteCallable(_, inst);
     default:
       break;
   }
diff --git a/source/val/validate_ray_tracing_reorder.cpp b/source/val/validate_ray_tracing_reorder.cpp
index cb190f9..d1c4ee5 100644
--- a/source/val/validate_ray_tracing_reorder.cpp
+++ b/source/val/validate_ray_tracing_reorder.cpp
@@ -12,7 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Validates ray tracing instructions from SPV_NV_shader_execution_reorder
+// Validates ray tracing instructions from SPV_NV_shader_invocation_reorder and
+// SPV_EXT_shader_invocation_reorder
 
 #include "source/opcode.h"
 #include "source/val/instruction.h"
@@ -26,18 +27,40 @@
 
 static const uint32_t KRayParamInvalidId = std::numeric_limits<uint32_t>::max();
 
+uint32_t GetArrayLength(ValidationState_t& _, const Instruction* array_type) {
+  assert(array_type->opcode() == spv::Op::OpTypeArray);
+  uint32_t const_int_id = array_type->GetOperandAs<uint32_t>(2U);
+  Instruction* array_length_inst = _.FindDef(const_int_id);
+  uint32_t array_length = 0;
+  if (array_length_inst->opcode() == spv::Op::OpConstant) {
+    array_length = array_length_inst->GetOperandAs<uint32_t>(2);
+  }
+  return array_length;
+}
+
+spv_result_t ValidateRayQueryPointer(ValidationState_t& _,
+                                     const Instruction* inst,
+                                     uint32_t ray_query_index) {
+  const uint32_t ray_query_id = inst->GetOperandAs<uint32_t>(ray_query_index);
+  auto variable = _.FindDef(ray_query_id);
+  auto pointer = _.FindDef(variable->GetOperandAs<uint32_t>(0));
+  if (!pointer || pointer->opcode() != spv::Op::OpTypePointer) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Ray Query must be a pointer";
+  }
+  auto type = _.FindDef(pointer->GetOperandAs<uint32_t>(2));
+  if (!type || type->opcode() != spv::Op::OpTypeRayQueryKHR) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Ray Query must be a pointer to OpTypeRayQueryKHR";
+  }
+  return SPV_SUCCESS;
+}
+
 spv_result_t ValidateHitObjectPointer(ValidationState_t& _,
                                       const Instruction* inst,
                                       uint32_t hit_object_index) {
   const uint32_t hit_object_id = inst->GetOperandAs<uint32_t>(hit_object_index);
   auto variable = _.FindDef(hit_object_id);
-  const auto var_opcode = variable->opcode();
-  if (!variable || (var_opcode != spv::Op::OpVariable &&
-                    var_opcode != spv::Op::OpFunctionParameter &&
-                    var_opcode != spv::Op::OpAccessChain)) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Hit Object must be a memory object declaration";
-  }
   auto pointer = _.FindDef(variable->GetOperandAs<uint32_t>(0));
   if (!pointer || pointer->opcode() != spv::Op::OpTypePointer) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -51,6 +74,24 @@
   return SPV_SUCCESS;
 }
 
+spv_result_t ValidateHitObjectPointerEXT(ValidationState_t& _,
+                                         const Instruction* inst,
+                                         uint32_t hit_object_index) {
+  const uint32_t hit_object_id = inst->GetOperandAs<uint32_t>(hit_object_index);
+  auto variable = _.FindDef(hit_object_id);
+  auto pointer = _.FindDef(variable->GetOperandAs<uint32_t>(0));
+  if (!pointer || pointer->opcode() != spv::Op::OpTypePointer) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Hit Object must be a pointer";
+  }
+  auto type = _.FindDef(pointer->GetOperandAs<uint32_t>(2));
+  if (!type || type->opcode() != spv::Op::OpTypeHitObjectEXT) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Type must be OpTypeHitObjectEXT";
+  }
+  return SPV_SUCCESS;
+}
+
 spv_result_t ValidateHitObjectInstructionCommonParameters(
     ValidationState_t& _, const Instruction* inst,
     uint32_t acceleration_struct_index, uint32_t instance_id_index,
@@ -72,7 +113,7 @@
 
   if (isValidId(instance_id_index)) {
     const uint32_t instance_id = _.GetOperandTypeId(inst, instance_id_index);
-    if (!_.IsIntScalarType(instance_id) || _.GetBitWidth(instance_id) != 32) {
+    if (!_.IsIntScalarType(instance_id, 32)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Instance Id must be a 32-bit int scalar";
     }
@@ -80,7 +121,7 @@
 
   if (isValidId(primtive_id_index)) {
     const uint32_t primitive_id = _.GetOperandTypeId(inst, primtive_id_index);
-    if (!_.IsIntScalarType(primitive_id) || _.GetBitWidth(primitive_id) != 32) {
+    if (!_.IsIntScalarType(primitive_id, 32)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Primitive Id must be a 32-bit int scalar";
     }
@@ -88,8 +129,7 @@
 
   if (isValidId(geometry_index)) {
     const uint32_t geometry_index_id = _.GetOperandTypeId(inst, geometry_index);
-    if (!_.IsIntScalarType(geometry_index_id) ||
-        _.GetBitWidth(geometry_index_id) != 32) {
+    if (!_.IsIntScalarType(geometry_index_id, 32)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Geometry Index must be a 32-bit int scalar";
     }
@@ -173,7 +213,7 @@
 
   if (isValidId(ray_tmin_index)) {
     const uint32_t ray_tmin_id = _.GetOperandTypeId(inst, ray_tmin_index);
-    if (!_.IsFloatScalarType(ray_tmin_id) || _.GetBitWidth(ray_tmin_id) != 32) {
+    if (!_.IsFloatScalarType(ray_tmin_id, 32)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Ray TMin must be a 32-bit float scalar";
     }
@@ -192,7 +232,7 @@
 
   if (isValidId(ray_tmax_index)) {
     const uint32_t ray_tmax_id = _.GetOperandTypeId(inst, ray_tmax_index);
-    if (!_.IsFloatScalarType(ray_tmax_id) || _.GetBitWidth(ray_tmax_id) != 32) {
+    if (!_.IsFloatScalarType(ray_tmax_id, 32)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Ray TMax must be a 32-bit float scalar";
     }
@@ -200,7 +240,7 @@
 
   if (isValidId(ray_flags_index)) {
     const uint32_t ray_flags_id = _.GetOperandTypeId(inst, ray_flags_index);
-    if (!_.IsIntScalarType(ray_flags_id) || _.GetBitWidth(ray_flags_id) != 32) {
+    if (!_.IsIntScalarType(ray_flags_id, 32)) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Ray Flags must be a 32-bit int scalar";
     }
@@ -236,8 +276,10 @@
     auto variable = _.FindDef(hit_object_attr_id);
     const auto var_opcode = variable->opcode();
     if (!variable || var_opcode != spv::Op::OpVariable ||
-        (variable->GetOperandAs<spv::StorageClass>(2)) !=
-            spv::StorageClass::HitObjectAttributeNV) {
+        !((variable->GetOperandAs<spv::StorageClass>(2) ==
+           spv::StorageClass::HitObjectAttributeNV) ||
+          (variable->GetOperandAs<spv::StorageClass>(2) ==
+           spv::StorageClass::HitObjectAttributeEXT))) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Hit Object Attributes id must be a OpVariable of storage "
                 "class HitObjectAttributeNV";
@@ -309,7 +351,7 @@
       RegisterOpcodeForValidModel(_, inst);
       if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error;
 
-      if (!_.IsIntScalarType(result_type) || !_.GetBitWidth(result_type))
+      if (!_.IsIntScalarType(result_type, 32))
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected 32-bit integer type scalar as Result Type: "
                << spvOpcodeString(opcode);
@@ -322,7 +364,7 @@
       RegisterOpcodeForValidModel(_, inst);
       if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error;
 
-      if (!_.IsFloatScalarType(result_type) || _.GetBitWidth(result_type) != 32)
+      if (!_.IsFloatScalarType(result_type, 32))
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Expected 32-bit floating-point type scalar as Result Type: "
                << spvOpcodeString(opcode);
@@ -438,7 +480,7 @@
       }
 
       const uint32_t ray_tmin = _.GetOperandTypeId(inst, 3);
-      if (!_.IsFloatScalarType(ray_tmin) || _.GetBitWidth(ray_tmin) != 32) {
+      if (!_.IsFloatScalarType(ray_tmin, 32)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Ray TMin must be a 32-bit float scalar";
       }
@@ -452,7 +494,7 @@
       }
 
       const uint32_t ray_tmax = _.GetOperandTypeId(inst, 5);
-      if (!_.IsFloatScalarType(ray_tmax) || _.GetBitWidth(ray_tmax) != 32) {
+      if (!_.IsFloatScalarType(ray_tmax, 32)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Ray TMax must be a 32-bit float scalar";
       }
@@ -520,8 +562,7 @@
         return error;
       // Current Time
       const uint32_t current_time_id = _.GetOperandTypeId(inst, 11);
-      if (!_.IsFloatScalarType(current_time_id) ||
-          _.GetBitWidth(current_time_id) != 32) {
+      if (!_.IsFloatScalarType(current_time_id, 32)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Current Times must be a 32-bit float scalar type";
       }
@@ -575,12 +616,12 @@
 
         // Validate the optional opreands Hint and Bits
         const uint32_t hint_id = _.GetOperandTypeId(inst, 1);
-        if (!_.IsIntScalarType(hint_id) || _.GetBitWidth(hint_id) != 32) {
+        if (!_.IsIntScalarType(hint_id, 32)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << "Hint must be a 32-bit int scalar";
         }
         const uint32_t bits_id = _.GetOperandTypeId(inst, 2);
-        if (!_.IsIntScalarType(bits_id) || _.GetBitWidth(bits_id) != 32) {
+        if (!_.IsIntScalarType(bits_id, 32)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << "bits must be a 32-bit int scalar";
         }
@@ -604,16 +645,750 @@
               });
 
       const uint32_t hint_id = _.GetOperandTypeId(inst, 0);
-      if (!_.IsIntScalarType(hint_id) || _.GetBitWidth(hint_id) != 32) {
+      if (!_.IsIntScalarType(hint_id, 32)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "Hint must be a 32-bit int scalar";
       }
 
       const uint32_t bits_id = _.GetOperandTypeId(inst, 1);
-      if (!_.IsIntScalarType(bits_id) || _.GetBitWidth(bits_id) != 32) {
+      if (!_.IsIntScalarType(bits_id, 32)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << "bits must be a 32-bit int scalar";
       }
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetClusterIdNV: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error;
+
+      if (!_.IsIntScalarType(result_type, 32))
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 32-bit integer type scalar as Result Type: "
+               << spvOpcodeString(opcode);
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetSpherePositionNV: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error;
+
+      if (!_.IsFloatVectorType(result_type) ||
+          _.GetDimension(result_type) != 3 ||
+          _.GetBitWidth(result_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 32-bit floating point 2 component vector type as "
+                  "Result Type: "
+               << spvOpcodeString(opcode);
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetSphereRadiusNV: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error;
+
+      if (!_.IsFloatScalarType(result_type, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 32-bit floating point scalar as Result Type: "
+               << spvOpcodeString(opcode);
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetLSSPositionsNV: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error;
+
+      auto result_id = _.FindDef(result_type);
+      if ((result_id->opcode() != spv::Op::OpTypeArray) ||
+          (GetArrayLength(_, result_id) != 2) ||
+          !_.IsFloatVectorType(_.GetComponentType(result_type)) ||
+          _.GetDimension(_.GetComponentType(result_type)) != 3) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 2 element array of 32-bit 3 component float point "
+                  "vector as Result Type: "
+               << spvOpcodeString(opcode);
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetLSSRadiiNV: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error;
+
+      if (!_.IsFloatArrayType(result_type) ||
+          (GetArrayLength(_, _.FindDef(result_type)) != 2) ||
+          !_.IsFloatScalarType(_.GetComponentType(result_type))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 2 element array of 32-bit floating point scalar as "
+                  "Result Type: "
+               << spvOpcodeString(opcode);
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectIsSphereHitNV: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error;
+
+      if (!_.IsBoolScalarType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected Boolean scalar as Result Type: "
+               << spvOpcodeString(opcode);
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectIsLSSHitNV: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointer(_, inst, 2)) return error;
+
+      if (!_.IsBoolScalarType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected Boolean scalar as Result Type: "
+               << spvOpcodeString(opcode);
+      }
+      break;
+    }
+
+    default:
+      break;
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t RayReorderEXTPass(ValidationState_t& _, const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+
+  auto RegisterOpcodeForValidModel = [](ValidationState_t& vs,
+                                        const Instruction* rtinst) {
+    std::string opcode_name = spvOpcodeString(rtinst->opcode());
+    vs.function(rtinst->function()->id())
+        ->RegisterExecutionModelLimitation(
+            [opcode_name](spv::ExecutionModel model, std::string* message) {
+              if (model != spv::ExecutionModel::RayGenerationKHR &&
+                  model != spv::ExecutionModel::ClosestHitKHR &&
+                  model != spv::ExecutionModel::MissKHR) {
+                if (message) {
+                  *message = opcode_name +
+                             " requires RayGenerationKHR, ClosestHitKHR and "
+                             "MissKHR execution models";
+                }
+                return false;
+              }
+              return true;
+            });
+    return;
+  };
+
+  switch (opcode) {
+    case spv::Op::OpHitObjectIsMissEXT:
+    case spv::Op::OpHitObjectIsHitEXT:
+    case spv::Op::OpHitObjectIsEmptyEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (!_.IsBoolScalarType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "expected Result Type to be bool scalar type";
+      }
+
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error;
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetShaderRecordBufferHandleEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error;
+
+      if (!_.IsIntVectorType(result_type) ||
+          (_.GetDimension(result_type) != 2) ||
+          (_.GetBitWidth(result_type) != 32))
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 32-bit integer type 2-component vector as Result "
+                  "Type: "
+               << spvOpcodeString(opcode);
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetHitKindEXT:
+    case spv::Op::OpHitObjectGetPrimitiveIndexEXT:
+    case spv::Op::OpHitObjectGetGeometryIndexEXT:
+    case spv::Op::OpHitObjectGetInstanceIdEXT:
+    case spv::Op::OpHitObjectGetInstanceCustomIndexEXT:
+    case spv::Op::OpHitObjectGetShaderBindingTableRecordIndexEXT:
+    case spv::Op::OpHitObjectGetRayFlagsEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error;
+
+      if (!_.IsIntScalarType(result_type, 32))
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 32-bit integer type scalar as Result Type: "
+               << spvOpcodeString(opcode);
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetCurrentTimeEXT:
+    case spv::Op::OpHitObjectGetRayTMaxEXT:
+    case spv::Op::OpHitObjectGetRayTMinEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error;
+
+      if (!_.IsFloatScalarType(result_type, 32))
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 32-bit floating-point type scalar as Result Type: "
+               << spvOpcodeString(opcode);
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetObjectToWorldEXT:
+    case spv::Op::OpHitObjectGetWorldToObjectEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error;
+
+      uint32_t num_rows = 0;
+      uint32_t num_cols = 0;
+      uint32_t col_type = 0;
+      uint32_t component_type = 0;
+
+      if (!_.GetMatrixTypeInfo(result_type, &num_rows, &num_cols, &col_type,
+                               &component_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "expected matrix type as Result Type: "
+               << spvOpcodeString(opcode);
+      }
+
+      if (num_cols != 4) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "expected Result Type matrix to have a Column Count of 4"
+               << spvOpcodeString(opcode);
+      }
+
+      if (!_.IsFloatScalarType(component_type) ||
+          _.GetBitWidth(result_type) != 32 || num_rows != 3) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "expected Result Type matrix to have a Column Type of "
+                  "3-component 32-bit float vectors: "
+               << spvOpcodeString(opcode);
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetObjectRayOriginEXT:
+    case spv::Op::OpHitObjectGetObjectRayDirectionEXT:
+    case spv::Op::OpHitObjectGetWorldRayDirectionEXT:
+    case spv::Op::OpHitObjectGetWorldRayOriginEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error;
+
+      if (!_.IsFloatVectorType(result_type) ||
+          (_.GetDimension(result_type) != 3) ||
+          (_.GetBitWidth(result_type) != 32))
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 32-bit floating-point type 3-component vector as "
+                  "Result Type: "
+               << spvOpcodeString(opcode);
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetIntersectionTriangleVertexPositionsEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error;
+
+      auto result_id = _.FindDef(result_type);
+      if ((result_id->opcode() != spv::Op::OpTypeArray) ||
+          (GetArrayLength(_, result_id) != 3) ||
+          !_.IsFloatVectorType(_.GetComponentType(result_type)) ||
+          _.GetDimension(_.GetComponentType(result_type)) != 3 ||
+          _.GetBitWidth(_.GetComponentType(result_type)) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 3 element array of 32-bit 3 component float "
+                  "vectors as Result Type: "
+               << spvOpcodeString(opcode);
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetAttributesEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      const uint32_t hit_object_attr_id = inst->GetOperandAs<uint32_t>(1);
+      auto variable = _.FindDef(hit_object_attr_id);
+      const auto var_opcode = variable->opcode();
+      if (!variable || var_opcode != spv::Op::OpVariable ||
+          variable->GetOperandAs<spv::StorageClass>(2) !=
+              spv::StorageClass::HitObjectAttributeEXT) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Hit Object Attributes id must be a OpVariable of storage "
+                  "class HitObjectAttributeEXT";
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectSetShaderBindingTableRecordIndexEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      const uint32_t sbt_index_id = _.GetOperandTypeId(inst, 1);
+      if (!_.IsIntScalarType(sbt_index_id, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "SBT Index must be a 32-bit integer scalar";
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectExecuteShaderEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      const uint32_t payload_id = inst->GetOperandAs<uint32_t>(1);
+      auto variable = _.FindDef(payload_id);
+      const auto var_opcode = variable->opcode();
+      if (!variable || var_opcode != spv::Op::OpVariable ||
+          (variable->GetOperandAs<spv::StorageClass>(2) !=
+               spv::StorageClass::RayPayloadKHR &&
+           variable->GetOperandAs<spv::StorageClass>(2) !=
+               spv::StorageClass::IncomingRayPayloadKHR)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Payload must be a OpVariable of storage "
+                  "class RayPayloadKHR or IncomingRayPayloadKHR";
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectRecordEmptyEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+      break;
+    }
+
+    case spv::Op::OpHitObjectRecordFromQueryEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+      if (auto error = ValidateRayQueryPointer(_, inst, 1)) return error;
+
+      if (!_.HasCapability(spv::Capability::RayQueryKHR))
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << spvOpcodeString(opcode)
+               << ": requires RayQueryKHR capability";
+
+      // Validate SBT Record Index (operand 2)
+      const uint32_t sbt_record_index_id = _.GetOperandTypeId(inst, 2);
+      if (!_.IsIntScalarType(sbt_record_index_id, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "SBT Record Index must be a 32-bit integer scalar";
+      }
+
+      // Validate Hit Object Attributes (operand 3)
+      const uint32_t hit_object_attr_id = inst->GetOperandAs<uint32_t>(3);
+      auto attr_variable = _.FindDef(hit_object_attr_id);
+      const auto attr_var_opcode = attr_variable->opcode();
+      if (!attr_variable || attr_var_opcode != spv::Op::OpVariable ||
+          attr_variable->GetOperandAs<spv::StorageClass>(2) !=
+              spv::StorageClass::HitObjectAttributeEXT) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Hit Object Attributes id must be a OpVariable of storage "
+                  "class HitObjectAttributeEXT";
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectRecordMissEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      // Ray Flags (operand 1)
+      const uint32_t ray_flags_id = _.GetOperandTypeId(inst, 1);
+      if (!_.IsIntScalarType(ray_flags_id, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray Flags must be a 32-bit int scalar";
+      }
+
+      // Miss Index (operand 2)
+      const uint32_t miss_index = _.GetOperandTypeId(inst, 2);
+      if (!_.IsUnsignedIntScalarType(miss_index) ||
+          _.GetBitWidth(miss_index) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Miss Index must be a 32-bit unsigned int scalar";
+      }
+
+      // Ray Origin (operand 3)
+      const uint32_t ray_origin = _.GetOperandTypeId(inst, 3);
+      if (!_.IsFloatVectorType(ray_origin) || _.GetDimension(ray_origin) != 3 ||
+          _.GetBitWidth(ray_origin) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray Origin must be a 32-bit float 3-component vector";
+      }
+
+      // Ray TMin (operand 4)
+      const uint32_t ray_tmin = _.GetOperandTypeId(inst, 4);
+      if (!_.IsFloatScalarType(ray_tmin, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray TMin must be a 32-bit float scalar";
+      }
+
+      // Ray Direction (operand 5)
+      const uint32_t ray_direction = _.GetOperandTypeId(inst, 5);
+      if (!_.IsFloatVectorType(ray_direction) ||
+          _.GetDimension(ray_direction) != 3 ||
+          _.GetBitWidth(ray_direction) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray Direction must be a 32-bit float 3-component vector";
+      }
+
+      // Ray TMax (operand 6)
+      const uint32_t ray_tmax = _.GetOperandTypeId(inst, 6);
+      if (!_.IsFloatScalarType(ray_tmax, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray TMax must be a 32-bit float scalar";
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectRecordMissMotionEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      // Ray Flags (operand 1)
+      const uint32_t ray_flags_id = _.GetOperandTypeId(inst, 1);
+      if (!_.IsIntScalarType(ray_flags_id, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray Flags must be a 32-bit int scalar";
+      }
+
+      // Miss Index (operand 2)
+      const uint32_t miss_index = _.GetOperandTypeId(inst, 2);
+      if (!_.IsUnsignedIntScalarType(miss_index) ||
+          _.GetBitWidth(miss_index) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Miss Index must be a 32-bit unsigned int scalar";
+      }
+
+      // Ray Origin (operand 3)
+      const uint32_t ray_origin = _.GetOperandTypeId(inst, 3);
+      if (!_.IsFloatVectorType(ray_origin) || _.GetDimension(ray_origin) != 3 ||
+          _.GetBitWidth(ray_origin) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray Origin must be a 32-bit float 3-component vector";
+      }
+
+      // Ray TMin (operand 4)
+      const uint32_t ray_tmin = _.GetOperandTypeId(inst, 4);
+      if (!_.IsFloatScalarType(ray_tmin, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray TMin must be a 32-bit float scalar";
+      }
+
+      // Ray Direction (operand 5)
+      const uint32_t ray_direction = _.GetOperandTypeId(inst, 5);
+      if (!_.IsFloatVectorType(ray_direction) ||
+          _.GetDimension(ray_direction) != 3 ||
+          _.GetBitWidth(ray_direction) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray Direction must be a 32-bit float 3-component vector";
+      }
+
+      // Ray TMax (operand 6)
+      const uint32_t ray_tmax = _.GetOperandTypeId(inst, 6);
+      if (!_.IsFloatScalarType(ray_tmax, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray TMax must be a 32-bit float scalar";
+      }
+
+      // Current Time (operand 7)
+      const uint32_t current_time_id = _.GetOperandTypeId(inst, 7);
+      if (!_.IsFloatScalarType(current_time_id, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Current Time must be a 32-bit float scalar";
+      }
+      break;
+    }
+
+    case spv::Op::OpReorderThreadWithHintEXT: {
+      std::string opcode_name = spvOpcodeString(inst->opcode());
+      _.function(inst->function()->id())
+          ->RegisterExecutionModelLimitation(
+              [opcode_name](spv::ExecutionModel model, std::string* message) {
+                if (model != spv::ExecutionModel::RayGenerationKHR) {
+                  if (message) {
+                    *message = opcode_name +
+                               " requires RayGenerationKHR execution model";
+                  }
+                  return false;
+                }
+                return true;
+              });
+
+      const uint32_t hint_id = _.GetOperandTypeId(inst, 0);
+      if (!_.IsIntScalarType(hint_id, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Hint must be a 32-bit int scalar";
+      }
+
+      const uint32_t bits_id = _.GetOperandTypeId(inst, 1);
+      if (!_.IsIntScalarType(bits_id, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Bits must be a 32-bit int scalar";
+      }
+      break;
+    }
+
+    case spv::Op::OpReorderThreadWithHitObjectEXT: {
+      std::string opcode_name = spvOpcodeString(inst->opcode());
+      _.function(inst->function()->id())
+          ->RegisterExecutionModelLimitation(
+              [opcode_name](spv::ExecutionModel model, std::string* message) {
+                if (model != spv::ExecutionModel::RayGenerationKHR) {
+                  if (message) {
+                    *message = opcode_name +
+                               " requires RayGenerationKHR execution model";
+                  }
+                  return false;
+                }
+                return true;
+              });
+
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      if (inst->operands().size() > 1) {
+        if (inst->operands().size() != 3) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Hint and Bits are optional together i.e "
+                 << " Either both Hint and Bits should be provided or neither.";
+        }
+
+        // Validate the optional operands Hint and Bits
+        const uint32_t hint_id = _.GetOperandTypeId(inst, 1);
+        if (!_.IsIntScalarType(hint_id, 32)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Hint must be a 32-bit int scalar";
+        }
+        const uint32_t bits_id = _.GetOperandTypeId(inst, 2);
+        if (!_.IsIntScalarType(bits_id, 32)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Bits must be a 32-bit int scalar";
+        }
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectTraceRayEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      if (auto error = ValidateHitObjectInstructionCommonParameters(
+              _, inst, 1 /* Acceleration Struct */,
+              KRayParamInvalidId /* Instance Id */,
+              KRayParamInvalidId /* Primitive Id */,
+              KRayParamInvalidId /* Geometry Index */, 2 /* Ray Flags */,
+              3 /* Cull Mask */, KRayParamInvalidId /* Hit Kind*/,
+              KRayParamInvalidId /* SBT index */, 4 /* SBT Offset */,
+              5 /* SBT Stride */, KRayParamInvalidId /* SBT Record Offset */,
+              KRayParamInvalidId /* SBT Record Stride */, 6 /* Miss Index */,
+              7 /* Ray Origin */, 8 /* Ray TMin */, 9 /* Ray Direction */,
+              10 /* Ray TMax */, 11 /* Payload */,
+              KRayParamInvalidId /* Hit Object Attribute */))
+        return error;
+      break;
+    }
+
+    case spv::Op::OpHitObjectTraceRayMotionEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      if (auto error = ValidateHitObjectInstructionCommonParameters(
+              _, inst, 1 /* Acceleration Struct */,
+              KRayParamInvalidId /* Instance Id */,
+              KRayParamInvalidId /* Primitive Id */,
+              KRayParamInvalidId /* Geometry Index */, 2 /* Ray Flags */,
+              3 /* Cull Mask */, KRayParamInvalidId /* Hit Kind*/,
+              KRayParamInvalidId /* SBT index */, 4 /* SBT Offset */,
+              5 /* SBT Stride */, KRayParamInvalidId /* SBT Record Offset */,
+              KRayParamInvalidId /* SBT Record Stride */, 6 /* Miss Index */,
+              7 /* Ray Origin */, 8 /* Ray TMin */, 9 /* Ray Direction */,
+              10 /* Ray TMax */, 12 /* Payload */,
+              KRayParamInvalidId /* Hit Object Attribute */))
+        return error;
+
+      // Current Time (operand 11)
+      const uint32_t current_time_id = _.GetOperandTypeId(inst, 11);
+      if (!_.IsFloatScalarType(current_time_id, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Current Time must be a 32-bit float scalar";
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectReorderExecuteShaderEXT: {
+      std::string opcode_name = spvOpcodeString(inst->opcode());
+      _.function(inst->function()->id())
+          ->RegisterExecutionModelLimitation(
+              [opcode_name](spv::ExecutionModel model, std::string* message) {
+                if (model != spv::ExecutionModel::RayGenerationKHR) {
+                  if (message) {
+                    *message = opcode_name +
+                               " requires RayGenerationKHR execution model";
+                  }
+                  return false;
+                }
+                return true;
+              });
+
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      // Validate Payload (operand 1)
+      const uint32_t payload_id = inst->GetOperandAs<uint32_t>(1);
+      auto variable = _.FindDef(payload_id);
+      const auto var_opcode = variable->opcode();
+      if (!variable || var_opcode != spv::Op::OpVariable ||
+          (variable->GetOperandAs<spv::StorageClass>(2) !=
+               spv::StorageClass::RayPayloadKHR &&
+           variable->GetOperandAs<spv::StorageClass>(2) !=
+               spv::StorageClass::IncomingRayPayloadKHR)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Payload must be a OpVariable of storage "
+                  "class RayPayloadKHR or IncomingRayPayloadKHR";
+      }
+
+      // Check for optional Hint and Bits (operands 2 and 3)
+      if (inst->operands().size() > 2) {
+        if (inst->operands().size() != 4) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Hint and Bits are optional together i.e "
+                 << " Either both Hint and Bits should be provided or neither.";
+        }
+
+        // Validate optional Hint and Bits
+        const uint32_t hint_id = _.GetOperandTypeId(inst, 2);
+        if (!_.IsIntScalarType(hint_id, 32)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Hint must be a 32-bit int scalar";
+        }
+        const uint32_t bits_id = _.GetOperandTypeId(inst, 3);
+        if (!_.IsIntScalarType(bits_id, 32)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Bits must be a 32-bit int scalar";
+        }
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectTraceReorderExecuteEXT: {
+      std::string opcode_name = spvOpcodeString(inst->opcode());
+      _.function(inst->function()->id())
+          ->RegisterExecutionModelLimitation(
+              [opcode_name](spv::ExecutionModel model, std::string* message) {
+                if (model != spv::ExecutionModel::RayGenerationKHR) {
+                  if (message) {
+                    *message = opcode_name +
+                               " requires RayGenerationKHR execution model";
+                  }
+                  return false;
+                }
+                return true;
+              });
+
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      // Validate base trace ray parameters (operands 1-11)
+      if (auto error = ValidateHitObjectInstructionCommonParameters(
+              _, inst, 1 /* Acceleration Struct */,
+              KRayParamInvalidId /* Instance Id */,
+              KRayParamInvalidId /* Primitive Id */,
+              KRayParamInvalidId /* Geometry Index */, 2 /* Ray Flags */,
+              3 /* Cull Mask */, KRayParamInvalidId /* Hit Kind*/,
+              KRayParamInvalidId /* SBT index */, 4 /* SBT Offset */,
+              5 /* SBT Stride */, KRayParamInvalidId /* SBT Record Offset */,
+              KRayParamInvalidId /* SBT Record Stride */, 6 /* Miss Index */,
+              7 /* Ray Origin */, 8 /* Ray TMin */, 9 /* Ray Direction */,
+              10 /* Ray TMax */, 11 /* Payload */,
+              KRayParamInvalidId /* Hit Object Attribute */))
+        return error;
+
+      // Check for optional Hint and Bits (operands 12 and 13)
+      if (inst->operands().size() > 12) {
+        if (inst->operands().size() != 14) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Hint and Bits are optional together i.e "
+                 << " Either both Hint and Bits should be provided or neither.";
+        }
+
+        // Validate optional Hint and Bits
+        const uint32_t hint_id = _.GetOperandTypeId(inst, 12);
+        if (!_.IsIntScalarType(hint_id, 32)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Hint must be a 32-bit int scalar";
+        }
+        const uint32_t bits_id = _.GetOperandTypeId(inst, 13);
+        if (!_.IsIntScalarType(bits_id, 32)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Bits must be a 32-bit int scalar";
+        }
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectTraceMotionReorderExecuteEXT: {
+      std::string opcode_name = spvOpcodeString(inst->opcode());
+      _.function(inst->function()->id())
+          ->RegisterExecutionModelLimitation(
+              [opcode_name](spv::ExecutionModel model, std::string* message) {
+                if (model != spv::ExecutionModel::RayGenerationKHR) {
+                  if (message) {
+                    *message = opcode_name +
+                               " requires RayGenerationKHR execution model";
+                  }
+                  return false;
+                }
+                return true;
+              });
+
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      // Validate base trace ray parameters (operands 1-12)
+      if (auto error = ValidateHitObjectInstructionCommonParameters(
+              _, inst, 1 /* Acceleration Struct */,
+              KRayParamInvalidId /* Instance Id */,
+              KRayParamInvalidId /* Primitive Id */,
+              KRayParamInvalidId /* Geometry Index */, 2 /* Ray Flags */,
+              3 /* Cull Mask */, KRayParamInvalidId /* Hit Kind*/,
+              KRayParamInvalidId /* SBT index */, 4 /* SBT Offset */,
+              5 /* SBT Stride */, KRayParamInvalidId /* SBT Record Offset */,
+              KRayParamInvalidId /* SBT Record Stride */, 6 /* Miss Index */,
+              7 /* Ray Origin */, 8 /* Ray TMin */, 9 /* Ray Direction */,
+              10 /* Ray TMax */, 12 /* Payload */,
+              KRayParamInvalidId /* Hit Object Attribute */))
+        return error;
+
+      // Current Time (operand 11)
+      const uint32_t current_time_id = _.GetOperandTypeId(inst, 11);
+      if (!_.IsFloatScalarType(current_time_id, 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Current Time must be a 32-bit float scalar";
+      }
+
+      // Check for optional Hint and Bits (operands 13 and 14)
+      if (inst->operands().size() > 13) {
+        if (inst->operands().size() != 15) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Hint and Bits are optional together i.e "
+                 << " Either both Hint and Bits should be provided or neither.";
+        }
+
+        // Validate optional Hint and Bits
+        const uint32_t hint_id = _.GetOperandTypeId(inst, 13);
+        if (!_.IsIntScalarType(hint_id, 32)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Hint must be a 32-bit int scalar";
+        }
+        const uint32_t bits_id = _.GetOperandTypeId(inst, 14);
+        if (!_.IsIntScalarType(bits_id, 32)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Bits must be a 32-bit int scalar";
+        }
+      }
+      break;
     }
 
     default:
diff --git a/source/val/validate_scopes.cpp b/source/val/validate_scopes.cpp
index 6b49353..c7ef5f2 100644
--- a/source/val/validate_scopes.cpp
+++ b/source/val/validate_scopes.cpp
@@ -94,7 +94,7 @@
 
   // Vulkan specific rules
   if (spvIsVulkanEnv(_.context()->target_env)) {
-    // Vulkan 1.1 specific rules
+    // Subgroups were not added until 1.1
     if (_.context()->target_env != SPV_ENV_VULKAN_1_0) {
       // Scope for Non Uniform Group Operations must be limited to Subgroup
       if ((spvOpcodeIsNonUniformGroupOperation(opcode) &&
diff --git a/source/val/validate_tensor.cpp b/source/val/validate_tensor.cpp
new file mode 100644
index 0000000..0b54129
--- /dev/null
+++ b/source/val/validate_tensor.cpp
@@ -0,0 +1,250 @@
+// Copyright (c) 2023-2025 Arm Ltd.
+//
+// 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.
+
+// Validates correctness of tensor instructions.
+
+#include "source/opcode.h"
+#include "source/val/validate.h"
+#include "source/val/validation_state.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+bool IsRankedTensor(ValidationState_t& _, uint32_t id) {
+  auto inst = _.FindDef(id);
+  if (!inst || inst->opcode() != spv::Op::OpTypeTensorARM ||
+      inst->words().size() <= 3) {
+    return false;
+  }
+  return true;
+}
+
+uint64_t GetTensorTypeRank(ValidationState_t& _, uint32_t id) {
+  auto inst = _.FindDef(id);
+  if (!inst || inst->opcode() != spv::Op::OpTypeTensorARM ||
+      inst->words().size() <= 3) {
+    return 0;
+  }
+  uint64_t rank = 0;
+  if (!_.EvalConstantValUint64(inst->word(3), &rank)) {
+    return 0;
+  }
+  return rank;
+}
+
+bool IsScalarTypeOrOrArrayOfScalarType(ValidationState_t& _, uint32_t id) {
+  auto inst = _.FindDef(id);
+  if (!inst) {
+    return false;
+  }
+  return _.IsScalarType(id) || (inst->opcode() == spv::Op::OpTypeArray &&
+                                _.IsScalarType(inst->word(2)));
+}
+
+spv_result_t ValidateTensorRead(ValidationState_t& _, const Instruction* inst) {
+  // Result Type must be a scalar type or array of scalar type.
+  if (!IsScalarTypeOrOrArrayOfScalarType(_, inst->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Result Type to be a scalar type or array of "
+              "scalar type.";
+  }
+
+  // Tensor must be a Ranked Tensor.
+  auto op_tensor = inst->word(3);
+  auto inst_tensor = _.FindDef(op_tensor);
+  if (!inst_tensor || !IsRankedTensor(_, inst_tensor->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Tensor to be an OpTypeTensorARM whose Rank is "
+              "specified";
+  }
+
+  // The scalar type must be the same as the Element Type of Tensor.
+  if (_.GetComponentType(inst_tensor->type_id()) !=
+      _.GetComponentType(inst->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Result Type to be the same as the Element Type of "
+              "Tensor.";
+  }
+
+  // Coordinates is an array whose Element Type must be an integer type and
+  // whose Length must be equal to the Rank of Tensor.
+  auto op_coord = inst->word(4);
+  auto inst_coord = _.FindDef(op_coord);
+  auto tensor_rank = GetTensorTypeRank(_, inst_tensor->type_id());
+  if (!_.IsIntArrayType(inst_coord->type_id(), tensor_rank)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Coordinates to be an array whose Element Type is an "
+              "integer type and whose Length is equal to the Rank of Tensor.";
+  }
+
+  // Validate Tensor Operands
+  if (inst->words().size() > 5) {
+    auto toperands = static_cast<spv::TensorOperandsMask>(inst->word(5));
+    if ((toperands & spv::TensorOperandsMask::OutOfBoundsValueARM) !=
+        spv::TensorOperandsMask::MaskNone) {
+      if (inst->words().size() < 7) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "A value must be provided after the OutOfBoundsValueARM "
+                  "Tensor Operand.";
+      }
+      auto op_oobval = inst->word(6);
+      auto inst_oobval = _.FindDef(op_oobval);
+      if (_.GetComponentType(inst_tensor->type_id()) !=
+          _.GetComponentType(inst_oobval->type_id())) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "Expected the type of the OutOfBoundsValueARM value to be "
+                  "the same "
+                  "as the Element Type of Tensor.";
+      }
+    }
+    if ((toperands & spv::TensorOperandsMask::MakeElementAvailableARM) !=
+        spv::TensorOperandsMask::MaskNone) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "MakeElementAvailableARM cannot be used with OpTensorReadARM.";
+    }
+    if (((toperands & spv::TensorOperandsMask::MakeElementVisibleARM) !=
+         spv::TensorOperandsMask::MaskNone) &&
+        ((toperands & spv::TensorOperandsMask::NonPrivateElementARM) ==
+         spv::TensorOperandsMask::MaskNone)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "MakeElementAvailableARM requires NonPrivateElementARM.";
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateTensorWrite(ValidationState_t& _,
+                                 const Instruction* inst) {
+  // Tensor must be a Ranked Tensor.
+  auto op_tensor = inst->word(1);
+  auto inst_tensor = _.FindDef(op_tensor);
+  if (!IsRankedTensor(_, inst_tensor->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Tensor to be an OpTypeTensorARM whose Rank is "
+              "specified";
+  }
+
+  // Coordinates is an array whose Element Type must be an integer type and
+  // whose Length must be equal to the Rank of Tensor.
+  auto op_coord = inst->word(2);
+  auto inst_coord = _.FindDef(op_coord);
+  auto tensor_rank = GetTensorTypeRank(_, inst_tensor->type_id());
+  if (!_.IsIntArrayType(inst_coord->type_id(), tensor_rank)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Coordinates to be an array whose Element Type is an "
+              "integer type and whose Length is equal to the Rank of Tensor.";
+  }
+
+  // Object must be an object of scalar type or array of scalar type.
+  // The scalar type must be the same as the Element Type of Tensor.
+  auto op_object = inst->word(3);
+  auto inst_object = _.FindDef(op_object);
+  if (!IsScalarTypeOrOrArrayOfScalarType(_, inst_object->type_id()) ||
+      (_.GetComponentType(inst_object->type_id()) !=
+       _.GetComponentType(inst_tensor->type_id()))) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Object to be a scalar type or array of scalar "
+              "type that is the same as the Element Type of Tensor.";
+  }
+
+  // Validate Tensor Operands
+  if (inst->words().size() > 5) {
+    auto toperands = static_cast<spv::TensorOperandsMask>(inst->word(4));
+    if ((toperands & spv::TensorOperandsMask::OutOfBoundsValueARM) !=
+        spv::TensorOperandsMask::MaskNone) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "OutOfBoundsValue Tensor Operand not allowed with "
+                "OpTensorWriteARM.";
+    }
+    if ((toperands & spv::TensorOperandsMask::MakeElementVisibleARM) !=
+        spv::TensorOperandsMask::MaskNone) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "MakeElementVisibleARM not allowed with OpTensorWriteARM.";
+    }
+    if (((toperands & spv::TensorOperandsMask::MakeElementAvailableARM) !=
+         spv::TensorOperandsMask::MaskNone) &&
+        ((toperands & spv::TensorOperandsMask::NonPrivateElementARM) ==
+         spv::TensorOperandsMask::MaskNone)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "MakeElementAvailableARM requires NonPrivateElementARM.";
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateTensorQuerySize(ValidationState_t& _,
+                                     const Instruction* inst) {
+  // Check result type
+  if (!_.IsIntScalarType(inst->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Result Type to be an integer type scalar";
+  }
+
+  // Check Tensor operand
+  auto op_tensor = inst->word(3);
+  auto inst_tensor = _.FindDef(op_tensor);
+  if (!inst_tensor || !IsRankedTensor(_, inst_tensor->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected Tensor to be an OpTypeTensorARM whose Rank is "
+              "specified";
+  }
+
+  // Check Dimension operand
+  auto op_dim = inst->word(4);
+  auto inst_dim = _.FindDef(op_dim);
+  if (!spvOpcodeIsConstant(inst_dim->opcode()) ||
+      !_.IsIntScalarType(inst_dim->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Dimension must come from a constant instruction of scalar "
+              "integer type.";
+  }
+
+  auto inst_tensor_type = _.FindDef(inst_tensor->type_id());
+  auto op_tensor_rank = inst_tensor_type->word(3);
+  uint64_t tensor_rank = 0;
+  uint64_t dim;
+  if (_.EvalConstantValUint64(op_tensor_rank, &tensor_rank) &&
+      _.EvalConstantValUint64(op_dim, &dim) && (dim >= tensor_rank)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Dimension (" << dim << ") must be less than the Rank of Tensor ("
+           << tensor_rank << ").";
+  }
+
+  return SPV_SUCCESS;
+}
+
+}  // namespace
+
+// Validates correctness of tensor instructions.
+spv_result_t TensorPass(ValidationState_t& _, const Instruction* inst) {
+  (void)_;
+  const spv::Op opcode = inst->opcode();
+  switch (opcode) {
+    case spv::Op::OpTensorReadARM:
+      return ValidateTensorRead(_, inst);
+    case spv::Op::OpTensorWriteARM:
+      return ValidateTensorWrite(_, inst);
+    case spv::Op::OpTensorQuerySizeARM:
+      return ValidateTensorQuerySize(_, inst);
+    default:
+      break;
+  }
+  return SPV_SUCCESS;
+}
+
+}  // namespace val
+}  // namespace spvtools
diff --git a/source/val/validate_tensor_layout.cpp b/source/val/validate_tensor_layout.cpp
index 35c766b..cbd9c98 100644
--- a/source/val/validate_tensor_layout.cpp
+++ b/source/val/validate_tensor_layout.cpp
@@ -129,8 +129,7 @@
   for (uint32_t i = 0; i < num_values; ++i) {
     const auto val_id = inst->GetOperandAs<uint32_t>(i + 3);
     const auto val = _.FindDef(val_id);
-    if (!val || !_.IsIntScalarType(val->type_id()) ||
-        _.GetBitWidth(val->type_id()) != 32) {
+    if (!val || !_.IsIntScalarType(val->type_id(), 32)) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << spvOpcodeString(inst->opcode()) << " operand <id> "
              << _.getIdName(val_id) << " is not a 32-bit integer.";
diff --git a/source/val/validate_type.cpp b/source/val/validate_type.cpp
index 5101a40..ce8fc75 100644
--- a/source/val/validate_type.cpp
+++ b/source/val/validate_type.cpp
@@ -1,4 +1,6 @@
 // Copyright (c) 2018 Google LLC.
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 // Copyright (c) 2024 NVIDIA Corporation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +17,8 @@
 
 // Ensures type declarations are unique unless allowed by the specification.
 
+#include <optional>
+
 #include "source/opcode.h"
 #include "source/spirv_target_env.h"
 #include "source/val/instruction.h"
@@ -36,6 +40,7 @@
 
   const auto opcode = inst->opcode();
   if (opcode != spv::Op::OpTypeArray && opcode != spv::Op::OpTypeRuntimeArray &&
+      opcode != spv::Op::OpTypeNodePayloadArrayAMDX &&
       opcode != spv::Op::OpTypeStruct && opcode != spv::Op::OpTypePointer &&
       opcode != spv::Op::OpTypeUntypedPointerKHR &&
       !_.RegisterUniqueTypeDeclaration(inst)) {
@@ -55,7 +60,15 @@
   // integers, respectively.
   auto num_bits = inst->GetOperandAs<const uint32_t>(1);
   if (num_bits != 32) {
-    if (num_bits == 8) {
+    if (num_bits == 4) {
+      if (_.HasCapability(spv::Capability::Int4TypeINTEL) ||
+          _.HasCapability(spv::Capability::ArbitraryPrecisionIntegersINTEL)) {
+        return SPV_SUCCESS;
+      }
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Using a 4-bit integer type requires the Int4TypeINTEL "
+                "or ArbitraryPrecisionIntegersINTEL capability.";
+    } else if (num_bits == 8) {
       if (_.features().declare_int8_type) {
         return SPV_SUCCESS;
       }
@@ -76,6 +89,15 @@
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Using a 64-bit integer type requires the Int64 capability.";
     } else {
+      // Check for SPV_INTEL_arbitrary_precision_integers extension
+      if (_.HasCapability(spv::Capability::ArbitraryPrecisionIntegersINTEL)) {
+        if (num_bits == 0) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "OpTypeInt has 0 bits, which is not allowed even with "
+                    "ArbitraryPrecisionIntegersINTEL.";
+        }
+        return SPV_SUCCESS;
+      }
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Invalid number of bits (" << num_bits
              << ") used for OpTypeInt.";
@@ -104,30 +126,78 @@
 }
 
 spv_result_t ValidateTypeFloat(ValidationState_t& _, const Instruction* inst) {
-  // Validates that the number of bits specified for an Int type is valid.
-  // Scalar integer types can be parameterized only with 32-bits.
-  // Int8, Int16, and Int64 capabilities allow using 8-bit, 16-bit, and 64-bit
-  // integers, respectively.
+  // Validates:
+  // - the number of bits specified for a float type is valid
+  // - the fp encoding is valid, and only used on matching bit widths
+  // - required capabilities are declared
   auto num_bits = inst->GetOperandAs<const uint32_t>(1);
+
+  std::optional<spv::FPEncoding> encoding;
+  if (inst->operands().size() > 2) {
+    encoding = inst->GetOperandAs<spv::FPEncoding>(2);
+  }
+  // The number of operands is already checked by the grammar structure.
+  // The fp encoding operand is an optional enum, and there are no further
+  // operands.
+
   if (num_bits == 32) {
+    if (encoding.has_value()) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "32-bit floating point type must not have encoding parameter.";
+    }
     return SPV_SUCCESS;
   }
+
   if (num_bits == 16) {
-    if (_.features().declare_float16_type) {
-      return SPV_SUCCESS;
+    // An absence of FP encoding implies IEEE 754. The Float16 and Float16Buffer
+    // capabilities only enable IEEE 754 binary 16
+    if (!encoding.has_value() && !_.features().declare_float16_type) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Using a 16-bit floating point "
+             << "type requires the Float16 or Float16Buffer capability,"
+                " or an extension that explicitly enables 16-bit floating "
+                "point.";
     }
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Using a 16-bit floating point "
-           << "type requires the Float16 or Float16Buffer capability,"
-              " or an extension that explicitly enables 16-bit floating point.";
+    if (encoding.has_value() &&
+        encoding.value() != spv::FPEncoding::BFloat16KHR) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Unsupported 16-bit floating point encoding ("
+             << static_cast<uint32_t>(encoding.value()) << ").";
+    }
+    return SPV_SUCCESS;
+  }
+  if (num_bits == 8) {
+    if (!_.features().declare_float8_type) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Using a 8-bit floating point "
+             << "type requires the Float8EXT capability.";
+    }
+    if (encoding.has_value()) {
+      const auto enc = encoding.value();
+      if (enc != spv::FPEncoding::Float8E4M3EXT &&
+          enc != spv::FPEncoding::Float8E5M2EXT) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Unsupported 8-bit floating point encoding ("
+               << static_cast<uint32_t>(enc) << ").";
+      }
+    } else {
+      // we don't support fp8 without encoding
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "8-bit floating point type requires an encoding.";
+    }
+    return SPV_SUCCESS;
   }
   if (num_bits == 64) {
-    if (_.HasCapability(spv::Capability::Float64)) {
-      return SPV_SUCCESS;
+    if (!_.HasCapability(spv::Capability::Float64)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Using a 64-bit floating point "
+             << "type requires the Float64 capability.";
     }
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Using a 64-bit floating point "
-           << "type requires the Float64 capability.";
+    if (encoding.has_value()) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "64-bit floating point type must not have encoding parameter.";
+    }
+    return SPV_SUCCESS;
   }
   return _.diag(SPV_ERROR_INVALID_DATA, inst)
          << "Invalid number of bits (" << num_bits << ") used for OpTypeFloat.";
@@ -137,7 +207,24 @@
   const auto component_index = 1;
   const auto component_id = inst->GetOperandAs<uint32_t>(component_index);
   const auto component_type = _.FindDef(component_id);
-  if (!component_type || !spvOpcodeIsScalarType(component_type->opcode())) {
+  if (component_type) {
+    bool isPointer = component_type->opcode() == spv::Op::OpTypePointer;
+    bool isScalar = spvOpcodeIsScalarType(component_type->opcode());
+
+    if (_.HasCapability(spv::Capability::MaskedGatherScatterINTEL) &&
+        !isPointer && !isScalar) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "Invalid OpTypeVector Component Type<id> "
+             << _.getIdName(component_id)
+             << ": Expected a scalar or pointer type when using the "
+                "SPV_INTEL_masked_gather_scatter extension.";
+    } else if (!_.HasCapability(spv::Capability::MaskedGatherScatterINTEL) &&
+               !isScalar) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "OpTypeVector Component Type <id> " << _.getIdName(component_id)
+             << " is not a scalar type.";
+    }
+  } else {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpTypeVector Component Type <id> " << _.getIdName(component_id)
            << " is not a scalar type.";
@@ -149,18 +236,66 @@
   auto num_components = inst->GetOperandAs<const uint32_t>(2);
   if (num_components == 2 || num_components == 3 || num_components == 4) {
     return SPV_SUCCESS;
+  } else if (num_components > 0 &&
+             _.HasCapability(spv::Capability::LongVectorEXT)) {
+    return SPV_SUCCESS;
   } else if (num_components == 8 || num_components == 16) {
     if (_.HasCapability(spv::Capability::Vector16)) {
       return SPV_SUCCESS;
     }
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Having " << num_components << " components for "
-           << spvOpcodeString(inst->opcode())
-           << " requires the Vector16 capability";
+           << _.VkErrorID(12295) << "Having " << num_components
+           << " components for " << spvOpcodeString(inst->opcode())
+           << " requires the Vector16 or LongVectorEXT capability";
   } else {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Illegal number of components (" << num_components << ") for "
-           << spvOpcodeString(inst->opcode());
+           << _.VkErrorID(12295) << "Illegal number of components ("
+           << num_components << ") for " << spvOpcodeString(inst->opcode());
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateTypeVectorIdEXT(ValidationState_t& _,
+                                     const Instruction* inst) {
+  const auto component_index = 1;
+  const auto component_type_id = inst->GetOperandAs<uint32_t>(component_index);
+  const auto component_type = _.FindDef(component_type_id);
+  if (!component_type || !_.IsScalarType(component_type_id) ||
+      (!_.HasCapability(spv::Capability::LongVectorEXT) &&
+       spv::Op::OpTypeBool == component_type->opcode())) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "OpTypeVectorIdEXT Component Type <id> "
+           << _.getIdName(component_type_id)
+           << " is not a scalar numerical type.";
+  }
+
+  const auto num_components_index = 2;
+  const auto num_components_id =
+      inst->GetOperandAs<uint32_t>(num_components_index);
+  const auto num_components = _.FindDef(num_components_id);
+  if (!num_components || !spvOpcodeIsConstant(num_components->opcode())) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "OpTypeVectorIdEXT component count <id> "
+           << _.getIdName(num_components_id)
+           << " is not a scalar constant type.";
+  }
+
+  if (!_.IsIntScalarType(num_components->type_id(), 32)) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "OpTypeVectorIdEXT component count type <id> "
+           << _.getIdName(num_components->type_id())
+           << " is not a 32-bit integer type.";
+  }
+
+  uint64_t num_components_value;
+  if (_.EvalConstantValUint64(num_components_id, &num_components_value)) {
+    if (num_components_value == 0) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "OpTypeVectorIdEXT component count <id> "
+             << _.getIdName(num_components_id)
+             << " default value must be at least 1: found 0.";
+    }
   }
 
   return SPV_SUCCESS;
@@ -212,6 +347,19 @@
            << " is a void type.";
   }
 
+  if (_.HasCapability(spv::Capability::Shader)) {
+    if (element_type->opcode() == spv::Op::OpTypeStruct &&
+        (_.HasDecoration(element_type->id(), spv::Decoration::Block) ||
+         _.HasDecoration(element_type->id(), spv::Decoration::BufferBlock))) {
+      if (_.HasDecoration(inst->id(), spv::Decoration::ArrayStride) ||
+          _.HasDecoration(inst->id(), spv::Decoration::ArrayStrideIdEXT)) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "Array containing a Block or BufferBlock must not be "
+                  "decorated with ArrayStride or ArrayStrideIdEXT";
+      }
+    }
+  }
+
   if (spvIsVulkanEnv(_.context()->target_env) &&
       element_type->opcode() == spv::Op::OpTypeRuntimeArray) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
@@ -270,6 +418,19 @@
            << " is a void type.";
   }
 
+  if (_.HasCapability(spv::Capability::Shader)) {
+    if (element_type->opcode() == spv::Op::OpTypeStruct &&
+        (_.HasDecoration(element_type->id(), spv::Decoration::Block) ||
+         _.HasDecoration(element_type->id(), spv::Decoration::BufferBlock))) {
+      if (_.HasDecoration(inst->id(), spv::Decoration::ArrayStride) ||
+          _.HasDecoration(inst->id(), spv::Decoration::ArrayStrideIdEXT)) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "Array containing a Block or BufferBlock must not be "
+                  "decorated with ArrayStride or ArrayStrideIdEXT";
+      }
+    }
+  }
+
   if (spvIsVulkanEnv(_.context()->target_env) &&
       element_type->opcode() == spv::Op::OpTypeRuntimeArray) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
@@ -307,10 +468,9 @@
              << "Structure <id> " << _.getIdName(member_type_id)
              << " contains members with BuiltIn decoration. Therefore this "
              << "structure may not be contained as a member of another "
-             << "structure "
-             << "type. Structure <id> " << _.getIdName(struct_id)
-             << " contains structure <id> " << _.getIdName(member_type_id)
-             << ".";
+             << "structure " << "type. Structure <id> "
+             << _.getIdName(struct_id) << " contains structure <id> "
+             << _.getIdName(member_type_id) << ".";
     }
 
     if (spvIsVulkanEnv(_.context()->target_env) &&
@@ -340,13 +500,20 @@
   // Struct members start at word 2 of OpTypeStruct instruction.
   for (size_t word_i = 2; word_i < inst->words().size(); ++word_i) {
     auto member = inst->word(word_i);
-    auto memberTypeInstr = _.FindDef(member);
-    if (memberTypeInstr && spv::Op::OpTypeStruct == memberTypeInstr->opcode()) {
-      if (_.HasDecoration(memberTypeInstr->id(), spv::Decoration::Block) ||
-          _.HasDecoration(memberTypeInstr->id(),
-                          spv::Decoration::BufferBlock) ||
-          _.GetHasNestedBlockOrBufferBlockStruct(memberTypeInstr->id()))
-        has_nested_blockOrBufferBlock_struct = true;
+    if (_.ContainsType(
+            member,
+            [&_](const Instruction* type_inst) {
+              if (type_inst->opcode() == spv::Op::OpTypeStruct &&
+                  (_.HasDecoration(type_inst->id(), spv::Decoration::Block) ||
+                   _.HasDecoration(type_inst->id(),
+                                   spv::Decoration::BufferBlock))) {
+                return true;
+              }
+              return false;
+            },
+            /* traverse_all_types = */ false)) {
+      has_nested_blockOrBufferBlock_struct = true;
+      break;
     }
   }
 
@@ -363,7 +530,9 @@
   std::unordered_set<uint32_t> built_in_members;
   for (auto decoration : _.id_decorations(struct_id)) {
     if (decoration.dec_type() == spv::Decoration::BuiltIn &&
-        decoration.struct_member_index() != Decoration::kInvalidMember) {
+        decoration.struct_member_index() != Decoration::kInvalidMember &&
+        decoration.builtin() != spv::BuiltIn::ResourceHeapEXT &&
+        decoration.builtin() != spv::BuiltIn::SamplerHeapEXT) {
       built_in_members.insert(decoration.struct_member_index());
     }
   }
@@ -381,25 +550,32 @@
     _.RegisterStructTypeWithBuiltInMember(struct_id);
   }
 
-  const auto isOpaqueType = [&_](const Instruction* opaque_inst) {
-    auto opcode = opaque_inst->opcode();
-    if (_.HasCapability(spv::Capability::BindlessTextureNV) &&
-        (opcode == spv::Op::OpTypeImage || opcode == spv::Op::OpTypeSampler ||
-         opcode == spv::Op::OpTypeSampledImage)) {
-      return false;
-    } else if (spvOpcodeIsBaseOpaqueType(opcode)) {
-      return true;
-    }
-    return false;
-  };
-
   if (spvIsVulkanEnv(_.context()->target_env) &&
-      !_.options()->before_hlsl_legalization &&
-      _.ContainsType(inst->id(), isOpaqueType)) {
-    return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << _.VkErrorID(4667) << "In "
-           << spvLogStringForEnv(_.context()->target_env)
-           << ", OpTypeStruct must not contain an opaque type.";
+      !_.options()->before_hlsl_legalization) {
+    // By default, without extensions, all opaque types are invalid in a struct.
+    // Check the exceptions allowed by the various capabilities
+    const auto IsInvalidOpaqueType = [&_](const Instruction* opaque_inst) {
+      const spv::Op opcode = opaque_inst->opcode();
+      if (_.HasCapability(spv::Capability::DescriptorHeapEXT) &&
+          _.IsDescriptorType(opcode)) {
+        return false;
+      } else if (_.HasCapability(spv::Capability::BindlessTextureNV) &&
+                 (opcode == spv::Op::OpTypeImage ||
+                  opcode == spv::Op::OpTypeSampler ||
+                  opcode == spv::Op::OpTypeSampledImage)) {
+        return false;
+      }
+      return spvOpcodeIsBaseOpaqueType(opcode);
+    };
+
+    if (_.ContainsType(inst->id(), IsInvalidOpaqueType)) {
+      const uint32_t vuid =
+          _.HasCapability(spv::Capability::DescriptorHeapEXT) ? 11482 : 4667;
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << _.VkErrorID(vuid) << "In "
+             << spvLogStringForEnv(_.context()->target_env)
+             << ", OpTypeStruct must not contain an invalid opaque type.";
+    }
   }
 
   return SPV_SUCCESS;
@@ -429,6 +605,9 @@
       // a storage image.
       if (sampled == 2) _.RegisterPointerToStorageImage(inst->id());
     }
+    if (type->opcode() == spv::Op::OpTypeTensorARM) {
+      _.RegisterPointerToTensor(inst->id());
+    }
   }
 
   if (!_.IsValidStorageClass(storage_class)) {
@@ -477,12 +656,20 @@
   }
 
   // The only valid uses of OpTypeFunction are in an OpFunction, debugging, or
-  // decoration instruction.
+  // decoration instruction, or in OpTypePointer when FunctionPointersINTEL
+  // capability is enabled.
   for (auto& pair : inst->uses()) {
     const auto* use = pair.first;
     if (use->opcode() != spv::Op::OpFunction &&
+        use->opcode() != spv::Op::OpAsmINTEL &&
         !spvOpcodeIsDebug(use->opcode()) && !use->IsNonSemantic() &&
         !spvOpcodeIsDecoration(use->opcode())) {
+      // Check if this is OpTypePointer with FunctionPointersINTEL capability
+      if (use->opcode() == spv::Op::OpTypePointer &&
+          _.HasCapability(spv::Capability::FunctionPointersINTEL)) {
+        // Allow OpTypePointer to use function types with this capability
+        continue;
+      }
       return _.diag(SPV_ERROR_INVALID_ID, use)
              << "Invalid use of function type result id "
              << _.getIdName(inst->id()) << ".";
@@ -541,6 +728,24 @@
            << " is not a scalar numerical type.";
   }
 
+  if (_.IsBfloat16ScalarType(component_type_id)) {
+    if (!_.HasCapability(spv::Capability::BFloat16CooperativeMatrixKHR)) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "OpTypeCooperativeMatrix Component Type <id> "
+             << _.getIdName(component_type_id)
+             << "require BFloat16CooperativeMatrixKHR be declared.";
+    }
+  }
+
+  if (_.IsFP8ScalarType(component_type_id)) {
+    if (!_.HasCapability(spv::Capability::Float8CooperativeMatrixEXT)) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "OpTypeCooperativeMatrix Component Type <id> "
+             << _.getIdName(component_type_id)
+             << "require Float8CooperativeMatrixEXT be declared.";
+    }
+  }
+
   const auto scope_index = 2;
   const auto scope_id = inst->GetOperandAs<uint32_t>(scope_index);
   const auto scope = _.FindDef(scope_id);
@@ -583,9 +788,10 @@
     }
   }
 
-  uint64_t scope_value;
-  if (_.EvalConstantValUint64(scope_id, &scope_value)) {
-    if (scope_value == static_cast<uint32_t>(spv::Scope::Workgroup)) {
+  uint64_t scope_raw_value;
+  if (_.EvalConstantValUint64(scope_id, &scope_raw_value)) {
+    spv::Scope scope_value = static_cast<spv::Scope>(scope_raw_value);
+    if (scope_value == spv::Scope::Workgroup) {
       for (auto entry_point_id : _.entry_points()) {
         if (!_.EntryPointHasLocalSizeOrId(entry_point_id)) {
           return _.diag(SPV_ERROR_INVALID_ID, inst)
@@ -612,6 +818,13 @@
         }
       }
     }
+    if (scope_value != spv::Scope::Workgroup &&
+        scope_value != spv::Scope::Subgroup) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << _.VkErrorID(12243)
+             << "OpTypeCooperativeMatrixKHR Scope is limited to Workgroup and "
+                "Subgroup";
+    }
   }
 
   return SPV_SUCCESS;
@@ -635,8 +848,23 @@
       case spv::StorageClass::Uniform:
       case spv::StorageClass::PushConstant:
         break;
+      case spv::StorageClass::UniformConstant:
+        if (!_.HasCapability(spv::Capability::DescriptorHeapEXT)) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "UniformConstant storage class untyped pointers in Vulkan "
+                    "require DescriptorHeapEXT be declared";
+        }
+        break;
+      case spv::StorageClass::Image:
+        if (!_.HasCapability(spv::Capability::DescriptorHeapEXT)) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "Image storage class untyped pointers in Vulkan "
+                    "require DescriptorHeapEXT be declared";
+        }
+        break;
       default:
         return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << _.VkErrorID(11417)
                << "In Vulkan, untyped pointers can only be used in an "
                   "explicitly laid out storage class";
     }
@@ -648,8 +876,7 @@
   const auto dim_index = 1;
   const auto dim_id = inst->GetOperandAs<uint32_t>(dim_index);
   const auto dim = _.FindDef(dim_id);
-  if (!dim || !_.IsIntScalarType(dim->type_id()) ||
-      _.GetBitWidth(dim->type_id()) != 32) {
+  if (!dim || !_.IsIntScalarType(dim->type_id(), 32)) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << spvOpcodeString(inst->opcode()) << " Dim <id> "
            << _.getIdName(dim_id) << " is not a 32-bit integer.";
@@ -716,8 +943,7 @@
   for (size_t p_index = 3; p_index < inst->operands().size(); ++p_index) {
     auto p_id = inst->GetOperandAs<uint32_t>(p_index);
     const auto p = _.FindDef(p_id);
-    if (!p || !_.IsIntScalarType(p->type_id()) ||
-        _.GetBitWidth(p->type_id()) != 32) {
+    if (!p || !_.IsIntScalarType(p->type_id(), 32)) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << spvOpcodeString(inst->opcode()) << " Permutation <id> "
              << _.getIdName(p_id) << " is not a 32-bit integer.";
@@ -752,6 +978,94 @@
 
   return SPV_SUCCESS;
 }
+
+spv_result_t ValidateTypeTensorARM(ValidationState_t& _,
+                                   const Instruction* inst) {
+  // Element type must be a scalar type
+  const auto element_type_index = 1;
+  const auto element_type_id = inst->GetOperandAs<uint32_t>(element_type_index);
+  const auto element_type = _.FindDef(element_type_id);
+  if (!element_type || (!_.IsFloatScalarType(element_type_id) &&
+                        !_.IsIntScalarType(element_type_id) &&
+                        !_.IsBoolScalarType(element_type_id))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "OpTypeTensorARM Element Type <id> "
+           << _.getIdName(element_type_id) << " is not a scalar type.";
+  }
+
+  if (inst->operands().size() < 3) {
+    return SPV_SUCCESS;
+  }
+
+  // Rank must be constant instruction with scalar integer type
+  const auto rank_index = 2;
+  const auto rank_id = inst->GetOperandAs<uint32_t>(rank_index);
+  const auto rank = _.FindDef(rank_id);
+  if (!rank || !spvOpcodeIsConstant(rank->opcode())) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "OpTypeTensorARM Rank <id> " << _.getIdName(rank_id)
+           << " is not a constant instruction.";
+  }
+  // Rank must have scalar integer type
+  if (!rank || !_.IsIntScalarType(rank->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "OpTypeTensorARM Rank <id> " << _.getIdName(rank_id)
+           << " does not have a scalar integer type.";
+  }
+  // Rank must be greater than 0
+  uint64_t rank_value = 0;
+  if (_.EvalConstantValUint64(rank_id, &rank_value) && rank_value == 0) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "OpTypeTensorARM Rank <id> " << _.getIdName(rank_id)
+           << " must define a value greater than 0.";
+  }
+
+  if (inst->operands().size() < 4) {
+    return SPV_SUCCESS;
+  }
+
+  // Shape must be constant instruction
+  const auto shape_index = 3;
+  const auto shape_id = inst->GetOperandAs<uint32_t>(shape_index);
+  const auto shape = _.FindDef(shape_id);
+  if (!shape || !spvOpcodeIsConstant(shape->opcode())) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "OpTypeTensorARM Shape <id> " << _.getIdName(shape_id)
+           << " is not a constant instruction.";
+  }
+
+  // Shape must be array of integer of length rank
+  if (!_.IsIntArrayType(shape->type_id(), rank_value)) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "OpTypeTensorARM Shape <id> " << _.getIdName(shape_id)
+           << " is not an array of integer type whose Length is equal to Rank.";
+  }
+
+  // Shape constituents must be greater than 0
+  for (size_t i = 2; i < shape->operands().size(); i++) {
+    const auto s_id = shape->GetOperandAs<uint32_t>(i);
+    uint64_t s_val = 0;
+    if (_.EvalConstantValUint64(s_id, &s_val) && s_val == 0) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "OpTypeTensorARM Shape constituent " << i - 2
+             << " is not greater than 0.";
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateTypeBufferEXT(ValidationState_t& _,
+                                   const Instruction* inst) {
+  auto sc = inst->GetOperandAs<spv::StorageClass>(1);
+  if (sc != spv::StorageClass::Uniform &&
+      sc != spv::StorageClass::StorageBuffer) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << spvOpcodeString(inst->opcode())
+           << " StorageClass could only be StorageBuffer or Uniform.";
+  }
+  return SPV_SUCCESS;
+}
 }  // namespace
 
 spv_result_t TypePass(ValidationState_t& _, const Instruction* inst) {
@@ -797,6 +1111,9 @@
     case spv::Op::OpTypeCooperativeMatrixKHR:
       if (auto error = ValidateTypeCooperativeMatrix(_, inst)) return error;
       break;
+    case spv::Op::OpTypeVectorIdEXT:
+      if (auto error = ValidateTypeVectorIdEXT(_, inst)) return error;
+      break;
     case spv::Op::OpTypeUntypedPointerKHR:
       if (auto error = ValidateTypeUntypedPointerKHR(_, inst)) return error;
       break;
@@ -806,6 +1123,12 @@
     case spv::Op::OpTypeTensorViewNV:
       if (auto error = ValidateTypeTensorViewNV(_, inst)) return error;
       break;
+    case spv::Op::OpTypeTensorARM:
+      if (auto error = ValidateTypeTensorARM(_, inst)) return error;
+      break;
+    case spv::Op::OpTypeBufferEXT:
+      if (auto error = ValidateTypeBufferEXT(_, inst)) return error;
+      break;
     default:
       break;
   }
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index da9174f..a950f71 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -1,4 +1,6 @@
 // Copyright (c) 2015-2016 The Khronos Group Inc.
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -15,12 +17,14 @@
 #include "source/val/validation_state.h"
 
 #include <cassert>
+#include <cstdint>
 #include <stack>
 #include <utility>
 
 #include "source/opcode.h"
 #include "source/spirv_constant.h"
 #include "source/spirv_target_env.h"
+#include "source/table2.h"
 #include "source/util/make_unique.h"
 #include "source/val/basic_block.h"
 #include "source/val/construct.h"
@@ -39,14 +43,17 @@
 
   switch (op) {
     case spv::Op::OpCapability:
+    case spv::Op::OpConditionalCapabilityINTEL:
       return kLayoutCapabilities;
     case spv::Op::OpExtension:
+    case spv::Op::OpConditionalExtensionINTEL:
       return kLayoutExtensions;
     case spv::Op::OpExtInstImport:
       return kLayoutExtInstImport;
     case spv::Op::OpMemoryModel:
       return kLayoutMemoryModel;
     case spv::Op::OpEntryPoint:
+    case spv::Op::OpConditionalEntryPointINTEL:
       return kLayoutEntryPoint;
     case spv::Op::OpExecutionMode:
     case spv::Op::OpExecutionModeId:
@@ -63,14 +70,22 @@
       return kLayoutDebug3;
     case spv::Op::OpDecorate:
     case spv::Op::OpMemberDecorate:
+    case spv::Op::OpMemberDecorateIdEXT:
     case spv::Op::OpGroupDecorate:
     case spv::Op::OpGroupMemberDecorate:
     case spv::Op::OpDecorationGroup:
     case spv::Op::OpDecorateId:
     case spv::Op::OpDecorateStringGOOGLE:
     case spv::Op::OpMemberDecorateStringGOOGLE:
+    // SPV_INTEL_memory_access_aliasing: alias scope instructions go in the
+    // annotations section so that OpDecorateId can reference them without
+    // requiring a forward reference across layout sections.
+    case spv::Op::OpAliasDomainDeclINTEL:
+    case spv::Op::OpAliasScopeDeclINTEL:
+    case spv::Op::OpAliasScopeListDeclINTEL:
       return kLayoutAnnotations;
     case spv::Op::OpTypeForwardPointer:
+    case spv::Op::OpTypeTaskSequenceINTEL:
       return kLayoutTypes;
     case spv::Op::OpVariable:
     case spv::Op::OpUntypedVariableKHR:
@@ -81,6 +96,9 @@
       // spv::Op::OpExtInst is only allowed in types section for certain
       // extended instruction sets. This will be checked separately.
       if (current_section == kLayoutTypes) return kLayoutTypes;
+      // SpvOpExtInst is allowed in graph definitions.
+      if (current_section == kLayoutGraphDefinitions)
+        return kLayoutGraphDefinitions;
       return kLayoutFunctionDefinitions;
     case spv::Op::OpLine:
     case spv::Op::OpNoLine:
@@ -95,6 +113,16 @@
       return kLayoutFunctionDefinitions;
     case spv::Op::OpSamplerImageAddressingModeNV:
       return kLayoutSamplerImageAddressMode;
+    case spv::Op::OpGraphEntryPointARM:
+    case spv::Op::OpGraphARM:
+    case spv::Op::OpGraphInputARM:
+    case spv::Op::OpGraphSetOutputARM:
+    case spv::Op::OpGraphEndARM:
+      return kLayoutGraphDefinitions;
+    case spv::Op::OpCompositeExtract:
+      if (current_section == kLayoutGraphDefinitions)
+        return kLayoutGraphDefinitions;
+      return kLayoutFunctionDefinitions;
     default:
       break;
   }
@@ -170,6 +198,7 @@
       pointer_size_and_alignment_(0),
       sampler_image_addressing_mode_(0),
       in_function_(false),
+      graph_definition_region_(kGraphDefinitionOutside),
       num_of_warnings_(0),
       max_num_of_warnings_(max_warnings) {
   assert(opt && "Validator options may not be Null.");
@@ -358,6 +387,10 @@
          module_functions_.back().current_block() != nullptr;
 }
 
+GraphDefinitionRegion ValidationState_t::graph_definition_region() const {
+  return graph_definition_region_;
+}
+
 void ValidationState_t::RegisterCapability(spv::Capability cap) {
   // Avoid redundant work.  Otherwise the recursion could induce work
   // quadrdatic in the capability dependency depth. (Ok, not much, but
@@ -365,11 +398,11 @@
   if (module_capabilities_.contains(cap)) return;
 
   module_capabilities_.insert(cap);
-  spv_operand_desc desc;
-  if (SPV_SUCCESS == grammar_.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY,
-                                            uint32_t(cap), &desc)) {
-    for (auto capability :
-         CapabilitySet(desc->numCapabilities, desc->capabilities)) {
+  const spvtools::OperandDesc* desc = nullptr;
+  if (SPV_SUCCESS == spvtools::LookupOperand(SPV_OPERAND_TYPE_CAPABILITY,
+                                             uint32_t(cap), &desc)) {
+    for (auto capability : CapabilitySet(desc->capabilities_range.count(),
+                                         desc->capabilities().data())) {
       RegisterCapability(capability);
     }
   }
@@ -395,6 +428,9 @@
     case spv::Capability::Float16Buffer:
       features_.declare_float16_type = true;
       break;
+    case spv::Capability::Float8EXT:
+      features_.declare_float8_type = true;
+      break;
     case spv::Capability::StorageUniformBufferBlock16:
     case spv::Capability::StorageUniform16:
     case spv::Capability::StoragePushConstant16:
@@ -525,6 +561,13 @@
   return SPV_SUCCESS;
 }
 
+void ValidationState_t::SetGraphDefinitionRegion(GraphDefinitionRegion region) {
+  assert((region == kGraphDefinitionOutside &&
+          graph_definition_region_ == kGraphDefinitionOutputs) ||
+         region >= graph_definition_region_);
+  graph_definition_region_ = region;
+}
+
 Instruction* ValidationState_t::AddOrderedInstruction(
     const spv_parsed_instruction_t* inst) {
   ordered_instructions_.emplace_back(inst);
@@ -671,13 +714,32 @@
                 *message =
                     errorVUID +
                     "in Vulkan environment, Workgroup Storage Class is limited "
-                    "to MeshNV, TaskNV, and GLCompute execution model";
+                    "to MeshEXT, TaskEXT, MeshNV, TaskNV, and GLCompute "
+                    "execution model";
               }
               return false;
             }
             return true;
           });
     }
+
+    if (storage_class == spv::StorageClass::TileImageEXT) {
+      std::string errorVUID = VkErrorID(8720);
+      function(consumer->function()->id())
+          ->RegisterExecutionModelLimitation(
+              [errorVUID](spv::ExecutionModel model, std::string* message) {
+                if (model != spv::ExecutionModel::Fragment) {
+                  if (message) {
+                    *message = errorVUID +
+                               "in Vulkan environment, TileImageEXT Storage "
+                               "Class is limited "
+                               "to Fragment execution model";
+                  }
+                  return false;
+                }
+                return true;
+              });
+    }
   }
 
   if (storage_class == spv::StorageClass::CallableDataKHR) {
@@ -824,6 +886,22 @@
           }
           return true;
         });
+  } else if (storage_class == spv::StorageClass::HitObjectAttributeEXT) {
+    function(consumer->function()->id())
+        ->RegisterExecutionModelLimitation([](spv::ExecutionModel model,
+                                              std::string* message) {
+          if (model != spv::ExecutionModel::RayGenerationKHR &&
+              model != spv::ExecutionModel::ClosestHitKHR &&
+              model != spv::ExecutionModel::MissKHR) {
+            if (message) {
+              *message =
+                  "HitObjectAttributeEXT Storage Class is limited to "
+                  "RayGenerationKHR, ClosestHitKHR or MissKHR execution model";
+            }
+            return false;
+          }
+          return true;
+        });
   }
 }
 
@@ -868,9 +946,12 @@
     case spv::Op::OpTypeFloat:
     case spv::Op::OpTypeInt:
     case spv::Op::OpTypeBool:
+    case spv::Op::OpTypePointer:
+    case spv::Op::OpTypeUntypedPointerKHR:
       return id;
 
     case spv::Op::OpTypeArray:
+    case spv::Op::OpTypeRuntimeArray:
       return inst->word(2);
 
     case spv::Op::OpTypeVector:
@@ -881,6 +962,10 @@
 
     case spv::Op::OpTypeCooperativeMatrixNV:
     case spv::Op::OpTypeCooperativeMatrixKHR:
+    case spv::Op::OpTypeVectorIdEXT:
+      return inst->word(2);
+
+    case spv::Op::OpTypeTensorARM:
       return inst->word(2);
 
     default:
@@ -912,6 +997,15 @@
       // Actual dimension isn't known, return 0
       return 0;
 
+    case spv::Op::OpTypeVectorIdEXT: {
+      uint64_t value = 0;
+      if (EvalConstantValUint64(inst->word(3), &value)) {
+        return static_cast<uint32_t>(value);
+      }
+
+      return 0;
+    }
+
     default:
       break;
   }
@@ -927,11 +1021,20 @@
   const Instruction* inst = FindDef(component_type_id);
   assert(inst);
 
-  if (inst->opcode() == spv::Op::OpTypeFloat ||
-      inst->opcode() == spv::Op::OpTypeInt)
-    return inst->word(2);
-
-  if (inst->opcode() == spv::Op::OpTypeBool) return 1;
+  switch (inst->opcode()) {
+    case spv::Op::OpTypeFloat:
+    case spv::Op::OpTypeInt:
+      return inst->word(2);
+    case spv::Op::OpTypeBool:
+      return 1;
+    case spv::Op::OpTypePointer:
+    case spv::Op::OpTypeUntypedPointerKHR:
+      assert(inst->GetOperandAs<spv::StorageClass>(1) ==
+             spv::StorageClass::PhysicalStorageBuffer);
+      return 64;  // all pointers to another PSB is 64-bit
+    default:
+      break;
+  }
 
   assert(0);
   return 0;
@@ -942,9 +1045,139 @@
   return inst && inst->opcode() == spv::Op::OpTypeVoid;
 }
 
-bool ValidationState_t::IsFloatScalarType(uint32_t id) const {
+bool ValidationState_t::IsScalarType(uint32_t id) const {
+  return IsIntScalarType(id) || IsFloatScalarType(id) || IsBoolScalarType(id);
+}
+
+bool ValidationState_t::IsVectorType(uint32_t id) const {
   const Instruction* inst = FindDef(id);
-  return inst && inst->opcode() == spv::Op::OpTypeFloat;
+  if (!inst) {
+    return false;
+  }
+
+  return inst->opcode() == spv::Op::OpTypeVector ||
+         inst->opcode() == spv::Op::OpTypeVectorIdEXT;
+}
+
+bool ValidationState_t::IsArrayType(uint32_t id, uint64_t length) const {
+  const Instruction* inst = FindDef(id);
+  if (!inst || inst->opcode() != spv::Op::OpTypeArray) {
+    return false;
+  }
+  if (length != 0) {
+    const auto len_id = inst->GetOperandAs<uint32_t>(2);
+    const auto len = FindDef(len_id);
+    uint64_t len_value = 0;
+    if (!len || !spvOpcodeIsConstant(len->opcode()) ||
+        (EvalConstantValUint64(len_id, &len_value) && (length != len_value))) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool ValidationState_t::IsBfloat16ScalarType(uint32_t id) const {
+  const Instruction* inst = FindDef(id);
+  if (inst && inst->opcode() == spv::Op::OpTypeFloat) {
+    if (inst->words().size() > 3) {
+      if (inst->GetOperandAs<spv::FPEncoding>(2) ==
+          spv::FPEncoding::BFloat16KHR) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+bool ValidationState_t::IsBfloat16VectorType(uint32_t id) const {
+  const Instruction* inst = FindDef(id);
+  if (!inst) {
+    return false;
+  }
+
+  if (IsVectorType(id)) {
+    return IsBfloat16ScalarType(GetComponentType(id));
+  }
+
+  return false;
+}
+
+bool ValidationState_t::IsBfloat16CoopMatType(uint32_t id) const {
+  const Instruction* inst = FindDef(id);
+  if (!inst) {
+    return false;
+  }
+
+  if (inst->opcode() == spv::Op::OpTypeCooperativeMatrixKHR) {
+    return IsBfloat16ScalarType(inst->word(2));
+  }
+
+  return false;
+}
+
+bool ValidationState_t::IsBfloat16Type(uint32_t id) const {
+  return IsBfloat16ScalarType(id) || IsBfloat16VectorType(id) ||
+         IsBfloat16CoopMatType(id);
+}
+
+bool ValidationState_t::IsFP8ScalarType(uint32_t id) const {
+  const Instruction* inst = FindDef(id);
+  if (inst && inst->opcode() == spv::Op::OpTypeFloat) {
+    if (inst->words().size() > 3) {
+      auto encoding = inst->GetOperandAs<spv::FPEncoding>(2);
+      if ((encoding == spv::FPEncoding::Float8E4M3EXT) ||
+          (encoding == spv::FPEncoding::Float8E5M2EXT)) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+bool ValidationState_t::IsFP8VectorType(uint32_t id) const {
+  const Instruction* inst = FindDef(id);
+  if (!inst) {
+    return false;
+  }
+
+  if (IsVectorType(id)) {
+    return IsFP8ScalarType(GetComponentType(id));
+  }
+
+  return false;
+}
+
+bool ValidationState_t::IsFP8CoopMatType(uint32_t id) const {
+  const Instruction* inst = FindDef(id);
+  if (!inst) {
+    return false;
+  }
+
+  if (inst->opcode() == spv::Op::OpTypeCooperativeMatrixKHR) {
+    return IsFP8ScalarType(inst->word(2));
+  }
+
+  return false;
+}
+
+bool ValidationState_t::IsFP8Type(uint32_t id) const {
+  return IsFP8ScalarType(id) || IsFP8VectorType(id) || IsFP8CoopMatType(id);
+}
+
+bool ValidationState_t::IsFloatScalarType(uint32_t id, uint32_t width) const {
+  const Instruction* inst = FindDef(id);
+  bool is_float = inst && inst->opcode() == spv::Op::OpTypeFloat;
+  if (!is_float) {
+    return false;
+  }
+  if ((width != 0) && (width != inst->word(2))) {
+    return false;
+  }
+  return true;
+}
+
+bool ValidationState_t::IsFloatArrayType(uint32_t id) const {
+  return IsArrayType(id) && IsFloatScalarType(GetComponentType(id));
 }
 
 bool ValidationState_t::IsFloatVectorType(uint32_t id) const {
@@ -953,7 +1186,7 @@
     return false;
   }
 
-  if (inst->opcode() == spv::Op::OpTypeVector) {
+  if (IsVectorType(id)) {
     return IsFloatScalarType(GetComponentType(id));
   }
 
@@ -961,10 +1194,7 @@
 }
 
 bool ValidationState_t::IsFloat16Vector2Or4Type(uint32_t id) const {
-  const Instruction* inst = FindDef(id);
-  assert(inst);
-
-  if (inst->opcode() == spv::Op::OpTypeVector) {
+  if (IsVectorType(id)) {
     uint32_t vectorDim = GetDimension(id);
     return IsFloatScalarType(GetComponentType(id)) &&
            (vectorDim == 2 || vectorDim == 4) &&
@@ -984,29 +1214,34 @@
     return true;
   }
 
-  if (inst->opcode() == spv::Op::OpTypeVector) {
+  if (IsVectorType(id)) {
     return IsFloatScalarType(GetComponentType(id));
   }
 
   return false;
 }
 
-bool ValidationState_t::IsIntScalarType(uint32_t id) const {
+bool ValidationState_t::IsIntScalarType(uint32_t id, uint32_t width) const {
   const Instruction* inst = FindDef(id);
-  return inst && inst->opcode() == spv::Op::OpTypeInt;
-}
-
-bool ValidationState_t::IsIntArrayType(uint32_t id) const {
-  const Instruction* inst = FindDef(id);
-  if (!inst) {
+  bool is_int = inst && inst->opcode() == spv::Op::OpTypeInt;
+  if (!is_int) {
     return false;
   }
-
-  if (inst->opcode() == spv::Op::OpTypeArray) {
-    return IsIntScalarType(GetComponentType(id));
+  if ((width != 0) && (width != inst->word(2))) {
+    return false;
   }
+  return true;
+}
 
-  return false;
+bool ValidationState_t::IsIntScalarTypeWithSignedness(
+    uint32_t id, uint32_t signedness) const {
+  const Instruction* inst = FindDef(id);
+  return inst && inst->opcode() == spv::Op::OpTypeInt &&
+         inst->word(3) == signedness;
+}
+
+bool ValidationState_t::IsIntArrayType(uint32_t id, uint64_t length) const {
+  return IsArrayType(id, length) && IsIntScalarType(GetComponentType(id));
 }
 
 bool ValidationState_t::IsIntVectorType(uint32_t id) const {
@@ -1015,7 +1250,7 @@
     return false;
   }
 
-  if (inst->opcode() == spv::Op::OpTypeVector) {
+  if (IsVectorType(id)) {
     return IsIntScalarType(GetComponentType(id));
   }
 
@@ -1032,7 +1267,7 @@
     return true;
   }
 
-  if (inst->opcode() == spv::Op::OpTypeVector) {
+  if (IsVectorType(id)) {
     return IsIntScalarType(GetComponentType(id));
   }
 
@@ -1040,8 +1275,7 @@
 }
 
 bool ValidationState_t::IsUnsignedIntScalarType(uint32_t id) const {
-  const Instruction* inst = FindDef(id);
-  return inst && inst->opcode() == spv::Op::OpTypeInt && inst->word(3) == 0;
+  return IsIntScalarTypeWithSignedness(id, 0);
 }
 
 bool ValidationState_t::IsUnsignedIntVectorType(uint32_t id) const {
@@ -1050,7 +1284,7 @@
     return false;
   }
 
-  if (inst->opcode() == spv::Op::OpTypeVector) {
+  if (IsVectorType(id)) {
     return IsUnsignedIntScalarType(GetComponentType(id));
   }
 
@@ -1067,7 +1301,7 @@
     return inst->GetOperandAs<uint32_t>(2) == 0;
   }
 
-  if (inst->opcode() == spv::Op::OpTypeVector) {
+  if (IsVectorType(id)) {
     return IsUnsignedIntScalarType(GetComponentType(id));
   }
 
@@ -1085,7 +1319,7 @@
     return false;
   }
 
-  if (inst->opcode() == spv::Op::OpTypeVector) {
+  if (IsVectorType(id)) {
     return IsSignedIntScalarType(GetComponentType(id));
   }
 
@@ -1103,7 +1337,7 @@
     return false;
   }
 
-  if (inst->opcode() == spv::Op::OpTypeVector) {
+  if (IsVectorType(id)) {
     return IsBoolScalarType(GetComponentType(id));
   }
 
@@ -1120,7 +1354,7 @@
     return true;
   }
 
-  if (inst->opcode() == spv::Op::OpTypeVector) {
+  if (IsVectorType(id)) {
     return IsBoolScalarType(GetComponentType(id));
   }
 
@@ -1185,6 +1419,7 @@
 }
 
 bool ValidationState_t::IsPointerType(uint32_t id) const {
+  if (!id) return false;
   const Instruction* inst = FindDef(id);
   assert(inst);
   return inst->opcode() == spv::Op::OpTypePointer ||
@@ -1211,6 +1446,29 @@
   return true;
 }
 
+uint32_t ValidationState_t::GetLargestScalarType(uint32_t id) const {
+  const Instruction* inst = FindDef(id);
+
+  switch (inst->opcode()) {
+    case spv::Op::OpTypeStruct: {
+      uint32_t size = 0;
+      for (uint32_t i = 1; i < inst->operands().size(); ++i) {
+        const uint32_t member_size =
+            GetLargestScalarType(inst->GetOperandAs<uint32_t>(i));
+        size = std::max(size, member_size);
+      }
+      return size;
+    }
+    case spv::Op::OpTypeArray:
+      return GetLargestScalarType(inst->GetOperandAs<uint32_t>(1));
+    case spv::Op::OpTypeVector:
+    case spv::Op::OpTypeVectorIdEXT:
+      return GetLargestScalarType(inst->GetOperandAs<uint32_t>(1));
+    default:
+      return GetBitWidth(id) / 8;
+  }
+}
+
 bool ValidationState_t::IsAccelerationStructureType(uint32_t id) const {
   const Instruction* inst = FindDef(id);
   return inst && inst->opcode() == spv::Op::OpTypeAccelerationStructureKHR;
@@ -1289,6 +1547,116 @@
            GetBitWidth(id) == 32));
 }
 
+bool ValidationState_t::IsCooperativeVectorNVType(uint32_t id) const {
+  const Instruction* inst = FindDef(id);
+  return inst && inst->opcode() == spv::Op::OpTypeVectorIdEXT;
+}
+
+bool ValidationState_t::IsFloatCooperativeVectorNVType(uint32_t id) const {
+  if (!IsCooperativeVectorNVType(id)) return false;
+  return IsFloatScalarType(FindDef(id)->word(2));
+}
+
+bool ValidationState_t::IsIntCooperativeVectorNVType(uint32_t id) const {
+  if (!IsCooperativeVectorNVType(id)) return false;
+  return IsIntScalarType(FindDef(id)->word(2));
+}
+
+bool ValidationState_t::IsUnsignedIntCooperativeVectorNVType(
+    uint32_t id) const {
+  if (!IsCooperativeVectorNVType(id)) return false;
+  return IsUnsignedIntScalarType(FindDef(id)->word(2));
+}
+
+bool ValidationState_t::IsTensorType(uint32_t id) const {
+  const Instruction* inst = FindDef(id);
+  return inst && inst->opcode() == spv::Op::OpTypeTensorARM;
+}
+
+// Opaque handles from [Descriptor] section (added from SPV_EXT_descriptor_heap)
+bool ValidationState_t::IsDescriptorType(spv::Op opcode) const {
+  return opcode == spv::Op::OpTypeBufferEXT || opcode == spv::Op::OpTypeImage ||
+         opcode == spv::Op::OpTypeTensorARM ||
+         opcode == spv::Op::OpTypeSampler ||
+         opcode == spv::Op::OpTypeAccelerationStructureKHR;
+}
+
+// Opaque handles from [Descriptor] section (added from SPV_EXT_descriptor_heap)
+bool ValidationState_t::IsDescriptorType(uint32_t id) const {
+  const Instruction* inst = FindDef(id);
+  return inst && IsDescriptorType(inst->opcode());
+}
+
+const Instruction* ValidationState_t::FindUntypedBaseVariable(
+    const Instruction* inst) {
+  bool found_heap_base = false;
+  const Instruction* base_inst = inst;
+  while (!found_heap_base) {
+    switch (base_inst->opcode()) {
+      case spv::Op::OpUntypedAccessChainKHR:
+      case spv::Op::OpUntypedInBoundsAccessChainKHR:
+      case spv::Op::OpUntypedPtrAccessChainKHR:
+      case spv::Op::OpUntypedInBoundsPtrAccessChainKHR:
+      case spv::Op::OpUntypedArrayLengthKHR:
+        base_inst = FindDef(base_inst->GetOperandAs<uint32_t>(3));
+        break;
+      case spv::Op::OpLoad:
+      case spv::Op::OpAtomicLoad:
+        if (GetIdOpcode(GetOperandTypeId(base_inst, 2)) ==
+            spv::Op::OpTypeUntypedPointerKHR) {
+          base_inst = FindDef(base_inst->GetOperandAs<uint32_t>(2));
+        }
+        break;
+      case spv::Op::OpAtomicExchange:
+      case spv::Op::OpAtomicCompareExchange:
+      case spv::Op::OpAtomicCompareExchangeWeak:
+      case spv::Op::OpAtomicIIncrement:
+      case spv::Op::OpAtomicIDecrement:
+      case spv::Op::OpAtomicIAdd:
+      case spv::Op::OpAtomicISub:
+      case spv::Op::OpAtomicSMin:
+      case spv::Op::OpAtomicUMin:
+      case spv::Op::OpAtomicSMax:
+      case spv::Op::OpAtomicUMax:
+      case spv::Op::OpAtomicAnd:
+      case spv::Op::OpAtomicOr:
+      case spv::Op::OpAtomicXor:
+        base_inst = FindDef(base_inst->GetOperandAs<uint32_t>(2));
+        break;
+      case spv::Op::OpStore:
+      case spv::Op::OpAtomicStore:
+        if (GetIdOpcode(GetOperandTypeId(base_inst, 0)) ==
+            spv::Op::OpTypeUntypedPointerKHR) {
+          base_inst = FindDef(base_inst->GetOperandAs<uint32_t>(0));
+        }
+        break;
+      default:
+        found_heap_base = true;
+        break;
+    }
+
+    if (found_heap_base) {
+      break;
+    }
+  }
+
+  return base_inst;
+}
+
+bool ValidationState_t::IsDescriptorHeapBaseVariable(const Instruction* inst) {
+  if (!HasCapability(spv::Capability::DescriptorHeapEXT)) {
+    return false;
+  }
+  const Instruction* base_inst = FindUntypedBaseVariable(inst);
+  const bool is_heap_base =
+      IsBuiltin(base_inst->id(), spv::BuiltIn::SamplerHeapEXT) ||
+      IsBuiltin(base_inst->id(), spv::BuiltIn::ResourceHeapEXT);
+
+  return FindDef(base_inst->id())->opcode() == spv::Op::OpBufferPointerEXT ||
+         (FindDef(base_inst->id())->opcode() == spv::Op::OpUntypedVariableKHR &&
+          is_heap_base);
+}
+
 spv_result_t ValidationState_t::CooperativeMatrixShapesMatch(
     const Instruction* inst, uint32_t result_type_id, uint32_t m2,
     bool is_conversion, bool swap_row_col) {
@@ -1323,8 +1691,7 @@
 
   if (m1_is_const_int32 && m2_is_const_int32 && m1_value != m2_value) {
     return diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Expected scopes of Matrix and Result Type to be "
-           << "identical";
+           << "Expected scopes of Matrix and Result Type to be " << "identical";
   }
 
   std::tie(m1_is_int32, m1_is_const_int32, m1_value) =
@@ -1372,6 +1739,36 @@
   return SPV_SUCCESS;
 }
 
+spv_result_t ValidationState_t::CooperativeVectorDimensionsMatch(
+    const Instruction* inst, uint32_t v1, uint32_t v2) {
+  const auto v1_type = FindDef(v1);
+  const auto v2_type = FindDef(v2);
+
+  if (v1_type->opcode() != v2_type->opcode()) {
+    return diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected cooperative vector types";
+  }
+
+  uint32_t v1_components_id = v1_type->GetOperandAs<uint32_t>(2);
+  uint32_t v2_components_id = v2_type->GetOperandAs<uint32_t>(2);
+
+  bool v1_is_int32 = false, v1_is_const_int32 = false, v2_is_int32 = false,
+       v2_is_const_int32 = false;
+  uint32_t v1_value = 0, v2_value = 0;
+
+  std::tie(v1_is_int32, v1_is_const_int32, v1_value) =
+      EvalInt32IfConst(v1_components_id);
+  std::tie(v2_is_int32, v2_is_const_int32, v2_value) =
+      EvalInt32IfConst(v2_components_id);
+
+  if (v1_is_const_int32 && v2_is_const_int32 && v1_value != v2_value) {
+    return diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Expected number of components to be identical";
+  }
+
+  return SPV_SUCCESS;
+}
+
 uint32_t ValidationState_t::GetOperandTypeId(const Instruction* inst,
                                              size_t operand_index) const {
   return GetTypeId(inst->GetOperandAs<uint32_t>(operand_index));
@@ -1429,13 +1826,14 @@
   return true;
 }
 
+// <is_int32, is_const_int32, value>
 std::tuple<bool, bool, uint32_t> ValidationState_t::EvalInt32IfConst(
     uint32_t id) const {
   const Instruction* const inst = FindDef(id);
   assert(inst);
   const uint32_t type = inst->type_id();
 
-  if (type == 0 || !IsIntScalarType(type) || GetBitWidth(type) != 32) {
+  if (type == 0 || !IsIntScalarType(type, 32)) {
     return std::make_tuple(false, false, 0);
   }
 
@@ -1664,6 +2062,7 @@
     case spv::Op::OpTypeSampledImage:
     case spv::Op::OpTypeCooperativeMatrixNV:
     case spv::Op::OpTypeCooperativeMatrixKHR:
+    case spv::Op::OpTypeVectorIdEXT:
       return ContainsType(inst->GetOperandAs<uint32_t>(1u), f,
                           traverse_all_types);
     case spv::Op::OpTypePointer:
@@ -1698,6 +2097,10 @@
 
   const auto f = [type, width](const Instruction* inst) {
     if (inst->opcode() == type) {
+      // Bfloat16 is a special type.
+      if (type == spv::Op::OpTypeFloat && inst->words().size() > 3)
+        return false;
+
       return inst->GetOperandAs<uint32_t>(1u) == width;
     }
     return false;
@@ -1734,6 +2137,7 @@
     case spv::Op::OpTypeArray:
     case spv::Op::OpTypeRuntimeArray:
     case spv::Op::OpTypeVector:
+    case spv::Op::OpTypeVectorIdEXT:
     case spv::Op::OpTypeMatrix:
     case spv::Op::OpTypeImage:
     case spv::Op::OpTypeSampledImage:
@@ -1757,6 +2161,15 @@
   return false;
 }
 
+std::vector<uint32_t>& ValidationState_t::GetDebugSourceLineLength(
+    uint32_t id) {
+  auto it = debug_source_line_length_.find(id);
+  if (it == debug_source_line_length_.end()) {
+    return debug_source_line_length_[id];
+  }
+  return it->second;
+}
+
 bool ValidationState_t::IsValidStorageClass(
     spv::StorageClass storage_class) const {
   if (spvIsVulkanEnv(context()->target_env)) {
@@ -1780,7 +2193,10 @@
       case spv::StorageClass::ShaderRecordBufferKHR:
       case spv::StorageClass::TaskPayloadWorkgroupEXT:
       case spv::StorageClass::HitObjectAttributeNV:
+      case spv::StorageClass::HitObjectAttributeEXT:
       case spv::StorageClass::TileImageEXT:
+      case spv::StorageClass::NodePayloadAMDX:
+      case spv::StorageClass::TileAttachmentQCOM:
         return true;
       default:
         return false;
@@ -1790,6 +2206,14 @@
   return true;
 }
 
+std::string ValidationState_t::MissingFeature(const std::string& feature,
+                                              const std::string& cmdline,
+                                              bool hint) const {
+  return "\nThis is " + (hint ? std::string("may be ") : "") +
+         "allowed if you enable the " + feature + " (or use the " + cmdline +
+         " command line flag)";
+}
+
 #define VUID_WRAP(vuid) "[" #vuid "] "
 
 // Currently no 2 VUID share the same id, so no need for |reference|
@@ -1992,6 +2416,12 @@
       return VUID_WRAP(VUID-LocalInvocationId-LocalInvocationId-04282);
     case 4283:
       return VUID_WRAP(VUID-LocalInvocationId-LocalInvocationId-04283);
+    case 4284:
+      return VUID_WRAP(VUID-LocalInvocationIndex-LocalInvocationIndex-04284);
+    case 4285:
+      return VUID_WRAP(VUID-LocalInvocationIndex-LocalInvocationIndex-04285);
+    case 4286:
+      return VUID_WRAP(VUID-LocalInvocationIndex-LocalInvocationIndex-04286);
     case 4293:
       return VUID_WRAP(VUID-NumSubgroups-NumSubgroups-04293);
     case 4294:
@@ -2052,8 +2482,12 @@
       return VUID_WRAP(VUID-Position-Position-04321);
     case 4330:
       return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04330);
+    case 4333:
+      return VUID_WRAP(VUID-PrimitiveId-Fragment-04333);
     case 4334:
       return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04334);
+    case 4336:
+      return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04336);
     case 4337:
       return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04337);
     case 4345:
@@ -2238,8 +2672,6 @@
       return VUID_WRAP(VUID-StandaloneSpirv-None-04644);
     case 4645:
       return VUID_WRAP(VUID-StandaloneSpirv-None-04645);
-    case 4650:
-      return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04650);
     case 4651:
       return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-04651);
     case 4652:
@@ -2258,8 +2690,8 @@
       return VUID_WRAP(VUID-StandaloneSpirv-OpImageTexelPointer-04658);
     case 4659:
       return VUID_WRAP(VUID-StandaloneSpirv-OpImageQuerySizeLod-04659);
-    case 4663:
-      return VUID_WRAP(VUID-StandaloneSpirv-Offset-04663);
+    case 4660:
+      return VUID_WRAP(VUID-StandaloneSpirv-SubpassData-04660);
     case 4664:
       return VUID_WRAP(VUID-StandaloneSpirv-OpImageGather-04664);
     case 4667:
@@ -2276,8 +2708,6 @@
       return VUID_WRAP(VUID-StandaloneSpirv-OpTypeRuntimeArray-04680);
     case 4682:
       return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682);
-    case 6426:
-      return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-06426); // formally 04683
     case 4685:
       return VUID_WRAP(VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685);
     case 4686:
@@ -2308,14 +2738,6 @@
       return VUID_WRAP(VUID-StandaloneSpirv-PhysicalStorageBuffer64-04710);
     case 4711:
       return VUID_WRAP(VUID-StandaloneSpirv-OpTypeForwardPointer-04711);
-    case 4730:
-      return VUID_WRAP(VUID-StandaloneSpirv-OpAtomicStore-04730);
-    case 4731:
-      return VUID_WRAP(VUID-StandaloneSpirv-OpAtomicLoad-04731);
-    case 4732:
-      return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04732);
-    case 4733:
-      return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04733);
     case 4734:
       return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-04734);
     case 4744:
@@ -2324,8 +2746,6 @@
       return VUID_WRAP(VUID-StandaloneSpirv-OpImage-04777);
     case 4780:
       return VUID_WRAP(VUID-StandaloneSpirv-Result-04780);
-    case 4781:
-      return VUID_WRAP(VUID-StandaloneSpirv-Base-04781);
     case 4915:
       return VUID_WRAP(VUID-StandaloneSpirv-Location-04915);
     case 4916:
@@ -2344,14 +2764,16 @@
       return VUID_WRAP(VUID-StandaloneSpirv-Component-04922);
     case 4923:
       return VUID_WRAP(VUID-StandaloneSpirv-Component-04923);
-    case 4924:
-      return VUID_WRAP(VUID-StandaloneSpirv-Component-04924);
+    case 4965:
+      return VUID_WRAP(VUID-StandaloneSpirv-Image-04965);
     case 6201:
       return VUID_WRAP(VUID-StandaloneSpirv-Flat-06201);
     case 6202:
       return VUID_WRAP(VUID-StandaloneSpirv-Flat-06202);
     case 6214:
       return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-06214);
+    case 6314:
+      return VUID_WRAP(VUID-StandaloneSpirv-PhysicalStorageBuffer64-06314);
     case 6491:
       return VUID_WRAP(VUID-StandaloneSpirv-DescriptorSet-06491);
     case 6671:
@@ -2382,30 +2804,64 @@
       return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-06924);
     case 6925:
       return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06925);
+    case 7034:
+      return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-07034);
+    case 7035:
+      return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-07035);
+    case 7036:
+      return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-07036);
+    case 7038:
+      return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-07038);
+    case 7039:
+        return VUID_WRAP(VUID-Layer-Layer-07039);
+    case 7040:
+      return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-07040);
     case 7041:
       return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07041);
+    case 7042:
+      return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07042);
     case 7043:
       return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07043);
     case 7044:
       return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07044);
+    case 7046:
+      return VUID_WRAP(VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07046);
     case 7047:
       return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07047);
+    case 7048:
+      return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07048);
     case 7049:
       return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07049);
     case 7050:
       return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07050);
+    case 7052:
+      return VUID_WRAP(VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07052);
     case 7053:
       return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07053);
+    case 7054:
+      return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07054);
     case 7055:
       return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07055);
     case 7056:
       return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07056);
+    case 7058:
+      return VUID_WRAP(VUID-PrimitiveTriangleIndicesEXT-PrimitiveTriangleIndicesEXT-07058);
+    case 7059:
+      return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-07059);
+    case 7060:
+      return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-07060);
     case 7102:
       return VUID_WRAP(VUID-StandaloneSpirv-MeshEXT-07102);
-    case 7320:
-      return VUID_WRAP(VUID-StandaloneSpirv-ExecutionModel-07320);
+    case 7107:
+      return VUID_WRAP(VUID-StandaloneSpirv-MeshEXT-07107);
     case 7290:
       return VUID_WRAP(VUID-StandaloneSpirv-Input-07290);
+    case 7320:
+      return VUID_WRAP(VUID-StandaloneSpirv-ExecutionModel-07320);
+    case 7330:
+      return VUID_WRAP(VUID-StandaloneSpirv-MeshEXT-07330);
+    case 7331:
+      return VUID_WRAP(VUID-StandaloneSpirv-MeshEXT-07331);
     case 7650:
       return VUID_WRAP(VUID-StandaloneSpirv-Base-07650);
     case 7651:
@@ -2416,18 +2872,139 @@
       return VUID_WRAP(VUID-StandaloneSpirv-Component-07703);
     case 7951:
       return VUID_WRAP(VUID-StandaloneSpirv-SubgroupVoteKHR-07951);
+    case 8720:
+      return VUID_WRAP(VUID-StandaloneSpirv-None-08720);
     case 8721:
       return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-08721);
     case 8722:
       return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-08722);
+    case 8747:
+      return VUID_WRAP(VUID-HitTriangleVertexPositionsKHR-HitTriangleVertexPositionsKHR-08747);
+    case 8748:
+      return VUID_WRAP(VUID-HitTriangleVertexPositionsKHR-HitTriangleVertexPositionsKHR-08748);
+    case 8749:
+      return VUID_WRAP(VUID-HitTriangleVertexPositionsKHR-HitTriangleVertexPositionsKHR-08749);
     case 8973:
       return VUID_WRAP(VUID-StandaloneSpirv-Pointer-08973);
+    case 9557:
+      return VUID_WRAP(VUID-StandaloneSpirv-Input-09557);
     case 9638:
       return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-09638);
     case 9658:
       return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-09658);
     case 9659:
       return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-09659);
+    case 10151:
+      return VUID_WRAP(VUID-StandaloneSpirv-DerivativeGroupQuadsKHR-10151);
+    case 10152:
+      return VUID_WRAP(VUID-StandaloneSpirv-DerivativeGroupLinearKHR-10152);
+    case 10213:
+      // This use to be a standalone, but maintenance8 will set allow_offset_texture_operand now
+      return VUID_WRAP(VUID-RuntimeSpirv-Offset-10213);
+    case 10370:
+      return VUID_WRAP(VUID-StandaloneSpirv-OpTypeFloat-10370);
+    case 10583:
+      return VUID_WRAP(VUID-StandaloneSpirv-Component-10583);
+    case 10589:
+      return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-10589);
+    case 10590:
+      return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-10590);
+    case 10591:
+      return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-10591);
+    case 10592:
+      return VUID_WRAP(VUID-Layer-Layer-10592);
+    case 10593:
+      return VUID_WRAP(VUID-Layer-Layer-10593);
+    case 10594:
+      return VUID_WRAP(VUID-Layer-Layer-10594);
+    case 10598:
+      return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-10598);
+    case 10599:
+      return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-10599);
+    case 10600:
+      return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-10600);
+    case 10601:
+      return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-10601);
+    case 10602:
+      return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-10602);
+    case 10603:
+      return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-10603);
+    case 10684:
+      return VUID_WRAP(VUID-StandaloneSpirv-None-10684);
+    case 10685:
+      return VUID_WRAP(VUID-StandaloneSpirv-None-10685); // formally 04683/06426
+    case 10823:
+      return VUID_WRAP(VUID-StandaloneSpirv-OpTypeFloat-10823);
+    case 10824:
+      // This use to be a standalone, but maintenance9 will set allow_vulkan_32_bit_bitwise now
+      return VUID_WRAP(VUID-RuntimeSpirv-None-10824);
+    case 10865:
+      return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10865);
+    case 10866:
+      return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10866);
+    case 10867:
+      return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10867);
+    case 10868:
+      return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10868);
+    case 10869:
+      return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10869);
+    case 10870:
+      return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10870);
+    case 10871:
+      return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10871);
+    case 10872:
+      return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10872);
+    case 10873:
+      return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10873);
+    case 10874:
+      return VUID_WRAP(VUID-StandaloneSpirv-MemorySemantics-10874);
+    case 10875:
+      return VUID_WRAP(VUID-StandaloneSpirv-UnequalMemorySemantics-10875);
+    case 10876:
+      return VUID_WRAP(VUID-StandaloneSpirv-UnequalMemorySemantics-10876);
+    case 10877:
+      return VUID_WRAP(VUID-StandaloneSpirv-UnequalMemorySemantics-10877);
+    case 10878:
+      return VUID_WRAP(VUID-StandaloneSpirv-UnequalMemorySemantics-10878);
+    case 10879:
+      return VUID_WRAP(VUID-StandaloneSpirv-UnequalMemorySemantics-10879);
+    case 10880:
+      return VUID_WRAP(VUID-StandaloneSpirv-TessLevelInner-10880);
+    case 11165:
+      // Validation (via GPU-AV) will catch this if a non-constant
+      return VUID_WRAP(VUID-RuntimeSpirv-Size-11165);
+    case 11167:
+      return VUID_WRAP(VUID-StandaloneSpirv-OpUntypedVariableKHR-11167);
+    case 11239:
+        return VUID_WRAP(VUID-SamplerHeapEXT-SamplerHeapEXT-11239);
+    case 11241:
+        return VUID_WRAP(VUID-ResourceHeapEXT-ResourceHeapEXT-11241);
+    case 11336:
+        return VUID_WRAP(VUID-StandaloneSpirv-Result-11336);
+    case 11337:
+        return VUID_WRAP(VUID-StandaloneSpirv-Result-11337);
+    case 11339:
+        return VUID_WRAP(VUID-StandaloneSpirv-Result-11339);
+    case 11346:
+        return VUID_WRAP(VUID-StandaloneSpirv-Result-11346);
+    case 11347:
+        return VUID_WRAP(VUID-StandaloneSpirv-OpUntypedVariableKHR-11347);
+    case 11416:
+        return VUID_WRAP(VUID-StandaloneSpirv-OpUntypedImageTexelPointerEXT-11416);
+    case 11417:
+        return VUID_WRAP(VUID-StandaloneSpirv-OpTypeUntypedPointerKHR-11417);
+    case 11482:
+      return VUID_WRAP(VUID-StandaloneSpirv-DescriptorHeapEXT-11482);
+    case 11805:
+      return VUID_WRAP(VUID-StandaloneSpirv-OpArrayLength-11805);
+    case 12243:
+      return VUID_WRAP(VUID-StandaloneSpirv-Scope-12243);
+    case 12294:
+      return VUID_WRAP(VUID-StandaloneSpirv-Function-12294);
+    case 12295:
+      return VUID_WRAP(VUID-StandaloneSpirv-None-12295);
+    case 12297:
+      return VUID_WRAP(VUID-StandaloneSpirv-Type-12297);
     default:
       return "";  // unknown id
   }
diff --git a/source/val/validation_state.h b/source/val/validation_state.h
index 44551ad..9eecb2a 100644
--- a/source/val/validation_state.h
+++ b/source/val/validation_state.h
@@ -32,6 +32,7 @@
 #include "source/name_mapper.h"
 #include "source/spirv_definition.h"
 #include "source/spirv_validator_options.h"
+#include "source/table2.h"
 #include "source/val/decoration.h"
 #include "source/val/function.h"
 #include "source/val/instruction.h"
@@ -49,6 +50,7 @@
   kLayoutExtInstImport,            /// < Section 2.4 #3
   kLayoutMemoryModel,              /// < Section 2.4 #4
   kLayoutSamplerImageAddressMode,  /// < Section 2.4 #5
+                                   /// (SPV_NV_bindless_texture)
   kLayoutEntryPoint,               /// < Section 2.4 #6
   kLayoutExecutionMode,            /// < Section 2.4 #7
   kLayoutDebug1,                   /// < Section 2.4 #8 > 1
@@ -57,7 +59,18 @@
   kLayoutAnnotations,              /// < Section 2.4 #9
   kLayoutTypes,                    /// < Section 2.4 #10
   kLayoutFunctionDeclarations,     /// < Section 2.4 #11
-  kLayoutFunctionDefinitions       /// < Section 2.4 #12
+  kLayoutFunctionDefinitions,      /// < Section 2.4 #12
+  kLayoutGraphDefinitions          /// < Section 2.4 #13 (SPV_ARM_graph)
+};
+
+/// This enum represents the regions of a graph definition. The relative
+/// ordering of the values is significant.
+enum GraphDefinitionRegion {
+  kGraphDefinitionOutside,
+  kGraphDefinitionBegin,
+  kGraphDefinitionInputs,
+  kGraphDefinitionBody,
+  kGraphDefinitionOutputs,
 };
 
 /// This class manages the state of the SPIR-V validation as it is being parsed.
@@ -67,6 +80,7 @@
   struct Feature {
     bool declare_int16_type = false;     // Allow OpTypeInt with 16 bit width?
     bool declare_float16_type = false;   // Allow OpTypeFloat with 16 bit width?
+    bool declare_float8_type = false;    // Allow OpTypeFloat with 8 bit width?
     bool free_fp_rounding_mode = false;  // Allow the FPRoundingMode decoration
                                          // and its values to be used without
                                          // requiring any capability
@@ -211,6 +225,9 @@
   /// instruction
   bool in_block() const;
 
+  /// Returns the region of a graph definition we are in.
+  GraphDefinitionRegion graph_definition_region() const;
+
   struct EntryPointDescription {
     std::string name;
     std::vector<uint32_t> interfaces;
@@ -245,6 +262,41 @@
                                    const Instruction* inst) {
     entry_point_to_local_size_or_id_[entry_point] = inst;
   }
+
+  /// Registers that the entry point maximum number of primitives
+  /// mesh shader will ever emit
+  void RegisterEntryPointOutputPrimitivesEXT(uint32_t entry_point,
+                                             const Instruction* inst) {
+    entry_point_to_output_primitives_[entry_point] = inst;
+  }
+
+  /// Returns the maximum number of primitives mesh shader can emit
+  uint32_t GetOutputPrimitivesEXT(uint32_t entry_point) const {
+    auto entry = entry_point_to_output_primitives_.find(entry_point);
+    if (entry != entry_point_to_output_primitives_.end()) {
+      auto inst = entry->second;
+      return inst->GetOperandAs<uint32_t>(2);
+    }
+    return 0;
+  }
+
+  /// Registers that the entry point maximum number of vertices
+  /// mesh shader will ever emit
+  void RegisterEntryPointOutputVertices(uint32_t entry_point,
+                                        const Instruction* inst) {
+    entry_point_to_output_vertices_[entry_point] = inst;
+  }
+
+  /// Returns the maximum number of primitives mesh shader can emit
+  uint32_t GetOutputVertices(uint32_t entry_point) const {
+    auto entry = entry_point_to_output_vertices_.find(entry_point);
+    if (entry != entry_point_to_output_vertices_.end()) {
+      auto inst = entry->second;
+      return inst->GetOperandAs<uint32_t>(2);
+    }
+    return 0;
+  }
+
   /// Returns whether the entry point declares its local size
   bool EntryPointHasLocalSizeOrId(uint32_t entry_point) const {
     return entry_point_to_local_size_or_id_.find(entry_point) !=
@@ -293,6 +345,16 @@
   /// ComputeFunctionToEntryPointMapping.
   void ComputeRecursiveEntryPoints();
 
+  /// Registers |id| as a graph entry point.
+  void RegisterGraphEntryPoint(const uint32_t id) {
+    graph_entry_points_.push_back(id);
+  }
+
+  /// Returns a list of graph entry point graph ids
+  const std::vector<uint32_t>& graph_entry_points() const {
+    return graph_entry_points_;
+  }
+
   /// Returns all the entry points that can call |func|.
   const std::vector<uint32_t>& FunctionEntryPoints(uint32_t func) const;
 
@@ -330,6 +392,9 @@
   /// Register a function end instruction
   spv_result_t RegisterFunctionEnd();
 
+  /// Sets the region of a graph definition we're in.
+  void SetGraphDefinitionRegion(GraphDefinitionRegion region);
+
   /// Returns true if the capability is enabled in the module.
   bool HasCapability(spv::Capability cap) const {
     return module_capabilities_.contains(cap);
@@ -474,6 +539,29 @@
         [dec](const Decoration& d) { return dec == d.dec_type(); });
   }
 
+  /// Returns true if the given id <id> has the given built-in decoration <bt>,
+  /// otherwise returns false.
+  bool IsBuiltin(spv::Id id, spv::BuiltIn bt) {
+    for (auto& dec : id_decorations(id)) {
+      if (dec.dec_type() == spv::Decoration::BuiltIn) {
+        if (dec.builtin() == bt) return true;
+        break;
+      }
+    }
+    return false;
+  }
+
+  bool ContainsBuiltin(spv::Id id, spv::BuiltIn bt) {
+    const auto isHeapType = [&](const Instruction* inst) {
+      if (HasCapability(spv::Capability::DescriptorHeapEXT) &&
+          IsBuiltin(inst->id(), bt)) {
+        return true;
+      }
+      return false;
+    };
+    return ContainsType(uint32_t(id), isHeapType);
+  }
+
   /// Finds id's def, if it exists.  If found, returns the definition otherwise
   /// nullptr
   const Instruction* FindDef(uint32_t id) const;
@@ -612,16 +700,27 @@
   bool GetStructMemberTypes(uint32_t struct_type_id,
                             std::vector<uint32_t>* member_types) const;
 
-  // Returns true iff |id| is a type corresponding to the name of the function.
+  // Returns true if |id| is a type corresponding to the name of the function.
   // Only works for types not for objects.
   bool IsVoidType(uint32_t id) const;
-  bool IsFloatScalarType(uint32_t id) const;
+  bool IsScalarType(uint32_t id) const;
+  bool IsVectorType(uint32_t id) const;
+  bool IsBfloat16ScalarType(uint32_t id) const;
+  bool IsBfloat16VectorType(uint32_t id) const;
+  bool IsBfloat16CoopMatType(uint32_t id) const;
+  bool IsBfloat16Type(uint32_t id) const;
+  bool IsFP8ScalarType(uint32_t id) const;
+  bool IsFP8VectorType(uint32_t id) const;
+  bool IsFP8CoopMatType(uint32_t id) const;
+  bool IsFP8Type(uint32_t id) const;
+  bool IsFloatScalarType(uint32_t id, uint32_t width = 0) const;
+  bool IsFloatArrayType(uint32_t id) const;
   bool IsFloatVectorType(uint32_t id) const;
   bool IsFloat16Vector2Or4Type(uint32_t id) const;
   bool IsFloatScalarOrVectorType(uint32_t id) const;
   bool IsFloatMatrixType(uint32_t id) const;
-  bool IsIntScalarType(uint32_t id) const;
-  bool IsIntArrayType(uint32_t id) const;
+  bool IsIntScalarType(uint32_t id, uint32_t width = 0) const;
+  bool IsIntScalarTypeWithSignedness(uint32_t id, uint32_t signedness) const;
   bool IsIntVectorType(uint32_t id) const;
   bool IsIntScalarOrVectorType(uint32_t id) const;
   bool IsUnsignedIntScalarType(uint32_t id) const;
@@ -644,6 +743,44 @@
   bool IsIntCooperativeMatrixType(uint32_t id) const;
   bool IsUnsignedIntCooperativeMatrixType(uint32_t id) const;
   bool IsUnsigned64BitHandle(uint32_t id) const;
+  bool IsCooperativeVectorNVType(uint32_t id) const;
+  bool IsFloatCooperativeVectorNVType(uint32_t id) const;
+  bool IsIntCooperativeVectorNVType(uint32_t id) const;
+  bool IsUnsignedIntCooperativeVectorNVType(uint32_t id) const;
+  bool IsTensorType(uint32_t id) const;
+  bool IsDescriptorType(spv::Op opcode) const;
+  bool IsDescriptorType(uint32_t id) const;
+  // When |length| is not 0, return true only if the array length is equal to
+  // |length| and the array length is not defined by a specialization constant.
+  bool IsArrayType(uint32_t id, uint64_t length = 0) const;
+  bool IsIntArrayType(uint32_t id, uint64_t length = 0) const;
+  template <unsigned int N>
+  bool IsIntNOrFP32OrFP16(unsigned int type_id) {
+    return this->ContainsType(
+        type_id,
+        [](const Instruction* inst) {
+          if (inst->opcode() == spv::Op::OpTypeInt) {
+            return inst->GetOperandAs<uint32_t>(1) == N;
+          } else if (inst->opcode() == spv::Op::OpTypeFloat) {
+            if (inst->operands().size() > 2) {
+              // Not IEEE
+              return false;
+            }
+            auto width = inst->GetOperandAs<uint32_t>(1);
+            return width == 32 || width == 16;
+          }
+          return false;
+        },
+        /* traverse_all_types = */ false);
+  }
+
+  // Will walk the type to find the largest scalar value size.
+  // Returns value is in bytes.
+  // This is designed to pass in the %type from a PSB pointer
+  //   %ptr = OpTypePointer PhysicalStorageBuffer %type
+  uint32_t GetLargestScalarType(uint32_t id) const;
+  bool IsDescriptorHeapBaseVariable(const Instruction* inst);
+  const Instruction* FindUntypedBaseVariable(const Instruction* inst);
 
   // Returns true if |id| is a type id that contains |type| (or integer or
   // floating point type) of |width| bits.
@@ -684,6 +821,17 @@
   bool GetPointerTypeInfo(uint32_t id, uint32_t* data_type,
                           spv::StorageClass* storage_class) const;
 
+  // Returns the value assocated with id via 'value' if id is an OpConstant
+  template <typename T>
+  bool GetConstantValueAs(unsigned int id, T& value) {
+    const auto inst = FindDef(id);
+    uint64_t ui64_val = 0u;
+    bool status = (inst && spvOpcodeIsConstant(inst->opcode()) &&
+                   EvalConstantValUint64(id, &ui64_val));
+    if (status == true) value = static_cast<T>(ui64_val);
+    return status;
+  }
+
   // Is the ID the type of a pointer to a uniform block: Block-decorated struct
   // in uniform storage class? The result is only valid after internal method
   // CheckDecorationsOfBuffers has been called.
@@ -741,6 +889,16 @@
     pointer_to_storage_image_.insert(type_id);
   }
 
+  // Is the ID the type of a pointer to a tensor?  That is, the pointee
+  // type is a tensor type.
+  bool IsPointerToTensor(uint32_t type_id) const {
+    return pointer_to_tensor_.find(type_id) != pointer_to_tensor_.cend();
+  }
+  // Save the ID of a pointer to a tensor.
+  void RegisterPointerToTensor(uint32_t type_id) {
+    pointer_to_tensor_.insert(type_id);
+  }
+
   // Tries to evaluate a any scalar integer OpConstant as uint64.
   // OpConstantNull is defined as zero for scalar int (will return true)
   // OpSpecConstant* return false since their values cannot be relied upon
@@ -763,12 +921,12 @@
 
   // Returns the string name for |decoration|.
   std::string SpvDecorationString(uint32_t decoration) {
-    spv_operand_desc desc = nullptr;
-    if (grammar_.lookupOperand(SPV_OPERAND_TYPE_DECORATION, decoration,
-                               &desc) != SPV_SUCCESS) {
+    const spvtools::OperandDesc* desc = nullptr;
+    if (spvtools::LookupOperand(SPV_OPERAND_TYPE_DECORATION, decoration,
+                                &desc) != SPV_SUCCESS) {
       return std::string("Unknown");
     }
-    return std::string(desc->name);
+    return std::string(desc->name().data());
   }
   std::string SpvDecorationString(spv::Decoration decoration) {
     return SpvDecorationString(uint32_t(decoration));
@@ -783,6 +941,9 @@
                                             uint32_t m2, bool is_conversion,
                                             bool swap_row_col = false);
 
+  spv_result_t CooperativeVectorDimensionsMatch(const Instruction* inst,
+                                                uint32_t v1, uint32_t v2);
+
   // Returns true if |lhs| and |rhs| logically match and, if the decorations of
   // |rhs| are a subset of |lhs|.
   //
@@ -810,6 +971,12 @@
   // Validates the storage class for the target environment.
   bool IsValidStorageClass(spv::StorageClass storage_class) const;
 
+  // Helps formulate a mesesage to user that setting one of the validator
+  // options might make their SPIR-V actually valid The |hint| option is because
+  // some checks are intertwined with each other, so hard to give confirmation
+  std::string MissingFeature(const std::string& feature,
+                             const std::string& cmdline, bool hint) const;
+
   // Takes a Vulkan Valid Usage ID (VUID) as |id| and optional |reference| and
   // will return a non-empty string only if ID is known and targeting Vulkan.
   // VUIDs are found in the Vulkan-Docs repo in the form "[[VUID-ref-ref-id]]"
@@ -833,6 +1000,10 @@
            qcom_image_processing_consumers_.end();
   }
 
+  // Get the list of line lengths for a given result ID of a DebugSource
+  // instruction Will create a new vector if DebugSource is not found
+  std::vector<uint32_t>& GetDebugSourceLineLength(uint32_t id);
+
  private:
   ValidationState_t(const ValidationState_t&);
 
@@ -905,6 +1076,9 @@
   /// graph that recurses.
   std::set<uint32_t> recursive_entry_points_;
 
+  /// IDs that are graph entry points, ie, arguments to OpGraphEntryPointARM.
+  std::vector<uint32_t> graph_entry_points_;
+
   /// Functions IDs that are target of OpFunctionCall.
   std::unordered_set<uint32_t> function_call_targets_;
 
@@ -947,9 +1121,13 @@
   /// bit width of sampler/image type variables. Valid values are 32 and 64
   uint32_t sampler_image_addressing_mode_;
 
-  /// NOTE: See correspoding getter functions
+  /// NOTE: See corresponding getter functions
   bool in_function_;
 
+  /// Where in a graph definition we are
+  /// NOTE: See corresponding getter/setter functions
+  GraphDefinitionRegion graph_definition_region_;
+
   /// The state of optional features.  These are determined by capabilities
   /// declared by the module and the environment.
   Feature features_;
@@ -971,6 +1149,14 @@
   std::unordered_map<uint32_t, const Instruction*>
       entry_point_to_local_size_or_id_;
 
+  // Mapping entry point -> OutputPrimitivesEXT execution mode instruction
+  std::unordered_map<uint32_t, const Instruction*>
+      entry_point_to_output_primitives_;
+
+  // Mapping entry point -> OutputVertices execution mode instruction
+  std::unordered_map<uint32_t, const Instruction*>
+      entry_point_to_output_vertices_;
+
   /// Mapping function -> array of entry points inside this
   /// module which can (indirectly) call the function.
   std::unordered_map<uint32_t, std::vector<uint32_t>> function_to_entry_points_;
@@ -992,6 +1178,13 @@
   // The IDs of types of pointers to storage images.  This is populated in the
   // TypePass.
   std::unordered_set<uint32_t> pointer_to_storage_image_;
+  // The IDs of types of pointers to tensors.  This is populated in the
+  // TypePass.
+  std::unordered_set<uint32_t> pointer_to_tensor_;
+
+  /// Maps an id of DebugSource to a vector that contains the length of each
+  /// line side of it. (Also will have the DebugSourceContinued source included)
+  std::unordered_map<uint32_t, std::vector<uint32_t>> debug_source_line_length_;
 
   /// Maps ids to friendly names.
   std::unique_ptr<spvtools::FriendlyNameMapper> friendly_mapper_;
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 119e9c9..2627f9a 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -103,24 +103,30 @@
   diagnostic_test.cpp
   enum_string_mapping_test.cpp
   enum_set_test.cpp
+  ext_inst.arm_motion_engine_test.cpp
   ext_inst.cldebug100_test.cpp
   ext_inst.debuginfo_test.cpp
   ext_inst.glsl_test.cpp
   ext_inst.non_semantic_test.cpp
   ext_inst.opencl_test.cpp
+  ext_inst.tosa_test.cpp
+  ext_inst_lookup_test.cpp
   fix_word_test.cpp
   generator_magic_number_test.cpp
+  handle_unknown_opcodes_test.cpp
   hex_float_test.cpp
   hex_to_text_test.cpp
   immediate_int_test.cpp
   libspirv_macros_test.cpp
   named_id_test.cpp
   name_mapper_test.cpp
+  op_unknown_test.cpp
+  opcode_lookup_test.cpp
   opcode_make_test.cpp
   opcode_require_capabilities_test.cpp
   opcode_split_test.cpp
-  opcode_table_get_test.cpp
   operand_capabilities_test.cpp
+  operand_lookup_test.cpp
   operand_test.cpp
   operand_pattern_test.cpp
   parse_number_test.cpp
diff --git a/test/binary_parse_test.cpp b/test/binary_parse_test.cpp
index 1b8d72e..9351e77 100644
--- a/test/binary_parse_test.cpp
+++ b/test/binary_parse_test.cpp
@@ -48,6 +48,24 @@
 using ::testing::InSequence;
 using ::testing::Return;
 
+using MaybeFlipWordsTest = spvtest::TextToBinaryTest;
+
+TEST_F(MaybeFlipWordsTest, DoNotFlip) {
+  std::vector<uint32_t> words{0x01234567, 0x89abcdef};
+  MaybeFlipWords(false, words.begin(), words.end());
+  EXPECT_EQ(words.size(), size_t(2));
+  EXPECT_EQ(words[0], 0x01234567);
+  EXPECT_EQ(words[1], 0x89abcdef);
+}
+
+TEST_F(MaybeFlipWordsTest, Flip) {
+  std::vector<uint32_t> words{0x01234567, 0x89abcdef};
+  MaybeFlipWords(true, words.begin(), words.end());
+  EXPECT_EQ(words.size(), size_t(2));
+  EXPECT_EQ(words[0], 0x67452301);
+  EXPECT_EQ(words[1], 0xefcdab89);
+}
+
 // An easily-constructible and comparable object for the contents of an
 // spv_parsed_instruction_t.  Unlike spv_parsed_instruction_t, owns the memory
 // of its components.
diff --git a/test/binary_to_text_test.cpp b/test/binary_to_text_test.cpp
index ecf4d1e..dde73e2 100644
--- a/test/binary_to_text_test.cpp
+++ b/test/binary_to_text_test.cpp
@@ -428,6 +428,33 @@
                 "OpDecorate %1 HostAccessINTEL ReadWriteINTEL \"readwrite\"\n",
             })));
 
+// clang-format off
+INSTANTIATE_TEST_SUITE_P(
+    MatrixMultiplyAccumulateOperands, RoundTripInstructionsTest,
+    Combine(::testing::Values(SPV_ENV_UNIVERSAL_1_0),
+            ::testing::ValuesIn(std::vector<std::string>{
+                "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6\n",
+                "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 None\n",
+                "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixASignedComponentsINTEL\n",
+                "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixBSignedComponentsINTEL\n",
+                "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixCBFloat16INTEL\n",
+                "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixResultBFloat16INTEL\n",
+                "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixAPackedInt8INTEL\n",
+                "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixBPackedInt8INTEL\n",
+                "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixAPackedInt4INTEL\n",
+                "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixBPackedInt4INTEL\n",
+                "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixATF32INTEL\n",
+                "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixBTF32INTEL\n",
+                "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixCBFloat16INTEL\n",
+                "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixAPackedFloat16INTEL\n",
+                "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixBPackedFloat16INTEL\n",
+                "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixAPackedBFloat16INTEL\n",
+                "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 MatrixBPackedBFloat16INTEL\n",
+                "%2 = OpSubgroupMatrixMultiplyAccumulateINTEL %1 %3 %4 %5 %6 "
+                "MatrixASignedComponentsINTEL|MatrixBSignedComponentsINTEL|MatrixAPackedInt8INTEL|MatrixBPackedInt8INTEL\n",
+            })));
+// clang-format on
+
 using MaskSorting = TextToBinaryTest;
 
 TEST_F(MaskSorting, MasksAreSortedFromLSBToMSB) {
diff --git a/test/diff/diff_files/OpExtInst_in_variables_autogen.cpp b/test/diff/diff_files/OpExtInst_in_variables_autogen.cpp
new file mode 100644
index 0000000..bc510d0
--- /dev/null
+++ b/test/diff/diff_files/OpExtInst_in_variables_autogen.cpp
@@ -0,0 +1,292 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 Google LLC.
+//
+// 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 "../diff_test_utils.h"
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests a diff where the shader has OpExtInst in the debug, decorations and
+// types/variables sections.
+constexpr char kSrc[] = R"(               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+          %1 = OpExtInstImport "GLSL.std.450"
+         %20 = OpExtInstImport "NonSemantic.MadeUp"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %9 %11
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+         %21 = OpExtInst %2 %20 123
+         %22 = OpExtInst %2 %20 234
+               OpName %9 "color"
+               OpName %11 "v"
+               OpDecorate %9 RelaxedPrecision
+               OpDecorate %9 Location 0
+               OpDecorate %11 RelaxedPrecision
+               OpDecorate %11 Location 0
+         %23 = OpExtInst %2 %20 345
+         %24 = OpExtInst %2 %20 456
+               OpDecorate %12 RelaxedPrecision
+               OpDecorate %13 RelaxedPrecision
+               OpDecorate %14 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+          %8 = OpTypePointer Output %7
+          %9 = OpVariable %8 Output
+         %10 = OpTypePointer Input %6
+         %11 = OpVariable %10 Input
+         %25 = OpExtInst %2 %20 567
+         %26 = OpExtInst %2 %20 678
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %12 = OpLoad %6 %11
+         %13 = OpExtInst %6 %1 Log2 %12
+         %14 = OpCompositeConstruct %7 %13 %13 %13 %13
+               OpStore %9 %14
+               OpReturn
+               OpFunctionEnd)";
+constexpr char kDst[] = R"(               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+          %1 = OpExtInstImport "GLSL.std.450"
+         %30 = OpExtInstImport "NonSemantic.MadeUp"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %9 %11
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+         %31 = OpExtInst %2 %30 123
+         %32 = OpExtInst %2 %30 2340
+               OpName %9 "color"
+               OpName %11 "v"
+               OpDecorate %9 RelaxedPrecision
+               OpDecorate %9 Location 0
+               OpDecorate %11 RelaxedPrecision
+               OpDecorate %11 Location 0
+         %33 = OpExtInst %2 %30 3450
+         %34 = OpExtInst %2 %30 456
+               OpDecorate %12 RelaxedPrecision
+               OpDecorate %13 RelaxedPrecision
+               OpDecorate %14 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+          %8 = OpTypePointer Output %7
+          %9 = OpVariable %8 Output
+         %10 = OpTypePointer Input %6
+         %11 = OpVariable %10 Input
+         %35 = OpExtInst %2 %30 567
+         %36 = OpExtInst %2 %30 678
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %12 = OpLoad %6 %11
+         %13 = OpExtInst %6 %1 Log2 %12
+         %14 = OpCompositeConstruct %7 %13 %13 %13 %13
+               OpStore %9 %14
+               OpReturn
+               OpFunctionEnd
+)";
+
+TEST(DiffTest, OpextinstInVariables) {
+  constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 27
++; Bound: 29
+ ; Schema: 0
+ OpCapability Shader
+ OpExtension "SPV_KHR_non_semantic_info"
+ %1 = OpExtInstImport "GLSL.std.450"
+ %20 = OpExtInstImport "NonSemantic.MadeUp"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %9 "color"
+ OpName %11 "v"
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ OpDecorate %14 RelaxedPrecision
+ %21 = OpExtInst %2 %20 123
+-%22 = OpExtInst %2 %20 234
+-%23 = OpExtInst %2 %20 345
++%27 = OpExtInst %2 %20 2340
++%28 = OpExtInst %2 %20 3450
+ %24 = OpExtInst %2 %20 456
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %25 = OpExtInst %2 %20 567
+ %26 = OpExtInst %2 %20 678
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
+ %13 = OpExtInst %6 %1 Log2 %12
+ %14 = OpCompositeConstruct %7 %13 %13 %13 %13
+ OpStore %9 %14
+ OpReturn
+ OpFunctionEnd
+)";
+  Options options;
+  DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, OpextinstInVariablesNoDebug) {
+  constexpr char kSrcNoDebug[] = R"(               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+          %1 = OpExtInstImport "GLSL.std.450"
+         %20 = OpExtInstImport "NonSemantic.MadeUp"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %9 %11
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+         %21 = OpExtInst %2 %20 123
+         %22 = OpExtInst %2 %20 234
+               OpDecorate %9 RelaxedPrecision
+               OpDecorate %9 Location 0
+               OpDecorate %11 RelaxedPrecision
+               OpDecorate %11 Location 0
+         %23 = OpExtInst %2 %20 345
+         %24 = OpExtInst %2 %20 456
+               OpDecorate %12 RelaxedPrecision
+               OpDecorate %13 RelaxedPrecision
+               OpDecorate %14 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+          %8 = OpTypePointer Output %7
+          %9 = OpVariable %8 Output
+         %10 = OpTypePointer Input %6
+         %11 = OpVariable %10 Input
+         %25 = OpExtInst %2 %20 567
+         %26 = OpExtInst %2 %20 678
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %12 = OpLoad %6 %11
+         %13 = OpExtInst %6 %1 Log2 %12
+         %14 = OpCompositeConstruct %7 %13 %13 %13 %13
+               OpStore %9 %14
+               OpReturn
+               OpFunctionEnd
+)";
+  constexpr char kDstNoDebug[] = R"(               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+          %1 = OpExtInstImport "GLSL.std.450"
+         %30 = OpExtInstImport "NonSemantic.MadeUp"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %9 %11
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+         %31 = OpExtInst %2 %30 123
+         %32 = OpExtInst %2 %30 2340
+               OpDecorate %9 RelaxedPrecision
+               OpDecorate %9 Location 0
+               OpDecorate %11 RelaxedPrecision
+               OpDecorate %11 Location 0
+         %33 = OpExtInst %2 %30 3450
+         %34 = OpExtInst %2 %30 456
+               OpDecorate %12 RelaxedPrecision
+               OpDecorate %13 RelaxedPrecision
+               OpDecorate %14 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+          %8 = OpTypePointer Output %7
+          %9 = OpVariable %8 Output
+         %10 = OpTypePointer Input %6
+         %11 = OpVariable %10 Input
+         %35 = OpExtInst %2 %30 567
+         %36 = OpExtInst %2 %30 678
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %12 = OpLoad %6 %11
+         %13 = OpExtInst %6 %1 Log2 %12
+         %14 = OpCompositeConstruct %7 %13 %13 %13 %13
+               OpStore %9 %14
+               OpReturn
+               OpFunctionEnd
+)";
+  constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 27
++; Bound: 29
+ ; Schema: 0
+ OpCapability Shader
+ OpExtension "SPV_KHR_non_semantic_info"
+ %1 = OpExtInstImport "GLSL.std.450"
+ %20 = OpExtInstImport "NonSemantic.MadeUp"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9 %11
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpDecorate %9 RelaxedPrecision
+ OpDecorate %9 Location 0
+ OpDecorate %11 RelaxedPrecision
+ OpDecorate %11 Location 0
+ OpDecorate %12 RelaxedPrecision
+ OpDecorate %13 RelaxedPrecision
+ OpDecorate %14 RelaxedPrecision
+ %21 = OpExtInst %2 %20 123
+-%22 = OpExtInst %2 %20 234
+-%23 = OpExtInst %2 %20 345
++%27 = OpExtInst %2 %20 2340
++%28 = OpExtInst %2 %20 3450
+ %24 = OpExtInst %2 %20 456
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpTypePointer Input %6
+ %11 = OpVariable %10 Input
+ %25 = OpExtInst %2 %20 567
+ %26 = OpExtInst %2 %20 678
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %12 = OpLoad %6 %11
+ %13 = OpExtInst %6 %1 Log2 %12
+ %14 = OpCompositeConstruct %7 %13 %13 %13 %13
+ OpStore %9 %14
+ OpReturn
+ OpFunctionEnd
+)";
+  Options options;
+  DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+}  // namespace
+}  // namespace diff
+}  // namespace spvtools
diff --git a/test/diff/diff_files/OpExtInst_in_variables_dst.spvasm b/test/diff/diff_files/OpExtInst_in_variables_dst.spvasm
new file mode 100644
index 0000000..59ba05b
--- /dev/null
+++ b/test/diff/diff_files/OpExtInst_in_variables_dst.spvasm
@@ -0,0 +1,40 @@
+               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+          %1 = OpExtInstImport "GLSL.std.450"
+         %30 = OpExtInstImport "NonSemantic.MadeUp"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %9 %11
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+         %31 = OpExtInst %2 %30 123
+         %32 = OpExtInst %2 %30 2340
+               OpName %9 "color"
+               OpName %11 "v"
+               OpDecorate %9 RelaxedPrecision
+               OpDecorate %9 Location 0
+               OpDecorate %11 RelaxedPrecision
+               OpDecorate %11 Location 0
+         %33 = OpExtInst %2 %30 3450
+         %34 = OpExtInst %2 %30 456
+               OpDecorate %12 RelaxedPrecision
+               OpDecorate %13 RelaxedPrecision
+               OpDecorate %14 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+          %8 = OpTypePointer Output %7
+          %9 = OpVariable %8 Output
+         %10 = OpTypePointer Input %6
+         %11 = OpVariable %10 Input
+         %35 = OpExtInst %2 %30 567
+         %36 = OpExtInst %2 %30 678
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %12 = OpLoad %6 %11
+         %13 = OpExtInst %6 %1 Log2 %12
+         %14 = OpCompositeConstruct %7 %13 %13 %13 %13
+               OpStore %9 %14
+               OpReturn
+               OpFunctionEnd
diff --git a/test/diff/diff_files/OpExtInst_in_variables_src.spvasm b/test/diff/diff_files/OpExtInst_in_variables_src.spvasm
new file mode 100644
index 0000000..ca78909
--- /dev/null
+++ b/test/diff/diff_files/OpExtInst_in_variables_src.spvasm
@@ -0,0 +1,41 @@
+;; Tests a diff where the shader has OpExtInst in the debug, decorations and types/variables sections.
+               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+          %1 = OpExtInstImport "GLSL.std.450"
+         %20 = OpExtInstImport "NonSemantic.MadeUp"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %9 %11
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+         %21 = OpExtInst %2 %20 123
+         %22 = OpExtInst %2 %20 234
+               OpName %9 "color"
+               OpName %11 "v"
+               OpDecorate %9 RelaxedPrecision
+               OpDecorate %9 Location 0
+               OpDecorate %11 RelaxedPrecision
+               OpDecorate %11 Location 0
+         %23 = OpExtInst %2 %20 345
+         %24 = OpExtInst %2 %20 456
+               OpDecorate %12 RelaxedPrecision
+               OpDecorate %13 RelaxedPrecision
+               OpDecorate %14 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+          %8 = OpTypePointer Output %7
+          %9 = OpVariable %8 Output
+         %10 = OpTypePointer Input %6
+         %11 = OpVariable %10 Input
+         %25 = OpExtInst %2 %20 567
+         %26 = OpExtInst %2 %20 678
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %12 = OpLoad %6 %11
+         %13 = OpExtInst %6 %1 Log2 %12
+         %14 = OpCompositeConstruct %7 %13 %13 %13 %13
+               OpStore %9 %14
+               OpReturn
+               OpFunctionEnd
diff --git a/test/diff/diff_files/diff_test_files_autogen.cmake b/test/diff/diff_files/diff_test_files_autogen.cmake
index 51cb62f..b916cd7 100644
--- a/test/diff/diff_files/diff_test_files_autogen.cmake
+++ b/test/diff/diff_files/diff_test_files_autogen.cmake
@@ -18,6 +18,7 @@
 list(APPEND DIFF_TEST_FILES
 "diff_files/OpExtInst_in_dst_only_autogen.cpp"
 "diff_files/OpExtInst_in_src_only_autogen.cpp"
+"diff_files/OpExtInst_in_variables_autogen.cpp"
 "diff_files/OpTypeForwardPointer_basic_autogen.cpp"
 "diff_files/OpTypeForwardPointer_intertwined_autogen.cpp"
 "diff_files/OpTypeForwardPointer_mismatching_class_autogen.cpp"
@@ -30,6 +31,8 @@
 "diff_files/different_decorations_vertex_autogen.cpp"
 "diff_files/different_function_parameter_count_autogen.cpp"
 "diff_files/extra_if_block_autogen.cpp"
+"diff_files/function_group_by_full_type_autogen.cpp"
+"diff_files/function_group_by_mapped_id_autogen.cpp"
 "diff_files/index_signedness_autogen.cpp"
 "diff_files/int_vs_uint_constants_autogen.cpp"
 "diff_files/large_functions_large_diffs_autogen.cpp"
@@ -44,5 +47,6 @@
 "diff_files/spec_constant_composite_autogen.cpp"
 "diff_files/spec_constant_op_autogen.cpp"
 "diff_files/spec_constant_specid_autogen.cpp"
+"diff_files/string_in_ext_inst_autogen.cpp"
 "diff_files/unrelated_shaders_autogen.cpp"
 )
diff --git a/test/diff/diff_files/function_group_by_full_type_autogen.cpp b/test/diff/diff_files/function_group_by_full_type_autogen.cpp
new file mode 100644
index 0000000..1843425
--- /dev/null
+++ b/test/diff/diff_files/function_group_by_full_type_autogen.cpp
@@ -0,0 +1,194 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 Google LLC.
+//
+// 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 "../diff_test_utils.h"
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Use functions' `OpFunctionType` to group before grouping by return type only.
+constexpr char kSrc[] = R"(                 OpCapability Shader
+                 OpCapability Linkage
+                 OpMemoryModel Logical GLSL450
+         %bool = OpTypeBool
+          %int = OpTypeInt 32 1
+        %int_0 = OpConstantNull %int
+
+       %fn_int = OpTypeFunction %bool %int
+   %fn_int_int = OpTypeFunction %bool %int %int
+
+            %f = OpFunction %bool None %fn_int
+          %fp1 = OpFunctionParameter %int
+   %f_prologue = OpLabel
+           %fr = OpIEqual %bool %fp1 %int_0
+                 OpReturnValue %fr
+                 OpFunctionEnd
+
+            %g = OpFunction %bool None %fn_int_int
+          %gp1 = OpFunctionParameter %int
+          %gp2 = OpFunctionParameter %int
+   %g_prologue = OpLabel
+           %gr = OpIEqual %bool %gp1 %int_0
+                 OpReturnValue %gr
+                 OpFunctionEnd)";
+constexpr char kDst[] =
+    R"(;; Use functions' `OpFunctionType` to group before grouping by return type only.
+                 OpCapability Shader
+                 OpCapability Linkage
+                 OpMemoryModel Logical GLSL450
+         %bool = OpTypeBool
+          %int = OpTypeInt 32 1
+        %int_0 = OpConstantNull %int
+
+       %fn_int = OpTypeFunction %bool %int
+   %fn_int_int = OpTypeFunction %bool %int %int
+
+            %g = OpFunction %bool None %fn_int_int
+          %gp1 = OpFunctionParameter %int
+          %gp2 = OpFunctionParameter %int
+   %g_prologue = OpLabel
+           %gr = OpIEqual %bool %gp1 %int_0
+                 OpReturnValue %gr
+                 OpFunctionEnd
+
+            %f = OpFunction %bool None %fn_int
+          %fp1 = OpFunctionParameter %int
+   %f_prologue = OpLabel
+           %fr = OpIEqual %bool %fp1 %int_0
+                 OpReturnValue %fr
+                 OpFunctionEnd
+)";
+
+TEST(DiffTest, FunctionGroupByFullType) {
+  constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+ ; Bound: 15
+ ; Schema: 0
+ OpCapability Shader
+ OpCapability Linkage
+ OpMemoryModel Logical GLSL450
+ %1 = OpTypeBool
+ %2 = OpTypeInt 32 1
+ %3 = OpConstantNull %2
+ %4 = OpTypeFunction %1 %2
+ %5 = OpTypeFunction %1 %2 %2
+ %6 = OpFunction %1 None %4
+ %7 = OpFunctionParameter %2
+ %8 = OpLabel
+ %9 = OpIEqual %1 %7 %3
+ OpReturnValue %9
+ OpFunctionEnd
+ %10 = OpFunction %1 None %5
+ %11 = OpFunctionParameter %2
+ %12 = OpFunctionParameter %2
+ %13 = OpLabel
+ %14 = OpIEqual %1 %11 %3
+ OpReturnValue %14
+ OpFunctionEnd
+)";
+  Options options;
+  DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, FunctionGroupByFullTypeNoDebug) {
+  constexpr char kSrcNoDebug[] = R"(                 OpCapability Shader
+                 OpCapability Linkage
+                 OpMemoryModel Logical GLSL450
+         %bool = OpTypeBool
+          %int = OpTypeInt 32 1
+        %int_0 = OpConstantNull %int
+
+       %fn_int = OpTypeFunction %bool %int
+   %fn_int_int = OpTypeFunction %bool %int %int
+
+            %f = OpFunction %bool None %fn_int
+          %fp1 = OpFunctionParameter %int
+   %f_prologue = OpLabel
+           %fr = OpIEqual %bool %fp1 %int_0
+                 OpReturnValue %fr
+                 OpFunctionEnd
+
+            %g = OpFunction %bool None %fn_int_int
+          %gp1 = OpFunctionParameter %int
+          %gp2 = OpFunctionParameter %int
+   %g_prologue = OpLabel
+           %gr = OpIEqual %bool %gp1 %int_0
+                 OpReturnValue %gr
+                 OpFunctionEnd
+)";
+  constexpr char kDstNoDebug[] = R"(                 OpCapability Shader
+                 OpCapability Linkage
+                 OpMemoryModel Logical GLSL450
+         %bool = OpTypeBool
+          %int = OpTypeInt 32 1
+        %int_0 = OpConstantNull %int
+
+       %fn_int = OpTypeFunction %bool %int
+   %fn_int_int = OpTypeFunction %bool %int %int
+
+            %g = OpFunction %bool None %fn_int_int
+          %gp1 = OpFunctionParameter %int
+          %gp2 = OpFunctionParameter %int
+   %g_prologue = OpLabel
+           %gr = OpIEqual %bool %gp1 %int_0
+                 OpReturnValue %gr
+                 OpFunctionEnd
+
+            %f = OpFunction %bool None %fn_int
+          %fp1 = OpFunctionParameter %int
+   %f_prologue = OpLabel
+           %fr = OpIEqual %bool %fp1 %int_0
+                 OpReturnValue %fr
+                 OpFunctionEnd
+)";
+  constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+ ; Bound: 15
+ ; Schema: 0
+ OpCapability Shader
+ OpCapability Linkage
+ OpMemoryModel Logical GLSL450
+ %1 = OpTypeBool
+ %2 = OpTypeInt 32 1
+ %3 = OpConstantNull %2
+ %4 = OpTypeFunction %1 %2
+ %5 = OpTypeFunction %1 %2 %2
+ %6 = OpFunction %1 None %4
+ %7 = OpFunctionParameter %2
+ %8 = OpLabel
+ %9 = OpIEqual %1 %7 %3
+ OpReturnValue %9
+ OpFunctionEnd
+ %10 = OpFunction %1 None %5
+ %11 = OpFunctionParameter %2
+ %12 = OpFunctionParameter %2
+ %13 = OpLabel
+ %14 = OpIEqual %1 %11 %3
+ OpReturnValue %14
+ OpFunctionEnd
+)";
+  Options options;
+  DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+}  // namespace
+}  // namespace diff
+}  // namespace spvtools
diff --git a/test/diff/diff_files/function_group_by_full_type_dst.spvasm b/test/diff/diff_files/function_group_by_full_type_dst.spvasm
new file mode 100644
index 0000000..eea5276
--- /dev/null
+++ b/test/diff/diff_files/function_group_by_full_type_dst.spvasm
@@ -0,0 +1,25 @@
+;; Use functions' `OpFunctionType` to group before grouping by return type only.
+                 OpCapability Shader
+                 OpCapability Linkage
+                 OpMemoryModel Logical GLSL450
+         %bool = OpTypeBool
+          %int = OpTypeInt 32 1
+        %int_0 = OpConstantNull %int
+
+       %fn_int = OpTypeFunction %bool %int
+   %fn_int_int = OpTypeFunction %bool %int %int
+
+            %g = OpFunction %bool None %fn_int_int
+          %gp1 = OpFunctionParameter %int
+          %gp2 = OpFunctionParameter %int
+   %g_prologue = OpLabel
+           %gr = OpIEqual %bool %gp1 %int_0
+                 OpReturnValue %gr
+                 OpFunctionEnd
+
+            %f = OpFunction %bool None %fn_int
+          %fp1 = OpFunctionParameter %int
+   %f_prologue = OpLabel
+           %fr = OpIEqual %bool %fp1 %int_0
+                 OpReturnValue %fr
+                 OpFunctionEnd
diff --git a/test/diff/diff_files/function_group_by_full_type_src.spvasm b/test/diff/diff_files/function_group_by_full_type_src.spvasm
new file mode 100644
index 0000000..5cc7dd5
--- /dev/null
+++ b/test/diff/diff_files/function_group_by_full_type_src.spvasm
@@ -0,0 +1,25 @@
+;; Use functions' `OpFunctionType` to group before grouping by return type only.
+                 OpCapability Shader
+                 OpCapability Linkage
+                 OpMemoryModel Logical GLSL450
+         %bool = OpTypeBool
+          %int = OpTypeInt 32 1
+        %int_0 = OpConstantNull %int
+
+       %fn_int = OpTypeFunction %bool %int
+   %fn_int_int = OpTypeFunction %bool %int %int
+
+            %f = OpFunction %bool None %fn_int
+          %fp1 = OpFunctionParameter %int
+   %f_prologue = OpLabel
+           %fr = OpIEqual %bool %fp1 %int_0
+                 OpReturnValue %fr
+                 OpFunctionEnd
+
+            %g = OpFunction %bool None %fn_int_int
+          %gp1 = OpFunctionParameter %int
+          %gp2 = OpFunctionParameter %int
+   %g_prologue = OpLabel
+           %gr = OpIEqual %bool %gp1 %int_0
+                 OpReturnValue %gr
+                 OpFunctionEnd
diff --git a/test/diff/diff_files/function_group_by_mapped_id_autogen.cpp b/test/diff/diff_files/function_group_by_mapped_id_autogen.cpp
new file mode 100644
index 0000000..66e7bd9
--- /dev/null
+++ b/test/diff/diff_files/function_group_by_mapped_id_autogen.cpp
@@ -0,0 +1,181 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 Google LLC.
+//
+// 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 "../diff_test_utils.h"
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Don't forget to map between source and destination ids when grouping
+// functions by return type.
+constexpr char kSrc[] = R"(                 OpCapability Shader
+                 OpCapability Linkage
+                 OpMemoryModel Logical GLSL450
+         %void = OpTypeVoid               
+            %1 = OpTypeInt 32 0
+            %2 = OpTypeInt 32 1
+
+      %fn_uint = OpTypeFunction %1
+       %fn_int = OpTypeFunction %2
+
+      %uint_42 = OpConstant %1 42
+     %int_1729 = OpConstant %2 1729
+
+            %f = OpFunction %1 None %fn_uint
+   %f_prologue = OpLabel
+                 OpReturnValue %uint_42
+                 OpFunctionEnd
+
+            %g = OpFunction %2 None %fn_int
+   %g_prologue = OpLabel
+                 OpReturnValue %int_1729
+                 OpFunctionEnd)";
+constexpr char kDst[] =
+    R"(;; Don't forget to map between source and destination ids when grouping functions by return type.
+                 OpCapability Shader
+                 OpCapability Linkage
+                 OpMemoryModel Logical GLSL450
+         %void = OpTypeVoid               
+            %2 = OpTypeInt 32 0
+            %1 = OpTypeInt 32 1
+
+      %fn_uint = OpTypeFunction %2
+       %fn_int = OpTypeFunction %1
+
+      %uint_42 = OpConstant %2 42
+     %int_1729 = OpConstant %1 1729
+
+            %f = OpFunction %2 None %fn_uint
+   %f_prologue = OpLabel
+                 OpReturnValue %uint_42
+                 OpFunctionEnd
+
+            %g = OpFunction %1 None %fn_int
+   %g_prologue = OpLabel
+                 OpReturnValue %int_1729
+                 OpFunctionEnd
+)";
+
+TEST(DiffTest, FunctionGroupByMappedId) {
+  constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+ ; Bound: 12
+ ; Schema: 0
+ OpCapability Shader
+ OpCapability Linkage
+ OpMemoryModel Logical GLSL450
+ %3 = OpTypeVoid
+ %1 = OpTypeInt 32 0
+ %2 = OpTypeInt 32 1
+ %4 = OpTypeFunction %1
+ %5 = OpTypeFunction %2
+ %6 = OpConstant %1 42
+ %7 = OpConstant %2 1729
+ %8 = OpFunction %1 None %4
+ %9 = OpLabel
+ OpReturnValue %6
+ OpFunctionEnd
+ %10 = OpFunction %2 None %5
+ %11 = OpLabel
+ OpReturnValue %7
+ OpFunctionEnd
+)";
+  Options options;
+  DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, FunctionGroupByMappedIdNoDebug) {
+  constexpr char kSrcNoDebug[] = R"(                 OpCapability Shader
+                 OpCapability Linkage
+                 OpMemoryModel Logical GLSL450
+         %void = OpTypeVoid               
+            %1 = OpTypeInt 32 0
+            %2 = OpTypeInt 32 1
+
+      %fn_uint = OpTypeFunction %1
+       %fn_int = OpTypeFunction %2
+
+      %uint_42 = OpConstant %1 42
+     %int_1729 = OpConstant %2 1729
+
+            %f = OpFunction %1 None %fn_uint
+   %f_prologue = OpLabel
+                 OpReturnValue %uint_42
+                 OpFunctionEnd
+
+            %g = OpFunction %2 None %fn_int
+   %g_prologue = OpLabel
+                 OpReturnValue %int_1729
+                 OpFunctionEnd
+)";
+  constexpr char kDstNoDebug[] = R"(                 OpCapability Shader
+                 OpCapability Linkage
+                 OpMemoryModel Logical GLSL450
+         %void = OpTypeVoid               
+            %2 = OpTypeInt 32 0
+            %1 = OpTypeInt 32 1
+
+      %fn_uint = OpTypeFunction %2
+       %fn_int = OpTypeFunction %1
+
+      %uint_42 = OpConstant %2 42
+     %int_1729 = OpConstant %1 1729
+
+            %f = OpFunction %2 None %fn_uint
+   %f_prologue = OpLabel
+                 OpReturnValue %uint_42
+                 OpFunctionEnd
+
+            %g = OpFunction %1 None %fn_int
+   %g_prologue = OpLabel
+                 OpReturnValue %int_1729
+                 OpFunctionEnd
+)";
+  constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+ ; Bound: 12
+ ; Schema: 0
+ OpCapability Shader
+ OpCapability Linkage
+ OpMemoryModel Logical GLSL450
+ %3 = OpTypeVoid
+ %1 = OpTypeInt 32 0
+ %2 = OpTypeInt 32 1
+ %4 = OpTypeFunction %1
+ %5 = OpTypeFunction %2
+ %6 = OpConstant %1 42
+ %7 = OpConstant %2 1729
+ %8 = OpFunction %1 None %4
+ %9 = OpLabel
+ OpReturnValue %6
+ OpFunctionEnd
+ %10 = OpFunction %2 None %5
+ %11 = OpLabel
+ OpReturnValue %7
+ OpFunctionEnd
+)";
+  Options options;
+  DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+}  // namespace
+}  // namespace diff
+}  // namespace spvtools
diff --git a/test/diff/diff_files/function_group_by_mapped_id_dst.spvasm b/test/diff/diff_files/function_group_by_mapped_id_dst.spvasm
new file mode 100644
index 0000000..6af4241
--- /dev/null
+++ b/test/diff/diff_files/function_group_by_mapped_id_dst.spvasm
@@ -0,0 +1,23 @@
+;; Don't forget to map between source and destination ids when grouping functions by return type.
+                 OpCapability Shader
+                 OpCapability Linkage
+                 OpMemoryModel Logical GLSL450
+         %void = OpTypeVoid               
+            %2 = OpTypeInt 32 0
+            %1 = OpTypeInt 32 1
+
+      %fn_uint = OpTypeFunction %2
+       %fn_int = OpTypeFunction %1
+
+      %uint_42 = OpConstant %2 42
+     %int_1729 = OpConstant %1 1729
+
+            %f = OpFunction %2 None %fn_uint
+   %f_prologue = OpLabel
+                 OpReturnValue %uint_42
+                 OpFunctionEnd
+
+            %g = OpFunction %1 None %fn_int
+   %g_prologue = OpLabel
+                 OpReturnValue %int_1729
+                 OpFunctionEnd
diff --git a/test/diff/diff_files/function_group_by_mapped_id_src.spvasm b/test/diff/diff_files/function_group_by_mapped_id_src.spvasm
new file mode 100644
index 0000000..d920381
--- /dev/null
+++ b/test/diff/diff_files/function_group_by_mapped_id_src.spvasm
@@ -0,0 +1,23 @@
+;; Don't forget to map between source and destination ids when grouping functions by return type.
+                 OpCapability Shader
+                 OpCapability Linkage
+                 OpMemoryModel Logical GLSL450
+         %void = OpTypeVoid               
+            %1 = OpTypeInt 32 0
+            %2 = OpTypeInt 32 1
+
+      %fn_uint = OpTypeFunction %1
+       %fn_int = OpTypeFunction %2
+
+      %uint_42 = OpConstant %1 42
+     %int_1729 = OpConstant %2 1729
+
+            %f = OpFunction %1 None %fn_uint
+   %f_prologue = OpLabel
+                 OpReturnValue %uint_42
+                 OpFunctionEnd
+
+            %g = OpFunction %2 None %fn_int
+   %g_prologue = OpLabel
+                 OpReturnValue %int_1729
+                 OpFunctionEnd
diff --git a/test/diff/diff_files/string_in_ext_inst_autogen.cpp b/test/diff/diff_files/string_in_ext_inst_autogen.cpp
new file mode 100644
index 0000000..fc752c9
--- /dev/null
+++ b/test/diff/diff_files/string_in_ext_inst_autogen.cpp
@@ -0,0 +1,205 @@
+// GENERATED FILE - DO NOT EDIT.
+// Generated by generate_tests.py
+//
+// Copyright (c) 2022 Google LLC.
+//
+// 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 "../diff_test_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace spvtools {
+namespace diff {
+namespace {
+
+// Tests a diff where the an OpString is used only as parameter of OpExtInst.
+constexpr char kSrc[] = R"(               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+          %1 = OpExtInstImport "GLSL.std.450"
+         %12 = OpExtInstImport "NonSemantic.DebugPrintf"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+         %10 = OpString "unsigned == %u"
+               OpSource GLSL 450
+               OpSourceExtension "GL_EXT_debug_printf"
+               OpName %main "main"
+               OpName %foo "foo"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+%_ptr_Function_uint = OpTypePointer Function %uint
+   %uint_127 = OpConstant %uint 127
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+        %foo = OpVariable %_ptr_Function_uint Function
+               OpStore %foo %uint_127
+         %11 = OpLoad %uint %foo
+         %13 = OpExtInst %void %12 1 %10 %11
+               OpReturn
+               OpFunctionEnd)";
+constexpr char kDst[] = R"(               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+          %1 = OpExtInstImport "GLSL.std.450"
+         %12 = OpExtInstImport "NonSemantic.DebugPrintf"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+         %10 = OpString "signed == %d"
+               OpSource GLSL 450
+               OpSourceExtension "GL_EXT_debug_printf"
+               OpName %main "main"
+               OpName %foo "foo"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+%_ptr_Function_uint = OpTypePointer Function %uint
+   %uint_127 = OpConstant %uint 127
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+        %foo = OpVariable %_ptr_Function_uint Function
+               OpStore %foo %uint_127
+         %11 = OpLoad %uint %foo
+         %13 = OpExtInst %void %12 1 %10 %11
+               OpReturn
+               OpFunctionEnd
+)";
+
+TEST(DiffTest, StringInExtInst) {
+  constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 14
++; Bound: 15
+ ; Schema: 0
+ OpCapability Shader
+ OpExtension "SPV_KHR_non_semantic_info"
+ %1 = OpExtInstImport "GLSL.std.450"
+ %12 = OpExtInstImport "NonSemantic.DebugPrintf"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %2 "main"
+ OpExecutionMode %2 LocalSize 1 1 1
+-%10 = OpString "unsigned == %u"
++%14 = OpString "signed == %d"
+ OpSource GLSL 450
+ OpSourceExtension "GL_EXT_debug_printf"
+ OpName %2 "main"
+ OpName %4 "foo"
+ %6 = OpTypeVoid
+ %3 = OpTypeFunction %6
+ %7 = OpTypeInt 32 0
+ %8 = OpTypePointer Function %7
+ %9 = OpConstant %7 127
+ %2 = OpFunction %6 None %3
+ %5 = OpLabel
+ %4 = OpVariable %8 Function
+ OpStore %4 %9
+ %11 = OpLoad %7 %4
+-%13 = OpExtInst %6 %12 1 %10 %11
++%13 = OpExtInst %6 %12 1 %14 %11
+ OpReturn
+ OpFunctionEnd
+)";
+  Options options;
+  DoStringDiffTest(kSrc, kDst, kDiff, options);
+}
+
+TEST(DiffTest, StringInExtInstNoDebug) {
+  constexpr char kSrcNoDebug[] = R"(               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+          %1 = OpExtInstImport "GLSL.std.450"
+         %12 = OpExtInstImport "NonSemantic.DebugPrintf"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+         %10 = OpString "unsigned == %u"
+               OpSource GLSL 450
+               OpSourceExtension "GL_EXT_debug_printf"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+%_ptr_Function_uint = OpTypePointer Function %uint
+   %uint_127 = OpConstant %uint 127
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+        %foo = OpVariable %_ptr_Function_uint Function
+               OpStore %foo %uint_127
+         %11 = OpLoad %uint %foo
+         %13 = OpExtInst %void %12 1 %10 %11
+               OpReturn
+               OpFunctionEnd
+)";
+  constexpr char kDstNoDebug[] = R"(               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+          %1 = OpExtInstImport "GLSL.std.450"
+         %12 = OpExtInstImport "NonSemantic.DebugPrintf"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+         %10 = OpString "signed == %d"
+               OpSource GLSL 450
+               OpSourceExtension "GL_EXT_debug_printf"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+%_ptr_Function_uint = OpTypePointer Function %uint
+   %uint_127 = OpConstant %uint 127
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+        %foo = OpVariable %_ptr_Function_uint Function
+               OpStore %foo %uint_127
+         %11 = OpLoad %uint %foo
+         %13 = OpExtInst %void %12 1 %10 %11
+               OpReturn
+               OpFunctionEnd
+)";
+  constexpr char kDiff[] = R"( ; SPIR-V
+ ; Version: 1.6
+ ; Generator: Khronos SPIR-V Tools Assembler; 0
+-; Bound: 14
++; Bound: 15
+ ; Schema: 0
+ OpCapability Shader
+ OpExtension "SPV_KHR_non_semantic_info"
+ %1 = OpExtInstImport "GLSL.std.450"
+ %12 = OpExtInstImport "NonSemantic.DebugPrintf"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %2 "main"
+ OpExecutionMode %2 LocalSize 1 1 1
+-%10 = OpString "unsigned == %u"
++%14 = OpString "signed == %d"
+ OpSource GLSL 450
+ OpSourceExtension "GL_EXT_debug_printf"
+ %4 = OpTypeVoid
+ %3 = OpTypeFunction %4
+ %6 = OpTypeInt 32 0
+ %7 = OpTypePointer Function %6
+ %8 = OpConstant %6 127
+ %2 = OpFunction %4 None %3
+ %5 = OpLabel
+ %9 = OpVariable %7 Function
+ OpStore %9 %8
+ %11 = OpLoad %6 %9
+-%13 = OpExtInst %4 %12 1 %10 %11
++%13 = OpExtInst %4 %12 1 %14 %11
+ OpReturn
+ OpFunctionEnd
+)";
+  Options options;
+  DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options);
+}
+
+}  // namespace
+}  // namespace diff
+}  // namespace spvtools
diff --git a/test/diff/diff_files/string_in_ext_inst_dst.spvasm b/test/diff/diff_files/string_in_ext_inst_dst.spvasm
new file mode 100644
index 0000000..3681332
--- /dev/null
+++ b/test/diff/diff_files/string_in_ext_inst_dst.spvasm
@@ -0,0 +1,25 @@
+               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+          %1 = OpExtInstImport "GLSL.std.450"
+         %12 = OpExtInstImport "NonSemantic.DebugPrintf"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+         %10 = OpString "signed == %d"
+               OpSource GLSL 450
+               OpSourceExtension "GL_EXT_debug_printf"
+               OpName %main "main"
+               OpName %foo "foo"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+%_ptr_Function_uint = OpTypePointer Function %uint
+   %uint_127 = OpConstant %uint 127
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+        %foo = OpVariable %_ptr_Function_uint Function
+               OpStore %foo %uint_127
+         %11 = OpLoad %uint %foo
+         %13 = OpExtInst %void %12 1 %10 %11
+               OpReturn
+               OpFunctionEnd
diff --git a/test/diff/diff_files/string_in_ext_inst_src.spvasm b/test/diff/diff_files/string_in_ext_inst_src.spvasm
new file mode 100644
index 0000000..dd55bce
--- /dev/null
+++ b/test/diff/diff_files/string_in_ext_inst_src.spvasm
@@ -0,0 +1,26 @@
+;; Tests a diff where the an OpString is used only as parameter of OpExtInst.
+               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+          %1 = OpExtInstImport "GLSL.std.450"
+         %12 = OpExtInstImport "NonSemantic.DebugPrintf"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+         %10 = OpString "unsigned == %u"
+               OpSource GLSL 450
+               OpSourceExtension "GL_EXT_debug_printf"
+               OpName %main "main"
+               OpName %foo "foo"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+%_ptr_Function_uint = OpTypePointer Function %uint
+   %uint_127 = OpConstant %uint 127
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+        %foo = OpVariable %_ptr_Function_uint Function
+               OpStore %foo %uint_127
+         %11 = OpLoad %uint %foo
+         %13 = OpExtInst %void %12 1 %10 %11
+               OpReturn
+               OpFunctionEnd
diff --git a/test/enum_string_mapping_test.cpp b/test/enum_string_mapping_test.cpp
index 01dede7..e073c4b 100644
--- a/test/enum_string_mapping_test.cpp
+++ b/test/enum_string_mapping_test.cpp
@@ -21,8 +21,7 @@
 #include <vector>
 
 #include "gtest/gtest.h"
-#include "source/enum_string_mapping.h"
-#include "source/extensions.h"
+#include "source/table2.h"
 
 namespace spvtools {
 namespace {
@@ -33,8 +32,6 @@
 using ExtensionTest =
     ::testing::TestWithParam<std::pair<Extension, std::string>>;
 using UnknownExtensionTest = ::testing::TestWithParam<std::string>;
-using CapabilityTest =
-    ::testing::TestWithParam<std::pair<spv::Capability, std::string>>;
 
 TEST_P(ExtensionTest, TestExtensionFromString) {
   const std::pair<Extension, std::string>& param = GetParam();
@@ -58,14 +55,6 @@
   ASSERT_FALSE(GetExtensionFromString(GetParam().c_str(), &result_extension));
 }
 
-TEST_P(CapabilityTest, TestCapabilityToString) {
-  const std::pair<spv::Capability, std::string>& param = GetParam();
-  const spv::Capability capability = param.first;
-  const std::string capability_str = param.second;
-  const std::string result_str = CapabilityToString(capability);
-  EXPECT_EQ(capability_str, result_str);
-}
-
 INSTANTIATE_TEST_SUITE_P(
     AllExtensions, ExtensionTest,
     ValuesIn(std::vector<std::pair<Extension, std::string>>({
@@ -97,107 +86,5 @@
                                 /*alphabetically after all extensions*/ "Z",
                                 "SPV_ERROR_random_string_hfsdklhlktherh"));
 
-INSTANTIATE_TEST_SUITE_P(
-    AllCapabilities, CapabilityTest,
-    ValuesIn(std::vector<std::pair<spv::Capability, std::string>>(
-        {{spv::Capability::Matrix, "Matrix"},
-         {spv::Capability::Shader, "Shader"},
-         {spv::Capability::Geometry, "Geometry"},
-         {spv::Capability::Tessellation, "Tessellation"},
-         {spv::Capability::Addresses, "Addresses"},
-         {spv::Capability::Linkage, "Linkage"},
-         {spv::Capability::Kernel, "Kernel"},
-         {spv::Capability::Vector16, "Vector16"},
-         {spv::Capability::Float16Buffer, "Float16Buffer"},
-         {spv::Capability::Float16, "Float16"},
-         {spv::Capability::Float64, "Float64"},
-         {spv::Capability::Int64, "Int64"},
-         {spv::Capability::Int64Atomics, "Int64Atomics"},
-         {spv::Capability::ImageBasic, "ImageBasic"},
-         {spv::Capability::ImageReadWrite, "ImageReadWrite"},
-         {spv::Capability::ImageMipmap, "ImageMipmap"},
-         {spv::Capability::Pipes, "Pipes"},
-         {spv::Capability::Groups, "Groups"},
-         {spv::Capability::DeviceEnqueue, "DeviceEnqueue"},
-         {spv::Capability::LiteralSampler, "LiteralSampler"},
-         {spv::Capability::AtomicStorage, "AtomicStorage"},
-         {spv::Capability::Int16, "Int16"},
-         {spv::Capability::TessellationPointSize, "TessellationPointSize"},
-         {spv::Capability::GeometryPointSize, "GeometryPointSize"},
-         {spv::Capability::ImageGatherExtended, "ImageGatherExtended"},
-         {spv::Capability::StorageImageMultisample, "StorageImageMultisample"},
-         {spv::Capability::UniformBufferArrayDynamicIndexing,
-          "UniformBufferArrayDynamicIndexing"},
-         {spv::Capability::SampledImageArrayDynamicIndexing,
-          "SampledImageArrayDynamicIndexing"},
-         {spv::Capability::StorageBufferArrayDynamicIndexing,
-          "StorageBufferArrayDynamicIndexing"},
-         {spv::Capability::StorageImageArrayDynamicIndexing,
-          "StorageImageArrayDynamicIndexing"},
-         {spv::Capability::ClipDistance, "ClipDistance"},
-         {spv::Capability::CullDistance, "CullDistance"},
-         {spv::Capability::ImageCubeArray, "ImageCubeArray"},
-         {spv::Capability::SampleRateShading, "SampleRateShading"},
-         {spv::Capability::ImageRect, "ImageRect"},
-         {spv::Capability::SampledRect, "SampledRect"},
-         {spv::Capability::GenericPointer, "GenericPointer"},
-         {spv::Capability::Int8, "Int8"},
-         {spv::Capability::InputAttachment, "InputAttachment"},
-         {spv::Capability::SparseResidency, "SparseResidency"},
-         {spv::Capability::MinLod, "MinLod"},
-         {spv::Capability::Sampled1D, "Sampled1D"},
-         {spv::Capability::Image1D, "Image1D"},
-         {spv::Capability::SampledCubeArray, "SampledCubeArray"},
-         {spv::Capability::SampledBuffer, "SampledBuffer"},
-         {spv::Capability::ImageBuffer, "ImageBuffer"},
-         {spv::Capability::ImageMSArray, "ImageMSArray"},
-         {spv::Capability::StorageImageExtendedFormats,
-          "StorageImageExtendedFormats"},
-         {spv::Capability::ImageQuery, "ImageQuery"},
-         {spv::Capability::DerivativeControl, "DerivativeControl"},
-         {spv::Capability::InterpolationFunction, "InterpolationFunction"},
-         {spv::Capability::TransformFeedback, "TransformFeedback"},
-         {spv::Capability::GeometryStreams, "GeometryStreams"},
-         {spv::Capability::StorageImageReadWithoutFormat,
-          "StorageImageReadWithoutFormat"},
-         {spv::Capability::StorageImageWriteWithoutFormat,
-          "StorageImageWriteWithoutFormat"},
-         {spv::Capability::MultiViewport, "MultiViewport"},
-         {spv::Capability::SubgroupDispatch, "SubgroupDispatch"},
-         {spv::Capability::NamedBarrier, "NamedBarrier"},
-         {spv::Capability::PipeStorage, "PipeStorage"},
-         {spv::Capability::SubgroupBallotKHR, "SubgroupBallotKHR"},
-         {spv::Capability::DrawParameters, "DrawParameters"},
-         {spv::Capability::SubgroupVoteKHR, "SubgroupVoteKHR"},
-         {spv::Capability::StorageBuffer16BitAccess,
-          "StorageBuffer16BitAccess"},
-         {spv::Capability::StorageUniformBufferBlock16,
-          "StorageBuffer16BitAccess"},  // Preferred name
-         {spv::Capability::UniformAndStorageBuffer16BitAccess,
-          "UniformAndStorageBuffer16BitAccess"},
-         {spv::Capability::StorageUniform16,
-          "UniformAndStorageBuffer16BitAccess"},  // Preferred name
-         {spv::Capability::StoragePushConstant16, "StoragePushConstant16"},
-         {spv::Capability::StorageInputOutput16, "StorageInputOutput16"},
-         {spv::Capability::DeviceGroup, "DeviceGroup"},
-         {spv::Capability::AtomicFloat32AddEXT, "AtomicFloat32AddEXT"},
-         {spv::Capability::AtomicFloat64AddEXT, "AtomicFloat64AddEXT"},
-         {spv::Capability::AtomicFloat32MinMaxEXT, "AtomicFloat32MinMaxEXT"},
-         {spv::Capability::AtomicFloat64MinMaxEXT, "AtomicFloat64MinMaxEXT"},
-         {spv::Capability::MultiView, "MultiView"},
-         {spv::Capability::Int64ImageEXT, "Int64ImageEXT"},
-         {spv::Capability::SampleMaskOverrideCoverageNV,
-          "SampleMaskOverrideCoverageNV"},
-         {spv::Capability::GeometryShaderPassthroughNV,
-          "GeometryShaderPassthroughNV"},
-         // The next two are different names for the same token.
-         {spv::Capability::ShaderViewportIndexLayerNV,
-          "ShaderViewportIndexLayerEXT"},
-         {spv::Capability::ShaderViewportIndexLayerEXT,
-          "ShaderViewportIndexLayerEXT"},
-         {spv::Capability::ShaderViewportMaskNV, "ShaderViewportMaskNV"},
-         {spv::Capability::ShaderStereoViewNV, "ShaderStereoViewNV"},
-         {spv::Capability::PerViewAttributesNV, "PerViewAttributesNV"}})));
-
 }  // namespace
 }  // namespace spvtools
diff --git a/test/ext_inst.arm_motion_engine_test.cpp b/test/ext_inst.arm_motion_engine_test.cpp
new file mode 100644
index 0000000..a4fa7be
--- /dev/null
+++ b/test/ext_inst.arm_motion_engine_test.cpp
@@ -0,0 +1,94 @@
+// Copyright (c) 2024-2025 Arm Ltd.
+//
+// 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 <string>
+
+#include "gmock/gmock.h"
+#include "source/util/string_utils.h"
+#include "spirv/unified1/ArmMotionEngine.100.h"
+#include "test/test_fixture.h"
+#include "test/unit_spirv.h"
+
+namespace spvtools {
+namespace {
+
+using spvtest::Concatenate;
+using spvtest::MakeInstruction;
+using spvtest::TextToBinaryTest;
+using testing::Eq;
+using utils::MakeVector;
+
+TEST_F(TextToBinaryTest, ArmMotionEngineImportTest) {
+  const std::string src = "%1 = OpExtInstImport \"Arm.MotionEngine.100\"";
+  EXPECT_THAT(CompiledInstructions(src),
+              Eq(MakeInstruction(spv::Op::OpExtInstImport, {1},
+                                 MakeVector("Arm.MotionEngine.100"))));
+}
+
+TEST_F(TextToBinaryTest, ArmMotionEngineInstructionMIN_SAD) {
+  const std::string src =
+      "%1 = OpExtInstImport \"Arm.MotionEngine.100\"\n"
+      "%3 = OpExtInst %2 %1 MIN_SAD %4 %5 %6 %7 %8 %9 %10 %11 %12\n";
+
+  // First make sure it assembles correctly.
+  EXPECT_THAT(
+      CompiledInstructions(src),
+      Eq(Concatenate({MakeInstruction(spv::Op::OpExtInstImport, {1},
+                                      MakeVector("Arm.MotionEngine.100")),
+                      MakeInstruction(spv::Op::OpExtInst,
+                                      {2, 3, 1, ArmMotionEngineMIN_SAD, 4, 5, 6,
+                                       7, 8, 9, 10, 11, 12})})))
+      << src;
+  // Now check the round trip through the disassembler.
+  EXPECT_THAT(EncodeAndDecodeSuccessfully(src), src) << src;
+}
+
+TEST_F(TextToBinaryTest, ArmMotionEngineInstructionMIN_SAD_COST) {
+  const std::string src =
+      "%1 = OpExtInstImport \"Arm.MotionEngine.100\"\n"
+      "%3 = OpExtInst %2 %1 MIN_SAD_COST %4 %5 %6 %7 %8 %9 %10 %11 %12\n";
+
+  // First make sure it assembles correctly.
+  EXPECT_THAT(
+      CompiledInstructions(src),
+      Eq(Concatenate({MakeInstruction(spv::Op::OpExtInstImport, {1},
+                                      MakeVector("Arm.MotionEngine.100")),
+                      MakeInstruction(spv::Op::OpExtInst,
+                                      {2, 3, 1, ArmMotionEngineMIN_SAD_COST, 4,
+                                       5, 6, 7, 8, 9, 10, 11, 12})})))
+      << src;
+  // Now check the round trip through the disassembler.
+  EXPECT_THAT(EncodeAndDecodeSuccessfully(src), src) << src;
+}
+
+TEST_F(TextToBinaryTest, ArmMotionEngineInstructionRAW_SAD) {
+  const std::string src =
+      "%1 = OpExtInstImport \"Arm.MotionEngine.100\"\n"
+      "%3 = OpExtInst %2 %1 RAW_SAD %4 %5 %6 %7 %8 %9 %10 %11\n";
+
+  // First make sure it assembles correctly.
+  EXPECT_THAT(
+      CompiledInstructions(src),
+      Eq(Concatenate(
+          {MakeInstruction(spv::Op::OpExtInstImport, {1},
+                           MakeVector("Arm.MotionEngine.100")),
+           MakeInstruction(spv::Op::OpExtInst, {2, 3, 1, ArmMotionEngineRAW_SAD,
+                                                4, 5, 6, 7, 8, 9, 10, 11})})))
+      << src;
+  // Now check the round trip through the disassembler.
+  EXPECT_THAT(EncodeAndDecodeSuccessfully(src), src) << src;
+}
+
+}  // namespace
+}  // namespace spvtools
diff --git a/test/ext_inst.tosa_test.cpp b/test/ext_inst.tosa_test.cpp
new file mode 100644
index 0000000..2a506bd
--- /dev/null
+++ b/test/ext_inst.tosa_test.cpp
@@ -0,0 +1,57 @@
+// Copyright (c) 2024-2025 Arm Ltd.
+//
+// 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 <string>
+
+#include "gmock/gmock.h"
+#include "source/util/string_utils.h"
+#include "spirv/unified1/TOSA.001000.1.h"
+#include "test/test_fixture.h"
+#include "test/unit_spirv.h"
+
+namespace spvtools {
+namespace {
+
+using spvtest::Concatenate;
+using spvtest::MakeInstruction;
+using spvtest::TextToBinaryTest;
+using testing::Eq;
+using utils::MakeVector;
+
+TEST_F(TextToBinaryTest, TOSAImportTest) {
+  const std::string src = "%1 = OpExtInstImport \"TOSA.001000.1\"";
+  EXPECT_THAT(CompiledInstructions(src),
+              Eq(MakeInstruction(spv::Op::OpExtInstImport, {1},
+                                 MakeVector("TOSA.001000.1"))));
+}
+
+TEST_F(TextToBinaryTest, TOSAInstructionSmokeTest) {
+  const std::string src =
+      "%1 = OpExtInstImport \"TOSA.001000.1\"\n"
+      "%3 = OpExtInst %2 %1 ARGMAX %4 %5 %6\n";
+
+  // First make sure it assembles correctly.
+  EXPECT_THAT(
+      CompiledInstructions(src),
+      Eq(Concatenate({MakeInstruction(spv::Op::OpExtInstImport, {1},
+                                      MakeVector("TOSA.001000.1")),
+                      MakeInstruction(spv::Op::OpExtInst,
+                                      {2, 3, 1, TOSAARGMAX, 4, 5, 6})})))
+      << src;
+  // Now check the round trip through the disassembler.
+  EXPECT_THAT(EncodeAndDecodeSuccessfully(src), src) << src;
+}
+
+}  // namespace
+}  // namespace spvtools
diff --git a/test/ext_inst_lookup_test.cpp b/test/ext_inst_lookup_test.cpp
new file mode 100644
index 0000000..a580c7c
--- /dev/null
+++ b/test/ext_inst_lookup_test.cpp
@@ -0,0 +1,87 @@
+// Copyright 2025 Google LLC
+//
+// 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 <array>
+#include <iostream>
+
+#include "gmock/gmock.h"
+#include "source/spirv_target_env.h"
+#include "source/table2.h"
+#include "test/unit_spirv.h"
+
+using ::testing::ContainerEq;
+using ::testing::ValuesIn;
+
+namespace spvtools {
+namespace {
+
+struct ExtInstLookupCase {
+  spv_ext_inst_type_t type;
+  std::string name;
+  uint32_t value;
+  bool expect_pass = true;
+};
+
+std::ostream& operator<<(std::ostream& os, const ExtInstLookupCase& eilc) {
+  os << "EILC(" << static_cast<int>(eilc.type) << ", '" << eilc.name << "', "
+     << eilc.value << ", expect pass? " << eilc.expect_pass << ")";
+  return os;
+}
+
+using ExtInstLookupTest = ::testing::TestWithParam<ExtInstLookupCase>;
+
+TEST_P(ExtInstLookupTest, ExtInstLookup_ByName) {
+  const ExtInstDesc* desc = nullptr;
+  auto status = LookupExtInst(GetParam().type, GetParam().name.data(), &desc);
+  if (GetParam().expect_pass) {
+    EXPECT_EQ(status, SPV_SUCCESS);
+    ASSERT_NE(desc, nullptr);
+    EXPECT_EQ(static_cast<uint32_t>(desc->value), GetParam().value);
+  } else {
+    EXPECT_NE(status, SPV_SUCCESS);
+    EXPECT_EQ(desc, nullptr);
+  }
+}
+
+TEST_P(ExtInstLookupTest, ExtInstLookup_ByValue_Success) {
+  const ExtInstDesc* desc = nullptr;
+  if (GetParam().expect_pass) {
+    auto status = LookupExtInst(GetParam().type, GetParam().value, &desc);
+    EXPECT_EQ(status, SPV_SUCCESS);
+    ASSERT_NE(desc, nullptr);
+    EXPECT_EQ(desc->value, GetParam().value);
+    EXPECT_EQ(std::string(desc->name().data()), GetParam().name);
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(Samples, ExtInstLookupTest,
+                         ValuesIn(std::vector<ExtInstLookupCase>{
+                             {SPV_EXT_INST_TYPE_GLSL_STD_450, "FMix", 46},
+                             {SPV_EXT_INST_TYPE_OPENCL_STD, "s_mul24", 169},
+                             {SPV_EXT_INST_TYPE_OPENCL_STD, "mix", 99},
+                         }));
+
+TEST(ExtInstLookupSingleTest, ExtInstLookup_Value_Fails) {
+  // This list may need adjusting over time.
+  std::array<uint32_t, 3> bad_values = {{99999, 37737, 110101}};
+  for (auto bad_value : bad_values) {
+    const ExtInstDesc* desc = nullptr;
+    auto status = LookupExtInst(SPV_EXT_INST_TYPE_OPENCL_STD, bad_value, &desc);
+    EXPECT_NE(status, SPV_SUCCESS);
+    ASSERT_EQ(desc, nullptr);
+  }
+}
+
+}  // namespace
+}  // namespace spvtools
diff --git a/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/test/fuzz/fuzzer_pass_donate_modules_test.cpp
index 81687ac..ac94fbf 100644
--- a/test/fuzz/fuzzer_pass_donate_modules_test.cpp
+++ b/test/fuzz/fuzzer_pass_donate_modules_test.cpp
@@ -569,6 +569,7 @@
   const auto env = SPV_ENV_UNIVERSAL_1_3;
   const auto consumer = nullptr;
   spvtools::ValidatorOptions validator_options;
+  validator_options.SetRelaxLogicalPointer(true);
 
   const auto recipient_context =
       BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption);
@@ -2002,6 +2003,7 @@
   const auto env = SPV_ENV_UNIVERSAL_1_3;
   const auto consumer = nullptr;
   spvtools::ValidatorOptions validator_options;
+  validator_options.SetRelaxLogicalPointer(true);
 
   const auto recipient_context =
       BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption);
@@ -2235,6 +2237,7 @@
   const auto env = SPV_ENV_UNIVERSAL_1_3;
   const auto consumer = nullptr;
   spvtools::ValidatorOptions validator_options;
+  validator_options.SetRelaxLogicalPointer(true);
 
   const auto recipient_context =
       BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption);
diff --git a/test/fuzz/transformation_access_chain_test.cpp b/test/fuzz/transformation_access_chain_test.cpp
index fa19aa5..4d79d03 100644
--- a/test/fuzz/transformation_access_chain_test.cpp
+++ b/test/fuzz/transformation_access_chain_test.cpp
@@ -105,6 +105,7 @@
   const auto consumer = nullptr;
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   spvtools::ValidatorOptions validator_options;
+  validator_options.SetRelaxLogicalPointer(true);
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
 
diff --git a/test/fuzz/transformation_add_copy_memory_test.cpp b/test/fuzz/transformation_add_copy_memory_test.cpp
index 29a936c..da82fed 100644
--- a/test/fuzz/transformation_add_copy_memory_test.cpp
+++ b/test/fuzz/transformation_add_copy_memory_test.cpp
@@ -142,6 +142,7 @@
   const auto consumer = nullptr;
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   spvtools::ValidatorOptions validator_options;
+  validator_options.SetRelaxLogicalPointer(true);
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
diff --git a/test/fuzz/transformation_add_synonym_test.cpp b/test/fuzz/transformation_add_synonym_test.cpp
index 385590e..7936cad 100644
--- a/test/fuzz/transformation_add_synonym_test.cpp
+++ b/test/fuzz/transformation_add_synonym_test.cpp
@@ -71,6 +71,7 @@
   const auto consumer = nullptr;
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   spvtools::ValidatorOptions validator_options;
+  validator_options.SetRelaxLogicalPointer(true);
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
@@ -208,6 +209,7 @@
   const auto consumer = nullptr;
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   spvtools::ValidatorOptions validator_options;
+  validator_options.SetRelaxLogicalPointer(true);
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
@@ -1271,6 +1273,7 @@
   const auto consumer = nullptr;
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   spvtools::ValidatorOptions validator_options;
+  validator_options.SetRelaxLogicalPointer(true);
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
diff --git a/test/fuzz/transformation_load_test.cpp b/test/fuzz/transformation_load_test.cpp
index 6fde49a..e3f6d0c 100644
--- a/test/fuzz/transformation_load_test.cpp
+++ b/test/fuzz/transformation_load_test.cpp
@@ -86,6 +86,7 @@
   const auto consumer = nullptr;
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   spvtools::ValidatorOptions validator_options;
+  validator_options.SetRelaxLogicalPointer(true);
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
diff --git a/test/fuzz/transformation_mutate_pointer_test.cpp b/test/fuzz/transformation_mutate_pointer_test.cpp
index e0cb615..92d1ab9 100644
--- a/test/fuzz/transformation_mutate_pointer_test.cpp
+++ b/test/fuzz/transformation_mutate_pointer_test.cpp
@@ -79,6 +79,7 @@
   const auto consumer = nullptr;
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   spvtools::ValidatorOptions validator_options;
+  validator_options.SetRelaxLogicalPointer(true);
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
diff --git a/test/fuzz/transformation_propagate_instruction_down_test.cpp b/test/fuzz/transformation_propagate_instruction_down_test.cpp
index 8bedef2..f9db30d 100644
--- a/test/fuzz/transformation_propagate_instruction_down_test.cpp
+++ b/test/fuzz/transformation_propagate_instruction_down_test.cpp
@@ -679,6 +679,7 @@
   const auto consumer = nullptr;
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   spvtools::ValidatorOptions validator_options;
+  validator_options.SetRelaxLogicalPointer(true);
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
diff --git a/test/fuzz/transformation_propagate_instruction_up_test.cpp b/test/fuzz/transformation_propagate_instruction_up_test.cpp
index 20cf4b3..ac0c14a 100644
--- a/test/fuzz/transformation_propagate_instruction_up_test.cpp
+++ b/test/fuzz/transformation_propagate_instruction_up_test.cpp
@@ -690,6 +690,7 @@
   const auto consumer = nullptr;
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   spvtools::ValidatorOptions validator_options;
+  validator_options.SetRelaxLogicalPointer(true);
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
@@ -770,6 +771,7 @@
   const auto consumer = nullptr;
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   spvtools::ValidatorOptions validator_options;
+  validator_options.SetRelaxLogicalPointer(true);
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
diff --git a/test/fuzz/transformation_push_id_through_variable_test.cpp b/test/fuzz/transformation_push_id_through_variable_test.cpp
index 0d4850d..a4641ba 100644
--- a/test/fuzz/transformation_push_id_through_variable_test.cpp
+++ b/test/fuzz/transformation_push_id_through_variable_test.cpp
@@ -99,6 +99,7 @@
       BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
 
   spvtools::ValidatorOptions validator_options;
+  validator_options.SetRelaxLogicalPointer(true);
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   // Tests the reference shader validity.
@@ -339,6 +340,7 @@
       BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
 
   spvtools::ValidatorOptions validator_options;
+  validator_options.SetRelaxLogicalPointer(true);
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   uint32_t value_id = 80;
@@ -606,6 +608,7 @@
       BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
 
   spvtools::ValidatorOptions validator_options;
+  validator_options.SetRelaxLogicalPointer(true);
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   // Tests the reference shader validity.
@@ -707,6 +710,7 @@
       BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
 
   spvtools::ValidatorOptions validator_options;
+  validator_options.SetRelaxLogicalPointer(true);
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   // Tests the reference shader validity.
@@ -772,6 +776,7 @@
       BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
 
   spvtools::ValidatorOptions validator_options;
+  validator_options.SetRelaxLogicalPointer(true);
   TransformationContext transformation_context(
       MakeUnique<FactManager>(context.get()), validator_options);
   // Tests the reference shader validity.
diff --git a/test/fuzz/transformation_set_memory_operands_mask_test.cpp b/test/fuzz/transformation_set_memory_operands_mask_test.cpp
index 44901f9..d08b98c 100644
--- a/test/fuzz/transformation_set_memory_operands_mask_test.cpp
+++ b/test/fuzz/transformation_set_memory_operands_mask_test.cpp
@@ -333,10 +333,12 @@
           %3 = OpTypeFunction %2
           %6 = OpTypeFloat 32
           %7 = OpTypeStruct %6 %6 %6
+      %1001 = OpTypeStruct %6 %6 %6
           %8 = OpTypeInt 32 0
           %9 = OpConstant %8 12
          %10 = OpTypeArray %7 %9
-         %11 = OpTypePointer Private %10
+     %1002 = OpTypeArray %1001 %9
+         %11 = OpTypePointer Private %1002
          %12 = OpVariable %11 Private
          %15 = OpTypeStruct %10 %7
          %16 = OpTypePointer Uniform %15
@@ -344,26 +346,32 @@
          %18 = OpTypeInt 32 1
          %19 = OpConstant %18 0
          %20 = OpTypePointer Uniform %10
-         %24 = OpTypePointer Private %7
+         %24 = OpTypePointer Private %1001
          %27 = OpTypePointer Private %6
          %30 = OpConstant %18 1
-        %132 = OpTypePointer Function %10
+        %132 = OpTypePointer Function %1002
         %135 = OpTypePointer Uniform %7
-        %145 = OpTypePointer Function %7
+        %145 = OpTypePointer Function %1001
           %4 = OpFunction %2 None %3
           %5 = OpLabel
         %133 = OpVariable %132 Function
          %21 = OpAccessChain %20 %17 %19
-               OpCopyMemory %12 %21 Aligned 16 Nontemporal|Aligned 16
+    %1003 = OpLoad %10 %21 Nontemporal|Aligned 16
+       %1004 = OpCopyLogical %1002 %1003
+               OpStore %12 %1004 Aligned 16
                OpCopyMemory %133 %12 Volatile
                OpCopyMemory %133 %12
                OpCopyMemory %133 %12
         %136 = OpAccessChain %135 %17 %30
         %138 = OpAccessChain %24 %12 %19
-               OpCopyMemory %138 %136 None Aligned 16
-               OpCopyMemory %138 %136 Aligned 16
+   %1005 = OpLoad %7 %136 Aligned 16
+      %1006 = OpCopyLogical %1001 %1005
+               OpStore %138 %1006
+  %1007 = OpLoad %7 %136
+      %1008 = OpCopyLogical %1001 %1007
+               OpStore %138 %1008 Aligned 16
         %146 = OpAccessChain %145 %133 %30
-        %147 = OpLoad %7 %146 Volatile|Nontemporal|Aligned 16
+        %147 = OpLoad %1001 %146 Volatile|Nontemporal|Aligned 16
         %148 = OpAccessChain %24 %12 %19
                OpStore %148 %147 Nontemporal
                OpReturn
@@ -382,14 +390,14 @@
         MakeUnique<FactManager>(context.get()), validator_options);
     {
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 0),
+          MakeInstructionDescriptor(21, spv::Op::OpLoad, 0),
           (uint32_t)spv::MemoryAccessMask::Aligned |
               (uint32_t)spv::MemoryAccessMask::Volatile,
-          1);
+          0);
       // Bad: cannot remove aligned
       ASSERT_FALSE(TransformationSetMemoryOperandsMask(
-                       MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 0),
-                       (uint32_t)spv::MemoryAccessMask::Volatile, 1)
+                       MakeInstructionDescriptor(21, spv::Op::OpLoad, 0),
+                       (uint32_t)spv::MemoryAccessMask::Volatile, 0)
                        .IsApplicable(context.get(), transformation_context));
       ASSERT_TRUE(
           transformation.IsApplicable(context.get(), transformation_context));
@@ -399,13 +407,13 @@
 
     {
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 1),
+          MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 0),
           (uint32_t)spv::MemoryAccessMask::Nontemporal |
               (uint32_t)spv::MemoryAccessMask::Volatile,
           1);
       // Bad: cannot remove volatile
       ASSERT_FALSE(TransformationSetMemoryOperandsMask(
-                       MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 1),
+                       MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 0),
                        (uint32_t)spv::MemoryAccessMask::Nontemporal, 0)
                        .IsApplicable(context.get(), transformation_context));
       ASSERT_TRUE(
@@ -417,7 +425,7 @@
     {
       // Creates the first operand.
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 2),
+          MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 1),
           (uint32_t)spv::MemoryAccessMask::Nontemporal |
               (uint32_t)spv::MemoryAccessMask::Volatile,
           0);
@@ -430,7 +438,7 @@
     {
       // Creates both operands.
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 3),
+          MakeInstructionDescriptor(21, spv::Op::OpCopyMemory, 2),
           (uint32_t)spv::MemoryAccessMask::Nontemporal |
               (uint32_t)spv::MemoryAccessMask::Volatile,
           1);
@@ -442,13 +450,13 @@
 
     {
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(138, spv::Op::OpCopyMemory, 0),
+          MakeInstructionDescriptor(138, spv::Op::OpLoad, 0),
           (uint32_t)spv::MemoryAccessMask::Aligned |
               (uint32_t)spv::MemoryAccessMask::Nontemporal,
-          1);
+          0);
       // Bad: the first mask is None, so Aligned cannot be added to it.
       ASSERT_FALSE(TransformationSetMemoryOperandsMask(
-                       MakeInstructionDescriptor(138, spv::Op::OpCopyMemory, 0),
+                       MakeInstructionDescriptor(138, spv::Op::OpStore, 0),
                        (uint32_t)spv::MemoryAccessMask::Aligned |
                            (uint32_t)spv::MemoryAccessMask::Nontemporal,
                        0)
@@ -461,8 +469,8 @@
 
     {
       TransformationSetMemoryOperandsMask transformation(
-          MakeInstructionDescriptor(138, spv::Op::OpCopyMemory, 1),
-          (uint32_t)spv::MemoryAccessMask::Volatile, 1);
+          MakeInstructionDescriptor(138, spv::Op::OpLoad, 1),
+          (uint32_t)spv::MemoryAccessMask::Volatile, 0);
       ASSERT_TRUE(
           transformation.IsApplicable(context.get(), transformation_context));
       ApplyAndCheckFreshIds(transformation, context.get(),
@@ -522,10 +530,12 @@
             %3 = OpTypeFunction %2
             %6 = OpTypeFloat 32
             %7 = OpTypeStruct %6 %6 %6
+         %1001 = OpTypeStruct %6 %6 %6
             %8 = OpTypeInt 32 0
             %9 = OpConstant %8 12
            %10 = OpTypeArray %7 %9
-           %11 = OpTypePointer Private %10
+         %1002 = OpTypeArray %1001 %9
+           %11 = OpTypePointer Private %1002
            %12 = OpVariable %11 Private
            %15 = OpTypeStruct %10 %7
            %16 = OpTypePointer Uniform %15
@@ -533,26 +543,32 @@
            %18 = OpTypeInt 32 1
            %19 = OpConstant %18 0
            %20 = OpTypePointer Uniform %10
-           %24 = OpTypePointer Private %7
+           %24 = OpTypePointer Private %1001
            %27 = OpTypePointer Private %6
            %30 = OpConstant %18 1
-          %132 = OpTypePointer Function %10
+          %132 = OpTypePointer Function %1002
           %135 = OpTypePointer Uniform %7
-          %145 = OpTypePointer Function %7
+          %145 = OpTypePointer Function %1001
             %4 = OpFunction %2 None %3
             %5 = OpLabel
           %133 = OpVariable %132 Function
            %21 = OpAccessChain %20 %17 %19
-                 OpCopyMemory %12 %21 Aligned 16 Aligned|Volatile 16
+         %1003 = OpLoad %10 %21 Aligned|Volatile 16
+         %1004 = OpCopyLogical %1002 %1003
+                 OpStore %12 %1004 Aligned 16
                  OpCopyMemory %133 %12 Volatile Nontemporal|Volatile
                  OpCopyMemory %133 %12 Nontemporal|Volatile
                  OpCopyMemory %133 %12 None Nontemporal|Volatile
           %136 = OpAccessChain %135 %17 %30
           %138 = OpAccessChain %24 %12 %19
-                 OpCopyMemory %138 %136 None Aligned|Nontemporal 16
-                 OpCopyMemory %138 %136 Aligned 16 Volatile
+         %1005 = OpLoad %7 %136 Aligned|Nontemporal 16
+         %1006 = OpCopyLogical %1001 %1005
+                 OpStore %138 %1006
+         %1007 = OpLoad %7 %136 Volatile
+         %1008 = OpCopyLogical %1001 %1007
+                 OpStore %138 %1008 Aligned 16
           %146 = OpAccessChain %145 %133 %30
-          %147 = OpLoad %7 %146 Volatile|Aligned 16
+          %147 = OpLoad %1001 %146 Volatile|Aligned 16
           %148 = OpAccessChain %24 %12 %19
                  OpStore %148 %147 None
                  OpReturn
diff --git a/test/fuzz/transformation_store_test.cpp b/test/fuzz/transformation_store_test.cpp
index fe24d74..6779ee2 100644
--- a/test/fuzz/transformation_store_test.cpp
+++ b/test/fuzz/transformation_store_test.cpp
@@ -95,6 +95,7 @@
   const auto consumer = nullptr;
   const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
   spvtools::ValidatorOptions validator_options;
+  validator_options.SetRelaxLogicalPointer(true);
   ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                kConsoleMessageConsumer));
   TransformationContext transformation_context(
diff --git a/test/handle_unknown_opcodes_test.cpp b/test/handle_unknown_opcodes_test.cpp
new file mode 100644
index 0000000..eaf00ff
--- /dev/null
+++ b/test/handle_unknown_opcodes_test.cpp
@@ -0,0 +1,511 @@
+// Copyright (c) 2026 NVIDIA Corporation

+//

+// 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.

+

+// Tests for SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES. Verifies that

+// the binary parser, disassembler, and FriendlyNameMapper correctly handle

+// SPIR-V binaries that contain unknown opcodes, unknown extended instruction

+// numbers, or known opcodes with unknown enum operands.

+

+#include <string>

+#include <vector>

+

+#include "gmock/gmock.h"

+#include "source/binary.h"

+#include "source/disassemble.h"

+#include "source/name_mapper.h"

+#include "spirv-tools/libspirv.h"

+#include "test/unit_spirv.h"

+

+namespace spvtools {

+namespace {

+

+using ::testing::HasSubstr;

+using ::testing::Not;

+

+// Opcode 0xFFFE is not assigned in any known SPIR-V grammar version.

+constexpr uint32_t kUnknownOpcode = 0xFFFEu;

+

+// Returns a SPIR-V module header for a module with the given ID bound.

+std::vector<uint32_t> MakeHeader(uint32_t id_bound) {

+  return {spv::MagicNumber, 0x00010000u,

+          SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 0), id_bound, 0u};

+}

+

+// Returns the packed first word of a SPIR-V instruction.

+uint32_t MakeFirstWord(uint32_t word_count, uint32_t opcode) {

+  return (word_count << 16) | (opcode & 0xFFFFu);

+}

+

+class HandleUnknownOpcodesTest : public ::testing::Test {

+ protected:

+  void SetUp() override {

+    context_ = spvContextCreate(SPV_ENV_UNIVERSAL_1_0);

+    ASSERT_NE(nullptr, context_);

+  }

+

+  void TearDown() override { spvContextDestroy(context_); }

+

+  spv_context context_ = nullptr;

+};

+

+// Binary parser: unknown opcode without the flag -> error.

+TEST_F(HandleUnknownOpcodesTest, ParseUnknownOpcodeWithoutFlagFails) {

+  std::vector<uint32_t> binary = spvtest::Concatenate({

+      MakeHeader(1),

+      {MakeFirstWord(1, kUnknownOpcode)},

+  });

+  spv_diagnostic diag = nullptr;

+  const spv_result_t result =

+      spvBinaryParseWithOptions(context_, nullptr, binary.data(), binary.size(),

+                                nullptr, nullptr, &diag, 0u);

+  EXPECT_NE(SPV_SUCCESS, result);

+  spvDiagnosticDestroy(diag);

+}

+

+// Binary parser: unknown opcode with the flag -> success.

+TEST_F(HandleUnknownOpcodesTest, ParseUnknownOpcodeWithFlagSucceeds) {

+  std::vector<uint32_t> binary = spvtest::Concatenate({

+      MakeHeader(1),

+      {MakeFirstWord(3, kUnknownOpcode), 42u, 99u},

+  });

+  spv_diagnostic diag = nullptr;

+  const spv_result_t result = spvBinaryParseWithOptions(

+      context_, nullptr, binary.data(), binary.size(), nullptr, nullptr, &diag,

+      SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES);

+  EXPECT_EQ(SPV_SUCCESS, result);

+  spvDiagnosticDestroy(diag);

+}

+

+// Disassembler: unknown opcode without the flag -> error.

+TEST_F(HandleUnknownOpcodesTest, DisassembleUnknownOpcodeWithoutFlagFails) {

+  std::vector<uint32_t> binary = spvtest::Concatenate({

+      MakeHeader(1),

+      {MakeFirstWord(3, kUnknownOpcode), 42u, 99u},

+  });

+  spv_text text = nullptr;

+  spv_diagnostic diag = nullptr;

+  const spv_result_t result =

+      spvBinaryToText(context_, binary.data(), binary.size(), 0u, &text, &diag);

+  EXPECT_NE(SPV_SUCCESS, result);

+  spvTextDestroy(text);

+  spvDiagnosticDestroy(diag);

+}

+

+// Disassembler: unknown opcode with the flag -> emits OpUnknown.

+TEST_F(HandleUnknownOpcodesTest,

+       DisassembleUnknownOpcodeWithFlagEmitsOpUnknown) {

+  // 3-word instruction: [opcode+wc, 42, 99]

+  std::vector<uint32_t> binary = spvtest::Concatenate({

+      MakeHeader(1),

+      {MakeFirstWord(3, kUnknownOpcode), 42u, 99u},

+  });

+  spv_text text = nullptr;

+  spv_diagnostic diag = nullptr;

+  const spv_result_t result = spvBinaryToText(

+      context_, binary.data(), binary.size(),

+      SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES, &text, &diag);

+  ASSERT_EQ(SPV_SUCCESS, result) << (diag ? diag->error : "(no diagnostic)");

+  const std::string output(text->str, text->length);

+  // kUnknownOpcode=0xFFFE=65534, word_count=3, operands=42 99

+  EXPECT_THAT(output, HasSubstr("OpUnknown(65534, 3) 42 99"));

+  EXPECT_THAT(output,

+              HasSubstr("; note: ID bound may be incorrect after reassembly"));

+  spvTextDestroy(text);

+  spvDiagnosticDestroy(diag);

+}

+

+// Disassembler: known opcode with the flag -> normal output (flag is a no-op

+// for known opcodes).

+TEST_F(HandleUnknownOpcodesTest, DisassembleKnownOpcodeWithFlagIsNoOp) {

+  // OpCapability Shader: opcode=17, word_count=2, operand=1 (Shader)

+  std::vector<uint32_t> binary = spvtest::Concatenate({

+      MakeHeader(1),

+      {MakeFirstWord(2, 17u), 1u},

+  });

+  spv_text text = nullptr;

+  spv_diagnostic diag = nullptr;

+  const spv_result_t result = spvBinaryToText(

+      context_, binary.data(), binary.size(),

+      SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES, &text, &diag);

+  ASSERT_EQ(SPV_SUCCESS, result) << (diag ? diag->error : "(no diagnostic)");

+  const std::string output(text->str, text->length);

+  EXPECT_THAT(output, HasSubstr("OpCapability Shader"));

+  EXPECT_THAT(output, Not(HasSubstr("OpUnknown")));

+  spvTextDestroy(text);

+  spvDiagnosticDestroy(diag);

+}

+

+// Disassembler: OpExtInst with unknown instruction number in a semantic set,

+// without the flag -> error.

+TEST_F(HandleUnknownOpcodesTest, DisassembleUnknownExtInstWithoutFlagFails) {

+  // OpExtInstImport %1 "GLSL.std.450" (6 words):

+  //   opcode=11 word_count=6, result_id=1, "GLSL.std.450\0" (4 words)

+  // OpExtInst %2 %3 %1 0xFFFF (5 words):

+  //   opcode=12 word_count=5, result_type=2, result_id=3, set_id=1,

+  //   inst_number=0xFFFF

+  std::vector<uint32_t> binary = spvtest::Concatenate({

+      MakeHeader(4),

+      {0x0006000Bu, 1u, 0x4C534C47u, 0x6474732Eu, 0x3035342Eu, 0x00000000u},

+      {MakeFirstWord(5, 12u), 2u, 3u, 1u, 0xFFFFu},

+  });

+  spv_text text = nullptr;

+  spv_diagnostic diag = nullptr;

+  const spv_result_t result =

+      spvBinaryToText(context_, binary.data(), binary.size(), 0u, &text, &diag);

+  EXPECT_NE(SPV_SUCCESS, result);

+  spvTextDestroy(text);

+  spvDiagnosticDestroy(diag);

+}

+

+// Disassembler: OpExtInst with unknown instruction number in a semantic set,

+// with the flag -> emits OpUnknown with all instruction words.

+TEST_F(HandleUnknownOpcodesTest,

+       DisassembleUnknownExtInstWithFlagEmitsOpUnknown) {

+  // See DisassembleUnknownExtInstWithoutFlagFails for binary layout.

+  std::vector<uint32_t> binary = spvtest::Concatenate({

+      MakeHeader(4),

+      {0x0006000Bu, 1u, 0x4C534C47u, 0x6474732Eu, 0x3035342Eu, 0x00000000u},

+      {MakeFirstWord(5, 12u), 2u, 3u, 1u, 0xFFFFu},

+  });

+  spv_text text = nullptr;

+  spv_diagnostic diag = nullptr;

+  const spv_result_t result = spvBinaryToText(

+      context_, binary.data(), binary.size(),

+      SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES, &text, &diag);

+  ASSERT_EQ(SPV_SUCCESS, result) << (diag ? diag->error : "(no diagnostic)");

+  const std::string output(text->str, text->length);

+  // OpExtInst opcode=12, word_count=5; operands: result_type=2, result_id=3,

+  // set_id=1, inst_number=0xFFFF=65535.

+  EXPECT_THAT(output, HasSubstr("OpUnknown(12, 5) 2 3 1 65535"));

+  spvTextDestroy(text);

+  spvDiagnosticDestroy(diag);

+}

+

+// Disassembler: OpExtInst with an unknown instruction number in a non-semantic

+// extended instruction set disassembles normally (not as OpUnknown) both with

+// and without the flag. Non-semantic sets handle unknown instruction numbers

+// gracefully regardless of the flag; setting the flag must not change that.

+TEST_F(HandleUnknownOpcodesTest,

+       NonSemanticExtInstUnknownNumberNotEmittedAsOpUnknown) {

+  // OpExtInstImport %1 "NonSemantic.DebugPrintf" (8 words):

+  //   opcode=11, word_count=8, result_id=1

+  //   "NonSemantic.DebugPrintf\0" packed into 6 32-bit words:

+  //     "NonS" = 0x536E6F4E, "eman" = 0x6E616D65, "tic." = 0x2E636974,

+  //     "Debu" = 0x75626544, "gPri" = 0x69725067, "ntf\0" = 0x0066746E

+  // OpExtInst %2 %3 %1 0xFFFF (5 words):

+  //   opcode=12, word_count=5, result_type=2, result_id=3, set_id=1,

+  //   inst_number=0xFFFF (not present in the NonSemantic.DebugPrintf grammar)

+  const std::vector<uint32_t> binary = spvtest::Concatenate({

+      MakeHeader(4),

+      {0x0008000Bu, 1u, 0x536E6F4Eu, 0x6E616D65u, 0x2E636974u, 0x75626544u,

+       0x69725067u, 0x0066746Eu},

+      {MakeFirstWord(5, 12u), 2u, 3u, 1u, 0xFFFFu},

+  });

+

+  // Without the flag: non-semantic sets already handle unknown instruction

+  // numbers gracefully; parsing and disassembly succeed.

+  {

+    spv_text text = nullptr;

+    spv_diagnostic diag = nullptr;

+    EXPECT_EQ(SPV_SUCCESS, spvBinaryToText(context_, binary.data(),

+                                           binary.size(), 0u, &text, &diag))

+        << (diag ? diag->error : "(no diagnostic)");

+    spvTextDestroy(text);

+    spvDiagnosticDestroy(diag);

+  }

+

+  // With the flag: the non-semantic graceful path is unchanged.  The

+  // instruction must not be emitted as OpUnknown.

+  {

+    spv_text text = nullptr;

+    spv_diagnostic diag = nullptr;

+    ASSERT_EQ(SPV_SUCCESS,

+              spvBinaryToText(context_, binary.data(), binary.size(),

+                              SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES,

+                              &text, &diag))

+        << (diag ? diag->error : "(no diagnostic)");

+    const std::string output(text->str, text->length);

+    EXPECT_THAT(output, Not(HasSubstr("OpUnknown")));

+    spvTextDestroy(text);

+    spvDiagnosticDestroy(diag);

+  }

+}

+

+// FriendlyNameMapper: when a binary contains an unknown opcode, IDs defined

+// after the unknown opcode get friendly names only when the flag is set.

+TEST_F(HandleUnknownOpcodesTest, FriendlyNameMapperContinuesPastUnknownOpcode) {

+  // OpTypeVoid %1    (opcode=19, word_count=2)

+  // Unknown opcode 0xFFFE  (word_count=1)

+  // OpTypeBool %2    (opcode=20, word_count=2)

+  std::vector<uint32_t> binary = spvtest::Concatenate({

+      MakeHeader(3),

+      {MakeFirstWord(2, 19u), 1u},

+      {MakeFirstWord(1, kUnknownOpcode)},

+      {MakeFirstWord(2, 20u), 2u},

+  });

+

+  // With the flag, parsing continues past the unknown opcode so %2 is named.

+  FriendlyNameMapper mapper_with_flag(

+      context_, binary.data(), binary.size(),

+      SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES);

+  EXPECT_EQ("void", mapper_with_flag.NameForId(1));

+  EXPECT_EQ("bool", mapper_with_flag.NameForId(2));

+

+  // Without the flag, parsing stops at the unknown opcode so %2 falls back to

+  // its trivial numeric name.

+  FriendlyNameMapper mapper_without_flag(context_, binary.data(),

+                                         binary.size());

+  EXPECT_EQ("void", mapper_without_flag.NameForId(1));

+  EXPECT_EQ("2", mapper_without_flag.NameForId(2));

+}

+

+// Binary parser: unknown opcode that claims more words than remain in the

+// binary -> error, even with the flag set.

+TEST_F(HandleUnknownOpcodesTest, ParseTruncatedUnknownOpcodeFails) {

+  // Instruction claims 3 words but only 1 is present.

+  std::vector<uint32_t> binary = spvtest::Concatenate({

+      MakeHeader(1),

+      {MakeFirstWord(3, kUnknownOpcode)},

+  });

+  spv_diagnostic diag = nullptr;

+  const spv_result_t result = spvBinaryParseWithOptions(

+      context_, nullptr, binary.data(), binary.size(), nullptr, nullptr, &diag,

+      SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES);

+  EXPECT_NE(SPV_SUCCESS, result);

+  spvDiagnosticDestroy(diag);

+}

+

+// Disassembler: known opcode with an unknown flat enum operand, without the

+// flag -> error.

+TEST_F(HandleUnknownOpcodesTest, DisassembleUnknownFlatEnumWithoutFlagFails) {

+  // OpCapability 0xFFFF: opcode=17, word_count=2, capability=0xFFFF (unknown).

+  std::vector<uint32_t> binary = spvtest::Concatenate({

+      MakeHeader(1),

+      {MakeFirstWord(2, 17u), 0xFFFFu},

+  });

+  spv_text text = nullptr;

+  spv_diagnostic diag = nullptr;

+  const spv_result_t result =

+      spvBinaryToText(context_, binary.data(), binary.size(), 0u, &text, &diag);

+  EXPECT_NE(SPV_SUCCESS, result);

+  spvTextDestroy(text);

+  spvDiagnosticDestroy(diag);

+}

+

+// Disassembler: known opcode with an unknown flat enum operand, with the flag

+// -> emits the whole instruction as OpUnknown.

+TEST_F(HandleUnknownOpcodesTest,

+       DisassembleUnknownFlatEnumWithFlagEmitsOpUnknown) {

+  // OpCapability 0xFFFF: opcode=17, word_count=2, capability=0xFFFF (unknown).

+  std::vector<uint32_t> binary = spvtest::Concatenate({

+      MakeHeader(1),

+      {MakeFirstWord(2, 17u), 0xFFFFu},

+  });

+  spv_text text = nullptr;

+  spv_diagnostic diag = nullptr;

+  const spv_result_t result = spvBinaryToText(

+      context_, binary.data(), binary.size(),

+      SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES, &text, &diag);

+  ASSERT_EQ(SPV_SUCCESS, result) << (diag ? diag->error : "(no diagnostic)");

+  const std::string output(text->str, text->length);

+  // OpCapability opcode=17, word_count=2; operand=0xFFFF=65535.

+  EXPECT_THAT(output, HasSubstr("OpUnknown(17, 2) 65535"));

+  spvTextDestroy(text);

+  spvDiagnosticDestroy(diag);

+}

+

+// Disassembler: known opcode with an unknown mask enum operand, without the

+// flag -> error.

+TEST_F(HandleUnknownOpcodesTest, DisassembleUnknownMaskEnumWithoutFlagFails) {

+  // OpFunction %1 %2 FunctionControl(0x80000000) %3:

+  //   opcode=54, word_count=5, result_type=1, result_id=2,

+  //   function_control=0x80000000 (unknown bit), function_type=3.

+  std::vector<uint32_t> binary = spvtest::Concatenate({

+      MakeHeader(4),

+      {MakeFirstWord(5, 54u), 1u, 2u, 0x80000000u, 3u},

+  });

+  spv_text text = nullptr;

+  spv_diagnostic diag = nullptr;

+  const spv_result_t result =

+      spvBinaryToText(context_, binary.data(), binary.size(), 0u, &text, &diag);

+  EXPECT_NE(SPV_SUCCESS, result);

+  spvTextDestroy(text);

+  spvDiagnosticDestroy(diag);

+}

+

+// Disassembler: known opcode with an unknown mask enum operand, with the flag

+// -> emits the whole instruction as OpUnknown.

+TEST_F(HandleUnknownOpcodesTest,

+       DisassembleUnknownMaskEnumWithFlagEmitsOpUnknown) {

+  // OpFunction %1 %2 FunctionControl(0x80000000) %3:

+  //   opcode=54, word_count=5, result_type=1, result_id=2,

+  //   function_control=0x80000000 (unknown bit), function_type=3.

+  std::vector<uint32_t> binary = spvtest::Concatenate({

+      MakeHeader(4),

+      {MakeFirstWord(5, 54u), 1u, 2u, 0x80000000u, 3u},

+  });

+  spv_text text = nullptr;

+  spv_diagnostic diag = nullptr;

+  const spv_result_t result = spvBinaryToText(

+      context_, binary.data(), binary.size(),

+      SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES, &text, &diag);

+  ASSERT_EQ(SPV_SUCCESS, result) << (diag ? diag->error : "(no diagnostic)");

+  const std::string output(text->str, text->length);

+  // OpFunction opcode=54, word_count=5; operands: 1, 2, 2147483648, 3.

+  EXPECT_THAT(output, HasSubstr("OpUnknown(54, 5) 1 2 2147483648 3"));

+  spvTextDestroy(text);

+  spvDiagnosticDestroy(diag);

+}

+

+// Round-trip: disassemble with the flag, reassemble, and verify the

+// instruction words are preserved byte-for-byte.

+TEST_F(HandleUnknownOpcodesTest, RoundTripUnknownOpcode) {

+  // 3-word instruction: [opcode+wc, 42, 99]

+  const std::vector<uint32_t> inst_words = {MakeFirstWord(3, kUnknownOpcode),

+                                            42u, 99u};

+  std::vector<uint32_t> original =

+      spvtest::Concatenate({MakeHeader(1), inst_words});

+

+  // Disassemble to text with the flag.

+  spv_text text = nullptr;

+  spv_diagnostic dis_diag = nullptr;

+  ASSERT_EQ(SPV_SUCCESS,

+            spvBinaryToText(context_, original.data(), original.size(),

+                            SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES,

+                            &text, &dis_diag))

+      << (dis_diag ? dis_diag->error : "(no diagnostic)");

+

+  // Reassemble the text back to binary.

+  spv_binary reassembled = nullptr;

+  spv_diagnostic asm_diag = nullptr;

+  ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context_, text->str, text->length,

+                                         &reassembled, &asm_diag))

+      << (asm_diag ? asm_diag->error : "(no diagnostic)");

+

+  // The instruction words start after the 5-word SPIR-V module header and

+  // must be byte-for-byte identical to the original instruction.

+  ASSERT_GE(reassembled->wordCount, 5u + inst_words.size());

+  for (size_t i = 0; i < inst_words.size(); i++) {

+    EXPECT_EQ(inst_words[i], reassembled->code[5 + i])

+        << "Word mismatch at instruction word " << i;

+  }

+

+  spvTextDestroy(text);

+  spvBinaryDestroy(reassembled);

+  spvDiagnosticDestroy(dis_diag);

+  spvDiagnosticDestroy(asm_diag);

+}

+

+// FriendlyNameMapper: when a known instruction has an unknown enum operand and

+// the flag is set, the instruction is retried as unknown.  inst.result_id is

+// not decoded (stays 0) so the real result ID falls back to its trivial numeric

+// name.  IDs defined by preceding and following instructions are still named.

+TEST_F(HandleUnknownOpcodesTest,

+       FriendlyNameMapperUnknownEnumOperandDropsResultName) {

+  // OpTypeVoid %1        (opcode=19, word_count=2)

+  // OpCapability 0xFFFF  (opcode=17, word_count=2; unknown Capability value)

+  // OpTypeBool %2        (opcode=20, word_count=2)

+  std::vector<uint32_t> binary = spvtest::Concatenate({

+      MakeHeader(3),

+      {MakeFirstWord(2, 19u), 1u},

+      {MakeFirstWord(2, 17u), 0xFFFFu},

+      {MakeFirstWord(2, 20u), 2u},

+  });

+

+  // With the flag, parsing continues past the unknown-enum instruction.

+  // %1 and %2 are both named; the OpCapability result (none for that opcode)

+  // does not affect naming.

+  FriendlyNameMapper mapper_with_flag(

+      context_, binary.data(), binary.size(),

+      SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES);

+  EXPECT_EQ("void", mapper_with_flag.NameForId(1));

+  EXPECT_EQ("bool", mapper_with_flag.NameForId(2));

+

+  // Without the flag, parsing stops at OpCapability 0xFFFF so %2 is unnamed.

+  FriendlyNameMapper mapper_without_flag(context_, binary.data(),

+                                         binary.size());

+  EXPECT_EQ("void", mapper_without_flag.NameForId(1));

+  EXPECT_EQ("2", mapper_without_flag.NameForId(2));

+}

+

+// Disassembler: a binary with a valid instruction before and after an unknown

+// opcode disassembles all three instructions correctly.

+TEST_F(HandleUnknownOpcodesTest, DisassembleContinuesPastUnknownOpcode) {

+  // OpTypeVoid %1 (opcode=19, word_count=2)

+  // Unknown opcode 0xFFFE (word_count=1)

+  // OpTypeBool %2 (opcode=20, word_count=2)

+  std::vector<uint32_t> binary = spvtest::Concatenate({

+      MakeHeader(3),

+      {MakeFirstWord(2, 19u), 1u},

+      {MakeFirstWord(1, kUnknownOpcode)},

+      {MakeFirstWord(2, 20u), 2u},

+  });

+  spv_text text = nullptr;

+  spv_diagnostic diag = nullptr;

+  ASSERT_EQ(SPV_SUCCESS,

+            spvBinaryToText(context_, binary.data(), binary.size(),

+                            SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES,

+                            &text, &diag))

+      << (diag ? diag->error : "(no diagnostic)");

+  const std::string output(text->str, text->length);

+  EXPECT_THAT(output, HasSubstr("OpTypeVoid"));

+  EXPECT_THAT(output, HasSubstr("OpUnknown(65534, 1)"));

+  EXPECT_THAT(output, HasSubstr("OpTypeBool"));

+  spvTextDestroy(text);

+  spvDiagnosticDestroy(diag);

+}

+

+// Disassembler: FRIENDLY_NAMES and HANDLE_UNKNOWN_OPCODES work together.

+// Known instructions use friendly names; the unknown opcode emits OpUnknown.

+TEST_F(HandleUnknownOpcodesTest, DisassembleFriendlyNamesWithUnknownOpcode) {

+  // OpTypeVoid %1 (opcode=19, word_count=2)

+  // Unknown opcode 0xFFFE (word_count=1)

+  // OpTypeBool %2 (opcode=20, word_count=2)

+  std::vector<uint32_t> binary = spvtest::Concatenate({

+      MakeHeader(3),

+      {MakeFirstWord(2, 19u), 1u},

+      {MakeFirstWord(1, kUnknownOpcode)},

+      {MakeFirstWord(2, 20u), 2u},

+  });

+  spv_text text = nullptr;

+  spv_diagnostic diag = nullptr;

+  ASSERT_EQ(SPV_SUCCESS,

+            spvBinaryToText(context_, binary.data(), binary.size(),

+                            SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES |

+                                SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES,

+                            &text, &diag))

+      << (diag ? diag->error : "(no diagnostic)");

+  const std::string output(text->str, text->length);

+  EXPECT_THAT(output, HasSubstr("%void"));

+  EXPECT_THAT(output, HasSubstr("%bool"));

+  EXPECT_THAT(output, HasSubstr("OpUnknown(65534, 1)"));

+  spvTextDestroy(text);

+  spvDiagnosticDestroy(diag);

+}

+

+// spvInstructionBinaryToText: unknown opcode with the flag emits OpUnknown.

+TEST_F(HandleUnknownOpcodesTest, InstructionBinaryToTextUnknownOpcode) {

+  const std::vector<uint32_t> inst_words = {MakeFirstWord(3, kUnknownOpcode),

+                                            42u, 99u};

+  std::vector<uint32_t> binary =

+      spvtest::Concatenate({MakeHeader(1), inst_words});

+  const std::string output = spvInstructionBinaryToText(

+      SPV_ENV_UNIVERSAL_1_0, inst_words.data(), inst_words.size(),

+      binary.data(), binary.size(),

+      SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES);

+  EXPECT_THAT(output, HasSubstr("OpUnknown(65534, 3) 42 99"));

+}

+

+}  // namespace

+}  // namespace spvtools

diff --git a/test/hex_float_test.cpp b/test/hex_float_test.cpp
index a44d9ec..a9722bb 100644
--- a/test/hex_float_test.cpp
+++ b/test/hex_float_test.cpp
@@ -30,6 +30,7 @@
 namespace utils {
 namespace {
 
+using spvtools::utils::LeadingSign;
 using ::testing::Eq;
 
 // In this file "encode" means converting a number into a string,
@@ -582,6 +583,15 @@
 int16_t unbiased_half_exponent(uint16_t f) {
   return HexFloat<FloatProxy<Float16>>(f).getUnbiasedNormalizedExponent();
 }
+int16_t unbiased_bfloat16_exponent(uint16_t f) {
+  return HexFloat<FloatProxy<BFloat16>>(f).getUnbiasedNormalizedExponent();
+}
+int8_t unbiased_E4M3_exponent(uint8_t f) {
+  return HexFloat<FloatProxy<Float8_E4M3>>(f).getUnbiasedNormalizedExponent();
+}
+int8_t unbiased_E5M2_exponent(uint8_t f) {
+  return HexFloat<FloatProxy<Float8_E5M2>>(f).getUnbiasedNormalizedExponent();
+}
 
 TEST(HexFloatOperationTest, UnbiasedExponent) {
   // Float cases
@@ -616,6 +626,42 @@
 
   // Smallest representable number
   EXPECT_EQ(-24, unbiased_half_exponent(0x0001));
+
+  EXPECT_EQ(0, unbiased_bfloat16_exponent(0x3F80));
+  EXPECT_EQ(3, unbiased_bfloat16_exponent(0x4100));
+  EXPECT_EQ(-1, unbiased_bfloat16_exponent(0x3F00));
+  EXPECT_EQ(-126, unbiased_bfloat16_exponent(0x0080));
+  EXPECT_EQ(127, unbiased_bfloat16_exponent(0x7F00));
+  EXPECT_EQ(10, unbiased_bfloat16_exponent(0x4480));
+  EXPECT_EQ(128, unbiased_bfloat16_exponent(0x7F80));
+  // Test case for the smallest representable denormal number.
+  // Effective exponent is 1 - bias - (leading_one_pos) = 1 - 127 - 7 = -133
+  EXPECT_EQ(-133, unbiased_bfloat16_exponent(0x0001));
+
+  // E4M3 cases
+  // The exponent is represented in the bits 0x78
+  // The offset is -7
+  EXPECT_EQ(0, unbiased_E4M3_exponent(0x38));
+  EXPECT_EQ(3, unbiased_E4M3_exponent(0x50));
+  EXPECT_EQ(-1, unbiased_E4M3_exponent(0x30));
+  EXPECT_EQ(-6, unbiased_E4M3_exponent(0x08));
+  EXPECT_EQ(8, unbiased_E4M3_exponent(0x78));
+
+  // Smallest representable number
+  EXPECT_EQ(-9, unbiased_E4M3_exponent(0x01));
+
+  // E5M2 cases
+  // The exponent is represented in the bits 0x7C
+  // The offset is -15
+  EXPECT_EQ(0, unbiased_E5M2_exponent(0x3C));
+  EXPECT_EQ(3, unbiased_E5M2_exponent(0x48));
+  EXPECT_EQ(-1, unbiased_E5M2_exponent(0x38));
+  EXPECT_EQ(-14, unbiased_E5M2_exponent(0x04));
+  EXPECT_EQ(16, unbiased_E5M2_exponent(0x7C));
+  EXPECT_EQ(10, unbiased_E5M2_exponent(0x64));
+
+  // Smallest representable number
+  EXPECT_EQ(-16, unbiased_E5M2_exponent(0x01));
 }
 
 // Creates a float that is the sum of 1/(2 ^ fractions[i]) for i in factions
@@ -839,6 +885,166 @@
     {static_cast<float>(ldexp(float_fractions({0, 1, 11, 13}), -131)), std::make_pair(half_bits_set({0}), false), RD::kToNegativeInfinity},
     {static_cast<float>(ldexp(float_fractions({0, 1, 11, 13}), -130)), std::make_pair(half_bits_set({0, 9}), false), RD::kToNearestEven},
   })));
+
+// clang-format on
+
+// The same as bits_set but for a E4M3 value instead of 32-bit floating
+// point.
+uint8_t e4m3_bits_set(const std::vector<uint32_t>& bits) {
+  const uint32_t top_bit = 1u << 2u;
+  uint32_t val = 0;
+  for (uint32_t i : bits) {
+    val |= top_bit >> i;
+  }
+  return static_cast<uint8_t>(val);
+}
+
+struct RoundSignificandCaseE4M3 {
+  float source_float;
+  std::pair<int8_t, bool> expected_results;
+  round_direction round;
+};
+
+using HexFloatRoundTestE4M3 =
+    ::testing::TestWithParam<RoundSignificandCaseE4M3>;
+
+TEST_P(HexFloatRoundTestE4M3, RoundDownToFPE4M3) {
+  using HF = HexFloat<FloatProxy<float>>;
+  using HFE4M3 = HexFloat<FloatProxy<Float8_E4M3>>;
+
+  HF input_value(GetParam().source_float);
+  bool carry_bit = false;
+  EXPECT_EQ(GetParam().expected_results.first,
+            input_value.getRoundedNormalizedSignificand<HFE4M3>(
+                GetParam().round, &carry_bit));
+  EXPECT_EQ(carry_bit, GetParam().expected_results.second);
+}
+
+// clang-format off
+INSTANTIATE_TEST_SUITE_P(F32ToE4M3, HexFloatRoundTestE4M3,
+  ::testing::ValuesIn(std::vector<RoundSignificandCaseE4M3>(
+  {
+    {float_fractions({0}), std::make_pair(e4m3_bits_set({}), false), RD::kToZero},
+    {float_fractions({0}), std::make_pair(e4m3_bits_set({}), false), RD::kToNearestEven},
+    {float_fractions({0}), std::make_pair(e4m3_bits_set({}), false), RD::kToPositiveInfinity},
+    {float_fractions({0}), std::make_pair(e4m3_bits_set({}), false), RD::kToNegativeInfinity},
+    {float_fractions({0, 1}), std::make_pair(e4m3_bits_set({0}), false), RD::kToZero},
+
+    {float_fractions({0, 1, 4}), std::make_pair(e4m3_bits_set({0}), false), RD::kToZero},
+    {float_fractions({0, 1, 4}), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToPositiveInfinity},
+    {float_fractions({0, 1, 4}), std::make_pair(e4m3_bits_set({0}), false), RD::kToNegativeInfinity},
+    {float_fractions({0, 1, 4}), std::make_pair(e4m3_bits_set({0}), false), RD::kToNearestEven},
+
+    {float_fractions({0, 1, 3, 4}), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToZero},
+    {float_fractions({0, 1, 3, 4}), std::make_pair(e4m3_bits_set({0, 1}), false), RD::kToPositiveInfinity},
+    {float_fractions({0, 1, 3, 4}), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToNegativeInfinity},
+    {float_fractions({0, 1, 3, 4}), std::make_pair(e4m3_bits_set({0, 1}), false), RD::kToNearestEven},
+
+    {float_fractions({0, 1, 4, 5}), std::make_pair(e4m3_bits_set({0}), false), RD::kToZero},
+    {float_fractions({0, 1, 4, 5}), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToPositiveInfinity},
+    {float_fractions({0, 1, 4, 5}), std::make_pair(e4m3_bits_set({0}), false), RD::kToNegativeInfinity},
+    {float_fractions({0, 1, 4, 5}), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToNearestEven},
+
+    {-float_fractions({0, 1, 4, 5}), std::make_pair(e4m3_bits_set({0}), false), RD::kToZero},
+    {-float_fractions({0, 1, 4, 5}), std::make_pair(e4m3_bits_set({0}), false), RD::kToPositiveInfinity},
+    {-float_fractions({0, 1, 4, 5}), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToNegativeInfinity},
+    {-float_fractions({0, 1, 4, 5}), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToNearestEven},
+
+    {float_fractions({0, 1, 4, 22}), std::make_pair(e4m3_bits_set({0}), false), RD::kToZero},
+    {float_fractions({0, 1, 4, 22}), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToPositiveInfinity},
+    {float_fractions({0, 1, 4, 22}), std::make_pair(e4m3_bits_set({0}), false), RD::kToNegativeInfinity},
+    {float_fractions({0, 1, 4, 22}), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToNearestEven},
+
+    // Carries
+    {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(e4m3_bits_set({0, 1, 2}), false), RD::kToZero},
+    {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(e4m3_bits_set({}), true), RD::kToPositiveInfinity},
+    {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(e4m3_bits_set({0, 1, 2}), false), RD::kToNegativeInfinity},
+    {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(e4m3_bits_set({}), true), RD::kToNearestEven},
+
+    // Cases where original number was denorm. Note: this should have no effect
+    // the number is pre-normalized.
+    {static_cast<float>(ldexp(float_fractions({0, 1, 4, 6}), -128)), std::make_pair(e4m3_bits_set({0}), false), RD::kToZero},
+    {static_cast<float>(ldexp(float_fractions({0, 1, 4, 6}), -129)), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToPositiveInfinity},
+    {static_cast<float>(ldexp(float_fractions({0, 1, 4, 6}), -131)), std::make_pair(e4m3_bits_set({0}), false), RD::kToNegativeInfinity},
+    {static_cast<float>(ldexp(float_fractions({0, 1, 4, 6}), -130)), std::make_pair(e4m3_bits_set({0, 2}), false), RD::kToNearestEven},
+  })));
+
+// clang-format on
+// The same as bits_set but for a E4M3 value instead of 32-bit floating
+// point.
+uint8_t e5m2_bits_set(const std::vector<uint32_t>& bits) {
+  const uint32_t top_bit = 1u << 1u;
+  uint32_t val = 0;
+  for (uint32_t i : bits) {
+    val |= top_bit >> i;
+  }
+  return static_cast<uint8_t>(val);
+}
+
+struct RoundSignificandCaseE5M2 {
+  float source_float;
+  std::pair<int8_t, bool> expected_results;
+  round_direction round;
+};
+
+using HexFloatRoundTestE5M2 =
+    ::testing::TestWithParam<RoundSignificandCaseE5M2>;
+
+TEST_P(HexFloatRoundTestE5M2, RoundDownToFPE4M3) {
+  using HF = HexFloat<FloatProxy<float>>;
+  using HFE5M2 = HexFloat<FloatProxy<Float8_E5M2>>;
+
+  HF input_value(GetParam().source_float);
+  bool carry_bit = false;
+  EXPECT_EQ(GetParam().expected_results.first,
+            input_value.getRoundedNormalizedSignificand<HFE5M2>(
+                GetParam().round, &carry_bit));
+  EXPECT_EQ(carry_bit, GetParam().expected_results.second);
+}
+
+// clang-format off
+INSTANTIATE_TEST_SUITE_P(F32ToE5M2, HexFloatRoundTestE5M2,
+  ::testing::ValuesIn(std::vector<RoundSignificandCaseE5M2>(
+  {
+    {float_fractions({0}), std::make_pair(e5m2_bits_set({}), false), RD::kToZero},
+    {float_fractions({0}), std::make_pair(e5m2_bits_set({}), false), RD::kToNearestEven},
+    {float_fractions({0}), std::make_pair(e5m2_bits_set({}), false), RD::kToPositiveInfinity},
+    {float_fractions({0}), std::make_pair(e5m2_bits_set({}), false), RD::kToNegativeInfinity},
+    {float_fractions({0, 1}), std::make_pair(e5m2_bits_set({0}), false), RD::kToZero},
+
+    {float_fractions({0, 1, 4}), std::make_pair(e5m2_bits_set({0}), false), RD::kToZero},
+    {float_fractions({0, 1, 4}), std::make_pair(e5m2_bits_set({0, 1}), false), RD::kToPositiveInfinity},
+    {float_fractions({0, 1, 4}), std::make_pair(e5m2_bits_set({0}), false), RD::kToNegativeInfinity},
+    {float_fractions({0, 1, 4}), std::make_pair(e5m2_bits_set({0}), false), RD::kToNearestEven},
+
+    {float_fractions({0, 3, 4}), std::make_pair(e5m2_bits_set({}), false), RD::kToZero},
+    {float_fractions({0, 3, 4}), std::make_pair(e5m2_bits_set({1}), false), RD::kToPositiveInfinity},
+    {float_fractions({0, 3, 4}), std::make_pair(e5m2_bits_set({}), false), RD::kToNegativeInfinity},
+    {float_fractions({0, 3, 4}), std::make_pair(e5m2_bits_set({1}), false), RD::kToNearestEven},
+
+    {float_fractions({0, 2, 3}), std::make_pair(e5m2_bits_set({1}), false), RD::kToZero},
+    {float_fractions({0, 2, 3}), std::make_pair(e5m2_bits_set({0}), false), RD::kToPositiveInfinity},
+    {float_fractions({0, 2, 3}), std::make_pair(e5m2_bits_set({1}), false), RD::kToNegativeInfinity},
+    {float_fractions({0, 2, 3}), std::make_pair(e5m2_bits_set({0}), false), RD::kToNearestEven},
+
+    {-float_fractions({0, 2, 3}), std::make_pair(e5m2_bits_set({1}), false), RD::kToZero},
+    {-float_fractions({0, 2, 3}), std::make_pair(e5m2_bits_set({1}), false), RD::kToPositiveInfinity},
+    {-float_fractions({0, 2, 3}), std::make_pair(e5m2_bits_set({0}), false), RD::kToNegativeInfinity},
+    {-float_fractions({0, 2, 3}), std::make_pair(e5m2_bits_set({0}), false), RD::kToNearestEven},
+
+    // Carries
+    {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(e5m2_bits_set({0, 1}), false), RD::kToZero},
+    {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(e5m2_bits_set({}), true), RD::kToPositiveInfinity},
+    {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(e5m2_bits_set({0, 1}), false), RD::kToNegativeInfinity},
+    {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(e5m2_bits_set({}), true), RD::kToNearestEven},
+
+    // Cases where original number was denorm. Note: this should have no effect
+    // the number is pre-normalized.
+    {static_cast<float>(ldexp(float_fractions({0, 3, 6}), -128)), std::make_pair(e5m2_bits_set({}), false), RD::kToZero},
+    {static_cast<float>(ldexp(float_fractions({0, 3, 6}), -129)), std::make_pair(e5m2_bits_set({1}), false), RD::kToPositiveInfinity},
+    {static_cast<float>(ldexp(float_fractions({0, 3, 6}), -131)), std::make_pair(e5m2_bits_set({}), false), RD::kToNegativeInfinity},
+    {static_cast<float>(ldexp(float_fractions({0, 3, 6}), -130)), std::make_pair(e5m2_bits_set({1}), false), RD::kToNearestEven},
+  })));
 // clang-format on
 
 struct UpCastSignificandCase {
@@ -1016,6 +1222,225 @@
         // Nans are below because we cannot test for equality.
     })));
 
+using HexFloatFP32ToE4M3Tests = ::testing::TestWithParam<DownCastTest>;
+
+TEST_P(HexFloatFP32ToE4M3Tests, NarrowingCasts) {
+  using HF = HexFloat<FloatProxy<float>>;
+  using HFE4M3 = HexFloat<FloatProxy<Float8_E4M3>>;
+  HF f(GetParam().source_float);
+  for (auto round : GetParam().directions) {
+    HFE4M3 e4m3(0);
+    f.castTo(e4m3, round);
+    EXPECT_EQ(GetParam().expected_half, e4m3.value().getAsFloat().get_value())
+        << get_round_text(round) << "  " << std::hex
+        << BitwiseCast<uint32_t>(GetParam().source_float)
+        << " cast to: " << (uint32_t)e4m3.value().getAsFloat().get_value();
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    F32ToE4M3, HexFloatFP32ToE4M3Tests,
+    ::testing::ValuesIn(std::vector<DownCastTest>({
+        // Exactly representable as half.
+        {0.f,
+         0x0,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {-0.f,
+         0x80,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {1.0f,
+         0x38,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {-1.0f,
+         0xB8,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+
+        {float_fractions({0, 1, 3}),
+         0x3D,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {-float_fractions({0, 1, 3}),
+         0xBD,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {static_cast<float>(ldexp(float_fractions({0, 1, 3}), 3)),
+         0x55,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {static_cast<float>(-ldexp(float_fractions({0, 1, 3}), 3)),
+         0xD5,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+
+        // Underflow
+        {static_cast<float>(ldexp(1.0f, -10)),
+         0x0,
+         {RD::kToZero, RD::kToNegativeInfinity, RD::kToNearestEven}},
+        {static_cast<float>(ldexp(1.0f, -10)), 0x1, {RD::kToPositiveInfinity}},
+        {static_cast<float>(-ldexp(1.0f, -10)),
+         0x80,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNearestEven}},
+        {static_cast<float>(-ldexp(1.0f, -9)), 0x81, {RD::kToNegativeInfinity}},
+        {static_cast<float>(ldexp(1.0f, -9)),
+         0x1,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+
+        // Overflow
+        {static_cast<float>(ldexp(1.0f, 9)),
+         Float8_E4M3::max().get_value(),
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {static_cast<float>(ldexp(1.0f, 10)),
+         Float8_E4M3::max().get_value(),
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {static_cast<float>(ldexp(1.3f, 9)),
+         Float8_E4M3::max().get_value(),
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {static_cast<float>(-ldexp(1.0f, 9)),
+         static_cast<uint16_t>(0x80 | Float8_E4M3::max().get_value()),
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {static_cast<float>(-ldexp(1.0f, 10)),
+         static_cast<uint16_t>(0x80 | Float8_E4M3::max().get_value()),
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {static_cast<float>(-ldexp(1.3f, 9)),
+         static_cast<uint16_t>(0x80 | Float8_E4M3::max().get_value()),
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+
+        // Transfer of Infinities
+        {std::numeric_limits<float>::infinity(),
+         Float8_E4M3::max().get_value(),
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {-std::numeric_limits<float>::infinity(),
+         static_cast<uint16_t>(0x80 | Float8_E4M3::max().get_value()),
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+
+        // Nans are below because we cannot test for equality.
+    })));
+
+using HexFloatFP32ToE5M2Tests = ::testing::TestWithParam<DownCastTest>;
+
+TEST_P(HexFloatFP32ToE5M2Tests, NarrowingCasts) {
+  using HF = HexFloat<FloatProxy<float>>;
+  using HFE5M2 = HexFloat<FloatProxy<Float8_E5M2>>;
+  HF f(GetParam().source_float);
+  for (auto round : GetParam().directions) {
+    HFE5M2 e5m2(0);
+    f.castTo(e5m2, round);
+    EXPECT_EQ(GetParam().expected_half, e5m2.value().getAsFloat().get_value())
+        << get_round_text(round) << "  " << std::hex
+        << BitwiseCast<uint32_t>(GetParam().source_float)
+        << " cast to: " << (uint32_t)e5m2.value().getAsFloat().get_value();
+  }
+}
+
+const uint8_t e5m2_positive_infinity = 0x7C;
+const uint8_t e5m2_negative_infinity = 0xFC;
+
+INSTANTIATE_TEST_SUITE_P(
+    F32ToE5M2, HexFloatFP32ToE5M2Tests,
+    ::testing::ValuesIn(std::vector<DownCastTest>({
+        // Exactly representable as half.
+        {0.f,
+         0x0,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {-0.f,
+         0x80,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {1.0f,
+         0x3C,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {-1.0f,
+         0xBC,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+
+        {float_fractions({0, 1, 2}),
+         0x3F,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {-float_fractions({0, 1, 2}),
+         0xBF,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {static_cast<float>(ldexp(float_fractions({0, 2}), 3)),
+         0x49,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {static_cast<float>(-ldexp(float_fractions({0, 2}), 3)),
+         0xC9,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+
+        // Underflow
+        {static_cast<float>(ldexp(1.0f, -17)),
+         0x0,
+         {RD::kToZero, RD::kToNegativeInfinity, RD::kToNearestEven}},
+        {static_cast<float>(ldexp(1.0f, -17)), 0x1, {RD::kToPositiveInfinity}},
+        {static_cast<float>(-ldexp(1.0f, -17)),
+         0x80,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNearestEven}},
+        {static_cast<float>(-ldexp(1.0f, -16)),
+         0x81,
+         {RD::kToNegativeInfinity}},
+        {static_cast<float>(ldexp(1.0f, -16)),
+         0x1,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+
+        // Overflow
+        {static_cast<float>(ldexp(1.0f, 16)),
+         e5m2_positive_infinity,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {static_cast<float>(ldexp(1.0f, 17)),
+         e5m2_positive_infinity,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {static_cast<float>(ldexp(1.3f, 16)),
+         e5m2_positive_infinity,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {static_cast<float>(-ldexp(1.0f, 16)),
+         e5m2_negative_infinity,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {static_cast<float>(-ldexp(1.0f, 17)),
+         e5m2_negative_infinity,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {static_cast<float>(-ldexp(1.3f, 16)),
+         e5m2_negative_infinity,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+
+        // Transfer of Infinities
+        {std::numeric_limits<float>::infinity(),
+         e5m2_positive_infinity,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+        {-std::numeric_limits<float>::infinity(),
+         e5m2_negative_infinity,
+         {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity,
+          RD::kToNearestEven}},
+
+        // Nans are below because we cannot test for equality.
+    })));
+
 struct UpCastCase {
   uint16_t source_half;
   float expected_float;
@@ -1069,6 +1494,9 @@
 TEST(HexFloatOperationTests, NanTests) {
   using HF = HexFloat<FloatProxy<float>>;
   using HF16 = HexFloat<FloatProxy<Float16>>;
+  using BF16 = HexFloat<FloatProxy<BFloat16>>;
+  using FE4M3 = HexFloat<FloatProxy<Float8_E4M3>>;
+  using FE5M2 = HexFloat<FloatProxy<Float8_E5M2>>;
   round_direction rounding[] = {round_direction::kToZero,
                                 round_direction::kToNearestEven,
                                 round_direction::kToPositiveInfinity,
@@ -1078,6 +1506,9 @@
   for (round_direction round : rounding) {
     HF16 f16(0);
     HF f(0.f);
+    BF16 bf16(0);
+    FE4M3 fe4m3(0);
+    FE5M2 fe5m2(0);
     HF(std::numeric_limits<float>::quiet_NaN()).castTo(f16, round);
     EXPECT_TRUE(f16.value().isNan());
     HF(std::numeric_limits<float>::signaling_NaN()).castTo(f16, round);
@@ -1093,6 +1524,45 @@
     EXPECT_TRUE(f.value().isNan());
     HF16(0xFF00).castTo(f, round);
     EXPECT_TRUE(f.value().isNan());
+
+    BF16(0x7F81).castTo(f, round);
+    EXPECT_TRUE(f.value().isNan());
+    BF16(0x7F91).castTo(f, round);
+    EXPECT_TRUE(f.value().isNan());
+    BF16(0xFF81).castTo(f, round);
+    EXPECT_TRUE(f.value().isNan());
+    BF16(0x7F90).castTo(f, round);
+    EXPECT_TRUE(f.value().isNan());
+    BF16(0xFFE0).castTo(f, round);
+    EXPECT_TRUE(f.value().isNan());
+
+    HF(std::numeric_limits<float>::quiet_NaN()).castTo(fe4m3, round);
+    EXPECT_TRUE(fe4m3.value().isNan());
+    HF(std::numeric_limits<float>::signaling_NaN()).castTo(fe4m3, round);
+    EXPECT_TRUE(fe4m3.value().isNan());
+
+    FE4M3(0x7F).castTo(f, round);
+    EXPECT_TRUE(f.value().isNan());
+    FE4M3(0xFF).castTo(f, round);
+    EXPECT_TRUE(f.value().isNan());
+
+    HF(std::numeric_limits<float>::quiet_NaN()).castTo(fe5m2, round);
+    EXPECT_TRUE(fe5m2.value().isNan());
+    HF(std::numeric_limits<float>::signaling_NaN()).castTo(fe5m2, round);
+    EXPECT_TRUE(fe5m2.value().isNan());
+
+    FE5M2(0x7D).castTo(f, round);
+    EXPECT_TRUE(f.value().isNan());
+    FE5M2(0x7E).castTo(f, round);
+    EXPECT_TRUE(f.value().isNan());
+    FE5M2(0x7F).castTo(f, round);
+    EXPECT_TRUE(f.value().isNan());
+    FE5M2(0xFD).castTo(f, round);
+    EXPECT_TRUE(f.value().isNan());
+    FE5M2(0xFE).castTo(f, round);
+    EXPECT_TRUE(f.value().isNan());
+    FE5M2(0xFF).castTo(f, round);
+    EXPECT_TRUE(f.value().isNan());
   }
 }
 
@@ -1100,73 +1570,96 @@
 template <typename T>
 struct FloatParseCase {
   std::string literal;
-  bool negate_value;
+  LeadingSign leading_sign;
   bool expect_success;
   HexFloat<FloatProxy<T>> expected_value;
 };
 
+const char* str(const LeadingSign& ls) {
+  switch (ls) {
+    case LeadingSign::None:
+      return "(none)";
+    case LeadingSign::Minus:
+      return "-";
+    case LeadingSign::Plus:
+      return "+";
+  }
+  return "";  // should not happen
+}
+
 using ParseNormalFloatTest = ::testing::TestWithParam<FloatParseCase<float>>;
 
 TEST_P(ParseNormalFloatTest, Samples) {
   std::stringstream input(GetParam().literal);
   HexFloat<FloatProxy<float>> parsed_value(0.0f);
-  ParseNormalFloat(input, GetParam().negate_value, parsed_value);
+  ParseNormalFloat(input, GetParam().leading_sign, parsed_value);
   EXPECT_NE(GetParam().expect_success, input.fail())
       << " literal: " << GetParam().literal
-      << " negate: " << GetParam().negate_value;
+      << " leading_sign: " << str(GetParam().leading_sign);
   if (GetParam().expect_success) {
     EXPECT_THAT(parsed_value.value(), Eq(GetParam().expected_value.value()))
         << " literal: " << GetParam().literal
-        << " negate: " << GetParam().negate_value;
+        << " leading_sign: " << str(GetParam().leading_sign);
   }
 }
 
 // Returns a FloatParseCase with expected failure.
 template <typename T>
-FloatParseCase<T> BadFloatParseCase(std::string literal, bool negate_value,
+FloatParseCase<T> BadFloatParseCase(std::string literal,
+                                    LeadingSign leading_sign,
                                     T expected_value) {
   HexFloat<FloatProxy<T>> proxy_expected_value(expected_value);
-  return FloatParseCase<T>{literal, negate_value, false, proxy_expected_value};
+  return FloatParseCase<T>{literal, leading_sign, false, proxy_expected_value};
 }
 
 // Returns a FloatParseCase that should successfully parse to a given value.
 template <typename T>
-FloatParseCase<T> GoodFloatParseCase(std::string literal, bool negate_value,
+FloatParseCase<T> GoodFloatParseCase(std::string literal,
+                                     LeadingSign leading_sign,
                                      T expected_value) {
   HexFloat<FloatProxy<T>> proxy_expected_value(expected_value);
-  return FloatParseCase<T>{literal, negate_value, true, proxy_expected_value};
+  return FloatParseCase<T>{literal, leading_sign, true, proxy_expected_value};
 }
 
 INSTANTIATE_TEST_SUITE_P(
     FloatParse, ParseNormalFloatTest,
     ::testing::ValuesIn(std::vector<FloatParseCase<float>>{
         // Failing cases due to trivially incorrect syntax.
-        BadFloatParseCase("abc", false, 0.0f),
-        BadFloatParseCase("abc", true, 0.0f),
+        BadFloatParseCase("abc", LeadingSign::None, 0.0f),
+        BadFloatParseCase("abc", LeadingSign::Minus, 0.0f),
+        BadFloatParseCase("abc", LeadingSign::Plus, 0.0f),
 
         // Valid cases.
-        GoodFloatParseCase("0", false, 0.0f),
-        GoodFloatParseCase("0.0", false, 0.0f),
-        GoodFloatParseCase("-0.0", false, -0.0f),
-        GoodFloatParseCase("2.0", false, 2.0f),
-        GoodFloatParseCase("-2.0", false, -2.0f),
-        GoodFloatParseCase("+2.0", false, 2.0f),
-        // Cases with negate_value being true.
-        GoodFloatParseCase("0.0", true, -0.0f),
-        GoodFloatParseCase("2.0", true, -2.0f),
+        GoodFloatParseCase("0", LeadingSign::None, 0.0f),
+        GoodFloatParseCase("0.0", LeadingSign::None, 0.0f),
+        GoodFloatParseCase("-0.0", LeadingSign::None, -0.0f),
+        GoodFloatParseCase("2.0", LeadingSign::None, 2.0f),
+        GoodFloatParseCase("-2.0", LeadingSign::None, -2.0f),
+        GoodFloatParseCase("+2.0", LeadingSign::None, 2.0f),
+        // Cases with leading sign
+        GoodFloatParseCase("0.0", LeadingSign::Minus, -0.0f),
+        GoodFloatParseCase("2.0", LeadingSign::Minus, -2.0f),
+        GoodFloatParseCase("0", LeadingSign::Plus, 0.0f),
+        GoodFloatParseCase("0.0", LeadingSign::Plus, 0.0f),
+        GoodFloatParseCase("2.0", LeadingSign::Plus, 2.0f),
 
-        // When negate_value is true, we should not accept a
+        // When a leading sign is present, we should not accept a
         // leading minus or plus.
-        BadFloatParseCase("-0.0", true, 0.0f),
-        BadFloatParseCase("-2.0", true, 0.0f),
-        BadFloatParseCase("+0.0", true, 0.0f),
-        BadFloatParseCase("+2.0", true, 0.0f),
+        BadFloatParseCase("-0.0", LeadingSign::Minus, 0.0f),
+        BadFloatParseCase("-2.0", LeadingSign::Minus, 0.0f),
+        BadFloatParseCase("+0.0", LeadingSign::Minus, 0.0f),
+        BadFloatParseCase("+2.0", LeadingSign::Minus, 0.0f),
+        BadFloatParseCase("-0.0", LeadingSign::Plus, 0.0f),
+        BadFloatParseCase("-2.0", LeadingSign::Plus, 0.0f),
+        BadFloatParseCase("+0.0", LeadingSign::Plus, 0.0f),
+        BadFloatParseCase("+2.0", LeadingSign::Plus, 0.0f),
 
         // Overflow is an error for 32-bit float parsing.
-        BadFloatParseCase("1e40", false, FLT_MAX),
-        BadFloatParseCase("1e40", true, -FLT_MAX),
-        BadFloatParseCase("-1e40", false, -FLT_MAX),
-        // We can't have -1e40 and negate_value == true since
+        BadFloatParseCase("1e40", LeadingSign::None, FLT_MAX),
+        BadFloatParseCase("1e40", LeadingSign::Plus, FLT_MAX),
+        BadFloatParseCase("1e40", LeadingSign::Minus, -FLT_MAX),
+        BadFloatParseCase("-1e40", LeadingSign::None, -FLT_MAX),
+        // We can't have -1e40 and leading sign == Minus since
         // that represents an original case of "--1e40" which
         // is invalid.
     }));
@@ -1177,14 +1670,14 @@
 TEST_P(ParseNormalFloat16Test, Samples) {
   std::stringstream input(GetParam().literal);
   HexFloat<FloatProxy<Float16>> parsed_value(0);
-  ParseNormalFloat(input, GetParam().negate_value, parsed_value);
+  ParseNormalFloat(input, GetParam().leading_sign, parsed_value);
   EXPECT_NE(GetParam().expect_success, input.fail())
       << " literal: " << GetParam().literal
-      << " negate: " << GetParam().negate_value;
+      << " leading_sign: " << str(GetParam().leading_sign);
   if (GetParam().expect_success) {
     EXPECT_THAT(parsed_value.value(), Eq(GetParam().expected_value.value()))
         << " literal: " << GetParam().literal
-        << " negate: " << GetParam().negate_value;
+        << " leading_sign: " << str(GetParam().leading_sign);
   }
 }
 
@@ -1192,26 +1685,158 @@
     Float16Parse, ParseNormalFloat16Test,
     ::testing::ValuesIn(std::vector<FloatParseCase<Float16>>{
         // Failing cases due to trivially incorrect syntax.
-        BadFloatParseCase<Float16>("abc", false, uint16_t{0}),
-        BadFloatParseCase<Float16>("abc", true, uint16_t{0}),
+        BadFloatParseCase<Float16>("abc", LeadingSign::None, uint16_t{0}),
+        BadFloatParseCase<Float16>("abc", LeadingSign::Minus, uint16_t{0}),
+        BadFloatParseCase<Float16>("abc", LeadingSign::Plus, uint16_t{0}),
 
         // Valid cases.
-        GoodFloatParseCase<Float16>("0", false, uint16_t{0}),
-        GoodFloatParseCase<Float16>("0.0", false, uint16_t{0}),
-        GoodFloatParseCase<Float16>("-0.0", false, uint16_t{0x8000}),
-        GoodFloatParseCase<Float16>("2.0", false, uint16_t{0x4000}),
-        GoodFloatParseCase<Float16>("-2.0", false, uint16_t{0xc000}),
-        GoodFloatParseCase<Float16>("+2.0", false, uint16_t{0x4000}),
-        // Cases with negate_value being true.
-        GoodFloatParseCase<Float16>("0.0", true, uint16_t{0x8000}),
-        GoodFloatParseCase<Float16>("2.0", true, uint16_t{0xc000}),
+        GoodFloatParseCase<Float16>("0", LeadingSign::None, uint16_t{0}),
+        GoodFloatParseCase<Float16>("0.0", LeadingSign::None, uint16_t{0}),
+        GoodFloatParseCase<Float16>("-0.0", LeadingSign::None,
+                                    uint16_t{0x8000}),
+        GoodFloatParseCase<Float16>("2.0", LeadingSign::None, uint16_t{0x4000}),
+        GoodFloatParseCase<Float16>("-2.0", LeadingSign::None,
+                                    uint16_t{0xc000}),
+        GoodFloatParseCase<Float16>("+2.0", LeadingSign::None,
+                                    uint16_t{0x4000}),
+        // Cases with leading sign
+        GoodFloatParseCase<Float16>("0", LeadingSign::Plus, uint16_t{0}),
+        GoodFloatParseCase<Float16>("0.0", LeadingSign::Plus, uint16_t{0}),
+        GoodFloatParseCase<Float16>("2.0", LeadingSign::Plus, uint16_t{0x4000}),
+        GoodFloatParseCase<Float16>("0.0", LeadingSign::Minus,
+                                    uint16_t{0x8000}),
+        GoodFloatParseCase<Float16>("2.0", LeadingSign::Minus,
+                                    uint16_t{0xc000}),
 
-        // When negate_value is true, we should not accept a leading minus or
+        // When a leading sign is present, we should not accept a leading minus
+        // or
         // plus.
-        BadFloatParseCase<Float16>("-0.0", true, uint16_t{0}),
-        BadFloatParseCase<Float16>("-2.0", true, uint16_t{0}),
-        BadFloatParseCase<Float16>("+0.0", true, uint16_t{0}),
-        BadFloatParseCase<Float16>("+2.0", true, uint16_t{0}),
+        BadFloatParseCase<Float16>("-0.0", LeadingSign::Minus, uint16_t{0}),
+        BadFloatParseCase<Float16>("-2.0", LeadingSign::Minus, uint16_t{0}),
+        BadFloatParseCase<Float16>("+0.0", LeadingSign::Minus, uint16_t{0}),
+        BadFloatParseCase<Float16>("+2.0", LeadingSign::Minus, uint16_t{0}),
+        BadFloatParseCase<Float16>("-0.0", LeadingSign::Plus, uint16_t{0}),
+        BadFloatParseCase<Float16>("-2.0", LeadingSign::Plus, uint16_t{0}),
+        BadFloatParseCase<Float16>("+0.0", LeadingSign::Plus, uint16_t{0}),
+        BadFloatParseCase<Float16>("+2.0", LeadingSign::Plus, uint16_t{0}),
+    }));
+
+using ParseNormalFloatE4M3Test =
+    ::testing::TestWithParam<FloatParseCase<Float8_E4M3>>;
+
+TEST_P(ParseNormalFloatE4M3Test, Samples) {
+  std::stringstream input(GetParam().literal);
+  HexFloat<FloatProxy<Float8_E4M3>> parsed_value(0);
+  ParseNormalFloat(input, GetParam().leading_sign, parsed_value);
+  EXPECT_NE(GetParam().expect_success, input.fail())
+      << " literal: " << GetParam().literal
+      << " leading_sign: " << str(GetParam().leading_sign);
+  if (GetParam().expect_success) {
+    EXPECT_THAT(parsed_value.value(), Eq(GetParam().expected_value.value()))
+        << " literal: " << GetParam().literal
+        << " leading_sign: " << str(GetParam().leading_sign);
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    FloatE4M3Parse, ParseNormalFloatE4M3Test,
+    ::testing::ValuesIn(std::vector<FloatParseCase<Float8_E4M3>>{
+        // Failing cases due to trivially incorrect syntax.
+        BadFloatParseCase<Float8_E4M3>("abc", LeadingSign::None, uint8_t{0}),
+        BadFloatParseCase<Float8_E4M3>("abc", LeadingSign::Minus, uint8_t{0}),
+        BadFloatParseCase<Float8_E4M3>("abc", LeadingSign::Plus, uint8_t{0}),
+
+        // Valid cases.
+        GoodFloatParseCase<Float8_E4M3>("0", LeadingSign::None, uint8_t{0}),
+        GoodFloatParseCase<Float8_E4M3>("0.0", LeadingSign::None, uint8_t{0}),
+        GoodFloatParseCase<Float8_E4M3>("-0.0", LeadingSign::None,
+                                        uint8_t{0x80}),
+        GoodFloatParseCase<Float8_E4M3>("2.0", LeadingSign::None,
+                                        uint8_t{0x40}),
+        GoodFloatParseCase<Float8_E4M3>("-2.0", LeadingSign::None,
+                                        uint8_t{0xc0}),
+        GoodFloatParseCase<Float8_E4M3>("+2.0", LeadingSign::None,
+                                        uint8_t{0x40}),
+        // Cases with leading sign.
+        GoodFloatParseCase<Float8_E4M3>("0", LeadingSign::Plus, uint8_t{0}),
+        GoodFloatParseCase<Float8_E4M3>("0.0", LeadingSign::Plus, uint8_t{0}),
+        GoodFloatParseCase<Float8_E4M3>("2.0", LeadingSign::Plus,
+                                        uint8_t{0x40}),
+        GoodFloatParseCase<Float8_E4M3>("0.0", LeadingSign::Minus,
+                                        uint8_t{0x80}),
+        GoodFloatParseCase<Float8_E4M3>("2.0", LeadingSign::Minus,
+                                        uint8_t{0xc0}),
+
+        // When a leading sign is present, we should not accept a leading minus
+        // or
+        // plus.
+        BadFloatParseCase<Float8_E4M3>("-0.0", LeadingSign::Minus, uint8_t{0}),
+        BadFloatParseCase<Float8_E4M3>("-2.0", LeadingSign::Minus, uint8_t{0}),
+        BadFloatParseCase<Float8_E4M3>("+0.0", LeadingSign::Minus, uint8_t{0}),
+        BadFloatParseCase<Float8_E4M3>("+2.0", LeadingSign::Minus, uint8_t{0}),
+        BadFloatParseCase<Float8_E4M3>("-0.0", LeadingSign::Plus, uint8_t{0}),
+        BadFloatParseCase<Float8_E4M3>("-2.0", LeadingSign::Plus, uint8_t{0}),
+        BadFloatParseCase<Float8_E4M3>("+0.0", LeadingSign::Plus, uint8_t{0}),
+        BadFloatParseCase<Float8_E4M3>("+2.0", LeadingSign::Plus, uint8_t{0}),
+    }));
+
+using ParseNormalFloatE5M2Test =
+    ::testing::TestWithParam<FloatParseCase<Float8_E5M2>>;
+
+TEST_P(ParseNormalFloatE5M2Test, Samples) {
+  std::stringstream input(GetParam().literal);
+  HexFloat<FloatProxy<Float8_E5M2>> parsed_value(0);
+  ParseNormalFloat(input, GetParam().leading_sign, parsed_value);
+  EXPECT_NE(GetParam().expect_success, input.fail())
+      << " literal: " << GetParam().literal
+      << " leading_sign: " << str(GetParam().leading_sign);
+  if (GetParam().expect_success) {
+    EXPECT_THAT(parsed_value.value(), Eq(GetParam().expected_value.value()))
+        << " literal: " << GetParam().literal
+        << " leading_sign: " << str(GetParam().leading_sign);
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    FloatE5M2Parse, ParseNormalFloatE5M2Test,
+    ::testing::ValuesIn(std::vector<FloatParseCase<Float8_E5M2>>{
+        // Failing cases due to trivially incorrect syntax.
+        BadFloatParseCase<Float8_E5M2>("abc", LeadingSign::None, uint8_t{0}),
+        BadFloatParseCase<Float8_E5M2>("abc", LeadingSign::Minus, uint8_t{0}),
+        BadFloatParseCase<Float8_E5M2>("abc", LeadingSign::Plus, uint8_t{0}),
+
+        // Valid cases.
+        GoodFloatParseCase<Float8_E5M2>("0", LeadingSign::None, uint8_t{0}),
+        GoodFloatParseCase<Float8_E5M2>("0.0", LeadingSign::None, uint8_t{0}),
+        GoodFloatParseCase<Float8_E5M2>("-0.0", LeadingSign::None,
+                                        uint8_t{0x80}),
+        GoodFloatParseCase<Float8_E5M2>("2.0", LeadingSign::None,
+                                        uint8_t{0x40}),
+        GoodFloatParseCase<Float8_E5M2>("-2.0", LeadingSign::None,
+                                        uint8_t{0xc0}),
+        GoodFloatParseCase<Float8_E5M2>("+2.0", LeadingSign::None,
+                                        uint8_t{0x40}),
+        // Cases with a leading sign
+        GoodFloatParseCase<Float8_E5M2>("0", LeadingSign::Plus, uint8_t{0}),
+        GoodFloatParseCase<Float8_E5M2>("0.0", LeadingSign::Plus, uint8_t{0}),
+        GoodFloatParseCase<Float8_E5M2>("2.0", LeadingSign::Plus,
+                                        uint8_t{0x40}),
+        GoodFloatParseCase<Float8_E5M2>("0.0", LeadingSign::Minus,
+                                        uint8_t{0x80}),
+        GoodFloatParseCase<Float8_E5M2>("2.0", LeadingSign::Minus,
+                                        uint8_t{0xc0}),
+
+        // When a leading sign is present, we should not accept a leading minus
+        // or
+        // plus.
+        BadFloatParseCase<Float8_E5M2>("-0.0", LeadingSign::Minus, uint8_t{0}),
+        BadFloatParseCase<Float8_E5M2>("-2.0", LeadingSign::Minus, uint8_t{0}),
+        BadFloatParseCase<Float8_E5M2>("+0.0", LeadingSign::Minus, uint8_t{0}),
+        BadFloatParseCase<Float8_E5M2>("+2.0", LeadingSign::Minus, uint8_t{0}),
+        BadFloatParseCase<Float8_E5M2>("-0.0", LeadingSign::Plus, uint8_t{0}),
+        BadFloatParseCase<Float8_E5M2>("-2.0", LeadingSign::Plus, uint8_t{0}),
+        BadFloatParseCase<Float8_E5M2>("+0.0", LeadingSign::Plus, uint8_t{0}),
+        BadFloatParseCase<Float8_E5M2>("+2.0", LeadingSign::Plus, uint8_t{0}),
     }));
 
 // A test case for detecting infinities.
@@ -1307,6 +1932,68 @@
         {"-1e400", false, uint16_t{0xfbff}},
     })));
 
+using FloatProxyParseOverflowFloatE4M3Test =
+    ::testing::TestWithParam<OverflowParseCase<uint8_t>>;
+
+TEST_P(FloatProxyParseOverflowFloatE4M3Test, Sample) {
+  std::istringstream input(GetParam().input);
+  HexFloat<FloatProxy<Float8_E4M3>> value(0);
+  input >> value;
+  EXPECT_NE(GetParam().expect_success, input.fail())
+      << " literal: " << GetParam().input;
+  if (GetParam().expect_success) {
+    EXPECT_THAT(value.value().data(), Eq(GetParam().expected_value))
+        << " literal: " << GetParam().input;
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    FloatE4M3Overflow, FloatProxyParseOverflowFloatE4M3Test,
+    ::testing::ValuesIn(std::vector<OverflowParseCase<uint8_t>>({
+        {"0", true, uint8_t{0}},
+        {"0.0", true, uint8_t{0}},
+        {"1.0", true, uint8_t{0x38}},
+        // Overflow for E4M3 float is an error, and returns max or
+        // lowest value.
+        {"1e38", false, uint8_t{0x7e}},
+        {"1e40", false, uint8_t{0x7e}},
+        {"1e400", false, uint8_t{0x7e}},
+        {"-1e38", false, uint8_t{0xfe}},
+        {"-1e40", false, uint8_t{0xfe}},
+        {"-1e400", false, uint8_t{0xfe}},
+    })));
+
+using FloatProxyParseOverflowFloatE5M2Test =
+    ::testing::TestWithParam<OverflowParseCase<uint8_t>>;
+
+TEST_P(FloatProxyParseOverflowFloatE5M2Test, Sample) {
+  std::istringstream input(GetParam().input);
+  HexFloat<FloatProxy<Float8_E5M2>> value(0);
+  input >> value;
+  EXPECT_NE(GetParam().expect_success, input.fail())
+      << " literal: " << GetParam().input;
+  if (GetParam().expect_success) {
+    EXPECT_THAT(value.value().data(), Eq(GetParam().expected_value))
+        << " literal: " << GetParam().input;
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    FloatE5M2Overflow, FloatProxyParseOverflowFloatE5M2Test,
+    ::testing::ValuesIn(std::vector<OverflowParseCase<uint8_t>>({
+        {"0", true, uint8_t{0}},
+        {"0.0", true, uint8_t{0}},
+        {"1.0", true, uint8_t{0x3c}},
+        // Overflow for E5M2 float is an error, and returns max or
+        // lowest value.
+        {"1e38", false, uint8_t{0x7b}},
+        {"1e40", false, uint8_t{0x7b}},
+        {"1e400", false, uint8_t{0x7b}},
+        {"-1e38", false, uint8_t{0xfb}},
+        {"-1e40", false, uint8_t{0xfb}},
+        {"-1e400", false, uint8_t{0xfb}},
+    })));
+
 TEST(FloatProxy, Max) {
   EXPECT_THAT(FloatProxy<Float16>::max().getAsFloat().get_value(),
               Eq(uint16_t{0x7bff}));
@@ -1351,6 +2038,10 @@
 using Float32StreamParseTest = ::testing::TestWithParam<StreamParseCase<float>>;
 using Float16StreamParseTest =
     ::testing::TestWithParam<StreamParseCase<Float16>>;
+using FloatE4M3StreamParseTest =
+    ::testing::TestWithParam<StreamParseCase<Float8_E4M3>>;
+using FloatE5M2StreamParseTest =
+    ::testing::TestWithParam<StreamParseCase<Float8_E5M2>>;
 
 TEST_P(Float32StreamParseTest, Samples) {
   std::stringstream input(GetParam().literal);
@@ -1518,6 +2209,18 @@
     }));
 
 INSTANTIATE_TEST_SUITE_P(
+    Underflow, Float32StreamParseTest,
+    ::testing::ValuesIn(std::vector<StreamParseCase<float>>{
+        // Underflow
+        {"0x1.p-149", true, "", ldexpf(1, -149)},
+        {"0x1.p-150", true, "", 0.0f},
+        {"+0x1.p-149", true, "", ldexpf(1, -149)},
+        {"+0x1.p-150", true, "", 0.0f},
+        {"-0x1.p-149", true, "", -ldexpf(1, -149)},
+        {"-0x1.p-150", true, "", -0.0f},
+    }));
+
+INSTANTIATE_TEST_SUITE_P(
     HexFloat16ExcessSignificantDigits, Float16StreamParseTest,
     ::testing::ValuesIn(std::vector<StreamParseCase<Float16>>{
         // Zero
@@ -1620,6 +2323,197 @@
         {"0x8.5a40000p0", true, "", makeF16(0, 3, 0x02d)},
         {"0x8.5a7ffffp0", true, "", makeF16(0, 3, 0x02d)}}));
 
+INSTANTIATE_TEST_SUITE_P(
+    Underflow, Float16StreamParseTest,
+    ::testing::ValuesIn(std::vector<StreamParseCase<Float16>>{
+        // Underflow
+        {"0x1.p-24", true, "", Float16(uint16_t(1))},
+        {"0x1.p-25", true, "", Float16(uint16_t(0))},
+        {"+0x1.p-24", true, "", Float16(uint16_t(1))},
+        {"+0x1.p-25", true, "", Float16(uint16_t(0))},
+        {"-0x1.p-24", true, "", Float16(uint16_t(0x8001))},
+        {"-0x1.p-25", true, "", Float16(uint16_t(0x8000))},
+    }));
+
+// Returns a E4M3 constructed from its sign bit, unbiased exponent, and
+// mantissa.
+Float8_E4M3 makeE4M3(int sign_bit, int unbiased_exp, int mantissa) {
+  EXPECT_LE(0, sign_bit);
+  EXPECT_LE(sign_bit, 1);
+  // Exponent is 4 bits, with bias of 7.
+  EXPECT_LE(-7, unbiased_exp);  // -7 means zero or subnormal
+  EXPECT_LE(unbiased_exp, 8);
+  EXPECT_LE(0, mantissa);
+  EXPECT_LE(mantissa, 0x7);
+  const unsigned biased_exp = 7 + unbiased_exp;
+  const uint32_t as_bits = sign_bit << 7 | (biased_exp << 3) | mantissa;
+  EXPECT_LE(as_bits, 0xffu);
+  return Float8_E4M3(static_cast<uint8_t>(as_bits));
+}
+
+TEST_P(FloatE4M3StreamParseTest, Samples) {
+  std::stringstream input(GetParam().literal);
+  HexFloat<FloatProxy<Float8_E4M3>> parsed_value(makeE4M3(0, 0, 0));
+  // Hex floats must be read with the stream input operator.
+  input >> parsed_value;
+  if (GetParam().expect_success) {
+    EXPECT_FALSE(input.fail());
+    std::string suffix;
+    input >> suffix;
+    const auto got = parsed_value.value();
+    const auto expected = GetParam().expected_value.value();
+    EXPECT_EQ(got.data(), expected.data())
+        << "got: " << got << " expected: " << expected;
+  } else {
+    EXPECT_TRUE(input.fail());
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    HexFloatE4M3IncreasingExponentsAndMantissa, FloatE4M3StreamParseTest,
+    ::testing::ValuesIn(std::vector<StreamParseCase<Float8_E4M3>>{
+        // Zero
+        {"0x0p0", true, "", makeE4M3(0, -7, 0x0)},
+        {"0x0p5000000000000", true, "", makeE4M3(0, -7, 0x0)},
+        {"-0x0p5000000000000", true, "", makeE4M3(1, -7, 0x0)},
+        // Leading 1
+        {"0x1p0", true, "", makeE4M3(0, 0, 0x0)},
+        {"0x1p1", true, "", makeE4M3(0, 1, 0x0)},
+        {"0x1p8", true, "", makeE4M3(0, 8, 0x0)},
+        {"0x1p-1", true, "", makeE4M3(0, -1, 0x0)},
+        {"0x1p-6", true, "", makeE4M3(0, -6, 0x0)},
+        // Leading 2
+        {"0x2p0", true, "", makeE4M3(0, 1, 0x0)},
+        {"0x2p1", true, "", makeE4M3(0, 2, 0x0)},
+        {"0x2p7", true, "", makeE4M3(0, 8, 0x0)},
+        {"0x2p-1", true, "", makeE4M3(0, 0, 0x0)},
+        {"0x2p-7", true, "", makeE4M3(0, -6, 0x0)},
+        // Leading 8
+        {"0x8p0", true, "", makeE4M3(0, 3, 0x0)},
+        {"0x8p1", true, "", makeE4M3(0, 4, 0x0)},
+        {"0x8p5", true, "", makeE4M3(0, 8, 0x0)},
+        {"0x8p-3", true, "", makeE4M3(0, 0, 0x0)},
+        {"0x8p-9", true, "", makeE4M3(0, -6, 0x0)},
+        // Leading 10
+        {"0x10.0p0", true, "", makeE4M3(0, 4, 0x0)},
+        {"0x10.0p1", true, "", makeE4M3(0, 5, 0x0)},
+        {"0x10.0p4", true, "", makeE4M3(0, 8, 0x0)},
+        {"0x10.0p-5", true, "", makeE4M3(0, -1, 0x0)},
+        {"0x10.0p-10", true, "", makeE4M3(0, -6, 0x0)},
+        // Samples that drop out bits *and* truncate significant bits
+        // that can't be represented.
+        // Progressively increase the leading digit.
+        {"0x1.5a40000p0", true, "", makeE4M3(0, 0, 0x2)},
+        {"0x1.5a7ffffp0", true, "", makeE4M3(0, 0, 0x2)},
+        {"0x2.5a40000p0", true, "", makeE4M3(0, 1, 0x1)},
+        {"0x2.5a7ffffp0", true, "", makeE4M3(0, 1, 0x1)},
+        {"0x4.5a40000p0", true, "", makeE4M3(0, 2, 0x0)},
+        {"0x4.5a7ffffp0", true, "", makeE4M3(0, 2, 0x0)},
+        {"0x8.5a40000p0", true, "", makeE4M3(0, 3, 0x0)},
+        {"0x8.5a7ffffp0", true, "", makeE4M3(0, 3, 0x0)}}));
+
+INSTANTIATE_TEST_SUITE_P(
+    Underflow, FloatE4M3StreamParseTest,
+    ::testing::ValuesIn(std::vector<StreamParseCase<Float8_E4M3>>{
+        // Underflow
+        {"0x1.p-9", true, "", Float8_E4M3(uint8_t(1))},
+        {"0x1.p-10", true, "", Float8_E4M3(uint8_t(0))},
+        {"+0x1.p-9", true, "", Float8_E4M3(uint8_t(1))},
+        {"+0x1.p-10", true, "", Float8_E4M3(uint8_t(0))},
+        {"-0x1.p-9", true, "", Float8_E4M3(uint8_t(0x81))},
+        {"-0x1.p-10", true, "", Float8_E4M3(uint8_t(0x80))},
+    }));
+
+// Returns a E5M2 constructed from its sign bit, unbiased exponent, and
+// mantissa.
+Float8_E5M2 makeE5M2(int sign_bit, int unbiased_exp, int mantissa) {
+  EXPECT_LE(0, sign_bit);
+  EXPECT_LE(sign_bit, 1);
+  // Exponent is 5 bits, with bias of 15.
+  EXPECT_LE(-15, unbiased_exp);  // -15 means zero or subnormal
+  EXPECT_LE(unbiased_exp, 16);
+  EXPECT_LE(0, mantissa);
+  EXPECT_LE(mantissa, 0x3);
+  const unsigned biased_exp = 15 + unbiased_exp;
+  const uint32_t as_bits = sign_bit << 7 | (biased_exp << 2) | mantissa;
+  EXPECT_LE(as_bits, 0xffu);
+  return Float8_E5M2(static_cast<uint8_t>(as_bits));
+}
+
+TEST_P(FloatE5M2StreamParseTest, Samples) {
+  std::stringstream input(GetParam().literal);
+  HexFloat<FloatProxy<Float8_E5M2>> parsed_value(makeE5M2(0, 0, 0));
+  // Hex floats must be read with the stream input operator.
+  input >> parsed_value;
+  if (GetParam().expect_success) {
+    EXPECT_FALSE(input.fail());
+    std::string suffix;
+    input >> suffix;
+    const auto got = parsed_value.value();
+    const auto expected = GetParam().expected_value.value();
+    EXPECT_EQ(got.data(), expected.data())
+        << "got: " << got << " expected: " << expected;
+  } else {
+    EXPECT_TRUE(input.fail());
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    HexFloatE5M2IncreasingExponentsAndMantissa, FloatE5M2StreamParseTest,
+    ::testing::ValuesIn(std::vector<StreamParseCase<Float8_E5M2>>{
+        // Zero
+        {"0x0p0", true, "", makeE5M2(0, -15, 0x0)},
+        {"0x0p5000000000000", true, "", makeE5M2(0, -15, 0x0)},
+        {"-0x0p5000000000000", true, "", makeE5M2(1, -15, 0x0)},
+        // Leading 1
+        {"0x1p0", true, "", makeE5M2(0, 0, 0x0)},
+        {"0x1p1", true, "", makeE5M2(0, 1, 0x0)},
+        {"0x1p16", true, "", makeE5M2(0, 16, 0x0)},
+        {"0x1p-1", true, "", makeE5M2(0, -1, 0x0)},
+        {"0x1p-14", true, "", makeE5M2(0, -14, 0x0)},
+        // Leading 2
+        {"0x2p0", true, "", makeE5M2(0, 1, 0x0)},
+        {"0x2p1", true, "", makeE5M2(0, 2, 0x0)},
+        {"0x2p15", true, "", makeE5M2(0, 16, 0x0)},
+        {"0x2p-1", true, "", makeE5M2(0, 0, 0x0)},
+        {"0x2p-15", true, "", makeE5M2(0, -14, 0x0)},
+        // Leading 8
+        {"0x8p0", true, "", makeE5M2(0, 3, 0x0)},
+        {"0x8p1", true, "", makeE5M2(0, 4, 0x0)},
+        {"0x8p13", true, "", makeE5M2(0, 16, 0x0)},
+        {"0x8p-3", true, "", makeE5M2(0, 0, 0x0)},
+        {"0x8p-17", true, "", makeE5M2(0, -14, 0x0)},
+        // Leading 10
+        {"0x10.0p0", true, "", makeE5M2(0, 4, 0x0)},
+        {"0x10.0p1", true, "", makeE5M2(0, 5, 0x0)},
+        {"0x10.0p12", true, "", makeE5M2(0, 16, 0x0)},
+        {"0x10.0p-5", true, "", makeE5M2(0, -1, 0x0)},
+        {"0x10.0p-18", true, "", makeE5M2(0, -14, 0x0)},
+        // Samples that drop out bits *and* truncate significant bits
+        // that can't be represented.
+        // Progressively increase the leading digit.
+        {"0x1.aa40000p0", true, "", makeE5M2(0, 0, 0x2)},
+        {"0x1.aa7ffffp0", true, "", makeE5M2(0, 0, 0x2)},
+        {"0x2.aa40000p0", true, "", makeE5M2(0, 1, 0x1)},
+        {"0x2.aa7ffffp0", true, "", makeE5M2(0, 1, 0x1)},
+        {"0x4.aa40000p0", true, "", makeE5M2(0, 2, 0x0)},
+        {"0x4.aa7ffffp0", true, "", makeE5M2(0, 2, 0x0)},
+        {"0x8.aa40000p0", true, "", makeE5M2(0, 3, 0x0)},
+        {"0x8.aa7ffffp0", true, "", makeE5M2(0, 3, 0x0)},
+    }));
+
+INSTANTIATE_TEST_SUITE_P(
+    Underflow, FloatE5M2StreamParseTest,
+    ::testing::ValuesIn(std::vector<StreamParseCase<Float8_E5M2>>{
+        // Underflow
+        {"0x1.p-16", true, "", Float8_E5M2(uint8_t(1))},
+        {"0x1.p-17", true, "", Float8_E5M2(uint8_t(0))},
+        {"+0x1.p-16", true, "", Float8_E5M2(uint8_t(1))},
+        {"+0x1.p-17", true, "", Float8_E5M2(uint8_t(0))},
+        {"-0x1.p-16", true, "", Float8_E5M2(uint8_t(0x81))},
+        {"-0x1.p-17", true, "", Float8_E5M2(uint8_t(0x80))},
+    }));
+
 }  // namespace
 }  // namespace utils
 }  // namespace spvtools
diff --git a/test/link/CMakeLists.txt b/test/link/CMakeLists.txt
index ee41b91..4076530 100644
--- a/test/link/CMakeLists.txt
+++ b/test/link/CMakeLists.txt
@@ -24,5 +24,6 @@
        partial_linkage_test.cpp
        unique_ids_test.cpp
        type_match_test.cpp
+       function_variants.cpp
   LIBS SPIRV-Tools-opt SPIRV-Tools-link
 )
diff --git a/test/link/function_variants.cpp b/test/link/function_variants.cpp
new file mode 100644
index 0000000..1f08414
--- /dev/null
+++ b/test/link/function_variants.cpp
@@ -0,0 +1,581 @@
+// Copyright 2025 The Khronos Group 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 <string>
+
+#include "gmock/gmock.h"
+#include "test/link/linker_fixture.h"
+
+namespace spvtools {
+namespace {
+
+using FunctionVariants = spvtest::LinkerTest;
+
+TEST_F(FunctionVariants, Dot4) {
+  constexpr const char* const dot4_fp32 = R"(
+    OpCapability Kernel
+    OpCapability Addresses
+    OpCapability Int8
+    OpCapability Int64
+    OpCapability Linkage
+    %1 = OpExtInstImport "OpenCL.std"
+    OpMemoryModel Physical64 OpenCL
+    OpEntryPoint Kernel %2 "dot4" %3
+    OpExecutionMode %2 ContractionOff
+    OpSource OpenCL_C 102000
+    OpName %2 "dot4"
+    OpName %3 "__spirv_BuiltInGlobalInvocationId"
+    OpName %4 "entry"
+    %5 = OpTypeFloat 32
+    %6 = OpTypePointer CrossWorkgroup %5
+    %7 = OpTypeInt 8 0
+    %8 = OpTypePointer CrossWorkgroup %7
+    %9 = OpTypeVoid
+    %10 = OpTypeFunction %9 %6 %6 %8
+    %11 = OpTypeInt 64 0
+    %12 = OpTypeInt 32 0
+    %13 = OpTypeVector %11 3
+    %14 = OpTypePointer Input %13
+    %15 = OpTypeVector %5 4
+    %16 = OpConstant %11 30
+    %17 = OpConstant %11 32
+    %18 = OpConstant %12 2
+    %3 = OpVariable %14 Input
+    %2 = OpFunction %9 None %10
+    %19 = OpFunctionParameter %6
+    %20 = OpFunctionParameter %6
+    %21 = OpFunctionParameter %8
+    %4 = OpLabel
+    %22 = OpLoad %13 %3 Aligned 1
+    %23 = OpCompositeExtract %11 %22 0
+    %24 = OpUConvert %12 %23
+    %25 = OpShiftLeftLogical %12 %24 %18
+    %26 = OpSConvert %11 %25
+    %27 = OpExtInst %15 %1 vloadn %26 %19 4
+    %28 = OpExtInst %15 %1 vloadn %26 %20 4
+    %29 = OpDot %5 %27 %28
+    %30 = OpShiftLeftLogical %11 %23 %17
+    %31 = OpShiftRightArithmetic %11 %30 %16
+    %32 = OpInBoundsPtrAccessChain %8 %21 %31
+    %33 = OpBitcast %6 %32
+    OpStore %33 %29 Aligned 4
+    OpReturn
+    OpFunctionEnd
+  )";
+
+  constexpr const char* const dot4_fp16 = R"(
+    OpCapability Kernel
+    OpCapability Addresses
+    OpCapability Int8
+    OpCapability Int64
+    OpCapability Float16
+    OpCapability Linkage
+    %1 = OpExtInstImport "OpenCL.std"
+    OpMemoryModel Physical64 OpenCL
+    OpEntryPoint Kernel %2 "dot4" %3
+    OpExecutionMode %2 ContractionOff
+    OpSource OpenCL_C 102000
+    OpName %2 "dot4"
+    OpName %3 "__spirv_BuiltInGlobalInvocationId"
+    OpName %4 "entry"
+    %5 = OpTypeFloat 16
+    %6 = OpTypePointer CrossWorkgroup %5
+    %7 = OpTypeInt 8 0
+    %8 = OpTypePointer CrossWorkgroup %7
+    %9 = OpTypeVoid
+    %10 = OpTypeFunction %9 %6 %6 %8
+    %11 = OpTypeInt 64 0
+    %12 = OpTypeInt 32 0
+    %13 = OpTypeVector %11 3
+    %14 = OpTypePointer Input %13
+    %15 = OpTypeVector %5 4
+    %16 = OpConstant %11 31
+    %17 = OpConstant %11 32
+    %18 = OpConstant %12 2
+    %3 = OpVariable %14 Input
+    %2 = OpFunction %9 None %10
+    %19 = OpFunctionParameter %6
+    %20 = OpFunctionParameter %6
+    %21 = OpFunctionParameter %8
+    %4 = OpLabel
+    %22 = OpLoad %13 %3 Aligned 1
+    %23 = OpCompositeExtract %11 %22 0
+    %24 = OpUConvert %12 %23
+    %25 = OpShiftLeftLogical %12 %24 %18
+    %26 = OpSConvert %11 %25
+    %27 = OpExtInst %15 %1 vloadn %26 %19 4
+    %28 = OpExtInst %15 %1 vloadn %26 %20 4
+    %29 = OpDot %5 %27 %28
+    %30 = OpShiftLeftLogical %11 %23 %17
+    %31 = OpShiftRightArithmetic %11 %30 %16
+    %32 = OpInBoundsPtrAccessChain %8 %21 %31
+    %33 = OpBitcast %6 %32
+    OpStore %33 %29 Aligned 4
+    OpReturn
+    OpFunctionEnd
+  )";
+
+  // clang-format off
+  const std::vector<const char*> expected_lines = {
+    "OpCapability FunctionVariantsINTEL",
+    "OpCapability SpecConditionalINTEL",
+    "OpCapability Kernel",
+    "OpCapability Addresses",
+    "OpCapability Int8",
+    "OpCapability Int64",
+    "OpCapability Linkage",
+    "OpConditionalCapabilityINTEL %dot4_fp16_spv Float16",
+    "OpExtension \"SPV_INTEL_function_variants\"",
+    "OpConditionalEntryPointINTEL %dot4_fp32_spv Kernel %dot4 \"dot4\" %__spirv_BuiltInGlobalInvocationId",
+    "OpConditionalEntryPointINTEL %dot4_fp16_spv Kernel %dot4_0 \"dot4\" %__spirv_BuiltInGlobalInvocationId_0",
+    "OpModuleProcessed \"SPV_INTEL_function_variants registry version 0\"",
+    "OpDecorate %ulong_30 ConditionalINTEL %dot4_fp32_spv",
+    "OpDecorate %ulong_32 ConditionalINTEL %dot4_fp32_spv",
+    "OpDecorate %uint_2 ConditionalINTEL %dot4_fp32_spv",
+    "OpDecorate %__spirv_BuiltInGlobalInvocationId ConditionalINTEL %dot4_fp32_spv",
+    "OpDecorate %dot4 ConditionalINTEL %dot4_fp32_spv",
+    "OpDecorate %ulong_31 ConditionalINTEL %dot4_fp16_spv",
+    "OpDecorate %ulong_32_0 ConditionalINTEL %dot4_fp16_spv",
+    "OpDecorate %uint_2_0 ConditionalINTEL %dot4_fp16_spv",
+    "OpDecorate %__spirv_BuiltInGlobalInvocationId_0 ConditionalINTEL %dot4_fp16_spv",
+    "OpDecorate %dot4_0 ConditionalINTEL %dot4_fp16_spv",
+    "OpDecorate %float ConditionalINTEL %dot4_fp32_spv",
+    "OpDecorate %_ptr_CrossWorkgroup_float ConditionalINTEL %dot4_fp32_spv",
+    "OpDecorate %18 ConditionalINTEL %dot4_fp32_spv",
+    "OpDecorate %v4float ConditionalINTEL %dot4_fp32_spv",
+    "OpDecorate %half ConditionalINTEL %dot4_fp16_spv",
+    "OpDecorate %_ptr_CrossWorkgroup_half ConditionalINTEL %dot4_fp16_spv",
+    "OpDecorate %22 ConditionalINTEL %dot4_fp16_spv",
+    "OpDecorate %v4half ConditionalINTEL %dot4_fp16_spv",
+    "%float = OpTypeFloat 32",
+    "%_ptr_CrossWorkgroup_float = OpTypePointer CrossWorkgroup %float",
+    "%18 = OpTypeFunction %void %_ptr_CrossWorkgroup_float %_ptr_CrossWorkgroup_float %_ptr_CrossWorkgroup_uchar",
+    "%v4float = OpTypeVector %float 4",
+    "%ulong_30 = OpConstant %ulong 30",
+    "%ulong_32 = OpConstant %ulong 32",
+    "%uint_2 = OpConstant %uint 2",
+    "%__spirv_BuiltInGlobalInvocationId = OpVariable %_ptr_Input_v3ulong Input",
+    "%bool = OpTypeBool",
+    "%32 = OpSpecConstantArchitectureINTEL %bool 2 3 174 0",
+    "%33 = OpSpecConstantTargetINTEL %bool 7",
+    "%34 = OpSpecConstantTargetINTEL %bool 8",
+    "%35 = OpSpecConstantCapabilitiesINTEL %bool Addresses Linkage Kernel Int64 Int8",
+    "%36 = OpSpecConstantOp %bool LogicalOr %33 %34",
+    "%37 = OpSpecConstantOp %bool LogicalAnd %35 %32",
+    "%38 = OpSpecConstantOp %bool LogicalAnd %37 %36",
+    "%half = OpTypeFloat 16",
+    "%_ptr_CrossWorkgroup_half = OpTypePointer CrossWorkgroup %half",
+    "%22 = OpTypeFunction %void %_ptr_CrossWorkgroup_half %_ptr_CrossWorkgroup_half %_ptr_CrossWorkgroup_uchar",
+    "%v4half = OpTypeVector %half 4",
+    "%ulong_31 = OpConstant %ulong 31",
+    "%ulong_32_0 = OpConstant %ulong 32",
+    "%uint_2_0 = OpConstant %uint 2",
+    "%__spirv_BuiltInGlobalInvocationId_0 = OpVariable %_ptr_Input_v3ulong Input",
+    "%39 = OpSpecConstantArchitectureINTEL %bool 2 3 174 4",
+    "%40 = OpSpecConstantTargetINTEL %bool 7",
+    "%41 = OpSpecConstantTargetINTEL %bool 8",
+    "%42 = OpSpecConstantCapabilitiesINTEL %bool Addresses Linkage Kernel Float16 Int64 Int8",
+    "%43 = OpSpecConstantOp %bool LogicalOr %40 %41",
+    "%44 = OpSpecConstantOp %bool LogicalAnd %42 %39",
+    "%dot4_fp16_spv = OpSpecConstantOp %bool LogicalAnd %44 %43",
+    "%45 = OpSpecConstantOp %bool LogicalNot %dot4_fp16_spv",
+    "%dot4_fp32_spv = OpSpecConstantOp %bool LogicalAnd %38 %45",
+    "%dot4 = OpFunction %void None %18",
+    "%56 = OpDot %float %54 %55",
+    "%dot4_0 = OpFunction %void None %22",
+    "%71 = OpDot %half %69 %70",
+  };
+  // clang-format on
+
+  const std::string targets_csv =
+      "module,target,features\n"
+      "dot4_fp32.spv,7,\n"
+      "dot4_fp32.spv,8,\n"
+      "dot4_fp16.spv,7,\n"
+      "dot4_fp16.spv,8,\n";
+  const std::string architectures_csv =
+      "module,category,family,op,architecture\n"
+      "dot4_fp32.spv,2,3,174,0\n"
+      "dot4_fp16.spv,2,3,174,4\n";
+  const std::vector<std::string> sources = {dot4_fp32, dot4_fp16};
+  const std::vector<std::string> in_files = {"dot4_fp32.spv", "dot4_fp16.spv"};
+
+  LinkerOptions options;
+  options.SetInFiles(in_files);
+  options.SetFnVarTargetsCsv(targets_csv);
+  options.SetFnVarArchitecturesCsv(architectures_csv);
+  options.SetHasFnVarCapabilities(true);
+  options.SetCreateLibrary(true);
+  options.SetVerifyIds(true);
+
+  spvtest::Binary linked_binary;
+  spv_result_t res = AssembleAndLink(sources, &linked_binary, options);
+  EXPECT_EQ(SPV_SUCCESS, res) << GetErrorMessage();
+  EXPECT_THAT(GetErrorMessage(), std::string());
+  EXPECT_TRUE(Validate(linked_binary));
+
+  std::string linked_asm;
+  res = Disassemble(linked_binary, &linked_asm);
+  EXPECT_EQ(SPV_SUCCESS, res) << GetErrorMessage();
+  for (const auto& expected : expected_lines) {
+    EXPECT_THAT(linked_asm, testing::HasSubstr(expected));
+  }
+}
+
+TEST_F(FunctionVariants, FAddAsm) {
+  constexpr const char* const foo_base = R"(
+    OpCapability Kernel
+    OpCapability Addresses
+    OpCapability Int8
+    OpCapability Int64
+    OpCapability Linkage
+    %1 = OpExtInstImport "OpenCL.std"
+    OpMemoryModel Physical64 OpenCL
+    OpSource OpenCL_CPP 100000
+    OpName %2 "foo"
+    OpName %3 "add"
+    OpName %4 "entry"
+    OpName %5 "work"
+    OpName %6 "call"
+    OpName %7 "entry"
+    OpDecorate %2 LinkageAttributes "foo" Export
+    OpDecorate %5 LinkageAttributes "work" Export
+    %8 = OpTypeFloat 32
+    %9 = OpTypePointer Function %8
+    %10 = OpTypeFunction %8 %9 %9 %9
+    %11 = OpTypeInt 8 0
+    %12 = OpTypePointer Function %11
+    %13 = OpTypeInt 64 0
+    %14 = OpConstant %13 28
+    %15 = OpConstant %13 24
+    %16 = OpConstant %13 20
+    %17 = OpConstant %13 16
+    %18 = OpConstant %13 12
+    %19 = OpConstant %13 8
+    %20 = OpConstant %13 4
+    %2 = OpFunction %8 DontInline %10
+    %21 = OpFunctionParameter %9
+    %22 = OpFunctionParameter %9
+    %23 = OpFunctionParameter %9
+    %4 = OpLabel
+    %24 = OpLoad %8 %21 Aligned 4
+    %25 = OpLoad %8 %22 Aligned 4
+    %3 = OpFAdd %8 %24 %25
+    OpStore %23 %3 Aligned 4
+    OpReturnValue %3
+    OpFunctionEnd
+    %5 = OpFunction %8 None %10
+    %26 = OpFunctionParameter %9
+    %27 = OpFunctionParameter %9
+    %28 = OpFunctionParameter %9
+    %7 = OpLabel
+    %6 = OpFunctionCall %8 %2 %26 %27 %28
+    %29 = OpFDiv %8 %6 %6
+    %30 = OpFDiv %8 %29 %6
+    OpReturnValue %30
+    OpFunctionEnd
+  )";
+
+  constexpr const char* const foo_asm = R"(
+    OpCapability Kernel
+    OpCapability Addresses
+    OpCapability Linkage
+    OpCapability AsmINTEL
+    OpExtension "SPV_INTEL_inline_assembly"
+    %1 = OpExtInstImport "OpenCL.std"
+    OpMemoryModel Physical64 OpenCL
+    OpSource OpenCL_CPP 100000
+    OpName %2 "a"
+    OpName %3 "b"
+    OpName %4 "c"
+    OpName %5 "foo"
+    OpName %6 "add"
+    OpName %7 "entry"
+    OpDecorate %2 FuncParamAttr NoWrite
+    OpDecorate %2 FuncParamAttr NoAlias
+    OpDecorate %3 FuncParamAttr NoWrite
+    OpDecorate %3 FuncParamAttr NoAlias
+    OpDecorate %4 FuncParamAttr NoAlias
+    OpDecorate %5 LinkageAttributes "foo" Export
+    OpDecorate %8 SideEffectsINTEL
+    %9 = OpTypeFloat 32
+    %10 = OpTypePointer Function %9
+    %11 = OpTypeFunction %9 %10 %10 %10
+    %12 = OpTypeVoid
+    %13 = OpTypeFunction %12
+    %14 = OpAsmTargetINTEL "spirv64-unknown-unknown"
+    %8 = OpAsmINTEL %12 %13 %14 "nop1" ""
+    %5 = OpFunction %9 None %11
+    %2 = OpFunctionParameter %10
+    %3 = OpFunctionParameter %10
+    %4 = OpFunctionParameter %10
+    %7 = OpLabel
+    %15 = OpLoad %9 %2 Aligned 4
+    %16 = OpLoad %9 %3 Aligned 4
+    %6 = OpFSub %9 %15 %16
+    OpStore %4 %6 Aligned 4
+    %17 = OpAsmCallINTEL %12 %8
+    OpReturnValue %6
+    OpFunctionEnd
+	)";
+
+  // same as foo_asm, just with OpFMul and a different assembly string in
+  // OpAsmINTEL
+  constexpr const char* const foo_asm2 = R"(
+    OpCapability Kernel
+    OpCapability Addresses
+    OpCapability Linkage
+    OpCapability AsmINTEL
+    OpExtension "SPV_INTEL_inline_assembly"
+    %1 = OpExtInstImport "OpenCL.std"
+    OpMemoryModel Physical64 OpenCL
+    OpSource OpenCL_CPP 100000
+    OpName %2 "a"
+    OpName %3 "b"
+    OpName %4 "c"
+    OpName %5 "foo"
+    OpName %6 "add"
+    OpName %7 "entry"
+    OpDecorate %2 FuncParamAttr NoWrite
+    OpDecorate %2 FuncParamAttr NoAlias
+    OpDecorate %3 FuncParamAttr NoWrite
+    OpDecorate %3 FuncParamAttr NoAlias
+    OpDecorate %4 FuncParamAttr NoAlias
+    OpDecorate %5 LinkageAttributes "foo" Export
+    OpDecorate %8 SideEffectsINTEL
+    %9 = OpTypeFloat 32
+    %10 = OpTypePointer Function %9
+    %11 = OpTypeFunction %9 %10 %10 %10
+    %12 = OpTypeVoid
+    %13 = OpTypeFunction %12
+    %14 = OpAsmTargetINTEL "spirv64-unknown-unknown"
+    %8 = OpAsmINTEL %12 %13 %14 "nop2" ""
+    %5 = OpFunction %9 None %11
+    %2 = OpFunctionParameter %10
+    %3 = OpFunctionParameter %10
+    %4 = OpFunctionParameter %10
+    %7 = OpLabel
+    %15 = OpLoad %9 %2 Aligned 4
+    %16 = OpLoad %9 %3 Aligned 4
+    %6 = OpFMul %9 %15 %16
+    OpStore %4 %6 Aligned 4
+    %17 = OpAsmCallINTEL %12 %8
+    OpReturnValue %6
+    OpFunctionEnd
+  )";
+
+  // clang-format off
+  const std::vector<const char*> expected_lines = {
+    "OpCapability FunctionVariantsINTEL",
+    "OpCapability SpecConditionalINTEL",
+    "OpCapability Kernel",
+    "OpCapability Addresses",
+    "OpConditionalCapabilityINTEL %foo_spv Int8",
+    "OpConditionalCapabilityINTEL %foo_spv Int64",
+    "OpCapability Linkage",
+    "OpConditionalCapabilityINTEL %2 AsmINTEL",
+    "OpExtension \"SPV_INTEL_function_variants\"",
+    "OpConditionalExtensionINTEL %2 \"SPV_INTEL_inline_assembly\"",
+    "OpModuleProcessed \"SPV_INTEL_function_variants registry version 0\"",
+    "OpDecorate %foo LinkageAttributes \"foo\" Export",
+    "OpDecorate %work LinkageAttributes \"work\" Export",
+    "OpDecorate %ulong_28 ConditionalINTEL %foo_spv",
+    "OpDecorate %ulong_24 ConditionalINTEL %foo_spv",
+    "OpDecorate %ulong_20 ConditionalINTEL %foo_spv",
+    "OpDecorate %ulong_16 ConditionalINTEL %foo_spv",
+    "OpDecorate %ulong_12 ConditionalINTEL %foo_spv",
+    "OpDecorate %ulong_8 ConditionalINTEL %foo_spv",
+    "OpDecorate %ulong_4 ConditionalINTEL %foo_spv",
+    "OpDecorate %foo ConditionalINTEL %foo_spv",
+    "OpDecorate %foo_0 LinkageAttributes \"foo\" Export",
+    "OpDecorate %32 ConditionalINTEL %foo_asm_spv",
+    "OpDecorate %31 ConditionalINTEL %foo_asm_spv",
+    "OpDecorate %foo_0 ConditionalINTEL %foo_asm_spv",
+    "OpDecorate %foo_1 LinkageAttributes \"foo\" Export",
+    "OpDecorate %34 ConditionalINTEL %foo_asm2_spv",
+    "OpDecorate %33 ConditionalINTEL %foo_asm2_spv",
+    "OpDecorate %foo_1 ConditionalINTEL %foo_asm2_spv",
+    "OpDecorate %call ConditionalINTEL %foo_spv",
+    "OpDecorate %35 ConditionalINTEL %foo_asm_spv",
+    "OpDecorate %36 ConditionalINTEL %foo_asm2_spv",
+    "OpDecorate %uchar ConditionalINTEL %foo_spv",
+    "OpDecorate %_ptr_Function_uchar ConditionalINTEL %foo_spv",
+    "OpDecorate %ulong ConditionalINTEL %foo_spv",
+    "OpDecorate %void ConditionalINTEL %2",
+    "OpDecorate %41 ConditionalINTEL %2",
+    "%ulong_28 = OpConstant %ulong 28",
+    "%ulong_24 = OpConstant %ulong 24",
+    "%ulong_20 = OpConstant %ulong 20",
+    "%ulong_16 = OpConstant %ulong 16",
+    "%ulong_12 = OpConstant %ulong 12",
+    "%ulong_8 = OpConstant %ulong 8",
+    "%ulong_4 = OpConstant %ulong 4",
+    "%bool = OpTypeBool",
+    "%46 = OpSpecConstantTargetINTEL %bool 4",
+    "%47 = OpSpecConstantCapabilitiesINTEL %bool Addresses Linkage Kernel Int64 Int8",
+    "%48 = OpSpecConstantOp %bool LogicalAnd %47 %46",
+    "%31 = OpAsmINTEL %void %41 %32 \"nop1\" \"\"",
+    "%49 = OpSpecConstantArchitectureINTEL %bool 1 1 170 1",
+    "%50 = OpSpecConstantTargetINTEL %bool 4 9 10",
+    "%51 = OpSpecConstantCapabilitiesINTEL %bool Addresses Linkage Kernel AsmINTEL",
+    "%52 = OpSpecConstantOp %bool LogicalAnd %51 %49",
+    "%foo_asm_spv = OpSpecConstantOp %bool LogicalAnd %52 %50",
+    "%33 = OpAsmINTEL %void %41 %34 \"nop2\" \"\"",
+    "%53 = OpSpecConstantArchitectureINTEL %bool 1 7 174 1",
+    "%54 = OpSpecConstantArchitectureINTEL %bool 1 7 178 3",
+    "%55 = OpSpecConstantArchitectureINTEL %bool 1 8 170 1",
+    "%56 = OpSpecConstantArchitectureINTEL %bool 1 9 174 1",
+    "%57 = OpSpecConstantArchitectureINTEL %bool 1 9 178 3",
+    "%58 = OpSpecConstantTargetINTEL %bool 5 2 4 5",
+    "%59 = OpSpecConstantTargetINTEL %bool 6 2 4 5",
+    "%60 = OpSpecConstantCapabilitiesINTEL %bool Addresses Linkage Kernel AsmINTEL",
+    "%61 = OpSpecConstantOp %bool LogicalAnd %53 %54",
+    "%62 = OpSpecConstantOp %bool LogicalAnd %56 %57",
+    "%63 = OpSpecConstantOp %bool LogicalOr %61 %55",
+    "%64 = OpSpecConstantOp %bool LogicalOr %63 %62",
+    "%65 = OpSpecConstantOp %bool LogicalOr %58 %59",
+    "%66 = OpSpecConstantOp %bool LogicalAnd %60 %64",
+    "%foo_asm2_spv = OpSpecConstantOp %bool LogicalAnd %66 %65",
+    "%67 = OpSpecConstantOp %bool LogicalOr %foo_asm_spv %foo_asm2_spv",
+    "%68 = OpSpecConstantOp %bool LogicalNot %67",
+    "%foo_spv = OpSpecConstantOp %bool LogicalAnd %48 %68",
+    "%2 = OpSpecConstantOp %bool LogicalOr %foo_asm_spv %foo_asm2_spv",
+    "%foo = OpFunction %float DontInline %44",
+    "%add = OpFAdd %float %72 %73",
+    "%work = OpFunction %float None %44",
+    "%call = OpFunctionCall %float %foo %74 %75 %76",
+    "%35 = OpFunctionCall %float %foo_0 %74 %75 %76",
+    "%36 = OpFunctionCall %float %foo_1 %74 %75 %76",
+    "%77 = OpConditionalCopyObjectINTEL %float %foo_spv %call %foo_asm_spv %35 %foo_asm2_spv %36",
+    "%78 = OpFDiv %float %77 %77",
+    "%79 = OpFDiv %float %78 %77",
+    "OpReturnValue %79",
+    "%foo_0 = OpFunction %float None %44",
+    "%add_0 = OpFSub %float %80 %81",
+    "%82 = OpAsmCallINTEL %void %31",
+    "%foo_1 = OpFunction %float None %44",
+    "%add_1 = OpFMul %float %83 %84",
+    "%85 = OpAsmCallINTEL %void %33",
+  };
+  // clang-format on
+
+  const std::string targets_csv =
+      "module,target,features\n"
+      "foo.spv,04,\n"           // test leading zeros
+      "foo_asm.spv,4,9/0010\n"  // test leading zeros
+      "foo_asm2.spv,5,2/4/5\n"
+      "foo_asm2.spv,6,2/4/5\n";
+  const std::string architectures_csv =
+      "module,category,family,op,architecture\n"
+      "foo_asm.spv,1,1,170,1\n"
+      "foo_asm2.spv,1,7,174,1\n"
+      "foo_asm2.spv,1,7,178,3\n"
+      "foo_asm2.spv,1,8,170,1\n"
+      "foo_asm2.spv,1,9,174,1\n"
+      "foo_asm2.spv,1,9,178,3\n";
+  const std::vector<std::string> sources = {foo_base, foo_asm, foo_asm2};
+  const std::vector<std::string> in_files = {"foo.spv", "foo_asm.spv",
+                                             "foo_asm2.spv"};
+
+  LinkerOptions options;
+  options.SetInFiles(in_files);
+  options.SetFnVarTargetsCsv(targets_csv);
+  options.SetFnVarArchitecturesCsv(architectures_csv);
+  options.SetHasFnVarCapabilities(true);
+  options.SetCreateLibrary(true);
+  options.SetVerifyIds(true);
+
+  spvtest::Binary linked_binary;
+  spv_result_t res = AssembleAndLink(sources, &linked_binary, options);
+  EXPECT_EQ(SPV_SUCCESS, res) << GetErrorMessage();
+  EXPECT_THAT(GetErrorMessage(), std::string());
+  EXPECT_TRUE(Validate(linked_binary));
+
+  std::string linked_asm;
+  res = Disassemble(linked_binary, &linked_asm);
+  EXPECT_EQ(SPV_SUCCESS, res) << GetErrorMessage();
+  for (const auto& expected : expected_lines) {
+    EXPECT_THAT(linked_asm, testing::HasSubstr(expected));
+  }
+}
+
+TEST_F(FunctionVariants, InvalidNumber1) {
+  const std::string targets_csv =
+      "module,target,features\n"
+      "foo.spv,-4,9/10\n";
+  const std::string architectures_csv = "";
+  const std::vector<std::string> sources = {""};
+  const std::vector<std::string> in_files = {"foo.spv"};
+
+  LinkerOptions options;
+  options.SetInFiles(in_files);
+  options.SetFnVarTargetsCsv(targets_csv);
+  options.SetFnVarArchitecturesCsv(architectures_csv);
+  options.SetHasFnVarCapabilities(true);
+  options.SetCreateLibrary(true);
+  options.SetVerifyIds(true);
+
+  spvtest::Binary linked_binary;
+  spv_result_t res = AssembleAndLink(sources, &linked_binary, options);
+  EXPECT_EQ(SPV_ERROR_FNVAR, res) << GetErrorMessage();
+  EXPECT_THAT(GetErrorMessage(), "ERROR: 0: Error converting -4 to target.");
+}
+
+TEST_F(FunctionVariants, InvalidNumber2) {
+  const std::string targets_csv =
+      "module,target,features\n"
+      "foo.spv,4,9/-10\n";
+  const std::string architectures_csv = "";
+  const std::vector<std::string> sources = {""};
+  const std::vector<std::string> in_files = {"foo.spv"};
+
+  LinkerOptions options;
+  options.SetInFiles(in_files);
+  options.SetFnVarTargetsCsv(targets_csv);
+  options.SetFnVarArchitecturesCsv(architectures_csv);
+  options.SetHasFnVarCapabilities(true);
+  options.SetCreateLibrary(true);
+  options.SetVerifyIds(true);
+
+  spvtest::Binary linked_binary;
+  spv_result_t res = AssembleAndLink(sources, &linked_binary, options);
+  EXPECT_EQ(SPV_ERROR_FNVAR, res) << GetErrorMessage();
+  EXPECT_THAT(GetErrorMessage(),
+              "ERROR: 0: Error converting -10 in 9/-10 to target feature.");
+}
+
+TEST_F(FunctionVariants, InvalidNumber3) {
+  const std::string targets_csv =
+      "module,target,features\n"
+      "foo.spv,4.0,9/10\n";
+  const std::string architectures_csv = "";
+  const std::vector<std::string> sources = {""};
+  const std::vector<std::string> in_files = {"foo.spv"};
+
+  LinkerOptions options;
+  options.SetInFiles(in_files);
+  options.SetFnVarTargetsCsv(targets_csv);
+  options.SetFnVarArchitecturesCsv(architectures_csv);
+  options.SetHasFnVarCapabilities(true);
+  options.SetCreateLibrary(true);
+  options.SetVerifyIds(true);
+
+  spvtest::Binary linked_binary;
+  spv_result_t res = AssembleAndLink(sources, &linked_binary, options);
+  EXPECT_EQ(SPV_ERROR_FNVAR, res) << GetErrorMessage();
+  EXPECT_THAT(GetErrorMessage(), "ERROR: 0: Error converting 4.0 to target.");
+}
+
+}  // namespace
+}  // namespace spvtools
diff --git a/test/link/linker_fixture.h b/test/link/linker_fixture.h
index d005288..08a2880 100644
--- a/test/link/linker_fixture.h
+++ b/test/link/linker_fixture.h
@@ -155,6 +155,19 @@
     }
   }
 
+  void Match(const std::string& templateBody,
+             const spvtest::Binary& linked_binary) {
+    std::string result;
+    EXPECT_TRUE(
+        tools_.Disassemble(linked_binary, &result, disassemble_options_))
+        << GetErrorMessage();
+    auto match_res = effcee::Match(result, templateBody);
+    EXPECT_EQ(effcee::Result::Status::Ok, match_res.status())
+        << match_res.message() << "\nExpanded from:\n"
+        << templateBody << "\nChecking result:\n"
+        << result;
+  }
+
   // An alternative to ExpandAndCheck, which uses the |templateBody| as the
   // match pattern for the disassembled linked result.
   void ExpandAndMatch(
@@ -165,15 +178,7 @@
     EXPECT_EQ(SPV_SUCCESS, res) << GetErrorMessage() << "\nExpanded from:\n"
                                 << templateBody;
     if (res == SPV_SUCCESS) {
-      std::string result;
-      EXPECT_TRUE(
-          tools_.Disassemble(linked_binary, &result, disassemble_options_))
-          << GetErrorMessage();
-      auto match_res = effcee::Match(result, templateBody);
-      EXPECT_EQ(effcee::Result::Status::Ok, match_res.status())
-          << match_res.message() << "\nExpanded from:\n"
-          << templateBody << "\nChecking result:\n"
-          << result;
+      Match(templateBody, linked_binary);
     }
   }
 
diff --git a/test/link/matching_imports_to_exports_test.cpp b/test/link/matching_imports_to_exports_test.cpp
index c7c962f..db94cb8 100644
--- a/test/link/matching_imports_to_exports_test.cpp
+++ b/test/link/matching_imports_to_exports_test.cpp
@@ -15,6 +15,7 @@
 #include <string>
 
 #include "gmock/gmock.h"
+#include "gtest/gtest.h"
 #include "test/link/linker_fixture.h"
 
 namespace spvtools {
@@ -904,5 +905,125 @@
   }
 }
 
+TEST_F(MatchingImportsToExports, LinkOnceODRLinkageVarSingle) {
+  const std::string body1 = R"(
+OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpExtension "SPV_KHR_linkonce_odr"
+OpMemoryModel Physical64 OpenCL
+OpDecorate %1 LinkageAttributes "foo" LinkOnceODR
+%2 = OpTypeFloat 32
+%3 = OpConstant %2 3.1415
+%1 = OpVariable %2 Uniform %3
+)";
+
+  const std::string body2 = R"(
+OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpExtension "SPV_KHR_linkonce_odr"
+OpMemoryModel Physical64 OpenCL
+OpDecorate %1 LinkageAttributes "foo" Import
+%2 = OpTypeFloat 32
+%1 = OpVariable %2 Uniform
+)";
+
+  const std::string matchTemplate = R"(
+; CHECK-NOT: OpDecorate {{.*}} Import
+; CHECK-NOT: OpDecorate {{.*}} LinkOnceODR
+)";
+
+  spvtest::Binary linked_binary;
+  EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
+      << GetErrorMessage();
+  Match(matchTemplate, linked_binary);
+}
+
+TEST_F(MatchingImportsToExports, LinkOnceODRLinkageFunMultiple) {
+  const std::string body1 = R"(
+OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpExtension "SPV_KHR_linkonce_odr"
+OpMemoryModel Physical64 OpenCL
+OpDecorate %1 LinkageAttributes "foo" LinkOnceODR
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%1 = OpFunction %2 Inline %3
+%4 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string body2 = R"(
+OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpExtension "SPV_KHR_linkonce_odr"
+OpMemoryModel Physical64 OpenCL
+OpDecorate %1 LinkageAttributes "foo" Import
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%1 = OpFunction %2 None %3
+OpFunctionEnd
+)";
+
+  const std::string matchTemplate = R"(
+; CHECK-NOT: OpDecorate {{.*}} Import
+; CHECK-NOT: OpDecorate {{.*}} LinkOnceODR
+)";
+
+  spvtest::Binary linked_binary;
+  EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body1, body2}, &linked_binary))
+      << GetErrorMessage();
+  Match(matchTemplate, linked_binary);
+}
+
+TEST_F(MatchingImportsToExports, LinkOnceODRAndExport) {
+  const std::string body1 = R"(
+OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpExtension "SPV_KHR_linkonce_odr"
+OpMemoryModel Physical64 OpenCL
+OpDecorate %1 LinkageAttributes "foo" LinkOnceODR
+%2 = OpTypeFloat 32
+%3 = OpConstant %2 3.1415
+%1 = OpVariable %2 Uniform %3
+)";
+
+  const std::string body2 = R"(
+OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpExtension "SPV_KHR_linkonce_odr"
+OpMemoryModel Physical64 OpenCL
+OpDecorate %1 LinkageAttributes "foo" Export
+%2 = OpTypeFloat 32
+%3 = OpConstant %2 2.7183
+%1 = OpVariable %2 Uniform %3
+)";
+
+  const std::string body3 = R"(
+OpCapability Linkage
+OpCapability Addresses
+OpCapability Kernel
+OpExtension "SPV_KHR_linkonce_odr"
+OpMemoryModel Physical64 OpenCL
+OpDecorate %1 LinkageAttributes "foo" Import
+%2 = OpTypeFloat 32
+%1 = OpVariable %2 Uniform
+)";
+  spvtest::Binary linked_binary;
+  ASSERT_EQ(SPV_ERROR_INVALID_BINARY,
+            AssembleAndLink({body1, body2, body3}, &linked_binary))
+      << GetErrorMessage();
+  EXPECT_THAT(
+      GetErrorMessage(),
+      HasSubstr("Combination of Export and LinkOnceODR is not allowed, found "
+                "for \"foo\""));
+}
+
 }  // namespace
 }  // namespace spvtools
diff --git a/test/op_unknown_test.cpp b/test/op_unknown_test.cpp
new file mode 100644
index 0000000..d41174e
--- /dev/null
+++ b/test/op_unknown_test.cpp
@@ -0,0 +1,128 @@
+// Copyright (c) 2025 The Khronos Group 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 <cassert>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "source/util/bitutils.h"
+#include "test/test_fixture.h"
+
+namespace spvtools {
+namespace utils {
+namespace {
+
+using spvtest::Concatenate;
+using spvtest::MakeInstruction;
+using spvtest::ScopedContext;
+using spvtest::TextToBinaryTest;
+using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using ::testing::StrEq;
+
+using OpUnknownTest = TextToBinaryTest;
+
+TEST_F(OpUnknownTest, OpUnknown) {
+  SetText("OpUnknown(255, 1)");
+  ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(ScopedContext().context, text.str,
+                                         text.length, &binary, &diagnostic));
+  EXPECT_EQ(0x000100FFu, binary->code[5]);
+  if (diagnostic) {
+    spvDiagnosticPrint(diagnostic);
+  }
+}
+
+TEST_F(OpUnknownTest, HandlesOperands) {
+  EXPECT_THAT(CompiledInstructions("OpUnknown(24, 4) %a %b %123"),
+              Eq(MakeInstruction(spv::Op::OpTypeMatrix, {1, 2, 3})));
+  EXPECT_THAT(CompiledInstructions("OpUnknown(24, 4) !1 %b %123"),
+              Eq(MakeInstruction(spv::Op::OpTypeMatrix, {1, 1, 2})));
+}
+
+TEST_F(OpUnknownTest, HandlesWhitespace) {
+  EXPECT_THAT(CompiledInstructions("OpUnknown ( 24 , 4 ) %a %b %123"),
+              Eq(MakeInstruction(spv::Op::OpTypeMatrix, {1, 2, 3})));
+  EXPECT_THAT(CompiledInstructions("OpUnknown(24,4) %a %b %123"),
+              Eq(MakeInstruction(spv::Op::OpTypeMatrix, {1, 2, 3})));
+}
+
+TEST_F(OpUnknownTest, MultipleInstructions) {
+  EXPECT_THAT(
+      CompiledInstructions(
+          "%a = OpTypeFunction %b\nOpUnknown(21, 5) %c %d 32 1\nOpNop"),
+      Eq(Concatenate({MakeInstruction(spv::Op::OpTypeFunction, {1, 2}),
+                      MakeInstruction(spv::Op::OpTypeInt, {3, 4, 32, 1}),
+                      MakeInstruction(spv::Op::OpNop, {})})));
+}
+
+TEST_F(OpUnknownTest, OpUnknownInAssignment) {
+  EXPECT_EQ(
+      "OpUnknown not allowed in assignment. Use an explicit result id operand "
+      "instead.",
+      CompileFailure("%2 = OpUnknown(22, 3) 32"));
+  EXPECT_EQ("OpUnknown not allowed before =.",
+            CompileFailure("OpUnknown(22, 3) = OpTypeFloat 32"));
+}
+
+TEST_F(OpUnknownTest, ParsingErrors) {
+  EXPECT_EQ("Expected '(', found end of stream.", CompileFailure("OpUnknown"));
+  EXPECT_EQ("'(' expected after OpUnknown but found 'a'.",
+            CompileFailure("OpUnknown abc"));
+
+  EXPECT_EQ("Expected opcode enumerant, found end of stream.",
+            CompileFailure("OpUnknown("));
+  EXPECT_EQ("Invalid opcode enumerant: \"abc\".",
+            CompileFailure("OpUnknown(abc"));
+  // Opcode enumerant must fit in 16 bits.
+  EXPECT_EQ("Invalid opcode enumerant: \"70000\".",
+            CompileFailure("OpUnknown(70000"));
+
+  EXPECT_EQ("Expected ',', found end of stream.",
+            CompileFailure("OpUnknown(22"));
+  EXPECT_EQ("',' expected after opcode enumerant but found 'a'.",
+            CompileFailure("OpUnknown(22 abc"));
+
+  EXPECT_EQ("Expected number of words, found end of stream.",
+            CompileFailure("OpUnknown(22,"));
+  EXPECT_EQ("Invalid number of words: \"abc\".",
+            CompileFailure("OpUnknown(22, abc"));
+  // Number of words must fit in 16 bits.
+  EXPECT_EQ("Invalid number of words: \"70000\".",
+            CompileFailure("OpUnknown(22, 70000"));
+  EXPECT_EQ(
+      "Number of words (which includes the opcode) must be greater than zero.",
+      CompileFailure("OpUnknown(22, 0"));
+
+  EXPECT_EQ("Expected ')', found end of stream.",
+            CompileFailure("OpUnknown(22, 3"));
+  EXPECT_EQ("')' expected after number of words but found 'a'.",
+            CompileFailure("OpUnknown(22, 3 abc"));
+
+  EXPECT_EQ(
+      "Unexpected start of new instruction: \"OpNop\". Expected 2 more "
+      "operands",
+      CompileFailure("OpUnknown(22, 3) OpNop"));
+  EXPECT_EQ("Expected 2 more operands, found end of stream.",
+            CompileFailure("OpUnknown(22, 3)"));
+
+  EXPECT_EQ(CompileFailure("OpUnknown(21, 4) %c %d 32 1"),
+            "Expected <opcode> or <result-id> at the beginning of an "
+            "instruction, found '1'.");
+}
+
+}  // namespace
+}  // namespace utils
+}  // namespace spvtools
diff --git a/test/opcode_lookup_test.cpp b/test/opcode_lookup_test.cpp
new file mode 100644
index 0000000..c3d2dd1
--- /dev/null
+++ b/test/opcode_lookup_test.cpp
@@ -0,0 +1,232 @@
+// Copyright 2025 Google LLC
+//
+// 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 <array>
+#include <iostream>
+
+#include "gmock/gmock.h"
+#include "source/spirv_target_env.h"
+#include "source/table2.h"
+#include "test/unit_spirv.h"
+
+using ::testing::ContainerEq;
+using ::testing::ValuesIn;
+
+namespace spvtools {
+namespace {
+
+struct OpcodeLookupCase {
+  std::string name;
+  uint32_t opcode;
+  bool expect_pass = true;
+};
+
+std::ostream& operator<<(std::ostream& os, const OpcodeLookupCase& olc) {
+  os << "OLC('" << olc.name << "', " << olc.opcode << ", expect pass? "
+     << olc.expect_pass << ")";
+  return os;
+}
+
+using OpcodeLookupTest = ::testing::TestWithParam<OpcodeLookupCase>;
+
+TEST_P(OpcodeLookupTest, OpcodeLookup_ByName) {
+  const InstructionDesc* desc = nullptr;
+  auto status = LookupOpcode(GetParam().name.data(), &desc);
+  if (GetParam().expect_pass) {
+    EXPECT_EQ(status, SPV_SUCCESS);
+    ASSERT_NE(desc, nullptr);
+    EXPECT_EQ(static_cast<uint32_t>(desc->opcode), GetParam().opcode);
+  } else {
+    EXPECT_NE(status, SPV_SUCCESS);
+    EXPECT_EQ(desc, nullptr);
+  }
+}
+
+TEST_P(OpcodeLookupTest, OpcodeLookup_ByOpcode_Success) {
+  const InstructionDesc* desc = nullptr;
+  if (GetParam().expect_pass) {
+    spv::Op opcode = static_cast<spv::Op>(GetParam().opcode);
+    auto status = LookupOpcode(opcode, &desc);
+    EXPECT_EQ(status, SPV_SUCCESS);
+    ASSERT_NE(desc, nullptr);
+    EXPECT_EQ(desc->opcode, opcode);
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(Samples, OpcodeLookupTest,
+                         ValuesIn(std::vector<OpcodeLookupCase>{
+                             {"Nop", 0},
+                             {"WritePipe", 275},
+                             {"TypeAccelerationStructureKHR", 5341},
+                             {"TypeAccelerationStructureNV", 5341},
+                             {"does not exist", 0, false},
+                             {"CopyLogical", 400},
+                             {"FPGARegINTEL", 5949},
+                             {"SubgroupMatrixMultiplyAccumulateINTEL", 6237},
+                         }));
+
+TEST(OpcodeLookupSingleTest, OpcodeLookup_ByOpcode_Fails) {
+  // This list may need adjusting over time.
+  std::array<uint32_t, 3> bad_opcodes = {{99999, 37737, 110101}};
+  for (auto bad_opcode : bad_opcodes) {
+    const InstructionDesc* desc = nullptr;
+    spv::Op opcode = static_cast<spv::Op>(bad_opcode);
+    auto status = LookupOpcode(opcode, &desc);
+    EXPECT_NE(status, SPV_SUCCESS);
+    ASSERT_EQ(desc, nullptr);
+  }
+}
+
+struct OpcodeLookupEnvCase {
+  std::string name;
+  uint32_t opcode;
+  spv_target_env env = SPV_ENV_UNIVERSAL_1_0;
+  bool expect_pass = true;
+};
+
+std::ostream& operator<<(std::ostream& os, const OpcodeLookupEnvCase& olec) {
+  os << "OLC('" << olec.name << "', " << olec.opcode << ", env "
+     << spvTargetEnvDescription(olec.env) << ", expect pass? "
+     << olec.expect_pass << ")";
+  return os;
+}
+
+using OpcodeLookupEnvTest = ::testing::TestWithParam<OpcodeLookupEnvCase>;
+
+TEST_P(OpcodeLookupEnvTest, OpcodeLookupForEnv_ByName) {
+  const InstructionDesc* desc = nullptr;
+  auto status =
+      LookupOpcodeForEnv(GetParam().env, GetParam().name.data(), &desc);
+  if (GetParam().expect_pass) {
+    EXPECT_EQ(status, SPV_SUCCESS);
+    ASSERT_NE(desc, nullptr);
+    EXPECT_EQ(static_cast<uint32_t>(desc->opcode), GetParam().opcode);
+  } else {
+    EXPECT_NE(status, SPV_SUCCESS);
+    EXPECT_EQ(desc, nullptr);
+  }
+}
+
+TEST_P(OpcodeLookupEnvTest, OpcodeLookupForEnv_ByOpcode) {
+  const InstructionDesc* desc = nullptr;
+  spv::Op opcode = static_cast<spv::Op>(GetParam().opcode);
+  auto status = LookupOpcodeForEnv(GetParam().env, opcode, &desc);
+  if (GetParam().expect_pass) {
+    EXPECT_EQ(status, SPV_SUCCESS);
+    ASSERT_NE(desc, nullptr);
+    EXPECT_EQ(desc->opcode, opcode);
+  } else {
+    // Skip nonsense cases created for the lookup-by-name case.
+    if (GetParam().name != "does not exist") {
+      EXPECT_NE(status, SPV_SUCCESS);
+      EXPECT_EQ(desc, nullptr);
+    }
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(Samples, OpcodeLookupEnvTest,
+                         ValuesIn(std::vector<OpcodeLookupEnvCase>{
+                             {"Nop", 0},
+                             {"WritePipe", 275},
+                             {"TypeAccelerationStructureKHR", 5341},
+                             {"TypeAccelerationStructureNV", 5341},
+                             {"does not exist", 0, SPV_ENV_UNIVERSAL_1_0,
+                              false},
+                             {"CopyLogical", 400, SPV_ENV_UNIVERSAL_1_0, false},
+                             {"CopyLogical", 400, SPV_ENV_UNIVERSAL_1_3, false},
+                             {"CopyLogical", 400, SPV_ENV_UNIVERSAL_1_4, true},
+                             {"FPGARegINTEL", 5949},
+                             {"SubgroupMatrixMultiplyAccumulateINTEL", 6237},
+                         }));
+
+TEST(OpcodeLookupExtInstTest, Operands) {
+  // The SPIR-V spec grammar has a single rule for OpExtInst, where the last
+  // item is "sequence of Ids".  SPIRV-Tools handles it differently. It drops
+  // that last item, and instead specifies those operands as operands of the
+  // extended instruction enum, such as 'cos'.
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/233
+  // Test the exact sequence of operand types extracted for OpExtInst.
+  const InstructionDesc* desc = nullptr;
+  auto status = LookupOpcode("ExtInst", &desc);
+  EXPECT_EQ(status, SPV_SUCCESS);
+  ASSERT_NE(desc, nullptr);
+
+  EXPECT_EQ(desc->operands_range.count(), 4u);
+
+  auto operands = desc->operands();
+  using vtype = std::vector<spv_operand_type_t>;
+
+  EXPECT_THAT(
+      vtype(operands.begin(), operands.end()),
+      ContainerEq(vtype{SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID,
+                        SPV_OPERAND_TYPE_ID,
+                        SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER}));
+}
+
+// Test printingClass
+
+struct OpcodePrintingClassCase {
+  std::string name;
+  PrintingClass expected;
+};
+
+std::ostream& operator<<(std::ostream& os,
+                         const OpcodePrintingClassCase& opcc) {
+  os << "OPCC('" << opcc.name << "', " << static_cast<int>(opcc.expected)
+     << ")";
+  return os;
+}
+
+using OpcodePrintingClassTest =
+    ::testing::TestWithParam<OpcodePrintingClassCase>;
+
+TEST_P(OpcodePrintingClassTest, OpcodeLookup_ByName) {
+  const InstructionDesc* desc = nullptr;
+  auto status = LookupOpcode(GetParam().name.data(), &desc);
+  EXPECT_EQ(status, SPV_SUCCESS);
+  ASSERT_NE(desc, nullptr);
+  EXPECT_EQ(desc->printingClass, GetParam().expected);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    Samples, OpcodePrintingClassTest,
+    ValuesIn(std::vector<OpcodePrintingClassCase>{
+        {"ConstantFunctionPointerINTEL", PrintingClass::k_exclude},
+        {"Nop", PrintingClass::kMiscellaneous},
+        {"SourceContinued", PrintingClass::kDebug},
+        {"Decorate", PrintingClass::kAnnotation},
+        {"Extension", PrintingClass::kExtension},
+        {"MemoryModel", PrintingClass::kMode_Setting},
+        {"Variable", PrintingClass::kMemory},
+        {"CooperativeMatrixPerElementOpNV", PrintingClass::kFunction},
+        {"SampledImage", PrintingClass::kImage},
+        {"ConvertFToU", PrintingClass::kConversion},
+        {"VectorExtractDynamic", PrintingClass::kComposite},
+        {"IAdd", PrintingClass::kArithmetic},
+        {"ShiftRightLogical", PrintingClass::kBit},
+        {"Any", PrintingClass::kRelational_and_Logical},
+        {"DPdx", PrintingClass::kDerivative},
+        {"Branch", PrintingClass::kControl_Flow},
+        {"AtomicLoad", PrintingClass::kAtomic},
+        {"ControlBarrier", PrintingClass::kBarrier},
+        {"GroupAll", PrintingClass::kGroup},
+        {"EnqueueMarker", PrintingClass::kDevice_Side_Enqueue},
+        {"ReadPipe", PrintingClass::kPipe},
+        {"GroupNonUniformElect", PrintingClass::kNon_Uniform},
+        // Skipping "Reserved" because it's probably an
+        // unstable class.
+    }));
+
+}  // namespace
+}  // namespace spvtools
diff --git a/test/opcode_require_capabilities_test.cpp b/test/opcode_require_capabilities_test.cpp
index 615c094..3694f14 100644
--- a/test/opcode_require_capabilities_test.cpp
+++ b/test/opcode_require_capabilities_test.cpp
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "test/unit_spirv.h"
-
 #include "source/enum_set.h"
+#include "source/table2.h"
+#include "test/unit_spirv.h"
 
 namespace spvtools {
 namespace {
@@ -31,15 +31,12 @@
     ::testing::TestWithParam<ExpectedOpCodeCapabilities>;
 
 TEST_P(OpcodeTableCapabilitiesTest, TableEntryMatchesExpectedCapabilities) {
-  auto env = SPV_ENV_UNIVERSAL_1_1;
-  spv_opcode_table opcodeTable;
-  ASSERT_EQ(SPV_SUCCESS, spvOpcodeTableGet(&opcodeTable, env));
-  spv_opcode_desc entry;
-  ASSERT_EQ(SPV_SUCCESS, spvOpcodeTableValueLookup(env, opcodeTable,
-                                                   GetParam().opcode, &entry));
-  EXPECT_EQ(
-      ElementsIn(GetParam().capabilities),
-      ElementsIn(CapabilitySet(entry->numCapabilities, entry->capabilities)));
+  const spvtools::InstructionDesc* desc = nullptr;
+  ASSERT_EQ(SPV_SUCCESS, spvtools::LookupOpcode(GetParam().opcode, &desc));
+  auto caps = desc->capabilities();
+  EXPECT_EQ(ElementsIn(GetParam().capabilities),
+            ElementsIn(CapabilitySet(static_cast<uint32_t>(caps.size()),
+                                     caps.data())));
 }
 
 INSTANTIATE_TEST_SUITE_P(
diff --git a/test/opcode_table_get_test.cpp b/test/opcode_table_get_test.cpp
deleted file mode 100644
index 4ff67d9..0000000
--- a/test/opcode_table_get_test.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) 2015-2016 The Khronos Group 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 "gmock/gmock.h"
-#include "test/unit_spirv.h"
-
-namespace spvtools {
-namespace {
-
-using GetTargetOpcodeTableGetTest = ::testing::TestWithParam<spv_target_env>;
-using ::testing::ValuesIn;
-
-TEST_P(GetTargetOpcodeTableGetTest, IntegrityCheck) {
-  spv_opcode_table table;
-  ASSERT_EQ(SPV_SUCCESS, spvOpcodeTableGet(&table, GetParam()));
-  ASSERT_NE(0u, table->count);
-  ASSERT_NE(nullptr, table->entries);
-}
-
-TEST_P(GetTargetOpcodeTableGetTest, InvalidPointerTable) {
-  ASSERT_EQ(SPV_ERROR_INVALID_POINTER, spvOpcodeTableGet(nullptr, GetParam()));
-}
-
-INSTANTIATE_TEST_SUITE_P(OpcodeTableGet, GetTargetOpcodeTableGetTest,
-                         ValuesIn(spvtest::AllTargetEnvironments()));
-
-}  // namespace
-}  // namespace spvtools
diff --git a/test/operand_capabilities_test.cpp b/test/operand_capabilities_test.cpp
index 53dbe07..cecde9e 100644
--- a/test/operand_capabilities_test.cpp
+++ b/test/operand_capabilities_test.cpp
@@ -23,6 +23,7 @@
 #include "source/operand.h"
 #include "source/spirv_target_env.h"
 #include "source/table.h"
+#include "source/table2.h"
 #include "spirv-tools/libspirv.h"
 #include "test/unit_spirv.h"
 
@@ -80,13 +81,13 @@
   const auto env = std::get<0>(GetParam());
   const auto context = spvContextCreate(env);
   const AssemblyGrammar grammar(context);
-  spv_operand_desc entry;
+  const spvtools::OperandDesc* entry = nullptr;
 
   ASSERT_EQ(SPV_SUCCESS,
-            grammar.lookupOperand(std::get<1>(GetParam()).type,
-                                  std::get<1>(GetParam()).value, &entry));
-  const auto cap_set = grammar.filterCapsAgainstTargetEnv(
-      entry->capabilities, entry->numCapabilities);
+            spvtools::LookupOperand(std::get<1>(GetParam()).type,
+                                    std::get<1>(GetParam()).value, &entry));
+  const auto cap_set =
+      grammar.filterCapsAgainstTargetEnv(entry->capabilities());
 
   EXPECT_THAT(ElementsIn(cap_set),
               Eq(ElementsIn(std::get<1>(GetParam()).expected_capabilities)))
diff --git a/test/operand_lookup_test.cpp b/test/operand_lookup_test.cpp
new file mode 100644
index 0000000..8fbf7bf
--- /dev/null
+++ b/test/operand_lookup_test.cpp
@@ -0,0 +1,133 @@
+// Copyright 2025 Google LLC
+//
+// 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 <array>
+#include <iostream>
+
+#include "gmock/gmock.h"
+#include "source/operand.h"
+#include "source/table2.h"
+#include "spirv-tools/libspirv.h"
+#include "test/unit_spirv.h"
+
+using ::testing::ContainerEq;
+using ::testing::ValuesIn;
+
+namespace spvtools {
+namespace {
+
+struct OperandLookupCase {
+  spv_operand_type_t type;
+  std::string name;
+  size_t name_length;
+  uint32_t value;
+  bool expect_pass = true;
+};
+
+std::ostream& operator<<(std::ostream& os, const OperandLookupCase& olc) {
+  os << "OLC('" << spvOperandTypeStr(olc.type) << " '" << olc.name
+     << "', len:" << olc.name_length << ", value:" << olc.value
+     << ", expect pass? " << olc.expect_pass << ")";
+  return os;
+}
+
+using OperandLookupTest = ::testing::TestWithParam<OperandLookupCase>;
+
+TEST_P(OperandLookupTest, OperandLookup_ByName) {
+  const OperandDesc* desc = nullptr;
+  auto status = LookupOperand(GetParam().type, GetParam().name.data(),
+                              GetParam().name_length, &desc);
+  if (GetParam().expect_pass) {
+    EXPECT_EQ(status, SPV_SUCCESS);
+    ASSERT_NE(desc, nullptr);
+    EXPECT_EQ(desc->value, GetParam().value);
+  } else {
+    EXPECT_NE(status, SPV_SUCCESS);
+    EXPECT_EQ(desc, nullptr);
+  }
+}
+
+TEST_P(OperandLookupTest, OperandLookup_ByValue_Success) {
+  const OperandDesc* desc = nullptr;
+  if (GetParam().expect_pass) {
+    const auto value = GetParam().value;
+    auto status = LookupOperand(GetParam().type, GetParam().value, &desc);
+    EXPECT_EQ(status, SPV_SUCCESS);
+    ASSERT_NE(desc, nullptr);
+    EXPECT_EQ(desc->value, value);
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    Samples, OperandLookupTest,
+    ValuesIn(std::vector<OperandLookupCase>{
+        {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, "Relaxed", 7, 0},
+        // "None" is an alias for "Relaxed"
+        {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, "None", 4, 0},
+        // "NonPrivatePointer" is the canonical name that appeared
+        // in an extension and incorporated in SPIR-V 1.5.
+        {SPV_OPERAND_TYPE_MEMORY_ACCESS, "NonPrivatePointer", 17, 32},
+        // "NonPrivatePointerKHR" is the name from the extension.
+        {SPV_OPERAND_TYPE_MEMORY_ACCESS, "NonPrivatePointerKHR", 20, 32},
+        // "NoAliasINTELMask" is only in an extension
+        {SPV_OPERAND_TYPE_MEMORY_ACCESS, "NoAliasINTELMask", 16, 0x20000},
+        {SPV_OPERAND_TYPE_RAY_FLAGS, "TerminateOnFirstHitKHR", 22, 4},
+        {SPV_OPERAND_TYPE_FPENCODING, "BFloat16KHR", 11, 0},
+        // Lookup on An optional operand type should match the base lookup.
+        {SPV_OPERAND_TYPE_OPTIONAL_FPENCODING, "BFloat16KHR", 11, 0},
+        // Lookup is type-specific.
+        {SPV_OPERAND_TYPE_OPTIONAL_FPENCODING, "Relaxed", 7, 0, false},
+        // Invalid string
+        {SPV_OPERAND_TYPE_RAY_FLAGS, "does_not_exist", 14, 0, false},
+        // Check lengths
+        {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, "Relaxed", 6, 0, false},
+        {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, "Relaxed", 7, 0, true},
+        {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, "Relaxed|", 7, 0, true},
+        {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, "Relaxed|", 8, 0, false},
+    }));
+
+TEST(OperandLookupSingleTest, OperandLookup_ByValue_Fails) {
+  // This list may need adjusting over time.
+  std::array<spv_operand_type_t, 3> types = {
+      {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, SPV_OPERAND_TYPE_RAY_FLAGS,
+       SPV_OPERAND_TYPE_FPENCODING}};
+  std::array<uint32_t, 3> bad_values = {{99999, 37737, 110101}};
+  for (auto type : types) {
+    for (auto bad_value : bad_values) {
+      const OperandDesc* desc = nullptr;
+      auto status = LookupOperand(type, bad_value, &desc);
+      EXPECT_NE(status, SPV_SUCCESS);
+      ASSERT_EQ(desc, nullptr);
+    }
+  }
+}
+
+TEST(OperandLookupOperands, Sample) {
+  // Check the operand list for a valid operand lookup.
+  const OperandDesc* desc = nullptr;
+  auto status = LookupOperand(SPV_OPERAND_TYPE_IMAGE, "Grad", 4, &desc);
+  EXPECT_EQ(status, SPV_SUCCESS);
+  ASSERT_NE(desc, nullptr);
+
+  EXPECT_EQ(desc->operands_range.count(), 2u);
+
+  auto operands = desc->operands();
+  using vtype = std::vector<spv_operand_type_t>;
+
+  EXPECT_THAT(vtype(operands.begin(), operands.end()),
+              ContainerEq(vtype{SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}));
+}
+
+}  // namespace
+}  // namespace spvtools
diff --git a/test/operand_pattern_test.cpp b/test/operand_pattern_test.cpp
index 58b8a08..2138c3f 100644
--- a/test/operand_pattern_test.cpp
+++ b/test/operand_pattern_test.cpp
@@ -69,13 +69,8 @@
 using MaskExpansionTest = ::testing::TestWithParam<MaskExpansionCase>;
 
 TEST_P(MaskExpansionTest, Sample) {
-  spv_operand_table operandTable = nullptr;
-  auto env = SPV_ENV_UNIVERSAL_1_0;
-  ASSERT_EQ(SPV_SUCCESS, spvOperandTableGet(&operandTable, env));
-
   spv_operand_pattern_t pattern(GetParam().initial);
-  spvPushOperandTypesForMask(env, operandTable, GetParam().type,
-                             GetParam().mask, &pattern);
+  spvPushOperandTypesForMask(GetParam().type, GetParam().mask, &pattern);
   EXPECT_THAT(pattern, Eq(GetParam().expected));
 }
 
diff --git a/test/operand_test.cpp b/test/operand_test.cpp
index ec45da5..4cc0818 100644
--- a/test/operand_test.cpp
+++ b/test/operand_test.cpp
@@ -19,25 +19,8 @@
 namespace spvtools {
 namespace {
 
-using GetTargetTest = ::testing::TestWithParam<spv_target_env>;
 using ::testing::ValuesIn;
 
-TEST_P(GetTargetTest, Default) {
-  spv_operand_table table;
-  ASSERT_EQ(SPV_SUCCESS, spvOperandTableGet(&table, GetParam()));
-  ASSERT_NE(0u, table->count);
-  ASSERT_NE(nullptr, table->types);
-}
-
-TEST_P(GetTargetTest, InvalidPointerTable) {
-  ASSERT_EQ(SPV_ERROR_INVALID_POINTER, spvOperandTableGet(nullptr, GetParam()));
-}
-
-INSTANTIATE_TEST_SUITE_P(OperandTableGet, GetTargetTest,
-                         ValuesIn(std::vector<spv_target_env>{
-                             SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
-                             SPV_ENV_VULKAN_1_0}));
-
 TEST(OperandString, AllAreDefinedExceptVariable) {
   // None has no string, so don't test it.
   EXPECT_EQ(0u, SPV_OPERAND_TYPE_NONE);
diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt
index 4ef119d..e2fdccf 100644
--- a/test/opt/CMakeLists.txt
+++ b/test/opt/CMakeLists.txt
@@ -70,6 +70,7 @@
        ir_loader_test.cpp
        iterator_test.cpp
        line_debug_info_test.cpp
+       legalize_multidim_array_test.cpp
        local_access_chain_convert_test.cpp
        local_redundancy_elimination_test.cpp
        local_single_block_elim.cpp
@@ -88,16 +89,19 @@
        propagator_test.cpp
        reduce_load_size_test.cpp
        redundancy_elimination_test.cpp
+       canonicalize_ids_test.cpp
        remove_dontinline_test.cpp
        remove_unused_interface_variables_test.cpp
        register_liveness.cpp
        relax_float_ops_test.cpp
        replace_desc_array_access_using_var_index_test.cpp
        replace_invalid_opc_test.cpp
+       resolve_binding_conflicts_pass_test.cpp
        scalar_analysis.cpp
        scalar_replacement_test.cpp
        set_spec_const_default_value_test.cpp
        simplification_test.cpp
+       split_combined_image_sampler_pass_test.cpp
        spread_volatile_semantics_test.cpp
        strength_reduction_test.cpp
        strip_debug_info_test.cpp
@@ -120,10 +124,9 @@
 )
 if (NOT "${SPIRV_SKIP_TESTS}" AND TARGET gmock_main)
   if (MSVC)
-    if (${MSVC_VERSION} LESS 1920)
-      # The VS 2017 debug build requires /bigobj on test_opt
-      # https://github.com/KhronosGroup/SPIRV-Tools/issues/5335
-      target_compile_options(test_opt PRIVATE /bigobj)
-    endif()
+    # MSVC builds (e.g., VS 2017, VS 2022) require /bigobj on test_opt to avoid
+    # exceeding the object file section limit.
+    # https://github.com/KhronosGroup/SPIRV-Tools/issues/5335
+    target_compile_options(test_opt PRIVATE /bigobj)
   endif()
 endif()
diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp
index 5b88117..2763afb 100644
--- a/test/opt/aggressive_dead_code_elim_test.cpp
+++ b/test/opt/aggressive_dead_code_elim_test.cpp
@@ -16,6 +16,7 @@
 #include <string>
 #include <vector>
 
+#include "gmock/gmock.h"
 #include "test/opt/assembly_builder.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
@@ -26,6 +27,8 @@
 
 using AggressiveDCETest = PassTest<::testing::Test>;
 
+using ::testing::HasSubstr;
+
 TEST_F(AggressiveDCETest, EliminateExtendedInst) {
   //  #version 140
   //
@@ -6754,79 +6757,78 @@
    %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
 %in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input
 %out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
-         %51 = OpExtInst %void %1 DebugInfoNone
-         %52 = OpExtInst %void %1 DebugExpression
-         %53 = OpExtInst %void %1 DebugOperation %uint_0
-         %54 = OpExtInst %void %1 DebugExpression %53
-         %55 = OpExtInst %void %1 DebugSource %7
-         %56 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %55 %uint_5
-         %59 = OpExtInst %void %1 DebugTypeBasic %9 %uint_32 %uint_3 %uint_0
-         %60 = OpExtInst %void %1 DebugTypeVector %59 %uint_4
-         %58 = OpExtInst %void %1 DebugTypeMember %10 %60 %55 %uint_12 %uint_5 %uint_0 %uint_128 %uint_3
-         %57 = OpExtInst %void %1 DebugTypeComposite %8 %uint_1 %55 %uint_10 %uint_1 %56 %8 %uint_128 %uint_3 %58
-         %63 = OpExtInst %void %1 DebugTypeVector %59 %uint_2
-         %62 = OpExtInst %void %1 DebugTypeMember %12 %63 %55 %uint_7 %uint_5 %uint_0 %uint_64 %uint_3
-         %61 = OpExtInst %void %1 DebugTypeComposite %11 %uint_1 %55 %uint_5 %uint_1 %56 %11 %uint_64 %uint_3 %62
-         %64 = OpExtInst %void %1 DebugTypeComposite %13 %uint_0 %55 %uint_0 %uint_0 %56 %14 %51 %uint_3
-         %67 = OpExtInst %void %1 DebugTypeFunction %uint_3 %57 %61
-         %68 = OpExtInst %void %1 DebugFunction %16 %67 %55 %uint_15 %uint_1 %56 %16 %uint_3 %uint_16
-         %69 = OpExtInst %void %1 DebugLexicalBlock %55 %uint_16 %uint_1 %68
-         %70 = OpExtInst %void %1 DebugLocalVariable %17 %63 %55 %uint_19 %uint_12 %69 %uint_4
-         %71 = OpExtInst %void %1 DebugLocalVariable %18 %57 %55 %uint_17 %uint_15 %69 %uint_4
-         %72 = OpExtInst %void %1 DebugLocalVariable %19 %61 %55 %uint_15 %uint_29 %68 %uint_4 %uint_1
-         %73 = OpExtInst %void %1 DebugTypeComposite %20 %uint_1 %55 %uint_0 %uint_0 %56 %21 %51 %uint_3
-         %74 = OpExtInst %void %1 DebugGlobalVariable %22 %73 %55 %uint_3 %uint_14 %56 %22 %g_sAniso %uint_8
-         %75 = OpExtInst %void %1 DebugGlobalVariable %23 %64 %55 %uint_1 %uint_11 %56 %23 %g_tColor %uint_8
+         %75 = OpExtInst %void %1 DebugInfoNone
+         %76 = OpExtInst %void %1 DebugExpression
+         %77 = OpExtInst %void %1 DebugOperation %uint_0
+         %78 = OpExtInst %void %1 DebugExpression %77
+         %79 = OpExtInst %void %1 DebugSource %7
+         %80 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %79 %uint_5
+         %81 = OpExtInst %void %1 DebugTypeBasic %9 %uint_32 %uint_3 %uint_0
+         %82 = OpExtInst %void %1 DebugTypeVector %81 %uint_4
+         %83 = OpExtInst %void %1 DebugTypeMember %10 %82 %79 %uint_12 %uint_5 %uint_0 %uint_128 %uint_3
+         %84 = OpExtInst %void %1 DebugTypeComposite %8 %uint_1 %79 %uint_10 %uint_1 %80 %8 %uint_128 %uint_3 %83
+         %85 = OpExtInst %void %1 DebugTypeVector %81 %uint_2
+         %86 = OpExtInst %void %1 DebugTypeMember %12 %85 %79 %uint_7 %uint_5 %uint_0 %uint_64 %uint_3
+         %87 = OpExtInst %void %1 DebugTypeComposite %11 %uint_1 %79 %uint_5 %uint_1 %80 %11 %uint_64 %uint_3 %86
+         %88 = OpExtInst %void %1 DebugTypeComposite %13 %uint_0 %79 %uint_0 %uint_0 %80 %14 %75 %uint_3
+         %89 = OpExtInst %void %1 DebugTypeFunction %uint_3 %84 %87
+         %90 = OpExtInst %void %1 DebugFunction %16 %89 %79 %uint_15 %uint_1 %80 %16 %uint_3 %uint_16
+         %91 = OpExtInst %void %1 DebugLexicalBlock %79 %uint_16 %uint_1 %90
+         %92 = OpExtInst %void %1 DebugLocalVariable %17 %85 %79 %uint_19 %uint_12 %91 %uint_4
+         %93 = OpExtInst %void %1 DebugLocalVariable %18 %84 %79 %uint_17 %uint_15 %91 %uint_4
+         %94 = OpExtInst %void %1 DebugLocalVariable %19 %87 %79 %uint_15 %uint_29 %90 %uint_4 %uint_1
+         %95 = OpExtInst %void %1 DebugTypeComposite %20 %uint_1 %79 %uint_0 %uint_0 %80 %21 %75 %uint_3
+         %96 = OpExtInst %void %1 DebugGlobalVariable %22 %95 %79 %uint_3 %uint_14 %80 %22 %g_sAniso %uint_8
+         %97 = OpExtInst %void %1 DebugGlobalVariable %23 %88 %79 %uint_1 %uint_11 %80 %23 %g_tColor %uint_8
      %MainPs = OpFunction %void None %45
-         %76 = OpLabel
-         %78 = OpVariable %_ptr_Function_PS_OUTPUT Function
-         %79 = OpVariable %_ptr_Function_v2float Function
-         %81 = OpVariable %_ptr_Function_PS_OUTPUT Function
+         %98 = OpLabel
+         %99 = OpVariable %_ptr_Function_PS_OUTPUT Function
+        %100 = OpVariable %_ptr_Function_v2float Function
+        %101 = OpVariable %_ptr_Function_PS_OUTPUT Function
 %param_var_i = OpVariable %_ptr_Function_PS_INPUT Function
-         %82 = OpLoad %v2float %in_var_TEXCOORD2
-         %83 = OpCompositeConstruct %PS_INPUT %82
-               OpStore %param_var_i %83
-        %112 = OpExtInst %void %1 DebugFunctionDefinition %68 %MainPs
-        %109 = OpExtInst %void %1 DebugScope %68
-         %85 = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52
-        %110 = OpExtInst %void %1 DebugScope %69
-         %87 = OpExtInst %void %1 DebugDeclare %71 %78 %52
-;CHECK: {{%\w+}} = OpExtInst %void %1 DebugFunctionDefinition %68 %MainPs
-;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %68
-;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52
-;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %69
-;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %71 %78 %52
-        %300 = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_17 %uint_30
-;CHECK: {{%\w+}} = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_17 %uint_30
-         %88 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0
-         %89 = OpLoad %v2float %88
-        %301 = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_12 %uint_35
-               OpStore %79 %89
-;CHECK-NOT:    OpStore %79 %89
-        %302 = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_12 %uint_35
-;CHECK: {{%\w+}} = OpExtInst %void %1 DebugLine %55 %uint_19 %uint_19 %uint_12 %uint_35
-        %106 = OpExtInst %void %1 DebugValue %70 %89 %52
-;CHECK: {{%\w+}} = OpExtInst %void %1 DebugValue %70 %89 %52
-        %303 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_25 %uint_32
-         %91 = OpLoad %type_2d_image %g_tColor
-        %304 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_41 %uint_48
-         %92 = OpLoad %type_sampler %g_sAniso
-        %305 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_25 %uint_53
-         %94 = OpSampledImage %type_sampled_image %91 %92
-         %95 = OpImageSampleImplicitLod %v4float %94 %89 None
-        %306 = OpExtInst %void %1 DebugLine %55 %uint_20 %uint_20 %uint_5 %uint_53
-         %96 = OpAccessChain %_ptr_Function_v4float %78 %int_0
-               OpStore %96 %95
-        %307 = OpExtInst %void %1 DebugLine %55 %uint_21 %uint_21 %uint_12 %uint_20
-         %97 = OpLoad %PS_OUTPUT %78
-        %308 = OpExtInst %void %1 DebugLine %55 %uint_21 %uint_21 %uint_5 %uint_20
-               OpStore %81 %97
-        %309 = OpExtInst %void %1 DebugNoLine
+        %102 = OpLoad %v2float %in_var_TEXCOORD2
+        %103 = OpCompositeConstruct %PS_INPUT %102
+               OpStore %param_var_i %103
+        %104 = OpExtInst %void %1 DebugFunctionDefinition %90 %MainPs
+        %142 = OpExtInst %void %1 DebugScope %90
+        %106 = OpExtInst %void %1 DebugDeclare %94 %param_var_i %76
+        %143 = OpExtInst %void %1 DebugScope %91
+        %108 = OpExtInst %void %1 DebugDeclare %93 %99 %76
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugFunctionDefinition %90 %MainPs
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %90
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %94 %param_var_i %76
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %91
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %93 %99 %76
+        %109 = OpExtInst %void %1 DebugLine %79 %uint_19 %uint_19 %uint_17 %uint_30
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugLine %79 %uint_19 %uint_19 %uint_17 %uint_30
+        %110 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0
+        %111 = OpLoad %v2float %110
+        %112 = OpExtInst %void %1 DebugLine %79 %uint_20 %uint_20 %uint_25 %uint_32
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugLine %79 %uint_20 %uint_20 %uint_25 %uint_32
+               OpStore %100 %111
+;CHECK-NOT:    OpStore %100 %111
+        %114 = OpExtInst %void %1 DebugValue %92 %111 %76
+;CHECK: {{%\w+}} = OpExtInst %void %1 DebugValue %92 %111 %76
+        %115 = OpExtInst %void %1 DebugLine %79 %uint_20 %uint_20 %uint_25 %uint_32
+        %116 = OpLoad %type_2d_image %g_tColor
+        %117 = OpExtInst %void %1 DebugLine %79 %uint_20 %uint_20 %uint_41 %uint_48
+        %118 = OpLoad %type_sampler %g_sAniso
+        %119 = OpExtInst %void %1 DebugLine %79 %uint_20 %uint_20 %uint_25 %uint_53
+        %120 = OpSampledImage %type_sampled_image %116 %118
+        %121 = OpImageSampleImplicitLod %v4float %120 %111 None
+        %122 = OpExtInst %void %1 DebugLine %79 %uint_20 %uint_20 %uint_5 %uint_53
+        %123 = OpAccessChain %_ptr_Function_v4float %99 %int_0
+               OpStore %123 %121
+        %124 = OpExtInst %void %1 DebugLine %79 %uint_21 %uint_21 %uint_12 %uint_20
+        %125 = OpLoad %PS_OUTPUT %99
+        %126 = OpExtInst %void %1 DebugLine %79 %uint_21 %uint_21 %uint_5 %uint_20
+               OpStore %101 %125
+        %127 = OpExtInst %void %1 DebugNoLine
 ;CHECK: {{%\w+}} = OpExtInst %void %1 DebugNoLine
-        %111 = OpExtInst %void %1 DebugNoScope
+        %144 = OpExtInst %void %1 DebugNoScope
 ;CHECK: {{%\w+}} = OpExtInst %void %1 DebugNoScope
-        %100 = OpCompositeExtract %v4float %97 0
-               OpStore %out_var_SV_Target0 %100
+        %129 = OpCompositeExtract %v4float %125 0
+               OpStore %out_var_SV_Target0 %129
                OpReturn
                OpFunctionEnd
 )";
@@ -7695,8 +7697,7 @@
 } ;
 
 "
-          %6 = OpString "
-PS_OUTPUT MainPs ( )
+          %6 = OpString "PS_OUTPUT MainPs ( )
 {
     PS_OUTPUT ps_output ;
     ps_output . vColor = float4( 1.0, 0.0, 0.0, 0.0 );
@@ -7999,8 +8000,10 @@
   const std::string text = R"(
                OpCapability CooperativeMatrixKHR
                OpCapability Shader
+               OpCapability VulkanMemoryModelKHR
                OpExtension "SPV_KHR_cooperative_matrix"
-               OpMemoryModel Logical GLSL450
+               OpExtension "SPV_KHR_vulkan_memory_model"
+               OpMemoryModel Logical VulkanKHR
                OpEntryPoint GLCompute %1 "main" %2
                OpExecutionMode %1 LocalSize 64 1 1
                OpSource HLSL 600
@@ -8233,6 +8236,1027 @@
   SinglePassRunAndCheck<AggressiveDCEPass>(spirv, spirv, true, false);
 }
 
+TEST_F(AggressiveDCETest, NoEliminateOpSource) {
+  // Should not eliminate OpSource
+
+  const std::string text =
+      R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET
+OpExecutionMode %main OriginUpperLeft
+%4 = OpString "D:\\directxshadercompiler\\tools\\clang\\test\\CodeGenSPIRV\\spirv.debug.opsource.include.hlsl"
+%5 = OpString "D:\\directxshadercompiler\\tools\\clang\\test\\CodeGenSPIRV/spirv.debug.opsource.include-file.hlsli"
+OpSource HLSL 600 %4 "// RUN: %dxc -T ps_6_0 -E main -Zi %s -spirv | FileCheck %s
+#include \"spirv.debug.opsource.include-file.hlsli\"
+
+struct ColorType
+{
+    float4 position : SV_POSITION;
+    float4 color : COLOR;
+};
+
+float4 main(UBER_TYPE(Color) input) : SV_TARGET
+{
+    return input.color;
+}
+"
+OpSource HLSL 600 %5 "#define UBER_TYPE(x) x ## Type
+"
+OpName %in_var_COLOR "in.var.COLOR"
+OpName %out_var_SV_TARGET "out.var.SV_TARGET"
+OpName %main "main"
+OpDecorate %in_var_COLOR Location 0
+OpDecorate %out_var_SV_TARGET Location 0
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%void = OpTypeVoid
+%11 = OpTypeFunction %void
+%in_var_COLOR = OpVariable %_ptr_Input_v4float Input
+%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
+OpLine %4 22 1
+%main = OpFunction %void None %11
+OpNoLine
+%12 = OpLabel
+OpLine %4 22 1
+%13 = OpLoad %v4float %in_var_COLOR
+OpStore %out_var_SV_TARGET %13
+OpLine %4 25 1
+OpReturn
+OpFunctionEnd
+)";
+
+  auto result = SinglePassRunAndDisassemble<AggressiveDCEPass>(
+      text, /* skip_nop = */ true, /* skip_validation = */ false);
+
+  EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
+  const std::string& output = std::get<0>(result);
+  EXPECT_THAT(
+      output,
+      HasSubstr("OpSource HLSL 600 %5 \"#define UBER_TYPE(x) x ## Type"));
+}
+
+TEST_F(AggressiveDCETest, EliminateCopyLogical) {
+  const std::string before = R"(
+; CHECK: [[float32:%\w+]] = OpTypeFloat 32
+; CHECK: [[v4float:%\w+]] = OpTypeVector [[float32]] 4
+; CHECK-NOT: %10 = OpTypeArray [[v4float]] %9
+; CHECK-NOT: %11 = OpTypeStruct %10 %10
+; CHECK-NOT: %22 = OpTypePointer Uniform %16
+; CHECK-NOT: %38 = OpTypePointer Function [[v4float]]
+; CHECK-NOT: %43 = OpTypePointer Function %10
+; CHECK-NOT: %44 = OpVariable %42 Function
+; CHECK-NOT: %23 = OpAccessChain %22 %19 %21
+; CHECK-NOT: %24 = OpLoad %16 %23
+; CHECK-NOT: %25 = OpCopyLogical %11 %24
+; CHECK-NOT: %46 = OpCompositeExtract %10 %25 0
+; CHECK-NOT:       OpStore %44 %46
+      OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint Vertex %4 "main" %19 %30 %32
+      OpSource GLSL 430
+      OpName %4 "main"
+      OpDecorate %14 ArrayStride 16
+      OpDecorate %15 ArrayStride 16
+      OpMemberDecorate %16 0 Offset 0
+      OpMemberDecorate %16 1 Offset 32
+      OpDecorate %17 Block
+      OpMemberDecorate %17 0 Offset 0
+      OpDecorate %19 Binding 0
+      OpDecorate %19 DescriptorSet 0
+      OpDecorate %28 Block
+      OpMemberDecorate %28 0 BuiltIn Position
+      OpMemberDecorate %28 1 BuiltIn PointSize
+      OpMemberDecorate %28 2 BuiltIn ClipDistance
+      OpDecorate %32 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypeInt 32 0
+ %9 = OpConstant %8 2
+%10 = OpTypeArray %7 %9
+%11 = OpTypeStruct %10 %10
+%14 = OpTypeArray %7 %9
+%15 = OpTypeArray %7 %9
+%16 = OpTypeStruct %14 %15
+%17 = OpTypeStruct %16
+%18 = OpTypePointer Uniform %17
+%19 = OpVariable %18 Uniform
+%20 = OpTypeInt 32 1
+%21 = OpConstant %20 0
+%22 = OpTypePointer Uniform %16
+%26 = OpConstant %8 1
+%27 = OpTypeArray %6 %26
+%28 = OpTypeStruct %7 %6 %27
+%29 = OpTypePointer Output %28
+%30 = OpVariable %29 Output
+%31 = OpTypePointer Input %7
+%32 = OpVariable %31 Input
+%33 = OpConstant %8 0
+%34 = OpTypePointer Input %6
+%38 = OpTypePointer Function %7
+%41 = OpTypePointer Output %7
+%43 = OpTypePointer Function %10
+%48 = OpTypePointer Uniform %14
+%49 = OpTypePointer Uniform %7
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+%44 = OpVariable %43 Function
+%23 = OpAccessChain %22 %19 %21
+%24 = OpLoad %16 %23
+%25 = OpCopyLogical %11 %24
+%46 = OpCompositeExtract %10 %25 0
+%50 = OpAccessChain %48 %19 %21 %33
+      OpStore %44 %46
+%35 = OpAccessChain %34 %32 %33
+%36 = OpLoad %6 %35
+%37 = OpConvertFToS %20 %36
+%47 = OpAccessChain %49 %50 %37
+%40 = OpLoad %7 %47
+%42 = OpAccessChain %41 %30 %21
+      OpStore %42 %40
+      OpReturn
+      OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_6);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  SinglePassRunAndMatch<AggressiveDCEPass>(before, true);
+}
+
+TEST_F(AggressiveDCETest, KeepCopyLogical) {
+  const std::string before = R"(
+; CHECK: OpCopyLogical
+      OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint GLCompute %4 "main" %15 %23 %38
+      OpExecutionMode %4 LocalSize 32 32 1
+      OpSource GLSL 430
+      OpName %4 "main"
+      OpDecorate %10 ArrayStride 16
+      OpDecorate %11 ArrayStride 16
+      OpMemberDecorate %12 0 Offset 0
+      OpMemberDecorate %12 1 Offset 2048
+      OpDecorate %13 Block
+      OpMemberDecorate %13 0 NonReadable
+      OpMemberDecorate %13 0 Offset 0
+      OpDecorate %15 NonReadable
+      OpDecorate %15 Binding 1
+      OpDecorate %15 DescriptorSet 0
+      OpDecorate %18 ArrayStride 16
+      OpDecorate %19 ArrayStride 16
+      OpMemberDecorate %20 0 Offset 0
+      OpMemberDecorate %20 1 Offset 2048
+      OpDecorate %21 Block
+      OpMemberDecorate %21 0 NonWritable
+      OpMemberDecorate %21 0 Offset 0
+      OpDecorate %23 NonWritable
+      OpDecorate %23 Binding 0
+      OpDecorate %23 DescriptorSet 0
+      OpDecorate %30 ArrayStride 16
+      OpDecorate %31 ArrayStride 16
+      OpMemberDecorate %32 0 Offset 0
+      OpMemberDecorate %32 1 Offset 2048
+      OpDecorate %34 ArrayStride 4096
+      OpMemberDecorate %35 0 Offset 0
+      OpDecorate %36 Block
+      OpMemberDecorate %36 0 Offset 0
+      OpDecorate %38 Binding 0
+      OpDecorate %38 DescriptorSet 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypeInt 32 0
+ %9 = OpConstant %8 128
+%10 = OpTypeArray %7 %9
+%11 = OpTypeArray %7 %9
+%12 = OpTypeStruct %10 %11
+%13 = OpTypeStruct %12
+%14 = OpTypePointer StorageBuffer %13
+%15 = OpVariable %14 StorageBuffer
+%16 = OpTypeInt 32 1
+%17 = OpConstant %16 0
+%18 = OpTypeArray %7 %9
+%19 = OpTypeArray %7 %9
+%20 = OpTypeStruct %18 %19
+%21 = OpTypeStruct %20
+%22 = OpTypePointer StorageBuffer %21
+%23 = OpVariable %22 StorageBuffer
+%24 = OpTypePointer StorageBuffer %20
+%27 = OpTypePointer StorageBuffer %12
+%30 = OpTypeArray %7 %9
+%31 = OpTypeArray %7 %9
+%32 = OpTypeStruct %30 %31
+%33 = OpConstant %8 8
+%34 = OpTypeArray %32 %33
+%35 = OpTypeStruct %34
+%36 = OpTypeStruct %35
+%37 = OpTypePointer Uniform %36
+%38 = OpVariable %37 Uniform
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+%25 = OpAccessChain %24 %23 %17
+%26 = OpLoad %20 %25
+%28 = OpAccessChain %27 %15 %17
+%29 = OpCopyLogical %12 %26
+      OpStore %28 %29
+      OpReturn
+      OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_6);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  SinglePassRunAndMatch<AggressiveDCEPass>(before, true);
+}
+TEST_F(AggressiveDCETest, KeepOnlyLiveDebugValues) {
+  // DebugValue should replace dead Value with Undef.
+  const std::string before =
+      R"(OpCapability MinLod
+OpCapability StorageImageWriteWithoutFormat
+OpCapability StorageImageReadWithoutFormat
+OpCapability FragmentShaderSampleInterlockEXT
+OpCapability FragmentShaderPixelInterlockEXT
+OpCapability FragmentShaderShadingRateInterlockEXT
+OpCapability ComputeDerivativeGroupQuadsKHR
+OpCapability ComputeDerivativeGroupLinearKHR
+OpCapability RayQueryKHR
+OpCapability GroupNonUniformPartitionedNV
+OpCapability InterpolationFunction
+OpCapability QuadControlKHR
+OpCapability Shader
+OpCapability SampledBuffer
+OpCapability ImageBuffer
+OpExtension "SPV_EXT_fragment_shader_interlock"
+OpExtension "SPV_KHR_compute_shader_derivatives"
+OpExtension "SPV_KHR_ray_query"
+OpExtension "SPV_NV_shader_subgroup_partitioned"
+OpExtension "SPV_KHR_quad_control"
+OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%3 = OpString "partially_unused_var.cs.hlsl"
+%17 = OpString "int"
+%25 = OpString "main"
+%26 = OpString ""
+%30 = OpString "a"
+%33 = OpString "__dxc_setup"
+%35 = OpString "cb000c74"
+%36 = OpString " -E main -T cs_6_6 -spirv -fspv-print-all -fspv-debug=vulkan -Qembed_debug"
+%39 = OpString "@type.buffer.image"
+%40 = OpString "type.buffer.image"
+%42 = OpString "TemplateParam"
+%45 = OpString "b"
+OpName %type_buffer_image "type.buffer.image"
+OpName %b "b"
+OpName %main "main"
+OpDecorate %b DescriptorSet 0
+OpDecorate %b Binding 0
+%uint = OpTypeInt 32 0
+%uint_1 = OpConstant %uint 1
+%uint_2 = OpConstant %uint 2
+%uint_3 = OpConstant %uint 3
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%uint_0 = OpConstant %uint 0
+%uint_32 = OpConstant %uint 32
+%type_buffer_image = OpTypeImage %int Buffer 2 0 0 2 R32i
+%_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image
+%void = OpTypeVoid
+%uint_4 = OpConstant %uint 4
+%uint_5 = OpConstant %uint 5
+%uint_13 = OpConstant %uint 13
+%uint_7 = OpConstant %uint 7
+%uint_15 = OpConstant %uint 15
+%uint_8 = OpConstant %uint 8
+%49 = OpTypeFunction %void
+%_arr_int_uint_3 = OpTypeArray %int %uint_3
+%_ptr_Function__arr_int_uint_3 = OpTypePointer Function %_arr_int_uint_3
+%v4int = OpTypeVector %int 4
+%uint_21 = OpConstant %uint 21
+%uint_27 = OpConstant %uint 27
+%uint_14 = OpConstant %uint 14
+%uint_31 = OpConstant %uint 31
+%_ptr_Function_int = OpTypePointer Function %int
+%uint_6 = OpConstant %uint 6
+%uint_10 = OpConstant %uint 10
+%b = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+; CHECK: %281 = OpUndef %int
+%38 = OpExtInst %void %1 DebugInfoNone
+%16 = OpExtInst %void %1 DebugExpression
+; CHECK: %216 = OpExtInst %void %1 DebugOperation %uint_0
+%216 = OpExtInst %void %1 DebugOperation %uint_0
+%18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 %uint_4 %uint_0
+%20 = OpExtInst %void %1 DebugTypeArray %18 %uint_3
+%21 = OpExtInst %void %1 DebugTypeFunction %uint_3 %void
+%22 = OpExtInst %void %1 DebugSource %3
+%23 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %22 %uint_5
+%27 = OpExtInst %void %1 DebugFunction %25 %21 %22 %uint_4 %uint_1 %23 %26 %uint_3 %uint_4
+%28 = OpExtInst %void %1 DebugLexicalBlock %22 %uint_4 %uint_13 %27
+%31 = OpExtInst %void %1 DebugLocalVariable %30 %20 %22 %uint_5 %uint_7 %28 %uint_4
+%34 = OpExtInst %void %1 DebugFunction %33 %21 %22 %uint_4 %uint_1 %23 %26 %uint_3 %uint_4
+%41 = OpExtInst %void %1 DebugTypeComposite %39 %uint_0 %22 %uint_0 %uint_0 %23 %40 %38 %uint_3
+%43 = OpExtInst %void %1 DebugTypeTemplateParameter %42 %18 %38 %22 %uint_0 %uint_0
+%44 = OpExtInst %void %1 DebugTypeTemplate %41 %43
+%46 = OpExtInst %void %1 DebugGlobalVariable %45 %44 %22 %uint_1 %uint_15 %23 %45 %b %uint_8
+%37 = OpExtInst %void %1 DebugEntryPoint %34 %23 %35 %36
+%138 = OpExtInst %void %1 DebugInlinedAt %uint_4 %34
+; CHECK: %215 = OpExtInst %void %1 DebugExpression %216
+%215 = OpExtInst %void %1 DebugExpression %216
+%main = OpFunction %void None %49
+%50 = OpLabel
+%205 = OpVariable %_ptr_Function_int Function
+%204 = OpVariable %_ptr_Function_int Function
+%203 = OpVariable %_ptr_Function_int Function
+%255 = OpExtInst %void %1 DebugScope %34
+%52 = OpExtInst %void %1 DebugFunctionDefinition %34 %main
+%256 = OpExtInst %void %1 DebugScope %28 %138
+%155 = OpExtInst %void %1 DebugLine %22 %uint_5 %uint_5 %uint_15 %uint_15
+%141 = OpLoad %type_buffer_image %b
+%142 = OpImageRead %v4int %141 %uint_1 None
+%143 = OpCompositeExtract %int %142 0
+%158 = OpExtInst %void %1 DebugLine %22 %uint_5 %uint_5 %uint_21 %uint_21
+%144 = OpLoad %type_buffer_image %b
+%145 = OpImageRead %v4int %144 %uint_2 None
+%146 = OpCompositeExtract %int %145 0
+%161 = OpExtInst %void %1 DebugLine %22 %uint_5 %uint_5 %uint_27 %uint_27
+%147 = OpLoad %type_buffer_image %b
+%148 = OpImageRead %v4int %147 %uint_3 None
+%149 = OpCompositeExtract %int %148 0
+%164 = OpExtInst %void %1 DebugLine %22 %uint_5 %uint_5 %uint_14 %uint_31
+%150 = OpCompositeConstruct %_arr_int_uint_3 %143 %146 %149
+%207 = OpExtInst %void %1 DebugLine %22 %uint_5 %uint_5 %uint_3 %uint_31
+%206 = OpCompositeExtract %int %150 0
+; CHECK-NOT: OpStore %203 %206
+OpStore %203 %206
+; CHECK: %253 = OpExtInst %void %1 DebugValue %31 %206 %16 %int_0
+%253 = OpExtInst %void %1 DebugValue %31 %206 %16 %int_0
+; CHECK-NOT: %209 = OpCompositeExtract %int %150 1
+%209 = OpCompositeExtract %int %150 1
+; CHECK-NOT: OpStore %204 %209
+OpStore %204 %209
+; CHECK: %250 = OpExtInst %void %1 DebugValue %31 %281 %16 %int_1
+%250 = OpExtInst %void %1 DebugValue %31 %209 %16 %int_1
+; CHECK-NOT: %212 = OpCompositeExtract %int %150 2
+%212 = OpCompositeExtract %int %150 2
+; CHECK-NOT: OpStore %205 %212
+OpStore %205 %212
+; CHECK: %247 = OpExtInst %void %1 DebugValue %31 %281 %16 %int_2
+%247 = OpExtInst %void %1 DebugValue %31 %212 %16 %int_2
+%169 = OpExtInst %void %1 DebugLine %22 %uint_6 %uint_6 %uint_3 %uint_13
+%154 = OpLoad %type_buffer_image %b
+OpImageWrite %154 %uint_0 %206 None
+%257 = OpExtInst %void %1 DebugScope %34
+%55 = OpExtInst %void %1 DebugLine %22 %uint_7 %uint_7 %uint_1 %uint_1
+OpReturn
+%258 = OpExtInst %void %1 DebugNoScope
+OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_6);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+                        SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+  SinglePassRunAndMatch<AggressiveDCEPass>(before, false);
+}
+TEST_F(AggressiveDCETest, KeepLocalDebugValue) {
+  // DebugValue should only be live when Value is live.
+  const std::string before =
+      R"(
+OpCapability MinLod
+OpCapability StorageImageWriteWithoutFormat
+OpCapability StorageImageReadWithoutFormat
+OpCapability FragmentShaderSampleInterlockEXT
+OpCapability FragmentShaderPixelInterlockEXT
+OpCapability FragmentShaderShadingRateInterlockEXT
+OpCapability ComputeDerivativeGroupQuadsKHR
+OpCapability ComputeDerivativeGroupLinearKHR
+OpCapability RayQueryKHR
+OpCapability GroupNonUniformPartitionedNV
+OpCapability InterpolationFunction
+OpCapability QuadControlKHR
+OpCapability Shader
+OpCapability SampledBuffer
+OpCapability ImageBuffer
+OpExtension "SPV_EXT_fragment_shader_interlock"
+OpExtension "SPV_KHR_compute_shader_derivatives"
+OpExtension "SPV_KHR_ray_query"
+OpExtension "SPV_NV_shader_subgroup_partitioned"
+OpExtension "SPV_KHR_quad_control"
+OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%3 = OpString "partially_unused_var2.cs.hlsl"
+%17 = OpString "int"
+%25 = OpString "main"
+%26 = OpString ""
+; CHECK: [[VarName:%\w+]] = OpString "some_real_obvious_name"
+%30 = OpString "some_real_obvious_name"
+%33 = OpString "__dxc_setup"
+%35 = OpString "cb000c74"
+%36 = OpString " -E main -T cs_6_6 -spirv -fspv-print-all -fspv-debug=vulkan -Qembed_debug"
+%39 = OpString "@type.buffer.image"
+%40 = OpString "type.buffer.image"
+%42 = OpString "TemplateParam"
+%45 = OpString "b"
+OpName %type_buffer_image "type.buffer.image"
+OpName %b "b"
+OpName %main "main"
+OpDecorate %b DescriptorSet 0
+OpDecorate %b Binding 0
+%uint = OpTypeInt 32 0
+%uint_1 = OpConstant %uint 1
+%uint_3 = OpConstant %uint 3
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%uint_0 = OpConstant %uint 0
+%uint_32 = OpConstant %uint 32
+%type_buffer_image = OpTypeImage %int Buffer 2 0 0 2 R32i
+%_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image
+%void = OpTypeVoid
+%uint_4 = OpConstant %uint 4
+%uint_5 = OpConstant %uint 5
+%uint_13 = OpConstant %uint 13
+%uint_7 = OpConstant %uint 7
+%uint_15 = OpConstant %uint 15
+%uint_8 = OpConstant %uint 8
+%49 = OpTypeFunction %void
+%uint_52 = OpConstant %uint 52
+%uint_6 = OpConstant %uint 6
+%uint_10 = OpConstant %uint 10
+%b = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+; CHECK: [[undef:%\w+]] = OpUndef %int
+%198 = OpUndef %int
+%38 = OpExtInst %void %1 DebugInfoNone
+%16 = OpExtInst %void %1 DebugExpression
+%18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 %uint_4 %uint_0
+%20 = OpExtInst %void %1 DebugTypeArray %18 %uint_3
+%21 = OpExtInst %void %1 DebugTypeFunction %uint_3 %void
+%22 = OpExtInst %void %1 DebugSource %3
+%23 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %22 %uint_5
+%27 = OpExtInst %void %1 DebugFunction %25 %21 %22 %uint_4 %uint_1 %23 %26 %uint_3 %uint_4
+%28 = OpExtInst %void %1 DebugLexicalBlock %22 %uint_4 %uint_13 %27
+%31 = OpExtInst %void %1 DebugLocalVariable %30 %20 %22 %uint_5 %uint_7 %28 %uint_4
+; CHECK: [[var:%\w+]] = OpExtInst %void {{%\w+}} DebugLocalVariable [[VarName]]
+%34 = OpExtInst %void %1 DebugFunction %33 %21 %22 %uint_4 %uint_1 %23 %26 %uint_3 %uint_4
+%41 = OpExtInst %void %1 DebugTypeComposite %39 %uint_0 %22 %uint_0 %uint_0 %23 %40 %38 %uint_3
+%43 = OpExtInst %void %1 DebugTypeTemplateParameter %42 %18 %38 %22 %uint_0 %uint_0
+%44 = OpExtInst %void %1 DebugTypeTemplate %41 %43
+%46 = OpExtInst %void %1 DebugGlobalVariable %45 %44 %22 %uint_1 %uint_15 %23 %45 %b %uint_8
+%37 = OpExtInst %void %1 DebugEntryPoint %34 %23 %35 %36
+%133 = OpExtInst %void %1 DebugInlinedAt %uint_4 %34
+%main = OpFunction %void None %49
+%50 = OpLabel
+%303 = OpExtInst %void %1 DebugScope %34
+%52 = OpExtInst %void %1 DebugFunctionDefinition %34 %main
+%304 = OpExtInst %void %1 DebugScope %28 %133
+%199 = OpExtInst %void %1 DebugLine %22 %uint_5 %uint_5 %uint_3 %uint_52
+%245 = OpExtInst %void %1 DebugValue %31 %198 %16 %int_0
+%242 = OpExtInst %void %1 DebugValue %31 %198 %16 %int_1
+%239 = OpExtInst %void %1 DebugValue %31 %198 %16 %int_2
+; CHECK: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[var]] [[undef]] {{%\w+}} %int_0
+; CHECK: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[var]] [[undef]] {{%\w+}} %int_1
+; CHECK: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[var]] [[undef]] {{%\w+}} %int_2
+%160 = OpExtInst %void %1 DebugLine %22 %uint_6 %uint_6 %uint_3 %uint_10
+%147 = OpLoad %type_buffer_image %b
+OpImageWrite %147 %uint_0 %int_0 None
+%305 = OpExtInst %void %1 DebugScope %34
+%55 = OpExtInst %void %1 DebugLine %22 %uint_7 %uint_7 %uint_1 %uint_1
+OpReturn
+%306 = OpExtInst %void %1 DebugNoScope
+OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_6);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+                        SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+  SinglePassRunAndMatch<AggressiveDCEPass>(before, false);
+}
+
+TEST_F(AggressiveDCETest, UndefIsOutsideFunction) {
+  const std::string spirv = R"(
+  ; CHECK: OpUndef
+  ; CHECK: OpFunction %void
+               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+          %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %2 "main"
+               OpExecutionMode %2 LocalSize 1 1 1
+          %3 = OpString ""
+          %4 = OpString "int"
+          %5 = OpString "x"
+          %6 = OpString "Val.set"
+          %7 = OpString "Val"
+          %8 = OpString "this"
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+    %uint_11 = OpConstant %uint 11
+     %uint_5 = OpConstant %uint 5
+   %uint_100 = OpConstant %uint 100
+         %14 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+%_ptr_Function_int = OpTypePointer Function %int
+     %uint_0 = OpConstant %uint 0
+    %uint_15 = OpConstant %uint 15
+     %uint_6 = OpConstant %uint 6
+    %uint_32 = OpConstant %uint 32
+     %uint_4 = OpConstant %uint 4
+%uint_131072 = OpConstant %uint 131072
+     %uint_1 = OpConstant %uint 1
+    %uint_10 = OpConstant %uint 10
+     %uint_8 = OpConstant %uint 8
+         %26 = OpExtInst %void %1 DebugOperation %uint_0
+         %27 = OpExtInst %void %1 DebugSource %3 %3
+         %28 = OpExtInst %void %1 DebugCompilationUnit %uint_100 %uint_5 %27 %uint_11
+         %29 = OpExtInst %void %1 DebugTypeBasic %4 %uint_32 %uint_4 %uint_131072
+         %30 = OpExtInst %void %1 DebugTypeFunction %uint_0 %void
+         %31 = OpExtInst %void %1 DebugFunction %6 %30 %27 %uint_6 %uint_10 %28 %6 %uint_0 %uint_6
+         %32 = OpExtInst %void %1 DebugTypeMember %5 %29 %27 %uint_1 %uint_8 %uint_0 %uint_32 %uint_0
+         %33 = OpExtInst %void %1 DebugTypeComposite %7 %uint_1 %27 %uint_1 %uint_8 %28 %7 %uint_32 %uint_131072 %32
+         %34 = OpExtInst %void %1 DebugLocalVariable %8 %33 %27 %uint_6 %uint_10 %31 %uint_0 %uint_1
+         %35 = OpExtInst %void %1 DebugExpression %26
+          %2 = OpFunction %void None %14
+         %36 = OpLabel
+         %37 = OpVariable %_ptr_Function_int Function
+         %38 = OpExtInst %void %1 DebugValue %34 %37 %35 %uint_0
+               OpReturn
+               OpFunctionEnd
+
+)";
+
+  SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
+}
+TEST_F(AggressiveDCETest, ConvertDebugDeclareToDebugValue) {
+  const std::string spirv =
+      R"(OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %input1 %input2 %output
+OpExecutionMode %main OriginUpperLeft
+%5 = OpString "test.hlsl"
+OpSource HLSL 600
+OpName %main "main"
+OpDecorate %input1 Location 0
+OpDecorate %input2 Location 1
+OpDecorate %output Location 0
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%v3float = OpTypeVector %float 3
+%v4float = OpTypeVector %float 4
+%uint = OpTypeInt 32 0
+%uint_1 = OpConstant %uint 1
+%uint_2 = OpConstant %uint 2
+%uint_3 = OpConstant %uint 3
+%uint_4 = OpConstant %uint 4
+%uint_32 = OpConstant %uint 32
+%uint_0 = OpConstant %uint 0
+%float_0 = OpConstant %float 0
+%float_1 = OpConstant %float 1
+%_ptr_Input_v3float = OpTypePointer Input %v3float
+%_ptr_Output_v3float = OpTypePointer Output %v3float
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%input1 = OpVariable %_ptr_Input_v3float Input
+%input2 = OpVariable %_ptr_Input_v3float Input
+%output = OpVariable %_ptr_Output_v3float Output
+%29 = OpTypeFunction %void
+; CHECK: [[initial:%\w+]] = OpConstantComposite
+; CHECK: [[expr:%\w+]] = OpExtInst %void {{%\w+}} DebugExpression
+; CHECK: [[source:%\w+]] = OpExtInst %void {{%\w+}} DebugSource %6
+%30 = OpExtInst %void %1 DebugSource %5
+%31 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_3 %30 %uint_32
+; CHECK: [[basic:%\w+]] = OpExtInst %void {{%\w+}} DebugTypeBasic
+%32 = OpExtInst %void %1 DebugTypeBasic %5 %uint_32 %uint_3 %uint_0
+; CHECK: [[vec_type:%\w+]] = OpExtInst %void {{%\w+}} DebugTypeVector [[basic]] %uint_4
+%33 = OpExtInst %void %1 DebugTypeVector %32 %uint_4
+%34 = OpExtInst %void %1 DebugTypeFunction %uint_0 %void
+%35 = OpExtInst %void %1 DebugFunction %5 %34 %30 %uint_1 %uint_0 %31 %5 %uint_0 %uint_1
+; CHECK: [[local:%\w+]] = OpExtInst %void {{%\w+}} DebugLocalVariable %6 [[vec_type]] [[source]] %uint_1
+%36 = OpExtInst %void %1 DebugLocalVariable %5 %33 %30 %uint_1 %uint_0 %35 %uint_0
+%38 = OpExtInst %void %1 DebugExpression
+%initial_value = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_1
+%main = OpFunction %void None %29
+%39 = OpLabel
+%dead_pos_w = OpVariable %_ptr_Function_v4float Function
+%live_var1 = OpVariable %_ptr_Function_v4float Function
+%live_var2 = OpVariable %_ptr_Function_v4float Function
+%input1_value = OpLoad %v3float %input1
+%input2_value = OpLoad %v3float %input2
+; CHECK-NOT: DebugDeclare
+%40 = OpExtInst %void %1 DebugDeclare %36 %dead_pos_w %38
+OpStore %dead_pos_w %initial_value
+; CHECK: DebugValue %30 [[initial]] %31
+OpStore %live_var1 %initial_value
+%computed1 = OpVectorTimesScalar %v3float %input1_value %float_1
+%computed2 = OpFAdd %v3float %computed1 %input2_value
+; CHECK: [[new:%\w+]] = OpCompositeConstruct
+%new_pos_w = OpCompositeConstruct %v4float %computed2 %float_1
+OpStore %dead_pos_w %new_pos_w
+; CHECK: DebugValue %30 [[new]] %31
+OpStore %live_var2 %new_pos_w
+%loaded1 = OpLoad %v4float %live_var1
+%loaded2 = OpLoad %v4float %live_var2
+%pos_xyz1 = OpVectorShuffle %v3float %loaded1 %loaded1 0 1 2
+%pos_xyz2 = OpVectorShuffle %v3float %loaded2 %loaded2 0 1 2
+%mixed_result = OpFAdd %v3float %pos_xyz1 %pos_xyz2
+OpStore %output %mixed_result
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
+}
+
+TEST_F(AggressiveDCETest, DebugDeclareConvertedToDebugValueKeepsDebugScope) {
+  const std::string spirv = R"(
+; CHECK: [[ext:%\w+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[inlined:%\w+]] = OpExtInst %void [[ext]] DebugInlinedAt
+; CHECK-DAG: [[UniformVar:%\w+]] = OpVariable %_ptr_Uniform_float Uniform
+; CHECK: [[scope:%\w+]] = OpExtInst %void [[ext]] DebugScope {{%\w+}} [[inlined]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue {{%\w+}} {{%\w+}} {{%\w+}}
+; CHECK-NEXT: DebugScope
+; CHECK-NEXT: DebugLine
+; CHECK-NEXT: OpStore [[UniformVar]] %float_0
+; CHECK-NEXT: OpReturn
+               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+          %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %2 "main"
+               OpExecutionMode %2 LocalSize 1 1 1
+          %3 = OpString ""
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+          %6 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+     %uint_3 = OpConstant %uint 3
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_Function_float = OpTypePointer Function %float
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+    %float_0 = OpConstant %float 0
+         %13 = OpVariable %_ptr_Uniform_float Uniform
+    %uint_57 = OpConstant %uint 57
+         %15 = OpExtInst %void %1 DebugExpression
+         %16 = OpExtInst %void %1 DebugSource %3 %3
+         %17 = OpExtInst %void %1 DebugCompilationUnit %uint_0 %uint_0 %16 %uint_0
+         %18 = OpExtInst %void %1 DebugTypeBasic %3 %uint_0 %uint_0 %uint_0
+         %19 = OpExtInst %void %1 DebugTypeVector %18 %uint_3
+         %20 = OpExtInst %void %1 DebugTypePointer %19 %uint_0 %uint_0
+         %21 = OpExtInst %void %1 DebugTypeFunction %uint_0 %void %20
+         %22 = OpExtInst %void %1 DebugFunction %3 %21 %16 %uint_0 %uint_0 %17 %3 %uint_0 %uint_0
+         %23 = OpExtInst %void %1 DebugTypeBasic %3 %uint_0 %uint_3 %uint_0
+         %24 = OpExtInst %void %1 DebugTypeMember %3 %23 %16 %uint_0 %uint_0 %uint_0 %uint_0 %uint_0
+         %25 = OpExtInst %void %1 DebugTypeComposite %3 %uint_0 %16 %uint_0 %uint_0 %17 %3 %uint_0 %uint_0 %24
+         %26 = OpExtInst %void %1 DebugTypeFunction %uint_0 %23 %25 %23
+         %27 = OpExtInst %void %1 DebugFunction %3 %26 %16 %uint_0 %uint_0 %17 %3 %uint_0 %uint_0
+         %28 = OpExtInst %void %1 DebugLocalVariable %3 %23 %16 %uint_0 %uint_0 %27 %uint_0 %uint_0
+         %29 = OpExtInst %void %1 DebugInlinedAt %uint_0 %22
+          %2 = OpFunction %void None %6
+         %30 = OpLabel
+         %31 = OpVariable %_ptr_Function_float Function
+         %32 = OpExtInst %void %1 DebugScope %27 %29
+         %33 = OpExtInst %void %1 DebugNoLine
+         %34 = OpExtInst %void %1 DebugDeclare %28 %31 %15
+               OpStore %31 %float_0
+         %35 = OpExtInst %void %1 DebugScope %22
+         %36 = OpExtInst %void %1 DebugLine %16 %uint_1 %uint_1 %uint_0 %uint_0
+               OpStore %13 %float_0
+               OpReturn
+         %37 = OpExtInst %void %1 DebugNoScope
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
+}
+
+TEST_F(AggressiveDCETest, DebugValueWithDeadOperandKeepsDebugScope) {
+  const std::string spirv = R"(
+; CHECK: [[ext:%\w+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[inlined:%\w+]] = OpExtInst %void [[ext]] DebugInlinedAt
+; CHECK-DAG: [[undef:%\w+]] = OpUndef %float
+; CHECK: [[scope:%\w+]] = OpExtInst %void [[ext]] DebugScope {{%\w+}} [[inlined]]
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue {{%\w+}} [[undef]] {{%\w+}}
+               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+          %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %2 "main"
+               OpExecutionMode %2 LocalSize 1 1 1
+          %4 = OpString ""
+          %5 = OpString "file.slang"
+          %6 = OpString "uint"
+          %7 = OpString "computeMain"
+          %8 = OpString "float"
+          %9 = OpString "x"
+         %10 = OpString "A"
+         %11 = OpString "test"
+         %12 = OpString "v"
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+    %uint_11 = OpConstant %uint 11
+     %uint_5 = OpConstant %uint 5
+   %uint_100 = OpConstant %uint 100
+         %21 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %uint_32 = OpConstant %uint 32
+     %uint_6 = OpConstant %uint 6
+%uint_131072 = OpConstant %uint 131072
+     %uint_3 = OpConstant %uint 3
+     %uint_7 = OpConstant %uint 7
+     %uint_0 = OpConstant %uint 0
+    %uint_46 = OpConstant %uint 46
+     %uint_1 = OpConstant %uint 1
+     %uint_8 = OpConstant %uint 8
+%_ptr_Function_float = OpTypePointer Function %float
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+    %float_0 = OpConstant %float 0
+    %uint_50 = OpConstant %uint 50
+    %uint_34 = OpConstant %uint 34
+     %uint_2 = OpConstant %uint 2
+    %uint_56 = OpConstant %uint 56
+%_runtimearr_float = OpTypeRuntimeArray %float
+    %uint_57 = OpConstant %uint 57
+         %42 = OpExtInst %void %1 DebugExpression
+         %43 = OpExtInst %void %1 DebugSource %5 %4
+         %44 = OpExtInst %void %1 DebugCompilationUnit %uint_100 %uint_5 %43 %uint_11
+         %45 = OpExtInst %void %1 DebugTypeBasic %6 %uint_32 %uint_6 %uint_131072
+         %46 = OpExtInst %void %1 DebugTypeVector %45 %uint_3
+         %47 = OpExtInst %void %1 DebugTypePointer %46 %uint_7 %uint_131072
+         %48 = OpExtInst %void %1 DebugTypeFunction %uint_0 %void %47
+         %49 = OpExtInst %void %1 DebugFunction %7 %48 %43 %uint_46 %uint_6 %44 %7 %uint_0 %uint_46
+         %50 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 %uint_3 %uint_131072
+         %51 = OpExtInst %void %1 DebugTypeMember %9 %50 %43 %uint_8 %uint_11 %uint_0 %uint_32 %uint_0
+         %52 = OpExtInst %void %1 DebugTypeComposite %10 %uint_1 %43 %uint_6 %uint_8 %44 %10 %uint_32 %uint_131072 %51
+         %53 = OpExtInst %void %1 DebugTypeFunction %uint_0 %50 %52 %50
+         %54 = OpExtInst %void %1 DebugFunction %11 %53 %43 %uint_34 %uint_7 %44 %11 %uint_0 %uint_34
+         %55 = OpExtInst %void %1 DebugLocalVariable %12 %50 %43 %uint_34 %uint_7 %54 %uint_0 %uint_2
+         %56 = OpExtInst %void %1 DebugInlinedAt %uint_50 %49
+          %2 = OpFunction %void None %21
+         %57 = OpLabel
+         %59 = OpExtInst %void %1 DebugScope %54 %56
+         %60 = OpExtInst %void %1 DebugNoLine
+         %val = OpFAdd %float %float_0 %float_0
+         %61 = OpExtInst %void %1 DebugValue %55 %val %42
+         %62 = OpExtInst %void %1 DebugScope %49
+         %63 = OpExtInst %void %1 DebugLine %43 %uint_56 %uint_56 %uint_5 %uint_6
+               OpReturn
+         %66 = OpExtInst %void %1 DebugNoScope
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
+}
+
+TEST_F(AggressiveDCETest, EliminateUntypedAccessChain) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability Sampled1D
+               OpCapability DescriptorHeapEXT
+               OpCapability UntypedPointersKHR
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpName %main "main"
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+%type_untyped_pointer = OpTypeUntypedPointerKHR Uniform
+       %void = OpTypeVoid
+      %float = OpTypeFloat 32
+         %10 = OpTypeFunction %void
+%type_1d_image = OpTypeImage %float 1D 2 0 0 1 Unknown
+%_ptr_Function_type_1d_image = OpTypePointer Function %type_1d_image
+%type_buffer_ext = OpTypeBufferEXT StorageBuffer
+%_runtimearr_type_buffer_ext = OpTypeRuntimeArray %type_buffer_ext
+%resource_heap = OpUntypedVariableKHR %type_untyped_pointer Uniform
+       %main = OpFunction %void None %10
+         %20 = OpLabel
+        %t1d = OpVariable %_ptr_Function_type_1d_image Function
+; CHECK-NOT: OpUntypedAccessChainKHR
+         %21 = OpUntypedAccessChainKHR %type_untyped_pointer %_runtimearr_type_buffer_ext %resource_heap %uint_0
+; CHECK-NOT: OpLoad %type_1d_image
+         %22 = OpLoad %type_1d_image %21
+; CHECK-NOT: OpStore %t1d
+               OpStore %t1d %22
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
+}
+
+TEST_F(AggressiveDCETest, NoEliminateLiveUntypedAccessChain) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability DescriptorHeapEXT
+               OpCapability UntypedPointersKHR
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %outColor
+               OpExecutionMode %main OriginUpperLeft
+               OpName %main "main"
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+%type_untyped_pointer = OpTypeUntypedPointerKHR Uniform
+       %void = OpTypeVoid
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+   %outColor = OpVariable %_ptr_Output_v4float Output
+         %10 = OpTypeFunction %void
+%type_buffer_ext = OpTypeBufferEXT StorageBuffer
+%_runtimearr_type_buffer_ext = OpTypeRuntimeArray %type_buffer_ext
+%resource_heap = OpUntypedVariableKHR %type_untyped_pointer Uniform
+       %main = OpFunction %void None %10
+         %20 = OpLabel
+; CHECK: OpUntypedAccessChainKHR
+         %21 = OpUntypedAccessChainKHR %type_untyped_pointer %_runtimearr_type_buffer_ext %resource_heap %uint_0
+; CHECK: OpLoad
+         %22 = OpLoad %v4float %21
+               OpStore %outColor %22
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
+}
+
+TEST_F(AggressiveDCETest, EliminateUntypedAccessChainWithCopyObject) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability Sampled1D
+               OpCapability DescriptorHeapEXT
+               OpCapability UntypedPointersKHR
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpName %main "main"
+               OpName %type_1d_image "type_1d_image"
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+%type_untyped_pointer = OpTypeUntypedPointerKHR Uniform
+       %void = OpTypeVoid
+      %float = OpTypeFloat 32
+         %10 = OpTypeFunction %void
+%type_1d_image = OpTypeImage %float 1D 2 0 0 1 Unknown
+%_ptr_Function_type_1d_image = OpTypePointer Function %type_1d_image
+%type_buffer_ext = OpTypeBufferEXT StorageBuffer
+%_runtimearr_type_buffer_ext = OpTypeRuntimeArray %type_buffer_ext
+%resource_heap = OpUntypedVariableKHR %type_untyped_pointer Uniform
+       %main = OpFunction %void None %10
+         %20 = OpLabel
+        %t1d = OpVariable %_ptr_Function_type_1d_image Function
+; CHECK-NOT: OpUntypedAccessChainKHR
+         %21 = OpUntypedAccessChainKHR %type_untyped_pointer %_runtimearr_type_buffer_ext %resource_heap %uint_0
+; CHECK-NOT: OpCopyObject
+         %22 = OpCopyObject %type_untyped_pointer %21
+; CHECK-NOT: OpLoad %type_1d_image
+         %23 = OpLoad %type_1d_image %22
+; CHECK-NOT: OpStore %t1d
+               OpStore %t1d %23
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
+}
+
+// For now, aggressive DCE does not optimizes this pattern. If you implement
+// it, remove this test.
+TEST_F(AggressiveDCETest, EliminateUntypedAtomic) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability Int64
+               OpCapability DescriptorHeapEXT
+               OpCapability UntypedPointersKHR
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+%type_untyped_pointer = OpTypeUntypedPointerKHR Uniform
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+%type_buffer_ext = OpTypeBufferEXT StorageBuffer
+%_runtimearr_type_buffer_ext = OpTypeRuntimeArray %type_buffer_ext
+%resource_heap = OpUntypedVariableKHR %type_untyped_pointer Uniform
+       %main = OpFunction %void None %10
+         %20 = OpLabel
+         %21 = OpUntypedAccessChainKHR %type_untyped_pointer %_runtimearr_type_buffer_ext %resource_heap %uint_0
+; CHECK: [[ptr:%\w+]] = OpUntypedAccessChainKHR
+         %22 = OpAtomicLoad %uint %21 %uint_0 %uint_0
+; CHECK:       OpAtomicLoad %uint [[ptr]]
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
+}
+
+TEST_F(AggressiveDCETest, EliminateUntypedAccessChainLoop) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability Sampled1D
+               OpCapability DescriptorHeapEXT
+               OpCapability UntypedPointersKHR
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+     %uint_10 = OpConstant %uint 10
+%type_untyped_pointer = OpTypeUntypedPointerKHR Uniform
+       %void = OpTypeVoid
+      %bool = OpTypeBool
+         %10 = OpTypeFunction %void
+%type_1d_image = OpTypeImage %uint 1D 2 0 0 1 Unknown
+%type_buffer_ext = OpTypeBufferEXT StorageBuffer
+%_runtimearr_type_buffer_ext = OpTypeRuntimeArray %type_buffer_ext
+%resource_heap = OpUntypedVariableKHR %type_untyped_pointer Uniform
+       %main = OpFunction %void None %10
+         %20 = OpLabel
+               OpBranch %header
+     %header = OpLabel
+      %count = OpPhi %uint %uint_0 %20 %next %loop
+       %cond = OpULessThan %bool %count %uint_10
+               OpLoopMerge %exit %loop None
+               OpBranchConditional %cond %loop %exit
+       %loop = OpLabel
+; CHECK-NOT: OpUntypedAccessChainKHR
+         %21 = OpUntypedAccessChainKHR %type_untyped_pointer %_runtimearr_type_buffer_ext %resource_heap %count
+; CHECK-NOT: OpLoad
+         %22 = OpLoad %type_1d_image %21
+       %next = OpIAdd %uint %count %uint_1
+               OpBranch %header
+       %exit = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
+}
+
+TEST_F(AggressiveDCETest, KeepDebugBuildIdentifier) {
+  // Regression test for https://github.com/KhronosGroup/SPIRV-Tools/issues/6619
+  //
+  // DebugBuildIdentifier was not added to the live-instruction worklist during
+  // initialization, so its operand dependencies (e.g. OpTypeInt used only by
+  // a constant that is only referenced by DebugBuildIdentifier) were never
+  // visited and were incorrectly eliminated. The surviving constant then
+  // referenced a deleted type, producing invalid SPIR-V.
+  //
+  // After the fix, DebugBuildIdentifier is enqueued in the worklist so its
+  // transitive operands (OpTypeInt 32 0, OpConstant %uint 0) are marked live.
+
+  const std::string spirv = R"(
+; CHECK: [[ext:%\w+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK: [[str:%\w+]] = OpString
+; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
+; CHECK: [[uint_0:%\w+]] = OpConstant [[uint]] 0
+; CHECK: OpExtInst %void [[ext]] DebugBuildIdentifier [[str]] [[uint_0]]
+               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+          %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+          %2 = OpString "01cfb4b77c321225f096da8ac72f29d42f0632a7"
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+          %3 = OpTypeFunction %void
+          %4 = OpExtInst %void %1 DebugBuildIdentifier %2 %uint_0
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/canonicalize_ids_test.cpp b/test/opt/canonicalize_ids_test.cpp
new file mode 100644
index 0000000..cc4b7bd
--- /dev/null
+++ b/test/opt/canonicalize_ids_test.cpp
@@ -0,0 +1,2118 @@
+// Copyright (c) 2025 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 "gmock/gmock.h"
+#include "test/opt/pass_fixture.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using CanonicalizeIdsTest = PassTest<::testing::Test>;
+
+// ported from remap.basic.everything.frag
+TEST_F(CanonicalizeIdsTest, remap_basic) {
+  const std::string before =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main" %9 %11
+OpExecutionMode %4 OriginUpperLeft
+OpSource GLSL 450
+OpName %4 "main"
+OpName %9 "outf4"
+OpName %11 "inf"
+OpDecorate %9 Location 0
+OpDecorate %11 Location 0
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%7 = OpTypeVector %6 4
+%8 = OpTypePointer Output %7
+%9 = OpVariable %8 Output
+%10 = OpTypePointer Input %6
+%11 = OpVariable %10 Input
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+%12 = OpLoad %6 %11
+%13 = OpCompositeConstruct %7 %12 %12 %12 %12
+OpStore %9 %13
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %5663 "main" %4539 %3773
+OpExecutionMode %5663 OriginUpperLeft
+OpSource GLSL 450
+OpName %5663 "main"
+OpName %4539 "outf4"
+OpName %3773 "inf"
+OpDecorate %4539 Location 0
+OpDecorate %3773 Location 0
+%8 = OpTypeVoid
+%1282 = OpTypeFunction %8
+%13 = OpTypeFloat 32
+%29 = OpTypeVector %13 4
+%666 = OpTypePointer Output %29
+%4539 = OpVariable %666 Output
+%650 = OpTypePointer Input %13
+%3773 = OpVariable %650 Input
+%5663 = OpFunction %8 None %1282
+%24968 = OpLabel
+%17486 = OpLoad %13 %3773
+%17691 = OpCompositeConstruct %29 %17486 %17486 %17486 %17486
+OpStore %4539 %17691
+OpReturn
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  SinglePassRunAndCheck<CanonicalizeIdsPass>(before, after, false, false);
+}
+
+// ported from remap.hlsl.sample.basic.everything.frag
+TEST_F(CanonicalizeIdsTest, remap_hlsl_sample_basic) {
+  const std::string before =
+      R"(OpCapability Shader
+OpCapability Sampled1D
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main" %188 %192
+OpExecutionMode %4 OriginUpperLeft
+OpExecutionMode %4 DepthReplacing
+OpSource HLSL 500
+OpName %4 "main"
+OpName %8 "PS_OUTPUT"
+OpMemberName %8 0 "Color"
+OpMemberName %8 1 "Depth"
+OpName %10 "@main("
+OpName %13 "MemberTest"
+OpMemberName %13 0 "Sample"
+OpMemberName %13 1 "CalculateLevelOfDetail"
+OpMemberName %13 2 "CalculateLevelOfDetailUnclamped"
+OpMemberName %13 3 "Gather"
+OpMemberName %13 4 "GetDimensions"
+OpMemberName %13 5 "GetSamplePosition"
+OpMemberName %13 6 "Load"
+OpMemberName %13 7 "SampleBias"
+OpMemberName %13 8 "SampleCmp"
+OpMemberName %13 9 "SampleCmpLevelZero"
+OpMemberName %13 10 "SampleGrad"
+OpMemberName %13 11 "SampleLevel"
+OpName %15 "mtest"
+OpName %54 "txval10"
+OpName %45 "g_tTex1df4"
+OpName %49 "g_sSamp"
+OpName %66 "txval11"
+OpName %60 "g_tTex1di4"
+OpName %79 "txval12"
+OpName %73 "g_tTex1du4"
+OpName %90 "txval20"
+OpName %83 "g_tTex2df4"
+OpName %101 "txval21"
+OpName %94 "g_tTex2di4"
+OpName %113 "txval22"
+OpName %105 "g_tTex2du4"
+OpName %124 "txval30"
+OpName %117 "g_tTex3df4"
+OpName %134 "txval31"
+OpName %128 "g_tTex3di4"
+OpName %147 "txval32"
+OpName %138 "g_tTex3du4"
+OpName %156 "txval40"
+OpName %151 "g_tTexcdf4"
+OpName %165 "txval41"
+OpName %160 "g_tTexcdi4"
+OpName %174 "txval42"
+OpName %169 "g_tTexcdu4"
+OpName %176 "psout"
+OpName %185 "flattenTemp"
+OpName %188 "@entryPointOutput.Color"
+OpName %192 "@entryPointOutput.Depth"
+OpDecorate %45 Binding 0
+OpDecorate %45 DescriptorSet 0
+OpDecorate %49 Binding 0
+OpDecorate %49 DescriptorSet 0
+OpDecorate %60 Binding 2
+OpDecorate %60 DescriptorSet 0
+OpDecorate %73 Binding 3
+OpDecorate %73 DescriptorSet 0
+OpDecorate %83 Binding 4
+OpDecorate %83 DescriptorSet 0
+OpDecorate %94 Binding 5
+OpDecorate %94 DescriptorSet 0
+OpDecorate %105 Binding 6
+OpDecorate %105 DescriptorSet 0
+OpDecorate %117 Binding 7
+OpDecorate %117 DescriptorSet 0
+OpDecorate %128 Binding 8
+OpDecorate %128 DescriptorSet 0
+OpDecorate %138 Binding 9
+OpDecorate %138 DescriptorSet 0
+OpDecorate %151 Binding 10
+OpDecorate %151 DescriptorSet 0
+OpDecorate %160 Binding 11
+OpDecorate %160 DescriptorSet 0
+OpDecorate %169 Binding 12
+OpDecorate %169 DescriptorSet 0
+OpDecorate %188 Location 0
+OpDecorate %192 BuiltIn FragDepth
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%7 = OpTypeVector %6 4
+%8 = OpTypeStruct %7 %6
+%9 = OpTypeFunction %8
+%12 = OpTypeInt 32 1
+%13 = OpTypeStruct %12 %12 %12 %12 %12 %12 %12 %12 %12 %12 %12 %12
+%14 = OpTypePointer Function %13
+%16 = OpConstant %12 1
+%17 = OpTypePointer Function %12
+%19 = OpConstant %12 2
+%21 = OpConstant %12 3
+%23 = OpConstant %12 4
+%25 = OpConstant %12 5
+%27 = OpConstant %12 6
+%29 = OpConstant %12 0
+%31 = OpConstant %12 7
+%33 = OpConstant %12 8
+%35 = OpConstant %12 9
+%37 = OpConstant %12 10
+%39 = OpConstant %12 11
+%41 = OpTypePointer Function %7
+%43 = OpTypeImage %6 1D 0 0 0 1 Unknown
+%44 = OpTypePointer UniformConstant %43
+%45 = OpVariable %44 UniformConstant
+%47 = OpTypeSampler
+%48 = OpTypePointer UniformConstant %47
+%49 = OpVariable %48 UniformConstant
+%51 = OpTypeSampledImage %43
+%53 = OpConstant %6 0.100000001
+%55 = OpTypeVector %12 4
+%58 = OpTypeImage %12 1D 0 0 0 1 Unknown
+%59 = OpTypePointer UniformConstant %58
+%60 = OpVariable %59 UniformConstant
+%63 = OpTypeSampledImage %58
+%65 = OpConstant %6 0.200000003
+%67 = OpTypeInt 32 0
+%68 = OpTypeVector %67 4
+%71 = OpTypeImage %67 1D 0 0 0 1 Unknown
+%72 = OpTypePointer UniformConstant %71
+%73 = OpVariable %72 UniformConstant
+%76 = OpTypeSampledImage %71
+%78 = OpConstant %6 0.300000012
+%81 = OpTypeImage %6 2D 0 0 0 1 Unknown
+%82 = OpTypePointer UniformConstant %81
+%83 = OpVariable %82 UniformConstant
+%86 = OpTypeSampledImage %81
+%88 = OpTypeVector %6 2
+%89 = OpConstantComposite %88 %53 %65
+%92 = OpTypeImage %12 2D 0 0 0 1 Unknown
+%93 = OpTypePointer UniformConstant %92
+%94 = OpVariable %93 UniformConstant
+%97 = OpTypeSampledImage %92
+%99 = OpConstant %6 0.400000006
+%100 = OpConstantComposite %88 %78 %99
+%103 = OpTypeImage %67 2D 0 0 0 1 Unknown
+%104 = OpTypePointer UniformConstant %103
+%105 = OpVariable %104 UniformConstant
+%108 = OpTypeSampledImage %103
+%110 = OpConstant %6 0.5
+%111 = OpConstant %6 0.600000024
+%112 = OpConstantComposite %88 %110 %111
+%115 = OpTypeImage %6 3D 0 0 0 1 Unknown
+%116 = OpTypePointer UniformConstant %115
+%117 = OpVariable %116 UniformConstant
+%120 = OpTypeSampledImage %115
+%122 = OpTypeVector %6 3
+%123 = OpConstantComposite %122 %53 %65 %78
+%126 = OpTypeImage %12 3D 0 0 0 1 Unknown
+%127 = OpTypePointer UniformConstant %126
+%128 = OpVariable %127 UniformConstant
+%131 = OpTypeSampledImage %126
+%133 = OpConstantComposite %122 %99 %110 %111
+%136 = OpTypeImage %67 3D 0 0 0 1 Unknown
+%137 = OpTypePointer UniformConstant %136
+%138 = OpVariable %137 UniformConstant
+%141 = OpTypeSampledImage %136
+%143 = OpConstant %6 0.699999988
+%144 = OpConstant %6 0.800000012
+%145 = OpConstant %6 0.899999976
+%146 = OpConstantComposite %122 %143 %144 %145
+%149 = OpTypeImage %6 Cube 0 0 0 1 Unknown
+%150 = OpTypePointer UniformConstant %149
+%151 = OpVariable %150 UniformConstant
+%154 = OpTypeSampledImage %149
+%158 = OpTypeImage %12 Cube 0 0 0 1 Unknown
+%159 = OpTypePointer UniformConstant %158
+%160 = OpVariable %159 UniformConstant
+%163 = OpTypeSampledImage %158
+%167 = OpTypeImage %67 Cube 0 0 0 1 Unknown
+%168 = OpTypePointer UniformConstant %167
+%169 = OpVariable %168 UniformConstant
+%172 = OpTypeSampledImage %167
+%175 = OpTypePointer Function %8
+%177 = OpConstant %6 1
+%178 = OpConstantComposite %7 %177 %177 %177 %177
+%180 = OpTypePointer Function %6
+%187 = OpTypePointer Output %7
+%188 = OpVariable %187 Output
+%191 = OpTypePointer Output %6
+%192 = OpVariable %191 Output
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+%185 = OpVariable %175 Function
+%186 = OpFunctionCall %8 %10
+OpStore %185 %186
+%189 = OpAccessChain %41 %185 %29
+%190 = OpLoad %7 %189
+OpStore %188 %190
+%193 = OpAccessChain %180 %185 %16
+%194 = OpLoad %6 %193
+OpStore %192 %194
+OpReturn
+OpFunctionEnd
+%10 = OpFunction %8 None %9
+%11 = OpLabel
+%15 = OpVariable %14 Function
+%176 = OpVariable %175 Function
+%18 = OpAccessChain %17 %15 %16
+OpStore %18 %16
+%20 = OpAccessChain %17 %15 %19
+OpStore %20 %16
+%22 = OpAccessChain %17 %15 %21
+OpStore %22 %16
+%24 = OpAccessChain %17 %15 %23
+OpStore %24 %16
+%26 = OpAccessChain %17 %15 %25
+OpStore %26 %16
+%28 = OpAccessChain %17 %15 %27
+OpStore %28 %16
+%30 = OpAccessChain %17 %15 %29
+OpStore %30 %16
+%32 = OpAccessChain %17 %15 %31
+OpStore %32 %16
+%34 = OpAccessChain %17 %15 %33
+OpStore %34 %16
+%36 = OpAccessChain %17 %15 %35
+OpStore %36 %16
+%38 = OpAccessChain %17 %15 %37
+OpStore %38 %16
+%40 = OpAccessChain %17 %15 %39
+OpStore %40 %16
+%46 = OpLoad %43 %45
+%50 = OpLoad %47 %49
+%52 = OpSampledImage %51 %46 %50
+%54 = OpImageSampleImplicitLod %7 %52 %53
+%61 = OpLoad %58 %60
+%62 = OpLoad %47 %49
+%64 = OpSampledImage %63 %61 %62
+%66 = OpImageSampleImplicitLod %55 %64 %65
+%74 = OpLoad %71 %73
+%75 = OpLoad %47 %49
+%77 = OpSampledImage %76 %74 %75
+%79 = OpImageSampleImplicitLod %68 %77 %78
+%84 = OpLoad %81 %83
+%85 = OpLoad %47 %49
+%87 = OpSampledImage %86 %84 %85
+%90 = OpImageSampleImplicitLod %7 %87 %89
+%95 = OpLoad %92 %94
+%96 = OpLoad %47 %49
+%98 = OpSampledImage %97 %95 %96
+%101 = OpImageSampleImplicitLod %55 %98 %100
+%106 = OpLoad %103 %105
+%107 = OpLoad %47 %49
+%109 = OpSampledImage %108 %106 %107
+%113 = OpImageSampleImplicitLod %68 %109 %112
+%118 = OpLoad %115 %117
+%119 = OpLoad %47 %49
+%121 = OpSampledImage %120 %118 %119
+%124 = OpImageSampleImplicitLod %7 %121 %123
+%129 = OpLoad %126 %128
+%130 = OpLoad %47 %49
+%132 = OpSampledImage %131 %129 %130
+%134 = OpImageSampleImplicitLod %55 %132 %133
+%139 = OpLoad %136 %138
+%140 = OpLoad %47 %49
+%142 = OpSampledImage %141 %139 %140
+%147 = OpImageSampleImplicitLod %68 %142 %146
+%152 = OpLoad %149 %151
+%153 = OpLoad %47 %49
+%155 = OpSampledImage %154 %152 %153
+%156 = OpImageSampleImplicitLod %7 %155 %123
+%161 = OpLoad %158 %160
+%162 = OpLoad %47 %49
+%164 = OpSampledImage %163 %161 %162
+%165 = OpImageSampleImplicitLod %55 %164 %133
+%170 = OpLoad %167 %169
+%171 = OpLoad %47 %49
+%173 = OpSampledImage %172 %170 %171
+%174 = OpImageSampleImplicitLod %68 %173 %146
+%179 = OpAccessChain %41 %176 %29
+OpStore %179 %178
+%181 = OpAccessChain %180 %176 %16
+OpStore %181 %177
+%182 = OpLoad %8 %176
+OpReturnValue %182
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(OpCapability Shader
+OpCapability Sampled1D
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %5663 "main" %4253 %3709
+OpExecutionMode %5663 OriginUpperLeft
+OpExecutionMode %5663 DepthReplacing
+OpSource HLSL 500
+OpName %5663 "main"
+OpName %1032 "PS_OUTPUT"
+OpMemberName %1032 0 "Color"
+OpMemberName %1032 1 "Depth"
+OpName %3317 "@main("
+OpName %1335 "MemberTest"
+OpMemberName %1335 0 "Sample"
+OpMemberName %1335 1 "CalculateLevelOfDetail"
+OpMemberName %1335 2 "CalculateLevelOfDetailUnclamped"
+OpMemberName %1335 3 "Gather"
+OpMemberName %1335 4 "GetDimensions"
+OpMemberName %1335 5 "GetSamplePosition"
+OpMemberName %1335 6 "Load"
+OpMemberName %1335 7 "SampleBias"
+OpMemberName %1335 8 "SampleCmp"
+OpMemberName %1335 9 "SampleCmpLevelZero"
+OpMemberName %1335 10 "SampleGrad"
+OpMemberName %1335 11 "SampleLevel"
+OpName %5830 "mtest"
+OpName %3559 "txval10"
+OpName %4727 "g_tTex1df4"
+OpName %3305 "g_sSamp"
+OpName %3560 "txval11"
+OpName %4743 "g_tTex1di4"
+OpName %3561 "txval12"
+OpName %4807 "g_tTex1du4"
+OpName %4568 "txval20"
+OpName %5042 "g_tTex2df4"
+OpName %4569 "txval21"
+OpName %5058 "g_tTex2di4"
+OpName %4570 "txval22"
+OpName %5122 "g_tTex2du4"
+OpName %5577 "txval30"
+OpName %3967 "g_tTex3df4"
+OpName %5578 "txval31"
+OpName %3983 "g_tTex3di4"
+OpName %5579 "txval32"
+OpName %4047 "g_tTex3du4"
+OpName %3575 "txval40"
+OpName %3789 "g_tTexcdf4"
+OpName %3576 "txval41"
+OpName %3805 "g_tTexcdi4"
+OpName %3577 "txval42"
+OpName %3869 "g_tTexcdu4"
+OpName %5072 "psout"
+OpName %4104 "flattenTemp"
+OpName %4253 "@entryPointOutput.Color"
+OpName %3709 "@entryPointOutput.Depth"
+OpDecorate %4727 Binding 0
+OpDecorate %4727 DescriptorSet 0
+OpDecorate %3305 Binding 0
+OpDecorate %3305 DescriptorSet 0
+OpDecorate %4743 Binding 2
+OpDecorate %4743 DescriptorSet 0
+OpDecorate %4807 Binding 3
+OpDecorate %4807 DescriptorSet 0
+OpDecorate %5042 Binding 4
+OpDecorate %5042 DescriptorSet 0
+OpDecorate %5058 Binding 5
+OpDecorate %5058 DescriptorSet 0
+OpDecorate %5122 Binding 6
+OpDecorate %5122 DescriptorSet 0
+OpDecorate %3967 Binding 7
+OpDecorate %3967 DescriptorSet 0
+OpDecorate %3983 Binding 8
+OpDecorate %3983 DescriptorSet 0
+OpDecorate %4047 Binding 9
+OpDecorate %4047 DescriptorSet 0
+OpDecorate %3789 Binding 10
+OpDecorate %3789 DescriptorSet 0
+OpDecorate %3805 Binding 11
+OpDecorate %3805 DescriptorSet 0
+OpDecorate %3869 Binding 12
+OpDecorate %3869 DescriptorSet 0
+OpDecorate %4253 Location 0
+OpDecorate %3709 BuiltIn FragDepth
+%8 = OpTypeVoid
+%1282 = OpTypeFunction %8
+%13 = OpTypeFloat 32
+%29 = OpTypeVector %13 4
+%1032 = OpTypeStruct %29 %13
+%319 = OpTypeFunction %1032
+%12 = OpTypeInt 32 1
+%1335 = OpTypeStruct %12 %12 %12 %12 %12 %12 %12 %12 %12 %12 %12 %12
+%1972 = OpTypePointer Function %1335
+%2574 = OpConstant %12 1
+%649 = OpTypePointer Function %12
+%2577 = OpConstant %12 2
+%2580 = OpConstant %12 3
+%2583 = OpConstant %12 4
+%2586 = OpConstant %12 5
+%2589 = OpConstant %12 6
+%2571 = OpConstant %12 0
+%2592 = OpConstant %12 7
+%2595 = OpConstant %12 8
+%2598 = OpConstant %12 9
+%2601 = OpConstant %12 10
+%2604 = OpConstant %12 11
+%666 = OpTypePointer Function %29
+%149 = OpTypeImage %13 1D 0 0 0 1 Unknown
+%786 = OpTypePointer UniformConstant %149
+%4727 = OpVariable %786 UniformConstant
+%508 = OpTypeSampler
+%1145 = OpTypePointer UniformConstant %508
+%3305 = OpVariable %1145 UniformConstant
+%510 = OpTypeSampledImage %149
+%2935 = OpConstant %13 0.100000001
+%26 = OpTypeVector %12 4
+%148 = OpTypeImage %12 1D 0 0 0 1 Unknown
+%785 = OpTypePointer UniformConstant %148
+%4743 = OpVariable %785 UniformConstant
+%511 = OpTypeSampledImage %148
+%2821 = OpConstant %13 0.200000003
+%11 = OpTypeInt 32 0
+%23 = OpTypeVector %11 4
+%147 = OpTypeImage %11 1D 0 0 0 1 Unknown
+%784 = OpTypePointer UniformConstant %147
+%4807 = OpVariable %784 UniformConstant
+%512 = OpTypeSampledImage %147
+%2151 = OpConstant %13 0.300000012
+%150 = OpTypeImage %13 2D 0 0 0 1 Unknown
+%787 = OpTypePointer UniformConstant %150
+%5042 = OpVariable %787 UniformConstant
+%513 = OpTypeSampledImage %150
+%19 = OpTypeVector %13 2
+%1825 = OpConstantComposite %19 %2935 %2821
+%151 = OpTypeImage %12 2D 0 0 0 1 Unknown
+%788 = OpTypePointer UniformConstant %151
+%5058 = OpVariable %788 UniformConstant
+%514 = OpTypeSampledImage %151
+%2707 = OpConstant %13 0.400000006
+%2028 = OpConstantComposite %19 %2151 %2707
+%152 = OpTypeImage %11 2D 0 0 0 1 Unknown
+%789 = OpTypePointer UniformConstant %152
+%5122 = OpVariable %789 UniformConstant
+%515 = OpTypeSampledImage %152
+%252 = OpConstant %13 0.5
+%2037 = OpConstant %13 0.600000024
+%2684 = OpConstantComposite %19 %252 %2037
+%153 = OpTypeImage %13 3D 0 0 0 1 Unknown
+%790 = OpTypePointer UniformConstant %153
+%3967 = OpVariable %790 UniformConstant
+%516 = OpTypeSampledImage %153
+%24 = OpTypeVector %13 3
+%1660 = OpConstantComposite %24 %2935 %2821 %2151
+%154 = OpTypeImage %12 3D 0 0 0 1 Unknown
+%791 = OpTypePointer UniformConstant %154
+%3983 = OpVariable %791 UniformConstant
+%517 = OpTypeSampledImage %154
+%2174 = OpConstantComposite %24 %2707 %252 %2037
+%155 = OpTypeImage %11 3D 0 0 0 1 Unknown
+%792 = OpTypePointer UniformConstant %155
+%4047 = OpVariable %792 UniformConstant
+%518 = OpTypeSampledImage %155
+%808 = OpConstant %13 0.699999988
+%2593 = OpConstant %13 0.800000012
+%1364 = OpConstant %13 0.899999976
+%2476 = OpConstantComposite %24 %808 %2593 %1364
+%156 = OpTypeImage %13 Cube 0 0 0 1 Unknown
+%793 = OpTypePointer UniformConstant %156
+%3789 = OpVariable %793 UniformConstant
+%519 = OpTypeSampledImage %156
+%157 = OpTypeImage %12 Cube 0 0 0 1 Unknown
+%794 = OpTypePointer UniformConstant %157
+%3805 = OpVariable %794 UniformConstant
+%520 = OpTypeSampledImage %157
+%158 = OpTypeImage %11 Cube 0 0 0 1 Unknown
+%795 = OpTypePointer UniformConstant %158
+%3869 = OpVariable %795 UniformConstant
+%521 = OpTypeSampledImage %158
+%1669 = OpTypePointer Function %1032
+%138 = OpConstant %13 1
+%1284 = OpConstantComposite %29 %138 %138 %138 %138
+%650 = OpTypePointer Function %13
+%667 = OpTypePointer Output %29
+%4253 = OpVariable %667 Output
+%651 = OpTypePointer Output %13
+%3709 = OpVariable %651 Output
+%5663 = OpFunction %8 None %1282
+%24877 = OpLabel
+%4104 = OpVariable %1669 Function
+%18803 = OpFunctionCall %1032 %3317
+OpStore %4104 %18803
+%13396 = OpAccessChain %666 %4104 %2571
+%7967 = OpLoad %29 %13396
+OpStore %4253 %7967
+%16622 = OpAccessChain %650 %4104 %2574
+%11539 = OpLoad %13 %16622
+OpStore %3709 %11539
+OpReturn
+OpFunctionEnd
+%3317 = OpFunction %1032 None %319
+%12442 = OpLabel
+%5830 = OpVariable %1972 Function
+%5072 = OpVariable %1669 Function
+%22671 = OpAccessChain %649 %5830 %2574
+OpStore %22671 %2574
+%20306 = OpAccessChain %649 %5830 %2577
+OpStore %20306 %2574
+%20307 = OpAccessChain %649 %5830 %2580
+OpStore %20307 %2574
+%20308 = OpAccessChain %649 %5830 %2583
+OpStore %20308 %2574
+%20309 = OpAccessChain %649 %5830 %2586
+OpStore %20309 %2574
+%20310 = OpAccessChain %649 %5830 %2589
+OpStore %20310 %2574
+%20311 = OpAccessChain %649 %5830 %2571
+OpStore %20311 %2574
+%20312 = OpAccessChain %649 %5830 %2592
+OpStore %20312 %2574
+%20313 = OpAccessChain %649 %5830 %2595
+OpStore %20313 %2574
+%20314 = OpAccessChain %649 %5830 %2598
+OpStore %20314 %2574
+%20315 = OpAccessChain %649 %5830 %2601
+OpStore %20315 %2574
+%20230 = OpAccessChain %649 %5830 %2604
+OpStore %20230 %2574
+%15508 = OpLoad %149 %4727
+%12260 = OpLoad %508 %3305
+%12514 = OpSampledImage %510 %15508 %12260
+%3559 = OpImageSampleImplicitLod %29 %12514 %2935
+%9477 = OpLoad %148 %4743
+%16280 = OpLoad %508 %3305
+%12515 = OpSampledImage %511 %9477 %16280
+%3560 = OpImageSampleImplicitLod %26 %12515 %2821
+%9478 = OpLoad %147 %4807
+%16281 = OpLoad %508 %3305
+%12516 = OpSampledImage %512 %9478 %16281
+%3561 = OpImageSampleImplicitLod %23 %12516 %2151
+%9479 = OpLoad %150 %5042
+%16282 = OpLoad %508 %3305
+%12517 = OpSampledImage %513 %9479 %16282
+%4568 = OpImageSampleImplicitLod %29 %12517 %1825
+%9480 = OpLoad %151 %5058
+%16283 = OpLoad %508 %3305
+%12518 = OpSampledImage %514 %9480 %16283
+%4569 = OpImageSampleImplicitLod %26 %12518 %2028
+%9481 = OpLoad %152 %5122
+%16284 = OpLoad %508 %3305
+%12519 = OpSampledImage %515 %9481 %16284
+%4570 = OpImageSampleImplicitLod %23 %12519 %2684
+%9482 = OpLoad %153 %3967
+%16285 = OpLoad %508 %3305
+%12520 = OpSampledImage %516 %9482 %16285
+%5577 = OpImageSampleImplicitLod %29 %12520 %1660
+%9483 = OpLoad %154 %3983
+%16286 = OpLoad %508 %3305
+%12521 = OpSampledImage %517 %9483 %16286
+%5578 = OpImageSampleImplicitLod %26 %12521 %2174
+%9484 = OpLoad %155 %4047
+%16287 = OpLoad %508 %3305
+%12522 = OpSampledImage %518 %9484 %16287
+%5579 = OpImageSampleImplicitLod %23 %12522 %2476
+%9485 = OpLoad %156 %3789
+%16288 = OpLoad %508 %3305
+%12523 = OpSampledImage %519 %9485 %16288
+%3575 = OpImageSampleImplicitLod %29 %12523 %1660
+%9486 = OpLoad %157 %3805
+%16289 = OpLoad %508 %3305
+%12524 = OpSampledImage %520 %9486 %16289
+%3576 = OpImageSampleImplicitLod %26 %12524 %2174
+%9487 = OpLoad %158 %3869
+%16290 = OpLoad %508 %3305
+%12590 = OpSampledImage %521 %9487 %16290
+%3577 = OpImageSampleImplicitLod %23 %12590 %2476
+%14275 = OpAccessChain %666 %5072 %2571
+OpStore %14275 %1284
+%20231 = OpAccessChain %650 %5072 %2574
+OpStore %20231 %138
+%8692 = OpLoad %1032 %5072
+OpReturnValue %8692
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  SinglePassRunAndCheck<CanonicalizeIdsPass>(before, after, false, false);
+}
+
+// ported from remap.hlsl.templatetypes.everything.frag
+TEST_F(CanonicalizeIdsTest, remap_hlsl_templatetypes) {
+  const std::string before =
+      R"(OpCapability Shader
+OpCapability Float64
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main" %153 %156
+OpExecutionMode %4 OriginUpperLeft
+OpSource HLSL 500
+OpName %4 "main"
+OpName %11 "@main(vf4;"
+OpName %10 "input"
+OpName %18 "r00"
+OpName %21 "r01"
+OpName %25 "r12"
+OpName %29 "r13"
+OpName %14 "r14"
+OpName %35 "r15"
+OpName %39 "r16"
+OpName %44 "r20"
+OpName %49 "r21"
+OpName %53 "r22"
+OpName %58 "r23"
+OpName %63 "r24"
+OpName %67 "r30"
+OpName %72 "r31"
+OpName %76 "r32"
+OpName %81 "r33"
+OpName %86 "r34"
+OpName %90 "r40"
+OpName %95 "r41"
+OpName %18 "r42"
+OpName %101 "r43"
+OpName %106 "r44"
+OpName %125 "r50"
+OpName %125 "r51"
+OpName %131 "r61"
+OpName %137 "r62"
+OpName %142 "r65"
+OpName %148 "r66"
+OpName %154 "input"
+OpName %153 "input"
+OpName %156 "@entryPointOutput"
+OpName %157 "param"
+OpDecorate %153 Location 0
+OpDecorate %156 Location 0
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%7 = OpTypeVector %6 4
+%8 = OpTypePointer Function %7
+%9 = OpTypeFunction %6 %8
+%14 = OpConstant %6 1
+%15 = OpConstant %6 2
+%16 = OpConstant %6 3
+%17 = OpConstant %6 4
+%18 = OpConstantComposite %7 %14 %15 %16 %17
+%20 = OpConstant %6 5
+%21 = OpConstantComposite %7 %15 %16 %17 %20
+%22 = OpTypeBool
+%25 = OpConstantFalse %22
+%26 = OpTypeInt 32 1
+%29 = OpConstant %26 1
+%32 = OpTypeFloat 64
+%35 = OpConstant %32 1
+%36 = OpTypeInt 32 0
+%39 = OpConstant %36 1
+%40 = OpTypeVector %22 2
+%43 = OpConstantTrue %22
+%44 = OpConstantComposite %40 %25 %43
+%45 = OpTypeVector %26 2
+%48 = OpConstant %26 2
+%49 = OpConstantComposite %45 %29 %48
+%50 = OpTypeVector %6 2
+%53 = OpConstantComposite %50 %14 %15
+%54 = OpTypeVector %32 2
+%57 = OpConstant %32 2
+%58 = OpConstantComposite %54 %35 %57
+%59 = OpTypeVector %36 2
+%62 = OpConstant %36 2
+%63 = OpConstantComposite %59 %39 %62
+%64 = OpTypeVector %22 3
+%67 = OpConstantComposite %64 %25 %43 %43
+%68 = OpTypeVector %26 3
+%71 = OpConstant %26 3
+%72 = OpConstantComposite %68 %29 %48 %71
+%73 = OpTypeVector %6 3
+%76 = OpConstantComposite %73 %14 %15 %16
+%77 = OpTypeVector %32 3
+%80 = OpConstant %32 3
+%81 = OpConstantComposite %77 %35 %57 %80
+%82 = OpTypeVector %36 3
+%85 = OpConstant %36 3
+%86 = OpConstantComposite %82 %39 %62 %85
+%87 = OpTypeVector %22 4
+%90 = OpConstantComposite %87 %25 %43 %43 %25
+%91 = OpTypeVector %26 4
+%94 = OpConstant %26 4
+%95 = OpConstantComposite %91 %29 %48 %71 %94
+%97 = OpTypeVector %32 4
+%100 = OpConstant %32 4
+%101 = OpConstantComposite %97 %35 %57 %80 %100
+%102 = OpTypeVector %36 4
+%105 = OpConstant %36 4
+%106 = OpConstantComposite %102 %39 %62 %85 %105
+%107 = OpTypeMatrix %7 4
+%110 = OpConstant %6 0
+%111 = OpConstantComposite %7 %110 %14 %15 %16
+%112 = OpConstant %6 6
+%113 = OpConstant %6 7
+%114 = OpConstantComposite %7 %17 %20 %112 %113
+%115 = OpConstant %6 8
+%116 = OpConstant %6 9
+%117 = OpConstant %6 10
+%118 = OpConstant %6 11
+%119 = OpConstantComposite %7 %115 %116 %117 %118
+%120 = OpConstant %6 12
+%121 = OpConstant %6 13
+%122 = OpConstant %6 14
+%123 = OpConstant %6 15
+%124 = OpConstantComposite %7 %120 %121 %122 %123
+%125 = OpConstantComposite %107 %111 %114 %119 %124
+%127 = OpTypeMatrix %73 2
+%130 = OpConstantComposite %73 %17 %20 %112
+%131 = OpConstantComposite %127 %76 %130
+%132 = OpTypeMatrix %50 3
+%135 = OpConstantComposite %50 %16 %17
+%136 = OpConstantComposite %50 %20 %112
+%137 = OpConstantComposite %132 %53 %135 %136
+%138 = OpTypeMatrix %50 4
+%141 = OpConstantComposite %50 %113 %115
+%142 = OpConstantComposite %138 %53 %135 %136 %141
+%143 = OpTypeMatrix %73 4
+%146 = OpConstantComposite %73 %113 %115 %116
+%147 = OpConstantComposite %73 %117 %118 %120
+%148 = OpConstantComposite %143 %76 %130 %146 %147
+%152 = OpTypePointer Input %7
+%153 = OpVariable %152 Input
+%155 = OpTypePointer Output %6
+%156 = OpVariable %155 Output
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+%157 = OpVariable %8 Function
+%154 = OpLoad %7 %153
+OpStore %157 %154
+%159 = OpFunctionCall %6 %11 %157
+OpStore %156 %159
+OpReturn
+OpFunctionEnd
+%11 = OpFunction %6 None %9
+%10 = OpFunctionParameter %8
+%12 = OpLabel
+OpReturnValue %110
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(OpCapability Shader
+OpCapability Float64
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %5663 "main" %4872 %4045
+OpExecutionMode %5663 OriginUpperLeft
+OpSource HLSL 500
+OpName %5663 "main"
+OpName %3917 "@main(vf4;"
+OpName %10636 "input"
+OpName %1616 "r00"
+OpName %590 "r01"
+OpName %1927 "r12"
+OpName %2574 "r13"
+OpName %138 "r14"
+OpName %1201 "r15"
+OpName %2573 "r16"
+OpName %311 "r20"
+OpName %1848 "r21"
+OpName %312 "r22"
+OpName %490 "r23"
+OpName %1840 "r24"
+OpName %869 "r30"
+OpName %2668 "r31"
+OpName %1271 "r32"
+OpName %1108 "r33"
+OpName %2654 "r34"
+OpName %340 "r40"
+OpName %56 "r41"
+OpName %1616 "r42"
+OpName %1328 "r43"
+OpName %35 "r44"
+OpName %1294 "r50"
+OpName %1294 "r51"
+OpName %1207 "r61"
+OpName %162 "r62"
+OpName %2695 "r65"
+OpName %55 "r66"
+OpName %24021 "input"
+OpName %4872 "input"
+OpName %4045 "@entryPointOutput"
+OpName %5786 "param"
+OpDecorate %4872 Location 0
+OpDecorate %4045 Location 0
+%8 = OpTypeVoid
+%1282 = OpTypeFunction %8
+%13 = OpTypeFloat 32
+%29 = OpTypeVector %13 4
+%666 = OpTypePointer Function %29
+%255 = OpTypeFunction %13 %666
+%138 = OpConstant %13 1
+%24 = OpConstant %13 2
+%2978 = OpConstant %13 3
+%2921 = OpConstant %13 4
+%1616 = OpConstantComposite %29 %138 %24 %2978 %2921
+%1387 = OpConstant %13 5
+%590 = OpConstantComposite %29 %24 %2978 %2921 %1387
+%9 = OpTypeBool
+%1927 = OpConstantFalse %9
+%12 = OpTypeInt 32 1
+%2574 = OpConstant %12 1
+%14 = OpTypeFloat 64
+%1201 = OpConstant %14 1
+%11 = OpTypeInt 32 0
+%2573 = OpConstant %11 1
+%15 = OpTypeVector %9 2
+%1926 = OpConstantTrue %9
+%311 = OpConstantComposite %15 %1927 %1926
+%18 = OpTypeVector %12 2
+%2577 = OpConstant %12 2
+%1848 = OpConstantComposite %18 %2574 %2577
+%19 = OpTypeVector %13 2
+%312 = OpConstantComposite %19 %138 %24
+%20 = OpTypeVector %14 2
+%2572 = OpConstant %14 2
+%490 = OpConstantComposite %20 %1201 %2572
+%17 = OpTypeVector %11 2
+%2576 = OpConstant %11 2
+%1840 = OpConstantComposite %17 %2573 %2576
+%16 = OpTypeVector %9 3
+%869 = OpConstantComposite %16 %1927 %1926 %1926
+%22 = OpTypeVector %12 3
+%2580 = OpConstant %12 3
+%2668 = OpConstantComposite %22 %2574 %2577 %2580
+%25 = OpTypeVector %13 3
+%1271 = OpConstantComposite %25 %138 %24 %2978
+%26 = OpTypeVector %14 3
+%1057 = OpConstant %14 3
+%1108 = OpConstantComposite %26 %1201 %2572 %1057
+%21 = OpTypeVector %11 3
+%2579 = OpConstant %11 3
+%2654 = OpConstantComposite %21 %2573 %2576 %2579
+%23 = OpTypeVector %9 4
+%340 = OpConstantComposite %23 %1927 %1926 %1926 %1927
+%27 = OpTypeVector %12 4
+%2583 = OpConstant %12 4
+%56 = OpConstantComposite %27 %2574 %2577 %2580 %2583
+%30 = OpTypeVector %14 4
+%2553 = OpConstant %14 4
+%1328 = OpConstantComposite %30 %1201 %2572 %1057 %2553
+%28 = OpTypeVector %11 4
+%2582 = OpConstant %11 4
+%35 = OpConstantComposite %28 %2573 %2576 %2579 %2582
+%101 = OpTypeMatrix %29 4
+%2575 = OpConstant %13 0
+%1199 = OpConstantComposite %29 %2575 %138 %24 %2978
+%2864 = OpConstant %13 6
+%1330 = OpConstant %13 7
+%2290 = OpConstantComposite %29 %2921 %1387 %2864 %1330
+%2807 = OpConstant %13 8
+%2040 = OpConstant %13 9
+%1273 = OpConstant %13 10
+%506 = OpConstant %13 11
+%694 = OpConstantComposite %29 %2807 %2040 %1273 %506
+%2750 = OpConstant %13 12
+%1983 = OpConstant %13 13
+%1216 = OpConstant %13 14
+%449 = OpConstant %13 15
+%2679 = OpConstantComposite %29 %2750 %1983 %1216 %449
+%1294 = OpConstantComposite %101 %1199 %2290 %694 %2679
+%54 = OpTypeMatrix %25 2
+%837 = OpConstantComposite %25 %2921 %1387 %2864
+%1207 = OpConstantComposite %54 %1271 %837
+%60 = OpTypeMatrix %19 3
+%2354 = OpConstantComposite %19 %2978 %2921
+%364 = OpConstantComposite %19 %1387 %2864
+%162 = OpConstantComposite %60 %312 %2354 %364
+%71 = OpTypeMatrix %19 4
+%2976 = OpConstantComposite %19 %1330 %2807
+%2695 = OpConstantComposite %71 %312 %2354 %364 %2976
+%86 = OpTypeMatrix %25 4
+%635 = OpConstantComposite %25 %1330 %2807 %2040
+%832 = OpConstantComposite %25 %1273 %506 %2750
+%55 = OpConstantComposite %86 %1271 %837 %635 %832
+%667 = OpTypePointer Input %29
+%4872 = OpVariable %667 Input
+%650 = OpTypePointer Output %13
+%4045 = OpVariable %650 Output
+%5663 = OpFunction %8 None %1282
+%24953 = OpLabel
+%5786 = OpVariable %666 Function
+%24021 = OpLoad %29 %4872
+OpStore %5786 %24021
+%9338 = OpFunctionCall %13 %3917 %5786
+OpStore %4045 %9338
+OpReturn
+OpFunctionEnd
+%3917 = OpFunction %13 None %255
+%10636 = OpFunctionParameter %666
+%10637 = OpLabel
+OpReturnValue %2575
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  SinglePassRunAndCheck<CanonicalizeIdsPass>(before, after, false, false);
+}
+
+// ported from remap.if.everything.frag
+TEST_F(CanonicalizeIdsTest, remap_if) {
+  const std::string before =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main" %8 %17
+OpExecutionMode %4 OriginUpperLeft
+OpSource GLSL 450
+OpName %4 "main"
+OpName %8 "inf"
+OpName %17 "outf4"
+OpDecorate %8 Location 0
+OpDecorate %17 Location 0
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%7 = OpTypePointer Input %6
+%8 = OpVariable %7 Input
+%10 = OpConstant %6 2
+%11 = OpTypeBool
+%15 = OpTypeVector %6 4
+%16 = OpTypePointer Output %15
+%17 = OpVariable %16 Output
+%22 = OpConstant %6 -0.5
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+%9 = OpLoad %6 %8
+%12 = OpFOrdGreaterThan %11 %9 %10
+OpSelectionMerge %14 None
+OpBranchConditional %12 %13 %20
+%13 = OpLabel
+%18 = OpLoad %6 %8
+%19 = OpCompositeConstruct %15 %18 %18 %18 %18
+OpStore %17 %19
+OpBranch %14
+%20 = OpLabel
+%21 = OpLoad %6 %8
+%23 = OpFAdd %6 %21 %22
+%24 = OpCompositeConstruct %15 %23 %23 %23 %23
+OpStore %17 %24
+OpBranch %14
+%14 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %5663 "main" %3773 %4539
+OpExecutionMode %5663 OriginUpperLeft
+OpSource GLSL 450
+OpName %5663 "main"
+OpName %3773 "inf"
+OpName %4539 "outf4"
+OpDecorate %3773 Location 0
+OpDecorate %4539 Location 0
+%8 = OpTypeVoid
+%1282 = OpTypeFunction %8
+%13 = OpTypeFloat 32
+%650 = OpTypePointer Input %13
+%3773 = OpVariable %650 Input
+%24 = OpConstant %13 2
+%9 = OpTypeBool
+%29 = OpTypeVector %13 4
+%666 = OpTypePointer Output %29
+%4539 = OpVariable %666 Output
+%947 = OpConstant %13 -0.5
+%5663 = OpFunction %8 None %1282
+%7911 = OpLabel
+%21734 = OpLoad %13 %3773
+%13508 = OpFOrdGreaterThan %9 %21734 %24
+OpSelectionMerge %19578 None
+OpBranchConditional %13508 %13182 %10142
+%13182 = OpLabel
+%9496 = OpLoad %13 %3773
+%17615 = OpCompositeConstruct %29 %9496 %9496 %9496 %9496
+OpStore %4539 %17615
+OpBranch %19578
+%10142 = OpLabel
+%22854 = OpLoad %13 %3773
+%9982 = OpFAdd %13 %22854 %947
+%12421 = OpCompositeConstruct %29 %9982 %9982 %9982 %9982
+OpStore %4539 %12421
+OpBranch %19578
+%19578 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  SinglePassRunAndCheck<CanonicalizeIdsPass>(before, after, false, false);
+}
+
+// ported from remap.similar_1a.everything.frag
+TEST_F(CanonicalizeIdsTest, remap_similar_1a) {
+  const std::string before =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main" %53 %73 %75
+OpExecutionMode %4 OriginUpperLeft
+OpSource GLSL 450
+OpName %4 "main"
+OpName %11 "Test1(i1;"
+OpName %10 "bound"
+OpName %14 "Test2(i1;"
+OpName %13 "bound"
+OpName %17 "r"
+OpName %19 "x"
+OpName %44 "param"
+OpName %53 "ini4"
+OpName %73 "outf4"
+OpName %75 "inf"
+OpName %78 "param"
+OpName %82 "param"
+OpDecorate %53 Flat
+OpDecorate %53 Location 1
+OpDecorate %73 Location 0
+OpDecorate %75 Location 0
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeInt 32 1
+%7 = OpTypePointer Function %6
+%8 = OpTypeFloat 32
+%9 = OpTypeFunction %8 %7
+%16 = OpTypePointer Function %8
+%18 = OpConstant %8 0
+%20 = OpConstant %6 0
+%28 = OpTypeBool
+%30 = OpConstant %8 0.5
+%34 = OpConstant %6 1
+%40 = OpConstant %6 2
+%51 = OpTypeVector %6 4
+%52 = OpTypePointer Input %51
+%53 = OpVariable %52 Input
+%54 = OpTypeInt 32 0
+%55 = OpConstant %54 1
+%56 = OpTypePointer Input %6
+%59 = OpConstant %54 2
+%64 = OpConstant %54 0
+%71 = OpTypeVector %8 4
+%72 = OpTypePointer Output %71
+%73 = OpVariable %72 Output
+%74 = OpTypePointer Input %8
+%75 = OpVariable %74 Input
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+%78 = OpVariable %7 Function
+%82 = OpVariable %7 Function
+%76 = OpLoad %8 %75
+%77 = OpConvertFToS %6 %76
+OpStore %78 %77
+%79 = OpFunctionCall %8 %11 %78
+%80 = OpLoad %8 %75
+%81 = OpConvertFToS %6 %80
+OpStore %82 %81
+%83 = OpFunctionCall %8 %14 %82
+%84 = OpFAdd %8 %79 %83
+%85 = OpCompositeConstruct %71 %84 %84 %84 %84
+OpStore %73 %85
+OpReturn
+OpFunctionEnd
+%11 = OpFunction %8 None %9
+%10 = OpFunctionParameter %7
+%12 = OpLabel
+%17 = OpVariable %16 Function
+%19 = OpVariable %7 Function
+OpStore %17 %18
+OpStore %19 %20
+OpBranch %21
+%21 = OpLabel
+OpLoopMerge %23 %24 None
+OpBranch %25
+%25 = OpLabel
+%26 = OpLoad %6 %19
+%27 = OpLoad %6 %10
+%29 = OpSLessThan %28 %26 %27
+OpBranchConditional %29 %22 %23
+%22 = OpLabel
+%31 = OpLoad %8 %17
+%32 = OpFAdd %8 %31 %30
+OpStore %17 %32
+OpBranch %24
+%24 = OpLabel
+%33 = OpLoad %6 %19
+%35 = OpIAdd %6 %33 %34
+OpStore %19 %35
+OpBranch %21
+%23 = OpLabel
+%36 = OpLoad %8 %17
+OpReturnValue %36
+OpFunctionEnd
+%14 = OpFunction %8 None %9
+%13 = OpFunctionParameter %7
+%15 = OpLabel
+%44 = OpVariable %7 Function
+%39 = OpLoad %6 %13
+%41 = OpSGreaterThan %28 %39 %40
+OpSelectionMerge %43 None
+OpBranchConditional %41 %42 %48
+%42 = OpLabel
+%45 = OpLoad %6 %13
+OpStore %44 %45
+%46 = OpFunctionCall %8 %11 %44
+OpReturnValue %46
+%48 = OpLabel
+%49 = OpLoad %6 %13
+%50 = OpIMul %6 %49 %40
+%57 = OpAccessChain %56 %53 %55
+%58 = OpLoad %6 %57
+%60 = OpAccessChain %56 %53 %59
+%61 = OpLoad %6 %60
+%62 = OpIMul %6 %58 %61
+%63 = OpIAdd %6 %50 %62
+%65 = OpAccessChain %56 %53 %64
+%66 = OpLoad %6 %65
+%67 = OpIAdd %6 %63 %66
+%68 = OpConvertSToF %8 %67
+OpReturnValue %68
+%43 = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %5663 "main" %4201 %4539 %3773
+OpExecutionMode %5663 OriginUpperLeft
+OpSource GLSL 450
+OpName %5663 "main"
+OpName %3782 "Test1(i1;"
+OpName %6931 "bound"
+OpName %3836 "Test2(i1;"
+OpName %4408 "bound"
+OpName %4292 "r"
+OpName %4298 "x"
+OpName %22102 "param"
+OpName %4201 "ini4"
+OpName %4539 "outf4"
+OpName %3773 "inf"
+OpName %18415 "param"
+OpName %5786 "param"
+OpDecorate %4201 Flat
+OpDecorate %4201 Location 1
+OpDecorate %4539 Location 0
+OpDecorate %3773 Location 0
+%8 = OpTypeVoid
+%1282 = OpTypeFunction %8
+%12 = OpTypeInt 32 1
+%649 = OpTypePointer Function %12
+%13 = OpTypeFloat 32
+%204 = OpTypeFunction %13 %649
+%650 = OpTypePointer Function %13
+%2572 = OpConstant %13 0
+%2571 = OpConstant %12 0
+%9 = OpTypeBool
+%252 = OpConstant %13 0.5
+%2574 = OpConstant %12 1
+%2577 = OpConstant %12 2
+%26 = OpTypeVector %12 4
+%663 = OpTypePointer Input %26
+%4201 = OpVariable %663 Input
+%11 = OpTypeInt 32 0
+%2573 = OpConstant %11 1
+%651 = OpTypePointer Input %12
+%2576 = OpConstant %11 2
+%2570 = OpConstant %11 0
+%29 = OpTypeVector %13 4
+%666 = OpTypePointer Output %29
+%4539 = OpVariable %666 Output
+%652 = OpTypePointer Input %13
+%3773 = OpVariable %652 Input
+%5663 = OpFunction %8 None %1282
+%24915 = OpLabel
+%18415 = OpVariable %649 Function
+%5786 = OpVariable %649 Function
+%8366 = OpLoad %13 %3773
+%8654 = OpConvertFToS %12 %8366
+OpStore %18415 %8654
+%17256 = OpFunctionCall %13 %3782 %18415
+%14512 = OpLoad %13 %3773
+%7041 = OpConvertFToS %12 %14512
+OpStore %5786 %7041
+%23993 = OpFunctionCall %13 %3836 %5786
+%9180 = OpFAdd %13 %17256 %23993
+%15728 = OpCompositeConstruct %29 %9180 %9180 %9180 %9180
+OpStore %4539 %15728
+OpReturn
+OpFunctionEnd
+%3782 = OpFunction %13 None %204
+%6931 = OpFunctionParameter %649
+%12220 = OpLabel
+%4292 = OpVariable %650 Function
+%4298 = OpVariable %649 Function
+OpStore %4292 %2572
+OpStore %4298 %2571
+OpBranch %14924
+%14924 = OpLabel
+OpLoopMerge %8882 %6488 None
+OpBranch %11857
+%11857 = OpLabel
+%13755 = OpLoad %12 %4298
+%22731 = OpLoad %12 %6931
+%20007 = OpSLessThan %9 %13755 %22731
+OpBranchConditional %20007 %24750 %8882
+%24750 = OpLabel
+%22912 = OpLoad %13 %4292
+%19471 = OpFAdd %13 %22912 %252
+OpStore %4292 %19471
+OpBranch %6488
+%6488 = OpLabel
+%19050 = OpLoad %12 %4298
+%8593 = OpIAdd %12 %19050 %2574
+OpStore %4298 %8593
+OpBranch %14924
+%8882 = OpLabel
+%11601 = OpLoad %13 %4292
+OpReturnValue %11601
+OpFunctionEnd
+%3836 = OpFunction %13 None %204
+%4408 = OpFunctionParameter %649
+%12143 = OpLabel
+%22102 = OpVariable %649 Function
+%24151 = OpLoad %12 %4408
+%13868 = OpSGreaterThan %9 %24151 %2577
+OpSelectionMerge %14966 None
+OpBranchConditional %13868 %9492 %17416
+%9492 = OpLabel
+%15624 = OpLoad %12 %4408
+OpStore %22102 %15624
+%17278 = OpFunctionCall %13 %3782 %22102
+OpReturnValue %17278
+%17416 = OpLabel
+%19506 = OpLoad %12 %4408
+%22773 = OpIMul %12 %19506 %2577
+%13472 = OpAccessChain %651 %4201 %2573
+%15280 = OpLoad %12 %13472
+%18079 = OpAccessChain %651 %4201 %2576
+%15199 = OpLoad %12 %18079
+%9343 = OpIMul %12 %15280 %15199
+%11462 = OpIAdd %12 %22773 %9343
+%11885 = OpAccessChain %651 %4201 %2570
+%21176 = OpLoad %12 %11885
+%10505 = OpIAdd %12 %11462 %21176
+%14626 = OpConvertSToF %13 %10505
+OpReturnValue %14626
+%14966 = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  SinglePassRunAndCheck<CanonicalizeIdsPass>(before, after, false, false);
+}
+
+// ported from remap.similar_1b.everything.frag
+TEST_F(CanonicalizeIdsTest, remap_similar_1b) {
+  const std::string before =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main" %58 %78 %80
+OpExecutionMode %4 OriginUpperLeft
+OpSource GLSL 450
+OpName %4 "main"
+OpName %11 "Test1(i1;"
+OpName %10 "bound"
+OpName %14 "Test2(i1;"
+OpName %13 "bound"
+OpName %17 "r"
+OpName %19 "x"
+OpName %49 "param"
+OpName %58 "ini4"
+OpName %78 "outf4"
+OpName %80 "inf"
+OpName %83 "param"
+OpName %87 "param"
+OpDecorate %58 Flat
+OpDecorate %58 Location 0
+OpDecorate %78 Location 0
+OpDecorate %80 Location 1
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeInt 32 1
+%7 = OpTypePointer Function %6
+%8 = OpTypeFloat 32
+%9 = OpTypeFunction %8 %7
+%16 = OpTypePointer Function %8
+%18 = OpConstant %8 0
+%20 = OpConstant %6 0
+%28 = OpTypeBool
+%30 = OpConstant %8 0.5
+%34 = OpConstant %6 1
+%36 = OpConstant %8 0.200000003
+%43 = OpConstant %6 2
+%54 = OpConstant %6 4
+%56 = OpTypeVector %6 4
+%57 = OpTypePointer Input %56
+%58 = OpVariable %57 Input
+%59 = OpTypeInt 32 0
+%60 = OpConstant %59 1
+%61 = OpTypePointer Input %6
+%64 = OpConstant %59 2
+%69 = OpConstant %59 0
+%76 = OpTypeVector %8 4
+%77 = OpTypePointer Output %76
+%78 = OpVariable %77 Output
+%79 = OpTypePointer Input %8
+%80 = OpVariable %79 Input
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+%83 = OpVariable %7 Function
+%87 = OpVariable %7 Function
+%81 = OpLoad %8 %80
+%82 = OpConvertFToS %6 %81
+OpStore %83 %82
+%84 = OpFunctionCall %8 %11 %83
+%85 = OpLoad %8 %80
+%86 = OpConvertFToS %6 %85
+OpStore %87 %86
+%88 = OpFunctionCall %8 %14 %87
+%89 = OpFAdd %8 %84 %88
+%90 = OpCompositeConstruct %76 %89 %89 %89 %89
+OpStore %78 %90
+OpReturn
+OpFunctionEnd
+%11 = OpFunction %8 None %9
+%10 = OpFunctionParameter %7
+%12 = OpLabel
+%17 = OpVariable %16 Function
+%19 = OpVariable %7 Function
+OpStore %17 %18
+OpStore %19 %20
+OpBranch %21
+%21 = OpLabel
+OpLoopMerge %23 %24 None
+OpBranch %25
+%25 = OpLabel
+%26 = OpLoad %6 %19
+%27 = OpLoad %6 %10
+%29 = OpSLessThan %28 %26 %27
+OpBranchConditional %29 %22 %23
+%22 = OpLabel
+%31 = OpLoad %8 %17
+%32 = OpFAdd %8 %31 %30
+OpStore %17 %32
+OpBranch %24
+%24 = OpLabel
+%33 = OpLoad %6 %19
+%35 = OpIAdd %6 %33 %34
+OpStore %19 %35
+OpBranch %21
+%23 = OpLabel
+%37 = OpLoad %8 %17
+%38 = OpFAdd %8 %37 %36
+OpStore %17 %38
+%39 = OpLoad %8 %17
+OpReturnValue %39
+OpFunctionEnd
+%14 = OpFunction %8 None %9
+%13 = OpFunctionParameter %7
+%15 = OpLabel
+%49 = OpVariable %7 Function
+%42 = OpLoad %6 %13
+%44 = OpSGreaterThan %28 %42 %43
+OpSelectionMerge %46 None
+OpBranchConditional %44 %45 %52
+%45 = OpLabel
+%47 = OpLoad %6 %13
+%48 = OpIMul %6 %47 %43
+OpStore %49 %48
+%50 = OpFunctionCall %8 %11 %49
+OpReturnValue %50
+%52 = OpLabel
+%53 = OpLoad %6 %13
+%55 = OpIMul %6 %53 %54
+%62 = OpAccessChain %61 %58 %60
+%63 = OpLoad %6 %62
+%65 = OpAccessChain %61 %58 %64
+%66 = OpLoad %6 %65
+%67 = OpIMul %6 %63 %66
+%68 = OpIAdd %6 %55 %67
+%70 = OpAccessChain %61 %58 %69
+%71 = OpLoad %6 %70
+%72 = OpIAdd %6 %68 %71
+%73 = OpConvertSToF %8 %72
+OpReturnValue %73
+%46 = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %5663 "main" %4201 %4539 %3773
+OpExecutionMode %5663 OriginUpperLeft
+OpSource GLSL 450
+OpName %5663 "main"
+OpName %3782 "Test1(i1;"
+OpName %6931 "bound"
+OpName %3836 "Test2(i1;"
+OpName %4408 "bound"
+OpName %4292 "r"
+OpName %4298 "x"
+OpName %22102 "param"
+OpName %4201 "ini4"
+OpName %4539 "outf4"
+OpName %3773 "inf"
+OpName %18415 "param"
+OpName %5786 "param"
+OpDecorate %4201 Flat
+OpDecorate %4201 Location 0
+OpDecorate %4539 Location 0
+OpDecorate %3773 Location 1
+%8 = OpTypeVoid
+%1282 = OpTypeFunction %8
+%12 = OpTypeInt 32 1
+%649 = OpTypePointer Function %12
+%13 = OpTypeFloat 32
+%204 = OpTypeFunction %13 %649
+%650 = OpTypePointer Function %13
+%2572 = OpConstant %13 0
+%2571 = OpConstant %12 0
+%9 = OpTypeBool
+%252 = OpConstant %13 0.5
+%2574 = OpConstant %12 1
+%2821 = OpConstant %13 0.200000003
+%2577 = OpConstant %12 2
+%2583 = OpConstant %12 4
+%26 = OpTypeVector %12 4
+%663 = OpTypePointer Input %26
+%4201 = OpVariable %663 Input
+%11 = OpTypeInt 32 0
+%2573 = OpConstant %11 1
+%651 = OpTypePointer Input %12
+%2576 = OpConstant %11 2
+%2570 = OpConstant %11 0
+%29 = OpTypeVector %13 4
+%666 = OpTypePointer Output %29
+%4539 = OpVariable %666 Output
+%652 = OpTypePointer Input %13
+%3773 = OpVariable %652 Input
+%5663 = OpFunction %8 None %1282
+%24915 = OpLabel
+%18415 = OpVariable %649 Function
+%5786 = OpVariable %649 Function
+%8366 = OpLoad %13 %3773
+%8654 = OpConvertFToS %12 %8366
+OpStore %18415 %8654
+%17256 = OpFunctionCall %13 %3782 %18415
+%14512 = OpLoad %13 %3773
+%7041 = OpConvertFToS %12 %14512
+OpStore %5786 %7041
+%23993 = OpFunctionCall %13 %3836 %5786
+%9180 = OpFAdd %13 %17256 %23993
+%15728 = OpCompositeConstruct %29 %9180 %9180 %9180 %9180
+OpStore %4539 %15728
+OpReturn
+OpFunctionEnd
+%3782 = OpFunction %13 None %204
+%6931 = OpFunctionParameter %649
+%12220 = OpLabel
+%4292 = OpVariable %650 Function
+%4298 = OpVariable %649 Function
+OpStore %4292 %2572
+OpStore %4298 %2571
+OpBranch %14924
+%14924 = OpLabel
+OpLoopMerge %6507 %6488 None
+OpBranch %11857
+%11857 = OpLabel
+%13755 = OpLoad %12 %4298
+%22731 = OpLoad %12 %6931
+%20007 = OpSLessThan %9 %13755 %22731
+OpBranchConditional %20007 %24750 %6507
+%24750 = OpLabel
+%22912 = OpLoad %13 %4292
+%19471 = OpFAdd %13 %22912 %252
+OpStore %4292 %19471
+OpBranch %6488
+%6488 = OpLabel
+%19050 = OpLoad %12 %4298
+%8593 = OpIAdd %12 %19050 %2574
+OpStore %4298 %8593
+OpBranch %14924
+%6507 = OpLabel
+%18877 = OpLoad %13 %4292
+%15899 = OpFAdd %13 %18877 %2821
+OpStore %4292 %15899
+%20342 = OpLoad %13 %4292
+OpReturnValue %20342
+OpFunctionEnd
+%3836 = OpFunction %13 None %204
+%4408 = OpFunctionParameter %649
+%12143 = OpLabel
+%22102 = OpVariable %649 Function
+%24151 = OpLoad %12 %4408
+%13868 = OpSGreaterThan %9 %24151 %2577
+OpSelectionMerge %14966 None
+OpBranchConditional %13868 %10822 %17416
+%10822 = OpLabel
+%22680 = OpLoad %12 %4408
+%23216 = OpIMul %12 %22680 %2577
+OpStore %22102 %23216
+%7042 = OpFunctionCall %13 %3782 %22102
+OpReturnValue %7042
+%17416 = OpLabel
+%19506 = OpLoad %12 %4408
+%22773 = OpIMul %12 %19506 %2583
+%13472 = OpAccessChain %651 %4201 %2573
+%15280 = OpLoad %12 %13472
+%18079 = OpAccessChain %651 %4201 %2576
+%15199 = OpLoad %12 %18079
+%9343 = OpIMul %12 %15280 %15199
+%11462 = OpIAdd %12 %22773 %9343
+%11885 = OpAccessChain %651 %4201 %2570
+%21176 = OpLoad %12 %11885
+%10505 = OpIAdd %12 %11462 %21176
+%14626 = OpConvertSToF %13 %10505
+OpReturnValue %14626
+%14966 = OpLabel
+OpUnreachable
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  SinglePassRunAndCheck<CanonicalizeIdsPass>(before, after, false, false);
+}
+
+// ported from remap.specconst.comp
+TEST_F(CanonicalizeIdsTest, remap_specconst) {
+  const std::string before =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %4 "main"
+OpExecutionMode %4 LocalSize 1 1 1
+OpSource GLSL 450
+OpName %4 "main"
+OpDecorate %7 SpecId 0
+OpDecorate %8 SpecId 1
+OpDecorate %9 SpecId 2
+OpDecorate %11 BuiltIn WorkgroupSize
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeInt 32 0
+%7 = OpSpecConstant %6 1
+%8 = OpSpecConstant %6 1
+%9 = OpSpecConstant %6 1
+%10 = OpTypeVector %6 3
+%11 = OpSpecConstantComposite %10 %7 %8 %9
+%14 = OpSpecConstantOp %6 CompositeExtract %11 0
+%16 = OpSpecConstantOp %6 CompositeExtract %11 1
+%18 = OpSpecConstantOp %6 CompositeExtract %11 2
+%19 = OpSpecConstantOp %6 IMul %16 %18
+%20 = OpSpecConstantOp %6 IAdd %14 %19
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %5663 "main"
+OpExecutionMode %5663 LocalSize 1 1 1
+OpSource GLSL 450
+OpName %5663 "main"
+OpDecorate %2 SpecId 0
+OpDecorate %3 SpecId 1
+OpDecorate %4 SpecId 2
+OpDecorate %5 BuiltIn WorkgroupSize
+%8 = OpTypeVoid
+%1282 = OpTypeFunction %8
+%11 = OpTypeInt 32 0
+%2 = OpSpecConstant %11 1
+%3 = OpSpecConstant %11 1
+%4 = OpSpecConstant %11 1
+%20 = OpTypeVector %11 3
+%5 = OpSpecConstantComposite %20 %2 %3 %4
+%6 = OpSpecConstantOp %11 CompositeExtract %5 0
+%7 = OpSpecConstantOp %11 CompositeExtract %5 1
+%9 = OpSpecConstantOp %11 CompositeExtract %5 2
+%10 = OpSpecConstantOp %11 IMul %7 %9
+%12 = OpSpecConstantOp %11 IAdd %6 %10
+%5663 = OpFunction %8 None %1282
+%16103 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  SinglePassRunAndCheck<CanonicalizeIdsPass>(before, after, false, false);
+}
+
+// ported from remap.switch.everything.frag
+TEST_F(CanonicalizeIdsTest, remap_switch) {
+  const std::string before =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main" %9 %23
+OpExecutionMode %4 OriginUpperLeft
+OpSource GLSL 450
+OpName %4 "main"
+OpName %9 "in0"
+OpName %23 "FragColor"
+OpDecorate %9 Location 0
+OpDecorate %23 RelaxedPrecision
+OpDecorate %23 Location 0
+OpDecorate %29 RelaxedPrecision
+OpDecorate %36 RelaxedPrecision
+OpDecorate %43 RelaxedPrecision
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%7 = OpTypeVector %6 4
+%8 = OpTypePointer Input %7
+%9 = OpVariable %8 Input
+%10 = OpTypeInt 32 0
+%11 = OpConstant %10 3
+%12 = OpTypePointer Input %6
+%15 = OpTypeInt 32 1
+%22 = OpTypePointer Output %7
+%23 = OpVariable %22 Output
+%24 = OpConstant %10 0
+%27 = OpConstant %6 0
+%31 = OpConstant %10 1
+%34 = OpConstant %6 1
+%38 = OpConstant %10 2
+%41 = OpConstant %6 2
+%45 = OpConstant %6 -1
+%46 = OpConstantComposite %7 %45 %45 %45 %45
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+%13 = OpAccessChain %12 %9 %11
+%14 = OpLoad %6 %13
+%16 = OpConvertFToS %15 %14
+OpSelectionMerge %21 None
+OpSwitch %16 %20 0 %17 1 %18 2 %19
+%20 = OpLabel
+OpStore %23 %46
+OpBranch %21
+%17 = OpLabel
+%25 = OpAccessChain %12 %9 %24
+%26 = OpLoad %6 %25
+%28 = OpFAdd %6 %26 %27
+%29 = OpCompositeConstruct %7 %28 %28 %28 %28
+OpStore %23 %29
+OpBranch %21
+%18 = OpLabel
+%32 = OpAccessChain %12 %9 %31
+%33 = OpLoad %6 %32
+%35 = OpFAdd %6 %33 %34
+%36 = OpCompositeConstruct %7 %35 %35 %35 %35
+OpStore %23 %36
+OpBranch %21
+%19 = OpLabel
+%39 = OpAccessChain %12 %9 %38
+%40 = OpLoad %6 %39
+%42 = OpFAdd %6 %40 %41
+%43 = OpCompositeConstruct %7 %42 %42 %42 %42
+OpStore %23 %43
+OpBranch %21
+%21 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %5663 "main" %3719 %3994
+OpExecutionMode %5663 OriginUpperLeft
+OpSource GLSL 450
+OpName %5663 "main"
+OpName %3719 "in0"
+OpName %3994 "FragColor"
+OpDecorate %3719 Location 0
+OpDecorate %3994 RelaxedPrecision
+OpDecorate %3994 Location 0
+OpDecorate %12421 RelaxedPrecision
+OpDecorate %12422 RelaxedPrecision
+OpDecorate %12423 RelaxedPrecision
+%8 = OpTypeVoid
+%1282 = OpTypeFunction %8
+%13 = OpTypeFloat 32
+%29 = OpTypeVector %13 4
+%666 = OpTypePointer Input %29
+%3719 = OpVariable %666 Input
+%11 = OpTypeInt 32 0
+%2579 = OpConstant %11 3
+%650 = OpTypePointer Input %13
+%12 = OpTypeInt 32 1
+%667 = OpTypePointer Output %29
+%3994 = OpVariable %667 Output
+%2570 = OpConstant %11 0
+%2572 = OpConstant %13 0
+%2573 = OpConstant %11 1
+%138 = OpConstant %13 1
+%2576 = OpConstant %11 2
+%24 = OpConstant %13 2
+%833 = OpConstant %13 -1
+%1284 = OpConstantComposite %29 %833 %833 %833 %833
+%5663 = OpFunction %8 None %1282
+%23915 = OpLabel
+%7984 = OpAccessChain %650 %3719 %2579
+%11376 = OpLoad %13 %7984
+%16859 = OpConvertFToS %12 %11376
+OpSelectionMerge %19578 None
+OpSwitch %16859 %15971 0 %8158 1 %8159 2 %8160
+%15971 = OpLabel
+OpStore %3994 %1284
+OpBranch %19578
+%8158 = OpLabel
+%21848 = OpAccessChain %650 %3719 %2570
+%23987 = OpLoad %13 %21848
+%19989 = OpFAdd %13 %23987 %2572
+%12421 = OpCompositeConstruct %29 %19989 %19989 %19989 %19989
+OpStore %3994 %12421
+OpBranch %19578
+%8159 = OpLabel
+%21849 = OpAccessChain %650 %3719 %2573
+%23988 = OpLoad %13 %21849
+%19990 = OpFAdd %13 %23988 %138
+%12422 = OpCompositeConstruct %29 %19990 %19990 %19990 %19990
+OpStore %3994 %12422
+OpBranch %19578
+%8160 = OpLabel
+%21850 = OpAccessChain %650 %3719 %2576
+%23989 = OpLoad %13 %21850
+%19991 = OpFAdd %13 %23989 %24
+%12423 = OpCompositeConstruct %29 %19991 %19991 %19991 %19991
+OpStore %3994 %12423
+OpBranch %19578
+%19578 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  SinglePassRunAndCheck<CanonicalizeIdsPass>(before, after, false, false);
+}
+
+// ported from remap.uniformarray.everything.frag
+TEST_F(CanonicalizeIdsTest, remap_uniformarray) {
+  const std::string before =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %4 "main" %14 %25 %43 %54
+OpExecutionMode %4 OriginUpperLeft
+OpSource GLSL 140
+OpName %4 "main"
+OpName %9 "texColor"
+OpName %14 "color"
+OpName %25 "inColor"
+OpName %43 "alpha"
+OpName %54 "gl_FragColor"
+OpDecorate %14 Location 1
+OpDecorate %25 Location 0
+OpDecorate %43 Location 7
+OpDecorate %54 Location 0
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%7 = OpTypeVector %6 4
+%8 = OpTypePointer Function %7
+%10 = OpTypeInt 32 0
+%11 = OpConstant %10 6
+%12 = OpTypeArray %7 %11
+%13 = OpTypePointer Input %12
+%14 = OpVariable %13 Input
+%15 = OpTypeInt 32 1
+%16 = OpConstant %15 1
+%17 = OpTypePointer Input %7
+%23 = OpTypeVector %6 3
+%24 = OpTypePointer Input %23
+%25 = OpVariable %24 Input
+%30 = OpConstant %10 0
+%31 = OpTypePointer Function %6
+%34 = OpConstant %10 1
+%37 = OpConstant %10 2
+%40 = OpConstant %10 16
+%41 = OpTypeArray %6 %40
+%42 = OpTypePointer Input %41
+%43 = OpVariable %42 Input
+%44 = OpConstant %15 12
+%45 = OpTypePointer Input %6
+%48 = OpConstant %10 3
+%53 = OpTypePointer Output %7
+%54 = OpVariable %53 Output
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+%9 = OpVariable %8 Function
+%18 = OpAccessChain %17 %14 %16
+%19 = OpLoad %7 %18
+%20 = OpAccessChain %17 %14 %16
+%21 = OpLoad %7 %20
+%22 = OpFAdd %7 %19 %21
+OpStore %9 %22
+%26 = OpLoad %23 %25
+%27 = OpLoad %7 %9
+%28 = OpVectorShuffle %23 %27 %27 0 1 2
+%29 = OpFAdd %23 %28 %26
+%32 = OpAccessChain %31 %9 %30
+%33 = OpCompositeExtract %6 %29 0
+OpStore %32 %33
+%35 = OpAccessChain %31 %9 %34
+%36 = OpCompositeExtract %6 %29 1
+OpStore %35 %36
+%38 = OpAccessChain %31 %9 %37
+%39 = OpCompositeExtract %6 %29 2
+OpStore %38 %39
+%46 = OpAccessChain %45 %43 %44
+%47 = OpLoad %6 %46
+%49 = OpAccessChain %31 %9 %48
+%50 = OpLoad %6 %49
+%51 = OpFAdd %6 %50 %47
+%52 = OpAccessChain %31 %9 %48
+OpStore %52 %51
+%55 = OpLoad %7 %9
+OpStore %54 %55
+OpReturn
+OpFunctionEnd
+)";
+
+  const std::string after =
+      R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %5663 "main" %3608 %4957 %4339 %5139
+OpExecutionMode %5663 OriginUpperLeft
+OpSource GLSL 140
+OpName %5663 "main"
+OpName %4902 "texColor"
+OpName %3608 "color"
+OpName %4957 "inColor"
+OpName %4339 "alpha"
+OpName %5139 "gl_FragColor"
+OpDecorate %3608 Location 1
+OpDecorate %4957 Location 0
+OpDecorate %4339 Location 7
+OpDecorate %5139 Location 0
+%8 = OpTypeVoid
+%1282 = OpTypeFunction %8
+%13 = OpTypeFloat 32
+%29 = OpTypeVector %13 4
+%666 = OpTypePointer Function %29
+%11 = OpTypeInt 32 0
+%2588 = OpConstant %11 6
+%740 = OpTypeArray %29 %2588
+%1377 = OpTypePointer Input %740
+%3608 = OpVariable %1377 Input
+%12 = OpTypeInt 32 1
+%2574 = OpConstant %12 1
+%667 = OpTypePointer Input %29
+%24 = OpTypeVector %13 3
+%661 = OpTypePointer Input %24
+%4957 = OpVariable %661 Input
+%2570 = OpConstant %11 0
+%650 = OpTypePointer Function %13
+%2573 = OpConstant %11 1
+%2576 = OpConstant %11 2
+%2618 = OpConstant %11 16
+%709 = OpTypeArray %13 %2618
+%1346 = OpTypePointer Input %709
+%4339 = OpVariable %1346 Input
+%2607 = OpConstant %12 12
+%651 = OpTypePointer Input %13
+%2579 = OpConstant %11 3
+%668 = OpTypePointer Output %29
+%5139 = OpVariable %668 Output
+%5663 = OpFunction %8 None %1282
+%25029 = OpLabel
+%4902 = OpVariable %666 Function
+%10645 = OpAccessChain %667 %3608 %2574
+%8181 = OpLoad %29 %10645
+%21370 = OpAccessChain %667 %3608 %2574
+%11355 = OpLoad %29 %21370
+%23084 = OpFAdd %29 %8181 %11355
+OpStore %4902 %23084
+%21218 = OpLoad %24 %4957
+%13695 = OpLoad %29 %4902
+%23959 = OpVectorShuffle %24 %13695 %13695 0 1 2
+%14937 = OpFAdd %24 %23959 %21218
+%15653 = OpAccessChain %650 %4902 %2570
+%21354 = OpCompositeExtract %13 %14937 0
+OpStore %15653 %21354
+%16378 = OpAccessChain %650 %4902 %2573
+%15746 = OpCompositeExtract %13 %14937 1
+OpStore %16378 %15746
+%16379 = OpAccessChain %650 %4902 %2576
+%15747 = OpCompositeExtract %13 %14937 2
+OpStore %16379 %15747
+%19895 = OpAccessChain %651 %4339 %2607
+%7372 = OpLoad %13 %19895
+%21371 = OpAccessChain %650 %4902 %2579
+%11412 = OpLoad %13 %21371
+%22584 = OpFAdd %13 %11412 %7372
+%17318 = OpAccessChain %650 %4902 %2579
+OpStore %17318 %22584
+%17934 = OpLoad %29 %4902
+OpStore %5139 %17934
+OpReturn
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  SinglePassRunAndCheck<CanonicalizeIdsPass>(before, after, false, false);
+}
+
+TEST_F(CanonicalizeIdsTest, NonSemanticDebugInfo) {
+  std::string before = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %2 "main"
+OpExecutionMode %2 LocalSize 1 1 1
+%4 = OpString ""
+OpDecorate %6 ArrayStride 4
+OpDecorate %7 Block
+OpMemberDecorate %7 0 Offset 0
+OpDecorate %3 Binding 0
+OpDecorate %3 DescriptorSet 0
+%8 = OpTypeVoid
+%9 = OpTypeInt 32 0
+%10 = OpConstant %9 100
+%11 = OpTypeFunction %8
+%12 = OpTypeInt 32 1
+%13 = OpConstant %12 0
+%14 = OpTypeFloat 32
+%5 = OpTypePointer StorageBuffer %14
+%6 = OpTypeRuntimeArray %14
+%7 = OpTypeStruct %6
+%15 = OpTypePointer StorageBuffer %7
+%16 = OpConstant %14 0
+%3 = OpVariable %15 StorageBuffer
+%17 = OpExtInst %8 %1 DebugInfoNone
+%18 = OpExtInst %8 %1 DebugSource %4 %4
+%19 = OpExtInst %8 %1 DebugCompilationUnit %10 %10 %18 %10
+%20 = OpExtInst %8 %1 DebugTypeFunction %10 %8
+%21 = OpExtInst %8 %1 DebugFunction %4 %20 %18 %10 %10 %19 %4 %10 %10
+%22 = OpExtInst %8 %1 DebugEntryPoint %21 %19 %4 %4
+%23 = OpExtInst %8 %1 DebugTypeBasic %4 %10 %10 %10
+%24 = OpExtInst %8 %1 DebugTypeArray %23 %10
+%25 = OpExtInst %8 %1 DebugTypeMember %4 %24 %18 %10 %10 %10 %10 %10
+%26 = OpExtInst %8 %1 DebugTypeComposite %4 %10 %18 %10 %10 %19 %4 %10 %10 %25
+%27 = OpExtInst %8 %1 DebugGlobalVariable %4 %26 %18 %10 %10 %19 %4 %3 %10
+%2 = OpFunction %8 None %11
+%28 = OpLabel
+%29 = OpExtInst %8 %1 DebugFunctionDefinition %21 %2
+%30 = OpExtInst %8 %1 DebugScope %21
+%31 = OpExtInst %8 %1 DebugLine %18 %10 %10 %10 %10
+%32 = OpAccessChain %5 %3 %13 %13
+OpStore %32 %16
+OpReturn
+%33 = OpExtInst %8 %1 DebugNoScope
+OpFunctionEnd
+)";
+
+  std::string after = R"(OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %10742 "main"
+OpExecutionMode %10742 LocalSize 1 1 1
+%3 = OpString ""
+OpDecorate %2002 ArrayStride 4
+OpDecorate %1952 Block
+OpMemberDecorate %1952 0 Offset 0
+OpDecorate %2 Binding 0
+OpDecorate %2 DescriptorSet 0
+%8 = OpTypeVoid
+%11 = OpTypeInt 32 0
+%2870 = OpConstant %11 100
+%1282 = OpTypeFunction %8
+%12 = OpTypeInt 32 1
+%2571 = OpConstant %12 0
+%13 = OpTypeFloat 32
+%650 = OpTypePointer StorageBuffer %13
+%2002 = OpTypeRuntimeArray %13
+%1952 = OpTypeStruct %2002
+%2589 = OpTypePointer StorageBuffer %1952
+%2572 = OpConstant %13 0
+%2 = OpVariable %2589 StorageBuffer
+%4 = OpExtInst %8 %1 DebugInfoNone
+%5 = OpExtInst %8 %1 DebugSource %3 %3
+%6 = OpExtInst %8 %1 DebugCompilationUnit %2870 %2870 %5 %2870
+%7 = OpExtInst %8 %1 DebugTypeFunction %2870 %8
+%9 = OpExtInst %8 %1 DebugFunction %3 %7 %5 %2870 %2870 %6 %3 %2870 %2870
+%10 = OpExtInst %8 %1 DebugEntryPoint %9 %6 %3 %3
+%14 = OpExtInst %8 %1 DebugTypeBasic %3 %2870 %2870 %2870
+%15 = OpExtInst %8 %1 DebugTypeArray %14 %2870
+%16 = OpExtInst %8 %1 DebugTypeMember %3 %15 %5 %2870 %2870 %2870 %2870 %2870
+%17 = OpExtInst %8 %1 DebugTypeComposite %3 %2870 %5 %2870 %2870 %6 %3 %2870 %2870 %16
+%18 = OpExtInst %8 %1 DebugGlobalVariable %3 %17 %5 %2870 %2870 %6 %3 %2 %2870
+%10742 = OpFunction %8 None %1282
+%9442 = OpLabel
+%15450 = OpExtInst %8 %1 DebugFunctionDefinition %9 %10742
+%20275 = OpExtInst %8 %1 DebugScope %9
+%19 = OpExtInst %8 %1 DebugLine %5 %2870 %2870 %2870 %2870
+%20274 = OpAccessChain %650 %2 %2571 %2571
+OpStore %20274 %2572
+OpReturn
+%20276 = OpExtInst %8 %1 DebugNoScope
+OpFunctionEnd
+)";
+
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  SinglePassRunAndCheck<CanonicalizeIdsPass>(before, after, false, true);
+}
+
+}  // namespace
+}  // namespace opt
+}  // namespace spvtools
diff --git a/test/opt/ccp_test.cpp b/test/opt/ccp_test.cpp
index a8e9557..cb67a2f 100644
--- a/test/opt/ccp_test.cpp
+++ b/test/opt/ccp_test.cpp
@@ -581,7 +581,7 @@
   EXPECT_EQ(std::get<1>(res), Pass::Status::SuccessWithoutChange);
 }
 
-TEST_F(CCPTest, FoldConstantCompositeInstrucitonsWithSpecConst) {
+TEST_F(CCPTest, FoldConstantCompositeInstructionsWithSpecConst) {
   const std::string spv_asm = R"(
                OpCapability Shader
                OpMemoryModel Logical GLSL450
@@ -595,7 +595,7 @@
        %true = OpConstantTrue %bool
 ; CHECK: [[spec_const:%\w+]] = OpSpecConstantComposite %v3bool
          %11 = OpSpecConstantComposite %v3bool %true %true %true
-         %12 = OpConstantComposite %_struct_8 %11
+         %12 = OpSpecConstantComposite %_struct_8 %11
 ; CHECK: OpFunction
           %1 = OpFunction %void None %4
          %29 = OpLabel
@@ -926,6 +926,7 @@
 OpFunctionEnd
 )";
 
+  ValidatorOptions()->relax_logical_pointer = true;
   SinglePassRunAndMatch<CCPPass>(text, true);
 }
 
@@ -1237,6 +1238,34 @@
   EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
 }
 
+TEST_F(CCPTest, CCPSuccessWithNoCCPOnExtendedInstruction) {
+  // We don't expect to have Constant Propagation on certain
+  // extended instructions with 16 bit integers. This is to
+  // test that this stage doesn't throw errors. If we add support
+  // for 16 bit integers, this test should change.
+
+  const std::string text = R"(OpCapability Shader
+OpCapability Float16
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource GLSL 140
+OpName %main "main"
+%void = OpTypeVoid
+%half = OpTypeFloat 16
+%half_0x1p_0 = OpConstant %half 0x1p+0
+%6 = OpTypeFunction %void
+%main = OpFunction %void None %6
+%7 = OpLabel
+%8 = OpExtInst %half %1 Cos %half_0x1p_0
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndCheck<CCPPass>(text, text, false);
+}
+
 TEST_F(CCPTest, FunctionDeclaration) {
   // Make sure the pass works with a function declaration that is called.
   const std::string text = R"(OpCapability Addresses
diff --git a/test/opt/combine_access_chains_test.cpp b/test/opt/combine_access_chains_test.cpp
index ef7addc..677f9bc 100644
--- a/test/opt/combine_access_chains_test.cpp
+++ b/test/opt/combine_access_chains_test.cpp
@@ -573,6 +573,7 @@
 OpFunctionEnd
 )";
 
+  ValidatorOptions()->relax_logical_pointer = true;
   SinglePassRunAndMatch<CombineAccessChains>(text, true);
 }
 
@@ -610,6 +611,7 @@
 OpFunctionEnd
 )";
 
+  ValidatorOptions()->relax_logical_pointer = true;
   SinglePassRunAndMatch<CombineAccessChains>(text, true);
 }
 
@@ -648,6 +650,7 @@
 OpFunctionEnd
 )";
 
+  ValidatorOptions()->relax_logical_pointer = true;
   SinglePassRunAndMatch<CombineAccessChains>(text, true);
 }
 
@@ -763,6 +766,7 @@
 OpFunctionEnd
 )";
 
+  ValidatorOptions()->relax_logical_pointer = true;
   SinglePassRunAndMatch<CombineAccessChains>(text, true);
 }
 
diff --git a/test/opt/constant_manager_test.cpp b/test/opt/constant_manager_test.cpp
index 54c8652..a0a1317 100644
--- a/test/opt/constant_manager_test.cpp
+++ b/test/opt/constant_manager_test.cpp
@@ -102,6 +102,59 @@
   EXPECT_EQ(inst, nullptr);
 }
 
+TEST_F(ConstantManagerTest, ConstantCompositeReplicateExtMapping) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability ReplicatedCompositesEXT
+OpExtension "SPV_EXT_replicated_composites"
+OpMemoryModel Logical Simple
+%1 = OpTypeInt 32 1
+%2 = OpTypeVector %1 4
+%3 = OpConstant %1 0
+%4 = OpConstantCompositeReplicateEXT %2 %3
+%5 = OpSpecConstantCompositeReplicateEXT %2 %3
+  )";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_5, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ASSERT_NE(context, nullptr);
+
+  ConstantManager* const_mgr = context->get_constant_mgr();
+  const Constant* base_constant = const_mgr->FindDeclaredConstant(3);
+  ASSERT_NE(base_constant, nullptr);
+
+  const Constant* composite_constant = const_mgr->FindDeclaredConstant(4);
+  ASSERT_NE(composite_constant, nullptr);
+  const Vector* vector_type = composite_constant->type()->AsVector();
+  ASSERT_NE(vector_type, nullptr);
+
+  const CompositeConstant* composite =
+      composite_constant->AsCompositeConstant();
+  ASSERT_NE(composite, nullptr);
+  ASSERT_EQ(composite->GetComponents().size(),
+            static_cast<size_t>(vector_type->element_count()));
+  ASSERT_FALSE(composite->GetComponents().empty());
+  for (const Constant* component : composite->GetComponents()) {
+    EXPECT_EQ(component, base_constant);
+  }
+
+  const Constant* spec_composite_constant = const_mgr->FindDeclaredConstant(5);
+  ASSERT_NE(spec_composite_constant, nullptr);
+  const Vector* spec_vector_type = spec_composite_constant->type()->AsVector();
+  ASSERT_NE(spec_vector_type, nullptr);
+
+  const CompositeConstant* spec_composite =
+      spec_composite_constant->AsCompositeConstant();
+  ASSERT_NE(spec_composite, nullptr);
+  ASSERT_EQ(spec_composite->GetComponents().size(),
+            static_cast<size_t>(spec_vector_type->element_count()));
+  ASSERT_FALSE(spec_composite->GetComponents().empty());
+  for (const Constant* component : spec_composite->GetComponents()) {
+    EXPECT_EQ(component, base_constant);
+  }
+}
+
 }  // namespace
 }  // namespace analysis
 }  // namespace opt
diff --git a/test/opt/convert_relaxed_to_half_test.cpp b/test/opt/convert_relaxed_to_half_test.cpp
index c577404..169ab29 100644
--- a/test/opt/convert_relaxed_to_half_test.cpp
+++ b/test/opt/convert_relaxed_to_half_test.cpp
@@ -514,28 +514,30 @@
 %14 = OpFAdd %half %55 %56
 %16 = OpCompositeConstruct %v2half %13 %14
 %58 = OpFConvert %float %14
-%18 = OpImageSampleDrefImplicitLod %float %46 %16 %58
+%59 = OpFConvert %v2float %16
+%18 = OpImageSampleDrefImplicitLod %float %46 %59 %58
 %48 = OpLoad %27 %g_tTex1df4
 %49 = OpLoad %29 %g_sSamp
 %50 = OpSampledImage %31 %48 %49
-%59 = OpFConvert %half %12
-%60 = OpFConvert %half %float_0_200000003
-%15 = OpFMul %half %59 %60
+%60 = OpFConvert %half %12
+%61 = OpFConvert %half %float_0_200000003
+%15 = OpFMul %half %60 %61
 %51 = OpAccessChain %_ptr_Uniform_float %_ %int_1
 %17 = OpLoad %float %51
-%61 = OpFConvert %half %17
-%62 = OpFConvert %half %float_0_200000003
-%19 = OpFAdd %half %61 %62
+%62 = OpFConvert %half %17
+%63 = OpFConvert %half %float_0_200000003
+%19 = OpFAdd %half %62 %63
 %20 = OpCompositeConstruct %v2half %15 %19
-%63 = OpFConvert %float %19
-%21 = OpImageSampleDrefImplicitLod %float %50 %20 %63
-%64 = OpFConvert %half %18
-%65 = OpFConvert %half %21
-%22 = OpFAdd %half %64 %65
-%66 = OpFConvert %half %float_0_5
-%23 = OpFMul %half %22 %66
-%67 = OpFConvert %float %23
-OpStore %_entryPointOutput_Color %67
+%64 = OpFConvert %float %19
+%65 = OpFConvert %v2float %20
+%21 = OpImageSampleDrefImplicitLod %float %50 %65 %64
+%66 = OpFConvert %half %18
+%67 = OpFConvert %half %21
+%22 = OpFAdd %half %66 %67
+%68 = OpFConvert %half %float_0_5
+%23 = OpFMul %half %22 %68
+%69 = OpFConvert %float %23
+OpStore %_entryPointOutput_Color %69
 OpReturn
 OpFunctionEnd
 )";
@@ -1616,6 +1618,7 @@
 TEST_F(ConvertToHalfTest, PreserveImageOperandPrecision) {
   // Ensure that a non-relaxed texture coordinate does not get relaxed nor
   // converted to half precision if the image instruction is marked relaxed.
+  // Update - coordinates are not allowed to be 16-bit regardless
 
   // Also ensure that a relaxed local variable does get converted to half
   // precision before being passed to an image opeartor.
@@ -1639,7 +1642,7 @@
                OpCapability Float16
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main" %13 %25
+               OpEntryPoint Fragment %4 "main" %13 %26
                OpExecutionMode %4 OriginUpperLeft
                OpSource ESSL 310
                OpDecorate %9 RelaxedPrecision
@@ -1648,8 +1651,8 @@
                OpDecorate %17 DescriptorSet 3
                OpDecorate %17 Binding 0
                OpDecorate %18 RelaxedPrecision
-               OpDecorate %23 RelaxedPrecision
-               OpDecorate %25 Location 10
+               OpDecorate %24 RelaxedPrecision
+               OpDecorate %26 Location 10
           %2 = OpTypeVoid
           %3 = OpTypeFunction %2
           %6 = OpTypeFloat 32
@@ -1669,13 +1672,13 @@
          %17 = OpVariable %16 UniformConstant
          %19 = OpTypeVector %6 2
 ;CHECK: [[vec2_t:%\w+]] = OpTypeVector [[float32_t]] 2
-         %24 = OpTypePointer Input %7
+         %25 = OpTypePointer Input %7
 ;CHECK: [[input_ptr_t:%\w+]] = OpTypePointer Input [[vec4_t]]
-         %25 = OpVariable %24 Input
-         %29 = OpTypeFloat 16
+         %26 = OpVariable %25 Input
+         %30 = OpTypeFloat 16
 ;CHECK: [[float16_t:%\w+]] = OpTypeFloat 16
-         %30 = OpTypeVector %29 4
-         %33 = OpTypeVector %29 2
+         %31 = OpTypeVector %30 4
+         %34 = OpTypeVector %30 2
 ;CHECK: [[vec2_16b_t:%\w+]] = OpTypeVector [[float16_t]] 2
           %4 = OpFunction %2 None %3
           %5 = OpLabel
@@ -1686,26 +1689,27 @@
                OpStore %9 %11
          %18 = OpLoad %15 %17
          %20 = OpLoad %7 %9
-         %31 = OpFConvert %30 %20
-         %32 = OpFConvert %30 %20
+         %32 = OpFConvert %31 %20
+         %33 = OpFConvert %31 %20
 
-; The first sample op should get a 16b coordinate
-         %21 = OpVectorShuffle %33 %31 %32 0 1
+; The first sample op should get a 32b coordinate, even if relaxed
+         %21 = OpVectorShuffle %34 %32 %33 0 1
 ;CHECK: [[uv_16b:%\w+]] = OpVectorShuffle [[vec2_16b_t]]
-         %22 = OpImageSampleImplicitLod %7 %18 %21
-;CHECK: OpImageSampleImplicitLod [[vec4_t]] {{%\w+}} [[uv_16b]]
+         %22 = OpFConvert %19 %21
+         %23 = OpImageSampleImplicitLod %7 %18 %22
+;CHECK: OpImageSampleImplicitLod [[vec4_t]] {{%\w+}} {{%\w+}}
 
-               OpStore %13 %22
-         %23 = OpLoad %15 %17
-         %26 = OpLoad %7 %25
+               OpStore %13 %23
+         %24 = OpLoad %15 %17
+         %27 = OpLoad %7 %26
 
 ; The second sample op should get a 32b coordinate
-         %27 = OpVectorShuffle %19 %26 %26 0 1
+         %28 = OpVectorShuffle %19 %27 %27 0 1
 ;CHECK: [[uv_32b:%\w+]] = OpVectorShuffle [[vec2_t]]
-         %28 = OpImageSampleImplicitLod %7 %23 %27
+         %29 = OpImageSampleImplicitLod %7 %24 %28
 ;CHECK: OpImageSampleImplicitLod [[vec4_t]] {{%\w+}} [[uv_32b]]
 
-               OpStore %13 %28
+               OpStore %13 %29
                OpReturn
                OpFunctionEnd
   )";
diff --git a/test/opt/convert_to_sampled_image_test.cpp b/test/opt/convert_to_sampled_image_test.cpp
index 37f6560..31a1786 100644
--- a/test/opt/convert_to_sampled_image_test.cpp
+++ b/test/opt/convert_to_sampled_image_test.cpp
@@ -83,7 +83,7 @@
         // 6. maximum spec id
         {"4294967295:0", true,
          VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{
-             4294967295, 0}})},
+             4294967295u, 0}})},
         // 7. minimum spec id
         {"0:100", true,
          VectorOfDescriptorSetAndBindingPairs({DescriptorSetAndBinding{0,
diff --git a/test/opt/copy_prop_array_test.cpp b/test/opt/copy_prop_array_test.cpp
index 7d8a325..de25ab9 100644
--- a/test/opt/copy_prop_array_test.cpp
+++ b/test/opt/copy_prop_array_test.cpp
@@ -2146,6 +2146,335 @@
   SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
   SinglePassRunAndMatch<CopyPropagateArrays>(before, true);
 }
+
+TEST_F(CopyPropArrayPassTest, PropCopyLogical) {
+  const std::string before = R"(
+; CHECK: [[v4array_ptr:%\w+]] = OpTypePointer Uniform %14
+; CHECK: [[v4_ptr:%\w+]] = OpTypePointer Uniform %7
+; CHECK: [[ac:%\w+]] = OpAccessChain [[v4array_ptr]] %19 %21 %33
+; CHECK: %47 = OpAccessChain [[v4_ptr]] [[ac]] %37
+      OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint Vertex %4 "main" %19 %30 %32
+      OpSource GLSL 430
+      OpName %4 "main"
+      OpDecorate %14 ArrayStride 16
+      OpDecorate %15 ArrayStride 16
+      OpMemberDecorate %16 0 Offset 0
+      OpMemberDecorate %16 1 Offset 32
+      OpDecorate %17 Block
+      OpMemberDecorate %17 0 Offset 0
+      OpDecorate %19 Binding 0
+      OpDecorate %19 DescriptorSet 0
+      OpDecorate %28 Block
+      OpMemberDecorate %28 0 BuiltIn Position
+      OpMemberDecorate %28 1 BuiltIn PointSize
+      OpMemberDecorate %28 2 BuiltIn ClipDistance
+      OpDecorate %32 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypeInt 32 0
+ %9 = OpConstant %8 2
+%10 = OpTypeArray %7 %9
+%11 = OpTypeStruct %10 %10
+%14 = OpTypeArray %7 %9
+%15 = OpTypeArray %7 %9
+%16 = OpTypeStruct %14 %15
+%17 = OpTypeStruct %16
+%18 = OpTypePointer Uniform %17
+%19 = OpVariable %18 Uniform
+%20 = OpTypeInt 32 1
+%21 = OpConstant %20 0
+%22 = OpTypePointer Uniform %16
+%26 = OpConstant %8 1
+%27 = OpTypeArray %6 %26
+%28 = OpTypeStruct %7 %6 %27
+%29 = OpTypePointer Output %28
+%30 = OpVariable %29 Output
+%31 = OpTypePointer Input %7
+%32 = OpVariable %31 Input
+%33 = OpConstant %8 0
+%34 = OpTypePointer Input %6
+%38 = OpTypePointer Function %7
+%41 = OpTypePointer Output %7
+%43 = OpTypePointer Function %10
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+%44 = OpVariable %43 Function
+%23 = OpAccessChain %22 %19 %21
+%24 = OpLoad %16 %23
+%25 = OpCopyLogical %11 %24
+%46 = OpCompositeExtract %10 %25 0
+      OpStore %44 %46
+%35 = OpAccessChain %34 %32 %33
+%36 = OpLoad %6 %35
+%37 = OpConvertFToS %20 %36
+%47 = OpAccessChain %38 %44 %37
+%40 = OpLoad %7 %47
+%42 = OpAccessChain %41 %30 %21
+      OpStore %42 %40
+      OpReturn
+      OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_6);
+  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  SinglePassRunAndMatch<CopyPropagateArrays>(before, true);
+}
+
+// Ensure that the use of the global variable in a debug instruction does not
+// stop copy propagation. We expect the image operand to OpImageTexelPointer to
+// be replaced.
+TEST_F(CopyPropArrayPassTest, DebugInstNotStore) {
+  const std::string before = R"(
+               OpCapability Shader
+               OpCapability SampledBuffer
+               OpExtension "SPV_KHR_non_semantic_info"
+              OpExtension "SPV_EXT_descriptor_indexing"
+          %1 = OpExtInstImport "GLSL.std.450"
+          %2 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %3 "maincomp"
+               OpExecutionMode %3 LocalSize 16 16 1
+          %4 = OpString ""
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+     %uint_6 = OpConstant %uint 6
+     %uint_8 = OpConstant %uint 8
+     %uint_2 = OpConstant %uint 2
+     %uint_7 = OpConstant %uint 7
+     %uint_3 = OpConstant %uint 3
+    %uint_32 = OpConstant %uint 32
+    %uint_16 = OpConstant %uint 16
+     %uint_4 = OpConstant %uint 4
+         %16 = OpTypeImage %uint Buffer 2 0 0 2 R32ui
+%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16
+       %void = OpTypeVoid
+     %uint_5 = OpConstant %uint 5
+    %uint_70 = OpConstant %uint 70
+    %uint_71 = OpConstant %uint 71
+    %uint_72 = OpConstant %uint 72
+    %uint_17 = OpConstant %uint 17
+     %uint_9 = OpConstant %uint 9
+    %uint_25 = OpConstant %uint 25
+    %uint_14 = OpConstant %uint 14
+    %uint_24 = OpConstant %uint 24
+    %uint_13 = OpConstant %uint 13
+         %29 = OpTypeFunction %void
+%_ptr_Function_16 = OpTypePointer Function %16
+%_ptr_Image_uint = OpTypePointer Image %uint
+; CHECK: [[GV:%\w+]] = OpVariable {{%\w+}} UniformConstant
+         %32 = OpVariable %_ptr_UniformConstant_16 UniformConstant
+         %33 = OpExtInst %void %2 DebugInfoNone
+         %34 = OpExtInst %void %2 DebugExpression
+         %35 = OpExtInst %void %2 DebugTypeBasic %4 %uint_32 %uint_3 %uint_0
+         %36 = OpExtInst %void %2 DebugTypeVector %35 %uint_3
+         %37 = OpExtInst %void %2 DebugSource %4 %4
+         %38 = OpExtInst %void %2 DebugCompilationUnit %uint_1 %uint_4 %37 %uint_5
+         %39 = OpExtInst %void %2 DebugTypeTemplateParameter %4 %36 %33 %37 %uint_0 %uint_0
+         %40 = OpExtInst %void %2 DebugTypeBasic %4 %uint_32 %uint_6 %uint_0
+         %41 = OpExtInst %void %2 DebugSource %4 %4
+         %42 = OpExtInst %void %2 DebugCompilationUnit %uint_1 %uint_4 %41 %uint_5
+         %43 = OpExtInst %void %2 DebugTypeComposite %4 %uint_0 %37 %uint_0 %uint_0 %38 %4 %33 %uint_3
+         %44 = OpExtInst %void %2 DebugTypeTemplateParameter %4 %40 %33 %37 %uint_0 %uint_0
+         %45 = OpExtInst %void %2 DebugTypeTemplate %43 %44
+         %46 = OpExtInst %void %2 DebugTypeFunction %uint_3 %void %40
+         %47 = OpExtInst %void %2 DebugFunction %4 %46 %37 %uint_70 %uint_1 %38 %4 %uint_3 %uint_71
+         %48 = OpExtInst %void %2 DebugLexicalBlock %37 %uint_71 %uint_1 %47
+         %49 = OpExtInst %void %2 DebugLocalVariable %4 %45 %37 %uint_72 %uint_17 %48 %uint_4
+         %50 = OpExtInst %void %2 DebugTypeFunction %uint_3 %void
+         %51 = OpExtInst %void %2 DebugSource %4 %4
+         %52 = OpExtInst %void %2 DebugCompilationUnit %uint_1 %uint_4 %51 %uint_5
+         %53 = OpExtInst %void %2 DebugFunction %4 %50 %51 %uint_24 %uint_1 %52 %4 %uint_3 %uint_25
+         %54 = OpExtInst %void %2 DebugGlobalVariable %4 %45 %37 %uint_17 %uint_16 %38 %4 %32 %uint_8
+         %55 = OpExtInst %void %2 DebugTypeMember %4 %40 %37 %uint_14 %uint_7 %uint_0 %uint_32 %uint_3
+         %56 = OpExtInst %void %2 DebugTypeComposite %4 %uint_1 %37 %uint_13 %uint_9 %38 %4 %uint_32 %uint_3 %55
+         %57 = OpExtInst %void %2 DebugEntryPoint %53 %42 %4 %4
+          %3 = OpFunction %void None %29
+         %58 = OpLabel
+         %59 = OpVariable %_ptr_Function_16 Function
+         %60 = OpLoad %16 %32
+               OpStore %59 %60
+         %61 = OpExtInst %void %2 DebugDeclare %49 %59 %34
+         %62 = OpExtInst %void %2 DebugLine %37 %uint_0 %uint_0 %uint_2 %uint_2
+; CHECK: OpImageTexelPointer %_ptr_Image_uint [[GV]] %uint_0 %uint_0
+         %63 = OpImageTexelPointer %_ptr_Image_uint %59 %uint_0 %uint_0
+         %64 = OpAtomicIAdd %uint %63 %uint_1 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_1);
+  SinglePassRunAndMatch<CopyPropagateArrays>(before, false);
+}
+
+TEST_F(CopyPropArrayPassTest, DebugInstNotDominatingStore) {
+  // Move the debug value to after the new access chain instruction.
+  const std::string before = R"(
+               OpCapability Shader
+               OpCapability MeshShadingEXT
+               OpExtension "SPV_EXT_mesh_shader"
+          %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %2 "main" %3
+               OpExecutionMode %2 LocalSize 1 1 1
+               OpExecutionMode %2 OutputTrianglesEXT
+               OpExecutionMode %2 OutputVertices 64
+               OpExecutionMode %2 OutputPrimitivesEXT 124
+          %4 = OpString ""
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+%_arr_uint_uint_32 = OpTypeArray %uint %uint_32
+ %_struct_10 = OpTypeStruct %_arr_uint_uint_32
+%_ptr_TaskPayloadWorkgroupEXT__struct_10 = OpTypePointer TaskPayloadWorkgroupEXT %_struct_10
+   %uint_124 = OpConstant %uint 124
+    %uint_64 = OpConstant %uint 64
+       %void = OpTypeVoid
+     %uint_6 = OpConstant %uint 6
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+     %uint_4 = OpConstant %uint 4
+     %uint_5 = OpConstant %uint 5
+  %uint_1024 = OpConstant %uint 1024
+     %uint_7 = OpConstant %uint 7
+     %uint_8 = OpConstant %uint 8
+     %uint_3 = OpConstant %uint 3
+    %uint_96 = OpConstant %uint 96
+    %uint_11 = OpConstant %uint 11
+    %uint_12 = OpConstant %uint 12
+    %uint_10 = OpConstant %uint 10
+    %uint_16 = OpConstant %uint 16
+    %uint_21 = OpConstant %uint 21
+    %uint_17 = OpConstant %uint 17
+    %uint_26 = OpConstant %uint 26
+         %32 = OpTypeFunction %void
+%_ptr_Function__arr_uint_uint_32 = OpTypePointer Function %_arr_uint_uint_32
+          %3 = OpVariable %_ptr_TaskPayloadWorkgroupEXT__struct_10 TaskPayloadWorkgroupEXT
+         %34 = OpExtInst %void %1 DebugOperation %uint_0
+         %35 = OpExtInst %void %1 DebugTypeBasic %4 %uint_32 %uint_6 %uint_0
+         %36 = OpExtInst %void %1 DebugSource %4 %4
+         %37 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %36 %uint_5
+         %38 = OpExtInst %void %1 DebugTypeArray %35 %uint_32
+         %39 = OpExtInst %void %1 DebugTypeMember %4 %38 %36 %uint_7 %uint_8 %uint_0 %uint_1024 %uint_3
+         %40 = OpExtInst %void %1 DebugTypeComposite %4 %uint_1 %36 %uint_6 %uint_8 %37 %4 %uint_1024 %uint_3 %39
+         %41 = OpExtInst %void %1 DebugTypeVector %35 %uint_3
+         %42 = OpExtInst %void %1 DebugTypeArray %41 %uint_124
+         %43 = OpExtInst %void %1 DebugTypeBasic %4 %uint_32 %uint_3 %uint_0
+         %44 = OpExtInst %void %1 DebugTypeVector %43 %uint_3
+         %45 = OpExtInst %void %1 DebugTypeMember %4 %44 %36 %uint_11 %uint_12 %uint_0 %uint_96 %uint_3
+         %46 = OpExtInst %void %1 DebugTypeComposite %4 %uint_1 %36 %uint_10 %uint_8 %37 %4 %uint_96 %uint_3 %45
+         %47 = OpExtInst %void %1 DebugTypeArray %46 %uint_64
+         %48 = OpExtInst %void %1 DebugTypeFunction %uint_3 %void %40 %35 %42 %47
+         %49 = OpExtInst %void %1 DebugFunction %4 %48 %36 %uint_16 %uint_1 %37 %4 %uint_3 %uint_21
+         %50 = OpExtInst %void %1 DebugLocalVariable %4 %40 %36 %uint_17 %uint_26 %49 %uint_4 %uint_1
+         %51 = OpExtInst %void %1 DebugExpression %34
+; CHECK: OpFunction
+          %2 = OpFunction %void None %32
+         %52 = OpLabel
+         %53 = OpVariable %_ptr_Function__arr_uint_uint_32 Function
+; CHECK: [[new_ptr:%\w+]] = OpAccessChain %_ptr_TaskPayloadWorkgroupEXT__arr_uint_uint_32
+; CHECK: OpExtInst %void %1 DebugValue {{%\w+}} [[new_ptr]]
+         %54 = OpExtInst %void %1 DebugValue %50 %53 %51 %int_0
+         %55 = OpLoad %_struct_10 %3
+         %56 = OpCompositeExtract %_arr_uint_uint_32 %55 0
+               OpStore %53 %56
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_1);
+  SinglePassRunAndMatch<CopyPropagateArrays>(before, false);
+}
+
+TEST_F(CopyPropArrayPassTest, DebugInstNotDominatingStoreInDifferentBB) {
+  // Move the debug value to after the new access chain instruction.
+  const std::string before = R"(
+               OpCapability Shader
+               OpCapability MeshShadingEXT
+               OpExtension "SPV_EXT_mesh_shader"
+          %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %2 "main" %3
+               OpExecutionMode %2 LocalSize 1 1 1
+               OpExecutionMode %2 OutputTrianglesEXT
+               OpExecutionMode %2 OutputVertices 64
+               OpExecutionMode %2 OutputPrimitivesEXT 124
+          %4 = OpString ""
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+%_arr_uint_uint_32 = OpTypeArray %uint %uint_32
+ %_struct_10 = OpTypeStruct %_arr_uint_uint_32
+%_ptr_TaskPayloadWorkgroupEXT__struct_10 = OpTypePointer TaskPayloadWorkgroupEXT %_struct_10
+   %uint_124 = OpConstant %uint 124
+    %uint_64 = OpConstant %uint 64
+       %void = OpTypeVoid
+     %uint_6 = OpConstant %uint 6
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+     %uint_4 = OpConstant %uint 4
+     %uint_5 = OpConstant %uint 5
+  %uint_1024 = OpConstant %uint 1024
+     %uint_7 = OpConstant %uint 7
+     %uint_8 = OpConstant %uint 8
+     %uint_3 = OpConstant %uint 3
+    %uint_96 = OpConstant %uint 96
+    %uint_11 = OpConstant %uint 11
+    %uint_12 = OpConstant %uint 12
+    %uint_10 = OpConstant %uint 10
+    %uint_16 = OpConstant %uint 16
+    %uint_21 = OpConstant %uint 21
+    %uint_17 = OpConstant %uint 17
+    %uint_26 = OpConstant %uint 26
+         %32 = OpTypeFunction %void
+%_ptr_Function__arr_uint_uint_32 = OpTypePointer Function %_arr_uint_uint_32
+          %3 = OpVariable %_ptr_TaskPayloadWorkgroupEXT__struct_10 TaskPayloadWorkgroupEXT
+         %34 = OpExtInst %void %1 DebugOperation %uint_0
+         %35 = OpExtInst %void %1 DebugTypeBasic %4 %uint_32 %uint_6 %uint_0
+         %36 = OpExtInst %void %1 DebugSource %4 %4
+         %37 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %36 %uint_5
+         %38 = OpExtInst %void %1 DebugTypeArray %35 %uint_32
+         %39 = OpExtInst %void %1 DebugTypeMember %4 %38 %36 %uint_7 %uint_8 %uint_0 %uint_1024 %uint_3
+         %40 = OpExtInst %void %1 DebugTypeComposite %4 %uint_1 %36 %uint_6 %uint_8 %37 %4 %uint_1024 %uint_3 %39
+         %41 = OpExtInst %void %1 DebugTypeVector %35 %uint_3
+         %42 = OpExtInst %void %1 DebugTypeArray %41 %uint_124
+         %43 = OpExtInst %void %1 DebugTypeBasic %4 %uint_32 %uint_3 %uint_0
+         %44 = OpExtInst %void %1 DebugTypeVector %43 %uint_3
+         %45 = OpExtInst %void %1 DebugTypeMember %4 %44 %36 %uint_11 %uint_12 %uint_0 %uint_96 %uint_3
+         %46 = OpExtInst %void %1 DebugTypeComposite %4 %uint_1 %36 %uint_10 %uint_8 %37 %4 %uint_96 %uint_3 %45
+         %47 = OpExtInst %void %1 DebugTypeArray %46 %uint_64
+         %48 = OpExtInst %void %1 DebugTypeFunction %uint_3 %void %40 %35 %42 %47
+         %49 = OpExtInst %void %1 DebugFunction %4 %48 %36 %uint_16 %uint_1 %37 %4 %uint_3 %uint_21
+         %50 = OpExtInst %void %1 DebugLocalVariable %4 %40 %36 %uint_17 %uint_26 %49 %uint_4 %uint_1
+         %51 = OpExtInst %void %1 DebugExpression %34
+; CHECK: OpFunction
+          %2 = OpFunction %void None %32
+         %52 = OpLabel
+         %53 = OpVariable %_ptr_Function__arr_uint_uint_32 Function
+; CHECK: [[new_ptr:%\w+]] = OpAccessChain %_ptr_TaskPayloadWorkgroupEXT__arr_uint_uint_32
+; CHECK: OpExtInst %void %1 DebugValue {{%\w+}} [[new_ptr]]
+         %54 = OpExtInst %void %1 DebugValue %50 %53 %51 %int_0
+         %55 = OpLoad %_struct_10 %3
+         %56 = OpCompositeExtract %_arr_uint_uint_32 %55 0
+               OpBranch %57
+         %57 = OpLabel
+               OpStore %53 %56
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_1);
+  SinglePassRunAndMatch<CopyPropagateArrays>(before, false);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/debug_info_manager_test.cpp b/test/opt/debug_info_manager_test.cpp
index 9c75728..08d8d99 100644
--- a/test/opt/debug_info_manager_test.cpp
+++ b/test/opt/debug_info_manager_test.cpp
@@ -767,8 +767,6 @@
         %101 = OpExtInst %void %1 DebugScope %22
                OpLine %5 13 7
                OpStore %100 %31
-               OpNoLine
-        %102 = OpExtInst %void %1 DebugNoScope
          %36 = OpExtInst %void %1 DebugDeclare %25 %100 %13
                OpReturn
                OpFunctionEnd
@@ -877,6 +875,44 @@
   EXPECT_EQ(dbg_var->GetInOperand(8), originalOperands[10]);
 }
 
+TEST(DebugInfoManagerTest, DebugInstructionWithForwardRefsKHR) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+               OpExtension "SPV_KHR_relaxed_extended_instruction"
+          %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_3 = OpConstant %uint 3
+         %10 = OpExtInstWithForwardRefsKHR %void %1 DebugTypeFunction %uint_3 %11
+         %11 = OpExtInst %void %1 DebugInfoNone
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  ASSERT_NE(nullptr, context);
+
+  Instruction* forward_ref_inst = context->get_def_use_mgr()->GetDef(10);
+  ASSERT_NE(nullptr, forward_ref_inst);
+  EXPECT_EQ(forward_ref_inst->opcode(), spv::Op::OpExtInstWithForwardRefsKHR);
+
+  EXPECT_EQ(forward_ref_inst->GetShaderDebugOpcode(),
+            NonSemanticShaderDebugInfoDebugTypeFunction);
+
+  EXPECT_EQ(forward_ref_inst->GetCommonDebugOpcode(),
+            CommonDebugInfoDebugTypeFunction);
+}
+
 }  // namespace
 }  // namespace analysis
 }  // namespace opt
diff --git a/test/opt/eliminate_dead_member_test.cpp b/test/opt/eliminate_dead_member_test.cpp
index bb0ec03..6fdf91b 100644
--- a/test/opt/eliminate_dead_member_test.cpp
+++ b/test/opt/eliminate_dead_member_test.cpp
@@ -958,7 +958,7 @@
 ; CHECK: OpMemberDecorate %type__Globals 1 Offset 16
 ; CHECK: %type__Globals = OpTypeStruct %float %float
 ; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_Uniform_type__Globals %_Globals %uint_0
-; CHECK: OpPtrAccessChain %_ptr_Uniform_float [[ac]] %uint_1 %uint_0
+; CHECK: OpPtrAccessChain %_ptr_Uniform_float [[ac]] %uint_0 %uint_0
 ; CHECK: OpPtrAccessChain %_ptr_Uniform_float [[ac]] %uint_0 %uint_1
                OpCapability Shader
                OpCapability VariablePointersStorageBuffer
@@ -995,12 +995,13 @@
        %main = OpFunction %void None %14
          %16 = OpLabel
          %17 = OpAccessChain %_ptr_Uniform_type__Globals %_Globals %uint_0
-         %18 = OpPtrAccessChain %_ptr_Uniform_float %17 %uint_1 %uint_0
+         %18 = OpPtrAccessChain %_ptr_Uniform_float %17 %uint_0 %uint_0
          %19 = OpPtrAccessChain %_ptr_Uniform_float %17 %uint_0 %uint_2
                OpReturn
                OpFunctionEnd
 )";
 
+  ValidatorOptions()->relax_logical_pointer = true;
   SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true);
 }
 
@@ -1060,6 +1061,7 @@
                OpFunctionEnd
 )";
 
+  ValidatorOptions()->relax_logical_pointer = true;
   SinglePassRunAndMatch<opt::EliminateDeadMembersPass>(text, true);
 }
 
diff --git a/test/opt/feature_manager_test.cpp b/test/opt/feature_manager_test.cpp
index 7e8f92c..6366d42 100644
--- a/test/opt/feature_manager_test.cpp
+++ b/test/opt/feature_manager_test.cpp
@@ -179,6 +179,45 @@
   EXPECT_TRUE(capabilities.contains(spv::Capability::Matrix));
 }
 
+TEST_F(FeatureManagerTest, HasConditionalExtension) {
+  const std::string text = R"(
+         OpCapability Linkage
+         OpCapability Shader
+         OpCapability SpecConditionalINTEL
+         OpExtension "SPV_INTEL_function_variants"
+         OpConditionalExtensionINTEL %1 "SPV_KHR_variable_pointers"
+         OpMemoryModel Logical GLSL450
+ %bool = OpTypeBool
+    %1 = OpSpecConstantTrue %bool
+  )";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_6, nullptr, text);
+  ASSERT_NE(context, nullptr);
+
+  EXPECT_TRUE(context->get_feature_mgr()->HasExtension(
+      Extension::kSPV_KHR_variable_pointers));
+}
+
+TEST_F(FeatureManagerTest, HasConditionalCapability) {
+  const std::string text = R"(
+         OpCapability Linkage
+         OpCapability Shader
+         OpCapability SpecConditionalINTEL
+         OpConditionalCapabilityINTEL %1 Int8
+         OpExtension "SPV_INTEL_function_variants"
+         OpMemoryModel Logical GLSL450
+ %bool = OpTypeBool
+    %1 = OpSpecConstantTrue %bool
+  )";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_6, nullptr, text);
+  ASSERT_NE(context, nullptr);
+
+  EXPECT_TRUE(context->get_feature_mgr()->HasCapability(spv::Capability::Int8));
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/fold_spec_const_op_composite_test.cpp b/test/opt/fold_spec_const_op_composite_test.cpp
index 335e0f5..2d5af28 100644
--- a/test/opt/fold_spec_const_op_composite_test.cpp
+++ b/test/opt/fold_spec_const_op_composite_test.cpp
@@ -1,4 +1,5 @@
 // Copyright (c) 2016 Google Inc.
+// Copyright (c) 2025 Arm Ltd.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -737,6 +738,9 @@
       "%inner_struct = OpTypeStruct %bool %int %float",
       "%outer_struct = OpTypeStruct %inner_struct %int",
       "%flat_struct = OpTypeStruct %bool %int %float",
+      "%flat_struct_replicate = OpTypeStruct %int %int %int",
+      "%inner_struct_replicate = OpTypeStruct %float %float %float",
+      "%outer_struct_replicate = OpTypeStruct %inner_struct_replicate %inner_struct_replicate",
 
       // common constants
       // scalar constants:
@@ -826,7 +830,7 @@
             StripOpNameInstructions(optimized));
 }
 
-// Tests that OpSpecConstantComposite opcodes are replace with
+// Tests that OpSpecConstantComposite opcodes are replaced with
 // OpConstantComposite correctly.
 INSTANTIATE_TEST_SUITE_P(
     Composite, FoldSpecConstantOpAndCompositePassTest,
@@ -946,6 +950,113 @@
         // clang-format on
     })));
 
+// Tests that OpSpecConstantCompositeReplicateEXT opcodes are replaced with
+// OpConstantCompositeReplicateEXT correctly.
+INSTANTIATE_TEST_SUITE_P(
+    CompositeReplicate, FoldSpecConstantOpAndCompositePassTest,
+    ::testing::ValuesIn(std::vector<
+                        FoldSpecConstantOpAndCompositePassTestCase>({
+        // clang-format off
+            // normal vector
+            {
+              // original
+              {
+                "%spec_v2bool = OpSpecConstantCompositeReplicateEXT %v2bool %bool_true",
+                "%spec_v2uint = OpSpecConstantCompositeReplicateEXT %v2uint %unsigned_one",
+                "%spec_v2int_a = OpSpecConstantCompositeReplicateEXT %v2int %signed_two",
+                // Spec constants whose value can not be fully resolved should
+                // not be processed.
+                "%spec_int = OpSpecConstant %int 99",
+                "%spec_v2int_b = OpSpecConstantCompositeReplicateEXT %v2int %spec_int",
+              },
+              // expected
+              {
+                "%spec_v2bool = OpConstantCompositeReplicateEXT %v2bool %bool_true",
+                "%spec_v2uint = OpConstantCompositeReplicateEXT %v2uint %unsigned_one",
+                "%spec_v2int_a = OpConstantCompositeReplicateEXT %v2int %signed_two",
+                "%spec_int = OpSpecConstant %int 99",
+                "%spec_v2int_b = OpSpecConstantCompositeReplicateEXT %v2int %spec_int",
+              },
+            },
+            // vector with null constants
+            {
+              // original
+              {
+                "%null_bool = OpConstantNull %bool",
+                "%null_int = OpConstantNull %int",
+                "%spec_v2bool = OpSpecConstantCompositeReplicateEXT %v2bool %null_bool",
+                "%spec_v3int = OpSpecConstantCompositeReplicateEXT %v3int %null_int",
+                "%spec_v4int = OpSpecConstantCompositeReplicateEXT %v4int %null_int",
+              },
+              // expected
+              {
+                "%null_bool = OpConstantNull %bool",
+                "%null_int = OpConstantNull %int",
+                "%spec_v2bool = OpConstantCompositeReplicateEXT %v2bool %null_bool",
+                "%spec_v3int = OpConstantCompositeReplicateEXT %v3int %null_int",
+                "%spec_v4int = OpConstantCompositeReplicateEXT %v4int %null_int",
+              },
+            },
+            // flat struct
+            {
+              // original
+              {
+                "%int_1 = OpConstant %int 1",
+                "%flat_1 = OpSpecConstantCompositeReplicateEXT %flat_struct_replicate %int_1",
+                // following struct should not be folded as the value of
+                // %spec_float is not determined.
+                "%spec_int = OpSpecConstant %int 1",
+                "%flat_2 = OpSpecConstantCompositeReplicateEXT %flat_struct_replicate %spec_int",
+              },
+              // expected
+              {
+                "%int_1 = OpConstant %int 1",
+                "%flat_1 = OpConstantCompositeReplicateEXT %flat_struct_replicate %int_1",
+                "%spec_int = OpSpecConstant %int 1",
+                "%flat_2 = OpSpecConstantCompositeReplicateEXT %flat_struct_replicate %spec_int",
+              }
+            },
+            // nested struct
+            {
+              // original
+              {
+                "%float_1 = OpConstant %float 1",
+                "%inner_1 = OpSpecConstantCompositeReplicateEXT %inner_struct_replicate %float_1",
+                "%outer_1 = OpSpecConstantCompositeReplicateEXT %outer_struct_replicate %inner_1",
+                // following structs should not be folded as the value of
+                // %spec_float is not determined.
+                "%spec_float = OpSpecConstant %float 1",
+                "%inner_2 = OpSpecConstantCompositeReplicateEXT %inner_struct_replicate %spec_float",
+                "%outer_2 = OpSpecConstantCompositeReplicateEXT %outer_struct_replicate %inner_2",
+              },
+              // expected
+              {
+                "%float_1 = OpConstant %float 1",
+                "%inner_1 = OpConstantCompositeReplicateEXT %inner_struct_replicate %float_1",
+                "%outer_1 = OpConstantCompositeReplicateEXT %outer_struct_replicate %inner_1",
+                "%spec_float = OpSpecConstant %float 1",
+                "%inner_2 = OpSpecConstantCompositeReplicateEXT %inner_struct_replicate %spec_float",
+                "%outer_2 = OpSpecConstantCompositeReplicateEXT %outer_struct_replicate %inner_2",
+              }
+            },
+            // composite constants touched by OpUndef should be skipped
+            {
+              // original
+              {
+                "%undef = OpUndef %float",
+                "%inner = OpConstantCompositeReplicateEXT %inner_struct_replicate %undef",
+                "%outer = OpSpecConstantCompositeReplicateEXT %outer_struct_replicate %inner",
+              },
+              // expected
+              {
+                "%undef = OpUndef %float",
+                "%inner = OpConstantCompositeReplicateEXT %inner_struct_replicate %undef",
+                "%outer = OpSpecConstantCompositeReplicateEXT %outer_struct_replicate %inner",
+              },
+            },
+        // clang-format on
+    })));
+
 // Tests for operations that resulting in different types.
 INSTANTIATE_TEST_SUITE_P(
     Cast, FoldSpecConstantOpAndCompositePassTest,
diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp
index 003c449..8db1350 100644
--- a/test/opt/fold_test.cpp
+++ b/test/opt/fold_test.cpp
@@ -208,6 +208,15 @@
 #define VEC4_0_ID 106
 #define DVEC4_0_ID 106
 #define HALF_0_ID 108
+#define UINT_0_ID 109
+#define INT_NULL_ID 110
+#define UINT_NULL_ID 111
+#define ULONG_NULL_ID 120
+#define UBYTE_NULL_ID 121
+#define USHORT_NULL_ID 122
+#define V2USHORT_NULL_ID 123
+#define HALF_3_ID 112
+#define FLOAT_NULL_ID 113
 const std::string& Header() {
   static const std::string header = R"(OpCapability Shader
 OpCapability Float16
@@ -243,16 +252,23 @@
 %ulong = OpTypeInt 64 0
 %v2int = OpTypeVector %int 2
 %v4int = OpTypeVector %int 4
+%v2ubyte = OpTypeVector %ubyte 2
+%v4ubyte = OpTypeVector %ubyte 4
+%v4byte = OpTypeVector %byte 4
+%v2ushort = OpTypeVector %ushort 2
+%v4ushort = OpTypeVector %ushort 4
 %v2short = OpTypeVector %short 2
 %v2long = OpTypeVector %long 2
 %v4long = OpTypeVector %long 4
 %v4float = OpTypeVector %float 4
 %v4double = OpTypeVector %double 4
+%v4uint = OpTypeVector %uint 4
 %v2uint = OpTypeVector %uint 2
 %v2ulong = OpTypeVector %ulong 2
 %v2float = OpTypeVector %float 2
 %v2double = OpTypeVector %double 2
 %v2half = OpTypeVector %half 2
+%v4half = OpTypeVector %half 4
 %v2bool = OpTypeVector %bool 2
 %m2x2int = OpTypeMatrix %v2int 2
 %mat4v2float = OpTypeMatrix %v2float 4
@@ -268,22 +284,38 @@
 %_ptr_half = OpTypePointer Function %half
 %_ptr_long = OpTypePointer Function %long
 %_ptr_ulong = OpTypePointer Function %ulong
+%_ptr_ubyte = OpTypePointer Function %ubyte
+%_ptr_ushort = OpTypePointer Function %ushort
 %_ptr_v2int = OpTypePointer Function %v2int
 %_ptr_v4int = OpTypePointer Function %v4int
 %_ptr_v4float = OpTypePointer Function %v4float
 %_ptr_v4double = OpTypePointer Function %v4double
+%_ptr_v4half = OpTypePointer Function %v4half
 %_ptr_struct_v2int_int_int = OpTypePointer Function %struct_v2int_int_int
 %_ptr_v2float = OpTypePointer Function %v2float
 %_ptr_v2double = OpTypePointer Function %v2double
+%_ptr_v2ushort = OpTypePointer Function %v2ushort
 %int_2 = OpConstant %int 2
 %int_arr_2 = OpTypeArray %int %int_2
+%short_n1 = OpConstant %short -1
 %short_0 = OpConstant %short 0
 %short_2 = OpConstant %short 2
 %short_3 = OpConstant %short 3
 %short_n5 = OpConstant %short -5
 %ubyte_1 = OpConstant %ubyte 1
+%ubyte_a = OpConstant %ubyte 10
+%ubyte_b = OpConstant %ubyte 11
+%ubyte_c = OpConstant %ubyte 12
+%ubyte_d = OpConstant %ubyte 13
 %byte_n1 = OpConstant %byte -1
+%byte_0 = OpConstant %byte 0
+%ushort_0 = OpConstant %ushort 0
+%ushort_1 = OpConstant %ushort 1
+%ushort_2 = OpConstant %ushort 2
+%ushort_3 = OpConstant %ushort 3
+%122 = OpConstantNull %ushort ; Need a def with an numerical id to define id maps.
 %100 = OpConstant %int 0 ; Need a def with an numerical id to define id maps.
+%110 = OpConstantNull %int ; Need a def with an numerical id to define id maps.
 %103 = OpConstant %int 7 ; Need a def with an numerical id to define id maps.
 %int_0 = OpConstant %int 0
 %int_1 = OpConstant %int 1
@@ -314,6 +346,11 @@
 %long_max = OpConstant %long 9223372036854775807
 %ulong_7 = OpConstant %ulong 7
 %ulong_4611686018427387904 = OpConstant %ulong 4611686018427387904
+%109 = OpConstant %uint 0 ; Need a def with an numerical id to define id maps.
+%111 = OpConstantNull %uint ; Need a def with an numerical id to define id maps.
+%120 = OpConstantNull %ulong ; Need a def with an numerical id to define id maps.
+%121 = OpConstantNull %ubyte ; Need a def with an numerical id to define id maps.
+%123 = OpConstantNull %v2ushort ; Need a def with an numerical id to define id maps.
 %uint_0 = OpConstant %uint 0
 %uint_1 = OpConstant %uint 1
 %uint_2 = OpConstant %uint 2
@@ -323,6 +360,7 @@
 %uint_42 = OpConstant %uint 42
 %uint_2147483649 = OpConstant %uint 2147483649
 %uint_max = OpConstant %uint 4294967295
+%uint_0x0000ffff = OpConstant %uint 65535
 %ulong_0 = OpConstant %ulong 0
 %ulong_1 = OpConstant %ulong 1
 %ulong_2 = OpConstant %ulong 2
@@ -338,6 +376,8 @@
 %v2int_4_4 = OpConstantComposite %v2int %int_4 %int_4
 %v2int_min_max = OpConstantComposite %v2int %int_min %int_max
 %v2short_2_n5 = OpConstantComposite %v2short %short_2 %short_n5
+%v2short_n1_0 = OpConstantComposite %v2short %short_n1 %short_0
+%v2short_null = OpConstantNull %v2short
 %v2long_2_2 = OpConstantComposite %v2long %long_2 %long_2
 %v2long_2_3 = OpConstantComposite %v2long %long_2 %long_3
 %v2bool_null = OpConstantNull %v2bool
@@ -352,6 +392,7 @@
 %struct_undef_0_0 = OpConstantComposite %struct_v2int_int_int %v2int_undef %int_0 %int_0
 %float_n1 = OpConstant %float -1
 %104 = OpConstant %float 0 ; Need a def with an numerical id to define id maps.
+%113 = OpConstantNull %float ; Need a def with an numerical id to define id maps.
 %float_null = OpConstantNull %float
 %float_0 = OpConstant %float 0
 %float_n0 = OpConstant %float -0.0
@@ -396,9 +437,13 @@
 %v2double_2_0p5 = OpConstantComposite %v2double %double_2 %double_0p5
 %v2double_null = OpConstantNull %v2double
 %108 = OpConstant %half 0
+%half_0p5 = OpConstant %half 0.5
 %half_1 = OpConstant %half 1
 %half_2 = OpConstant %half 2
+%112 = OpConstant %half 3
+%half_null = OpConstantNull %half
 %half_0_1 = OpConstantComposite %v2half %108 %half_1
+%v4half_0_1_0_0 = OpConstantComposite %v4half %108 %half_1 %108 %108
 %106 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
 %v4float_0_0_0_0 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
 %v4float_0_0_0_1 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_1
@@ -425,6 +470,14 @@
 %uint_0x3f800000 = OpConstant %uint 0x3f800000
 %uint_0xbf800000 = OpConstant %uint 0xbf800000
 %v2uint_0x3f800000_0xbf800000 = OpConstantComposite %v2uint %uint_0x3f800000 %uint_0xbf800000
+%v4uint_1_0x0000ffff_uint_0_uint_max = OpConstantComposite %v4uint %uint_1 %uint_0x0000ffff %uint_0 %uint_max
+%v2uint_1_null = OpConstantComposite %v2uint %uint_1 %111
+%v2uint_null = OpConstantNull %v2uint
+%v2ushort_1_null = OpConstantComposite %v2ushort %ushort_1 %122
+%v4ushort_0_1_2_3 = OpConstantComposite %v4ushort %ushort_0 %ushort_1 %ushort_2 %ushort_3
+%v2ubyte_a_b = OpConstantComposite %v2ubyte %ubyte_a %ubyte_b
+%v4ubyte_a_b_c_d = OpConstantComposite %v4ubyte %ubyte_a %ubyte_b %ubyte_c %ubyte_d
+%v4byte_n1_0_0_0 = OpConstantComposite %v4byte %byte_n1 %byte_0 %byte_0 %byte_0
 %long_0xbf8000003f800000 = OpConstant %long 0xbf8000003f800000
 %int_0x3FF00000 = OpConstant %int 0x3FF00000
 %int_0x00000000 = OpConstant %int 0x00000000
@@ -765,7 +818,7 @@
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
-          "%2 = OpExtInst %int %1 UMin %int_3 %int_4\n" +
+          "%2 = OpExtInst %int %1 SMin %int_3 %int_4\n" +
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 3),
@@ -777,7 +830,39 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 2),
-  // Test case 34: fold UMax 3 4
+    // Test case 34: fold SMin short -5 2
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %short %1 SMin %short_n5 %short_2\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, -5),
+    // Test case 35: fold UMin ushort 2 0
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %ushort %1 UMin %ushort_2 %ushort_0\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 0),
+    // Test case 36: fold SMin int OpConstNull -1
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %int %1 SMin %110 %int_n1\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, -1),
+  // Test case 37: fold SMin int -1 OpConstNull
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %int %1 SMin %int_n1 %110\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, -1),
+    // Test case 38: fold UMax 3 4
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
@@ -785,7 +870,7 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 4),
-  // Test case 35: fold UMax 3 2
+    // Test case 39: fold UMax 3 2
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
@@ -793,15 +878,15 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 3),
-  // Test case 36: fold SMax 3 4
+    // Test case 40: fold SMax 3 4
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
-          "%2 = OpExtInst %int %1 UMax %int_3 %int_4\n" +
+          "%2 = OpExtInst %int %1 SMax %int_3 %int_4\n" +
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 4),
-  // Test case 37: fold SMax 3 2
+    // Test case 41: fold SMax 3 2
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
@@ -809,7 +894,31 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 3),
-  // Test case 38: fold UClamp 2 3 4
+    // Test case 42: fold SMax short -5 2
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %short %1 SMax %short_n5 %short_2\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 2),
+    // Test case 43: fold UMax ushort 2 0
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %ushort %1 UMax %ushort_2 %ushort_0\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 2),
+    // Test case 44: fold SMax int OpConstNull 1
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %int %1 SMax %110 %int_1\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 1),
+    // Test case 45: fold UClamp 2 3 4
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
@@ -817,7 +926,7 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 3),
-  // Test case 39: fold UClamp 2 0 4
+    // Test case 46: fold UClamp 2 0 4
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
@@ -825,7 +934,7 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 2),
-  // Test case 40: fold UClamp 2 0 1
+    // Test case 47: fold UClamp 2 0 1
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
@@ -833,7 +942,15 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 1),
-  // Test case 41: fold SClamp 2 3 4
+    // Test case 48: fold UClamp short 2 0 1
+  InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpExtInst %ushort %1 UClamp %ushort_2 %ushort_0 %ushort_1\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 1),
+    // Test case 49: fold SClamp 2 3 4
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
@@ -841,7 +958,7 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 3),
-  // Test case 42: fold SClamp 2 0 4
+    // Test case 50: fold SClamp 2 0 4
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
@@ -849,7 +966,7 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 2),
-  // Test case 43: fold SClamp 2 0 1
+    // Test case 51: fold SClamp 2 0 1
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
@@ -857,7 +974,7 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 1),
-  // Test case 44: SClamp 1 2 x
+    // Test case 52: SClamp 1 2 x
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
@@ -866,7 +983,7 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 2),
-  // Test case 45: SClamp 2 x 1
+    // Test case 53: SClamp 2 x 1
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
@@ -875,7 +992,7 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 1),
-  // Test case 46: UClamp 1 2 x
+    // Test case 54: UClamp 1 2 x
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
@@ -884,7 +1001,7 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 2),
-  // Test case 47: UClamp 2 x 1
+    // Test case 55: UClamp 2 x 1
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
@@ -893,7 +1010,7 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 1),
-    // Test case 48: Bit-cast int 0 to unsigned int
+    // Test case 56: Bit-cast int 0 to unsigned int
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -901,7 +1018,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0),
-    // Test case 49: Bit-cast int -24 to unsigned int
+    // Test case 57: Bit-cast int -24 to unsigned int
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -909,7 +1026,15 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, static_cast<uint32_t>(-24)),
-    // Test case 50: Bit-cast float 1.0f to unsigned int
+    // Test case 58: Bit-cast int const null to unsigned int
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpBitcast %uint %110\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0),
+    // Test case 59: Bit-cast float 1.0f to unsigned int
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -917,7 +1042,15 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, static_cast<uint32_t>(0x3f800000)),
-    // Test case 51: Bit-cast ushort 0xBC00 to ushort
+    // Test case 60: Bit-cast float constant null to unsigned int
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpBitcast %uint %float_null\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0),
+    // Test case 61: Bit-cast ushort 0xBC00 to ushort
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -925,7 +1058,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0xBC00),
-    // Test case 52: Bit-cast short 0xBC00 to ushort
+    // Test case 62: Bit-cast short 0xBC00 to ushort
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -933,7 +1066,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0xBC00),
-    // Test case 53: Bit-cast half 1 to ushort
+    // Test case 63: Bit-cast half 1 to ushort
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -941,7 +1074,15 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0x3C00),
-    // Test case 54: Bit-cast ushort 0xBC00 to short
+    // Test case 64: Bit-cast half const null to ushort
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpBitcast %ushort %half_null\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x0000), 
+    // Test case 65: Bit-cast ushort 0xBC00 to short
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -949,7 +1090,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0xFFFFBC00),
-    // Test case 55: Bit-cast short 0xBC00 to short
+    // Test case 66: Bit-cast short 0xBC00 to short
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -957,7 +1098,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0xFFFFBC00),
-    // Test case 56: Bit-cast half 1 to short
+    // Test case 67: Bit-cast half 1 to short
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -965,7 +1106,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0x3C00),
-    // Test case 57: Bit-cast ushort 0xBC00 to half
+    // Test case 68: Bit-cast ushort 0xBC00 to half
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -973,7 +1114,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0xBC00),
-    // Test case 58: Bit-cast short 0xBC00 to half
+    // Test case 69: Bit-cast short 0xBC00 to half
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -981,7 +1122,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0xFFFFBC00),
-    // Test case 59: Bit-cast half 1 to half
+    // Test case 70: Bit-cast half 1 to half
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -989,7 +1130,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0x3C00),
-    // Test case 60: Bit-cast ubyte 1 to byte
+    // Test case 71: Bit-cast ubyte 1 to byte
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -997,7 +1138,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 1),
-    // Test case 61: Bit-cast byte -1 to ubyte
+    // Test case 72: Bit-cast byte -1 to ubyte
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -1005,7 +1146,63 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0xFF),
-    // Test case 62: Negate 2.
+    // Test case 73: Bit-cast v2shorts (-5,2) to uint
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpBitcast %uint %v2short_2_n5\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0xFFFB0002),
+    // Test case 74: Bit-cast v4ubytes (0xA, 0xB, 0xC, 0xD) to int
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpBitcast %int %v4ubyte_a_b_c_d\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x0D0C0B0A),
+    // Test case 75: Bit-cast v2ushort (1, OpConstNull) to int
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpBitcast %int %v2ushort_1_null\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x00000001),
+    // Test case 76: Bit-cast v2short OpConstNull to int
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpBitcast %int %v2short_null\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0),
+    // Test case 77: Bit-cast v2ubytes (0xA, 0xB) to  ushort
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpBitcast %int %v2ubyte_a_b\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x0B0A),
+    // Test case 78: Bit-cast v2short (-1, 0) to  uint
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpBitcast %uint %v2short_n1_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x0000FFFF),
+    // Test case 79: Bit-cast v4bytes (-1, 0, 0, 0) to  uint
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpBitcast %uint %v4byte_n1_0_0_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x000000FF),
+    // Test case 80: Negate 2.
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -1013,7 +1210,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, -2),
-    // Test case 63: Negate negative short.
+    // Test case 81: Negate negative short.
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -1021,7 +1218,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0x4400 /* expected to be sign extended. */),
-    // Test case 64: Negate positive short.
+    // Test case 82: Negate positive short.
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -1029,7 +1226,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0xFFFFBC00 /* expected to be sign extended. */),
-    // Test case 65: Negate a negative short.
+    // Test case 83: Negate a negative short.
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -1037,7 +1234,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0x4400 /* expected to be zero extended. */),
-    // Test case 66: Negate positive short.
+    // Test case 84: Negate positive short.
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -1045,7 +1242,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0xBC00 /* expected to be zero extended. */),
-    // Test case 67: Fold 2 + 3 (short)
+    // Test case 85: Fold 2 + 3 (short)
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -1053,7 +1250,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 5),
-    // Test case 68: Fold 2 + -5 (short)
+    // Test case 86: Fold 2 + -5 (short)
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -1061,7 +1258,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, -3),
-  // Test case 69: Fold int(3ll)
+    // Test case 87: Fold int(3ll)
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
@@ -1069,7 +1266,7 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 3),
-  // Test case 70: Fold short(-3ll)
+    // Test case 88: Fold short(-3ll)
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
@@ -1077,7 +1274,7 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, -3),
-  // Test case 71: Fold short(32768ll) - This should do a sign extend when
+    // Test case 89: Fold short(32768ll) - This should do a sign extend when
   // converting to short.
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
@@ -1086,7 +1283,7 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, -32768),
-  // Test case 72: Fold short(-57344) - This should do a sign extend when
+    // Test case 90: Fold short(-57344) - This should do a sign extend when
   // converting to short making the upper bits 0.
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
@@ -1095,7 +1292,7 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 8192),
-  // Test case 73: Fold int(-5(short)). The -5 should be interpreted as an unsigned value, and be zero extended to 32-bits.
+    // Test case 91: Fold int(-5(short)). The -5 should be interpreted as an unsigned value, and be zero extended to 32-bits.
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
@@ -1103,14 +1300,53 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, 65531),
-  // Test case 74: Fold short(-24(int)). The upper bits should be cleared. So 0xFFFFFFE8 should become 0x0000FFE8.
+    // Test case 92: Fold short(-24(int)). The upper bits should be cleared. So 0xFFFFFFE8 should become 0x0000FFE8.
   InstructionFoldingCase<uint32_t>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
           "%2 = OpUConvert %ushort %int_n24\n" +
           "OpReturn\n" +
           "OpFunctionEnd",
-      2, 65512)
+      2, 65512),
+   // Test case 93: Fold BitReverse of 1
+  InstructionFoldingCase<uint32_t>(
+    Header() + "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%2 = OpBitReverse %uint %uint_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+    2, 0x80000000),
+   // Test case 94: Fold BitReverse of 0
+  InstructionFoldingCase<uint32_t>(
+    Header() + "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%2 = OpBitReverse %uint %uint_0\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+    2, 0),
+    // Test case 95: Fold BitReverse of uint max
+    InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpBitReverse %uint %uint_max\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, 0xffffffff),
+    // Test case 96: Fold BitReverse of 0x0000FFFF
+  InstructionFoldingCase<uint32_t>(
+    Header() + "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%2 = OpBitReverse %uint %uint_0x0000ffff\n" +
+        "OpReturn\n" + "OpFunctionEnd",
+    2, 0xffff0000),
+    // Test case 97: Fold BitReverse of OpConstNull
+  InstructionFoldingCase<uint32_t>(
+    Header() + "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%2 = OpBitReverse %uint %111\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+    2, 0)
 ));
 // clang-format on
 
@@ -1130,6 +1366,7 @@
       });
 }
 
+// clang-format off
 INSTANTIATE_TEST_SUITE_P(
     TestCase, LongIntegerInstructionFoldingTest,
     ::testing::Values(
@@ -1274,7 +1511,41 @@
             Header() + "%main = OpFunction %void None %void_func\n" +
                 "%main_lab = OpLabel\n" + "%2 = OpUConvert %ulong %short_n5\n" +
                 "OpReturn\n" + "OpFunctionEnd",
-            2, 65531ull)));
+            2, 65531ull),
+        // Test case 16: fold SMin long 0 -1.
+        InstructionFoldingCase<uint64_t>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+                "%main_lab = OpLabel\n" +
+                "%2 = OpExtInst %long %1 SMin %long_0 %long_n1\n" +
+                "OpReturn\n" +
+                "OpFunctionEnd",
+            2, -1ll),
+        // Test case 17: fold UMin ulong 9223372036854775809 max.
+        InstructionFoldingCase<uint64_t>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+                "%main_lab = OpLabel\n" +
+                "%2 = OpExtInst %ulong %1 UMin %ulong_9223372036854775809 %ulong_max\n" +
+                "OpReturn\n" +
+                "OpFunctionEnd",
+            2, 9223372036854775809ull),
+        // Test case 18: fold SMax long 0 -1.
+        InstructionFoldingCase<uint64_t>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+                "%main_lab = OpLabel\n" +
+                "%2 = OpExtInst %long %1 SMax %long_0 %long_n1\n" +
+                "OpReturn\n" +
+                "OpFunctionEnd",
+            2, 0ll),
+        // Test case 19: fold UMax ulong 9223372036854775809 max.
+        InstructionFoldingCase<uint64_t>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+                "%main_lab = OpLabel\n" +
+                "%2 = OpExtInst %ulong %1 UMax %ulong_9223372036854775809 %ulong_max\n" +
+                "OpReturn\n" +
+                "OpFunctionEnd",
+            2, 18446744073709551615ull)
+        ));
+// clang-format on
 
 using UIntVectorInstructionFoldingTest =
     ::testing::TestWithParam<InstructionFoldingCase<std::vector<uint32_t>>>;
@@ -1285,7 +1556,8 @@
   std::unique_ptr<IRContext> context;
   Instruction* inst;
   std::tie(context, inst) =
-      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1);
+      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5);
+
   CheckForExpectedVectorConstant(
       inst, tc.expected_result,
       [](const analysis::Constant* c) { return c->GetU32(); });
@@ -1298,42 +1570,59 @@
     InstructionFoldingCase<std::vector<uint32_t>>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
-            "%n = OpVariable %_ptr_int Function\n" +
-            "%load = OpLoad %int %n\n" +
             "%2 = OpVectorShuffle %v2int %v2int_2_2 %v2int_2_3 0 3\n" +
             "OpReturn\n" +
             "OpFunctionEnd",
         2, {2,3}),
+    // Test case 1: fold vectorshuffle of nullconstant of type vec2int
     InstructionFoldingCase<std::vector<uint32_t>>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
-          "%n = OpVariable %_ptr_int Function\n" +
-          "%load = OpLoad %int %n\n" +
           "%2 = OpVectorShuffle %v2int %v2int_null %v2int_2_3 0 3\n" +
           "OpReturn\n" +
           "OpFunctionEnd",
       2, {0,3}),
-    // Test case 4: fold bit-cast int -24 to unsigned int
+    // Test case 2: fold bit-cast int -24 to unsigned int
     InstructionFoldingCase<std::vector<uint32_t>>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
-          "%n = OpVariable %_ptr_int Function\n" +
-          "%load = OpLoad %int %n\n" +
           "%2 = OpBitcast %v2uint %v2int_min_max\n" +
           "OpReturn\n" +
           "OpFunctionEnd",
       2, {2147483648, 2147483647}),
-    // Test case 5: fold SNegate vector of uint
+    // Test case 3: fold bit-cast nullconstant of type vec2uint to int2
     InstructionFoldingCase<std::vector<uint32_t>>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
-          "%n = OpVariable %_ptr_int Function\n" +
-          "%load = OpLoad %int %n\n" +
+          "%2 = OpBitcast %v2int %v2uint_null\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, {0, 0}),
+    // Test case 4: fold bit-cast if any component is nullconstant
+    InstructionFoldingCase<std::vector<uint32_t>>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpBitcast %v2int %v2uint_1_null\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, {1, 0}),
+    // Test case 5: fold bit-cast v4ushort (0, 1, 2, 3) to v2uint
+    InstructionFoldingCase<std::vector<uint32_t>>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpBitcast %v2uint %v4ushort_0_1_2_3\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, {0x00010000, 0x00030002}),
+    // Test case 6: fold SNegate vector of uint
+    InstructionFoldingCase<std::vector<uint32_t>>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
           "%2 = OpSNegate %v2uint %v2uint_0x3f800000_0xbf800000\n" +
           "OpReturn\n" +
           "OpFunctionEnd",
-      2, {static_cast<uint32_t>(-0x3f800000), static_cast<uint32_t>(-0xbf800000)}),
-    // Test case 6: fold vector components of uint (including integer overflow)
+      2, {static_cast<uint32_t>(-0x3f800000), static_cast<uint32_t>(-0xbf800000ll)}),
+    // Test case 7: fold vector components of uint (including integer overflow)
     InstructionFoldingCase<std::vector<uint32_t>>(
       Header() + "%main = OpFunction %void None %void_func\n" +
           "%main_lab = OpLabel\n" +
@@ -1341,7 +1630,7 @@
           "OpReturn\n" +
           "OpFunctionEnd",
       2, {0x7f000000u, 0x7f000000u}),
-    // Test case 6: fold vector components of uint
+    // Test case 8: fold vector components of uint
     InstructionFoldingCase<std::vector<uint32_t>>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -1349,14 +1638,38 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, {2,static_cast<uint32_t>(-5)}),
-    // Test case 6: fold vector components of uint (incuding integer overflow)
+    // Test case 9: fold vector components of uint (incuding integer overflow)
     InstructionFoldingCase<std::vector<uint32_t>>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
             "%2 = OpUConvert %v2uint %v2short_2_n5\n" +
             "OpReturn\n" +
             "OpFunctionEnd",
-        2, {2,65531})
+        2, {2,65531}),
+    // Test case 10: fold bitreverse of a vector of uint
+    InstructionFoldingCase<std::vector<uint32_t>>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpBitReverse %v4uint %v4uint_1_0x0000ffff_uint_0_uint_max\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, {0x80000000, 0xffff0000, 0, 0xffffffff}),
+    // Test case 11: Fold bitreverse of ConstantNull of type v2uint 
+    InstructionFoldingCase<std::vector<uint32_t>>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpBitReverse %v2uint %v2uint_null\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, {0, 0}),
+    // Test case 12: Fold bitreverse if any component is NullConstant
+    InstructionFoldingCase<std::vector<uint32_t>>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%2 = OpBitReverse %v2uint %v2uint_1_null\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, {0x80000000, 0})
 ));
 // clang-format on
 
@@ -1369,7 +1682,7 @@
   std::unique_ptr<IRContext> context;
   Instruction* inst;
   std::tie(context, inst) =
-      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1);
+      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5);
 
   CheckForExpectedVectorConstant(
       inst, tc.expected_result,
@@ -1423,7 +1736,7 @@
   std::unique_ptr<IRContext> context;
   Instruction* inst;
   std::tie(context, inst) =
-      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1);
+      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5);
   CheckForExpectedVectorConstant(
       inst, tc.expected_result,
       [](const analysis::Constant* c) { return c->GetU64(); });
@@ -1468,7 +1781,7 @@
   std::unique_ptr<IRContext> context;
   Instruction* inst;
   std::tie(context, inst) =
-      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1);
+      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5);
   CheckForExpectedVectorConstant(
       inst, tc.expected_result,
       [](const analysis::Constant* c) { return c->GetDouble(); });
@@ -1568,14 +1881,14 @@
 
   std::unique_ptr<IRContext> context;
   Instruction* inst;
-  std::tie(context, inst) = FoldInstruction(tc.test_body, tc.id_to_fold,SPV_ENV_UNIVERSAL_1_1);
+  std::tie(context, inst) = FoldInstruction(tc.test_body, tc.id_to_fold,SPV_ENV_UNIVERSAL_1_5);
   CheckForExpectedVectorConstant(inst, tc.expected_result, [](const analysis::Constant* c){ return c->GetFloat();});
 }
 
 // clang-format off
 INSTANTIATE_TEST_SUITE_P(TestCase, FloatVectorInstructionFoldingTest,
 ::testing::Values(
-   // Test case 0: FMix {2.0, 2.0}, {2.0, 3.0} {0.2,0.5}
+   // Test case 0: FMix {2.0, 3.0}, {0.0, 0.0} {0.2,0.5}
    InstructionFoldingCase<std::vector<float>>(
        Header() + "%main = OpFunction %void None %void_func\n" +
            "%main_lab = OpLabel\n" +
@@ -1684,7 +1997,7 @@
   std::unique_ptr<IRContext> context;
   Instruction* inst;
   std::tie(context, inst) =
-      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1);
+      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5);
 
   EXPECT_EQ(inst->opcode(), spv::Op::OpCopyObject);
   if (inst->opcode() == spv::Op::OpCopyObject) {
@@ -1753,7 +2066,7 @@
   std::unique_ptr<IRContext> context;
   Instruction* inst;
   std::tie(context, inst) =
-      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1);
+      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5);
   CheckForExpectedScalarConstant(
       inst, tc.expected_result,
       [](const analysis::Constant* c) { return c->AsBoolConstant()->value(); });
@@ -2345,7 +2658,7 @@
   std::unique_ptr<IRContext> context;
   Instruction* inst;
   std::tie(context, inst) =
-      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1);
+      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5);
 
   CheckForExpectedScalarConstant(inst, tc.expected_result,
                                  [](const analysis::Constant* c) {
@@ -2353,6 +2666,27 @@
                                  });
 }
 
+using FloatBitsInstructionFoldingTest =
+    ::testing::TestWithParam<InstructionFoldingCase<uint32_t>>;
+
+TEST_P(FloatBitsInstructionFoldingTest, Case) {
+  const auto& tc = GetParam();
+
+  std::unique_ptr<IRContext> context;
+  Instruction* inst;
+  std::tie(context, inst) =
+      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5);
+
+  CheckForExpectedScalarConstant(
+      inst, tc.expected_result, [](const analysis::Constant* c) {
+        float f = c->AsFloatConstant()->GetFloatValue();
+        uint32_t fbits{};
+        static_assert(sizeof(float) == sizeof(uint32_t));
+        std::memcpy(&fbits, &f, sizeof(uint32_t));
+        return fbits;
+      });
+}
+
 // Not testing NaNs because there are no expectations concerning NaNs according
 // to the "Precision and Operation of SPIR-V Instructions" section of the Vulkan
 // specification.
@@ -2745,8 +3079,417 @@
             "%2 = OpFDiv %float %float_0 %float_n0\n" +
             "OpReturn\n" +
             "OpFunctionEnd",
+        2, std::numeric_limits<float>::quiet_NaN()),
+    // Test case 47: NMin 1.0 4.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NMin %float_1 %float_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 1.0f),
+    // Test case 48: NMin 4.0 0.2
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NMin %float_4 %float_0p2\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0.2f),
+    // Test case 49: NMin NaN 4.0
+    InstructionFoldingCase<float>(
+        HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NMin %float_nan %float_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 4.0f),
+    // Test case 50: NMin 2.0 NaN
+    InstructionFoldingCase<float>(
+        HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NMin %float_2 %float_nan\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 2.0f),
+    // Test case 51: NMin NaN NaN
+    InstructionFoldingCase<float>(
+        HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NMin %float_nan %float_nan\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, std::numeric_limits<float>::quiet_NaN()),
+    // Test case 52: NMax 1.0 4.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NMax %float_1 %float_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 4.0f),
+    // Test case 53: NMax 1.0 0.2
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NMax %float_1 %float_0p2\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 1.0f),
+    // Test case 54: NMax 1.0 NaN
+    InstructionFoldingCase<float>(
+        HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NMax %float_1 %float_nan\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 1.0f),
+    // Test case 55: NMax NaN 0.5
+    InstructionFoldingCase<float>(
+        HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NMax %float_nan %float_0p5\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0.5f),
+    // Test case 56: NMax NaN NaN
+    InstructionFoldingCase<float>(
+        HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NMax %float_nan %float_nan\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, std::numeric_limits<float>::quiet_NaN()),
+    // Test case 57: NClamp 1.0 0.2 4.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NClamp %float_1 %float_0p2 %float_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 1.0f),
+    // Test case 58: NClamp 0.2 2.0 4.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NClamp %float_0p2 %float_2 %float_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 2.0f),
+    // Test case 59: NClamp 2049.0 2.0 4.0
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NClamp %float_2049 %float_2 %float_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 4.0f),
+    // Test case 60: NClamp 1.0 2.0 x
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%undef = OpUndef %float\n" +
+            "%2 = OpExtInst %float %1 NClamp %float_1 %float_2 %undef\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 2.0),
+    // Test case 61: NClamp 1.0 x 0.5
+    InstructionFoldingCase<float>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%undef = OpUndef %float\n" +
+            "%2 = OpExtInst %float %1 NClamp %float_1 %undef %float_0p5\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0.5),
+    // Test case 62: NClamp NaN 2.0 4.0
+    InstructionFoldingCase<float>(
+        HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NClamp %float_nan %float_2 %float_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 2.0f),
+    // Test case 63: NClamp 2049.0 NaN 4.0
+    InstructionFoldingCase<float>(
+        HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NClamp %float_2049 %float_nan %float_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 4.0f),
+    // Test case 64: NClamp 1.0 2.0 NaN
+    InstructionFoldingCase<float>(
+        HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NClamp %float_1 %float_2 %float_nan\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 2.0f),
+    // Test case 65: NClamp 4.0 NaN NaN
+    InstructionFoldingCase<float>(
+        HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NClamp %float_4 %float_nan %float_nan\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 4.0f),
+    // Test case 66: NClamp NaN NaN NaN
+    InstructionFoldingCase<float>(
+        HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NClamp %float_nan %float_nan %float_nan\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
         2, std::numeric_limits<float>::quiet_NaN())
 ));
+
+INSTANTIATE_TEST_SUITE_P(MinMaxZeroFoldingTest, FloatBitsInstructionFoldingTest,
+::testing::Values(
+    // Test case 0: Fold FMin 0.0 -0.0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 FMin %float_0 %float_n0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x80000000u),
+    // Test case 1: Fold FMin -0.0 0.0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 FMin %float_n0 %float_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x80000000u),
+    // Test case 2: Fold FMax 0.0 -0.0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 FMax %float_0 %float_n0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x00000000u),
+    // Test case 3: Fold FMax -0.0 0.0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 FMax %float_n0 %float_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x00000000u),
+    // Test case 4: Fold FClamp -0.0 0.0 1.0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 FClamp %float_n0 %float_0 %float_1\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x00000000u),
+    // Test case 5: Fold FClamp 0.0 -0.0 1.0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 FClamp %float_0 %float_n0 %float_1\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x00000000u),
+    // Test case 6: Fold FClamp 1.0 -0.0 0.0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 FClamp %float_1 %float_n0 %float_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x00000000u),
+    // Test case 7: Fold FClamp 0.0 -1.0 -0.0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 FClamp %float_0 %float_n1 %float_n0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x80000000u),
+    // Test case 8: Fold FClamp -0.0 -1.0 0.0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 FClamp %float_n0 %float_n1 %float_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x80000000u),
+    // Test case 9: Fold FClamp -1.0 -0.0 0.0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 FClamp %float_n1 %float_n0 %float_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x80000000u),
+    // Test case 10: Fold NMin 0.0 -0.0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NMin %float_0 %float_n0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x80000000u),
+    // Test case 11: Fold NMin -0.0 0.0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NMin %float_n0 %float_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x80000000u),
+    // Test case 12: Fold NMax 0.0 -0.0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NMax %float_0 %float_n0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x00000000u),
+    // Test case 13: Fold NMax -0.0 0.0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NMax %float_n0 %float_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x00000000u),
+    // Test case 14: Fold NClamp -0.0 0.0 1.0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NClamp %float_n0 %float_0 %float_1\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x00000000u),
+    // Test case 15: Fold NClamp 0.0 -0.0 1.0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NClamp %float_0 %float_n0 %float_1\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x00000000u),
+    // Test case 16: Fold NClamp 1.0 -0.0 0.0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NClamp %float_1 %float_n0 %float_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x00000000u),
+    // Test case 17: Fold NClamp 0.0 -1.0 -0.0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NClamp %float_0 %float_n1 %float_n0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x80000000u),
+    // Test case 18: Fold NClamp -0.0 -1.0 0.0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NClamp %float_n0 %float_n1 %float_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x80000000u),
+    // Test case 19: Fold NClamp -1.0 -0.0 0.0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %float %1 NClamp %float_n1 %float_n0 %float_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0x80000000u)
+));
+
+INSTANTIATE_TEST_SUITE_P(RedundantDivFloatTest, FloatInstructionFoldingTest,
+  ::testing::Values(
+    // Test case 0: Fold x / x
+    InstructionFoldingCase<float>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_float Function\n" +
+      "%3 = OpLoad %float %n\n" +
+      "%2 = OpFDiv %float %3 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, 1.0),
+    // Test case 1: Fold -x / x
+    InstructionFoldingCase<float>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_float Function\n" +
+      "%3 = OpLoad %float %n\n" +
+      "%4 = OpFNegate %float %3\n" +
+      "%2 = OpFDiv %float %4 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, -1.0),
+    // Test case 2: Fold x / -x
+    InstructionFoldingCase<float>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_float Function\n" +
+      "%3 = OpLoad %float %n\n" +
+      "%4 = OpFNegate %float %3\n" +
+      "%2 = OpFDiv %float %3 %4\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, -1.0)
+  ));
+
+INSTANTIATE_TEST_SUITE_P(RedundantDivIntTest, IntegerInstructionFoldingTest,
+  ::testing::Values(
+    // Test case 0: Fold x / x
+    InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_int Function\n" +
+      "%3 = OpLoad %int %n\n" +
+      "%2 = OpSDiv %int %3 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, 1),
+    // Test case 1: Fold x / x
+    InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%3 = OpLoad %uint %n\n" +
+      "%2 = OpUDiv %uint %3 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, 1),
+    // Test case 2: Fold -x / x
+    InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_int Function\n" +
+      "%3 = OpLoad %int %n\n" +
+      "%4 = OpSNegate %int %3\n" +
+      "%2 = OpSDiv %int %4 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, UINT32_MAX),
+    // Test case 3: Fold x / -x
+    InstructionFoldingCase<uint32_t>(
+      Header() + "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_int Function\n" +
+      "%3 = OpLoad %int %n\n" +
+      "%4 = OpSNegate %int %3\n" +
+      "%2 = OpSDiv %int %3 %4\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, UINT32_MAX)
+  ));
+
 // clang-format on
 
 using DoubleInstructionFoldingTest =
@@ -2758,7 +3501,7 @@
   std::unique_ptr<IRContext> context;
   Instruction* inst;
   std::tie(context, inst) =
-      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1);
+      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5);
   CheckForExpectedScalarConstant(
       inst, tc.expected_result, [](const analysis::Constant* c) {
         return c->AsFloatConstant()->GetDoubleValue();
@@ -2995,6 +3738,168 @@
                 "%2 = OpFDiv %double %double_0 %double_n0\n" +
                 "OpReturn\n" +
                 "OpFunctionEnd",
+            2, std::numeric_limits<double>::quiet_NaN()),
+        // Test case 27: NMin 1.0 4.0
+        InstructionFoldingCase<double>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %double %1 NMin %double_1 %double_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+            2, 1.0),
+        // Test case 28: NMin 4.0 0.2
+        InstructionFoldingCase<double>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %double %1 NMin %double_4 %double_0p2\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+            2, 0.2),
+        // Test case 29: NMax 1.0 4.0
+        InstructionFoldingCase<double>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %double %1 NMax %double_1 %double_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+            2, 4.0),
+        // Test case 30: NMin NaN 4.0
+        InstructionFoldingCase<double>(
+            HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %double %1 NMin %double_nan %double_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+            2, 4.0),
+        // Test case 31: NMin 2.0 NaN
+        InstructionFoldingCase<double>(
+            HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %double %1 NMin %double_2 %double_nan\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+            2, 2.0),
+        // Test case 32: NMin NaN NaN
+        InstructionFoldingCase<double>(
+            HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %double %1 NMin %double_nan %double_nan\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+            2, std::numeric_limits<double>::quiet_NaN()),
+        // Test case 33: NMax 1.0 0.2
+        InstructionFoldingCase<double>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %double %1 NMax %double_1 %double_0p2\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+            2, 1.0),
+        // Test case 34: NMax 1.0 NaN
+        InstructionFoldingCase<double>(
+            HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %double %1 NMax %double_1 %double_nan\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+            2, 1.0),
+        // Test case 35: NMax NaN 0.5
+        InstructionFoldingCase<double>(
+            HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %double %1 NMax %double_nan %double_0p5\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+            2, 0.5),
+        // Test case 36: NMax NaN NaN
+        InstructionFoldingCase<double>(
+            HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %double %1 NMax %double_nan %double_nan\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+            2, std::numeric_limits<double>::quiet_NaN()),
+        // Test case 37: NClamp 1.0 0.2 4.0
+        InstructionFoldingCase<double>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %double %1 NClamp %double_1 %double_0p2 %double_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+            2, 1.0),
+        // Test case 38: NClamp 0.2 2.0 4.0
+        InstructionFoldingCase<double>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %double %1 NClamp %double_0p2 %double_2 %double_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+            2, 2.0),
+        // Test case 39: NClamp 5.0 2.0 4.0
+        InstructionFoldingCase<double>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %double %1 NClamp %double_5 %double_2 %double_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+            2, 4.0),
+        // Test case 40: NClamp 1.0 2.0 x
+        InstructionFoldingCase<double>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%undef = OpUndef %double\n" +
+            "%2 = OpExtInst %double %1 NClamp %double_1 %double_2 %undef\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+            2, 2.0),
+        // Test case 41: NClamp 1.0 x 0.5
+        InstructionFoldingCase<double>(
+            Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%undef = OpUndef %double\n" +
+            "%2 = OpExtInst %double %1 NClamp %double_1 %undef %double_0p5\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+            2, 0.5),
+        // Test case 42: NClamp NaN 2.0 4.0
+        InstructionFoldingCase<double>(
+            HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %double %1 NClamp %double_nan %double_2 %double_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+            2, 2.0),
+        // Test case 43: NClamp 5.0 NaN 4.0
+        InstructionFoldingCase<double>(
+            HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %double %1 NClamp %double_5 %double_nan %double_4\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+            2, 4.0),
+        // Test case 44: NClamp 1.0 2.0 NaN
+        InstructionFoldingCase<double>(
+            HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %double %1 NClamp %double_1 %double_2 %double_nan\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+            2, 2.0),
+        // Test case 45: NClamp 4.0 NaN NaN
+        InstructionFoldingCase<double>(
+            HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %double %1 NClamp %double_4 %double_nan %double_nan\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+            2, 4.0),
+        // Test case 46: NClamp NaN NaN NaN
+        InstructionFoldingCase<double>(
+            HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %double %1 NClamp %double_nan %double_nan %double_nan\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
             2, std::numeric_limits<double>::quiet_NaN())
 ));
 // clang-format on
@@ -3696,7 +4601,7 @@
   std::unique_ptr<IRContext> context;
   Instruction* inst;
   std::tie(context, inst) =
-      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1);
+      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5);
 
   EXPECT_TRUE((inst == nullptr) == (tc.expected_result == 0));
   if (inst != nullptr) {
@@ -4603,7 +5508,18 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 3),
-    // Test case 9: Fold n * 0.0
+    // Test case 9: Don't fold n % 1.0
+    // If `n` is not a whole number, the answer is not 0.
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_float Function\n" +
+            "%3 = OpLoad %float %n\n" +
+            "%2 = OpFMod %float %3 %float_1\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0),
+    // Test case 10: Fold n * 0.0
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -4613,7 +5529,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, FLOAT_0_ID),
-    // Test case 10: Fold 0.0 * n
+    // Test case 11: Fold 0.0 * n
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -4623,7 +5539,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, FLOAT_0_ID),
-    // Test case 11: Fold 0.0 / n
+    // Test case 12: Fold 0.0 / n
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -4633,7 +5549,17 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, FLOAT_0_ID),
-    // Test case 12: Don't fold mix(a, b, 2.0)
+    // Test case 13: Fold 0.0 % n
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_float Function\n" +
+            "%3 = OpLoad %float %n\n" +
+            "%2 = OpFMod %float %104 %3\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, FLOAT_0_ID),
+    // Test case 14: Don't fold mix(a, b, 2.0)
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -4645,7 +5571,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0),
-    // Test case 13: Fold mix(a, b, 0.0)
+    // Test case 15: Fold mix(a, b, 0.0)
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -4657,7 +5583,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 3),
-    // Test case 14: Fold mix(a, b, 1.0)
+    // Test case 16: Fold mix(a, b, 1.0)
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -4669,7 +5595,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 4),
-    // Test case 15: Fold vector fadd with null
+    // Test case 17: Fold vector fadd with null
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -4679,7 +5605,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         3, 2),
-    // Test case 16: Fold vector fadd with null
+    // Test case 18: Fold vector fadd with null
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -4689,7 +5615,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         3, 2),
-    // Test case 17: Fold vector fsub with null
+    // Test case 19: Fold vector fsub with null
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -4699,7 +5625,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         3, 2),
-    // Test case 18: Fold 0.0(half) * n
+    // Test case 20: Fold 0.0(half) * n
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -4709,7 +5635,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, HALF_0_ID),
-    // Test case 19: Don't fold 1.0(half) * n
+    // Test case 21: Don't fold 1.0(half) * n
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -4719,7 +5645,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0),
-    // Test case 20: Don't fold 1.0 * 1.0 (half)
+    // Test case 22: Don't fold 1.0 * 1.0 (half)
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -4727,7 +5653,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0),
-    // Test case 21: Don't fold (0.0, 1.0) * (0.0, 1.0) (half)
+    // Test case 23: Don't fold (0.0, 1.0) * (0.0, 1.0) (half)
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -4735,7 +5661,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0),
-    // Test case 22: Don't fold (0.0, 1.0) dotp (0.0, 1.0) (half)
+    // Test case 24: Don't fold (0.0, 1.0) dotp (0.0, 1.0) (half)
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -4743,7 +5669,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0),
-    // Test case 23: Don't fold 1.0(half) / 2.0(half)
+    // Test case 25: Don't fold 1.0(half) / 2.0(half)
     // We do not have to code to emulate 16-bit float operations. Just make sure we do not crash.
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
@@ -4754,7 +5680,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0),
-    // Test case 24: Don't fold OpFNegate for cooperative matrices.
+    // Test case 26: Don't fold OpFNegate for cooperative matrices.
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -4762,7 +5688,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0),
-    // Test case 25: Don't fold OpIAdd for cooperative matrices.
+    // Test case 27: Don't fold OpFAdd for cooperative matrices.
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -4770,7 +5696,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0),
-    // Test case 26: Don't fold OpISub for cooperative matrices.
+    // Test case 28: Don't fold OpFSub for cooperative matrices.
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -4778,7 +5704,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0),
-    // Test case 27: Don't fold OpIMul for cooperative matrices.
+    // Test case 29: Don't fold OpFMul for cooperative matrices.
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -4786,7 +5712,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0),
-    // Test case 28: Don't fold OpSDiv for cooperative matrices.
+    // Test case 30: Don't fold OpFDiv for cooperative matrices.
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -4794,14 +5720,66 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0),
-    // Test case 29: Don't fold OpMatrixTimesScalar for cooperative matrices.
+    // Test case 31: Don't fold OpMatrixTimesScalar for cooperative matrices.
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
             "%2 = OpMatrixTimesScalar %float_coop_matrix %undef_float_coop_matrix %float_3\n" +
             "OpReturn\n" +
             "OpFunctionEnd",
-        2, 0)
+        2, 0),
+    // Test case 32: Don't fold FMix half (1.0, 2.0, 0.5)
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %half %1 FMix %half_1 %half_2 %half_0p5\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0),
+    // Test case 33: Fold FMix half (3.0, 2.0, 0.0)
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %half %1 FMix %112 %half_2 %108\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, HALF_3_ID),
+    // Test case 34: Fold FMix half (3.0, 2.0, null)
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %half %1 FMix %112 %half_2 %half_null\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, HALF_3_ID),
+    // Test case 35: Don't fold FMix half (1.0, 2.0, 1.0)
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%2 = OpExtInst %half %1 FMix %half_1 %half_2 %half_1\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 0),
+    // Test case 36: Fold n - n
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_float Function\n" +
+            "%3 = OpLoad %float %n\n" +
+            "%2 = OpFSub %float %3 %3\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, FLOAT_NULL_ID),
+    // Test case 37: Fold n - n
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_int Function\n" +
+            "%3 = OpLoad %int %n\n" +
+            "%2 = OpISub %int %3 %3\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, INT_NULL_ID)
 ));
 
 INSTANTIATE_TEST_SUITE_P(DoubleRedundantFoldingTest, GeneralInstructionFoldingTest,
@@ -5020,7 +5998,27 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, DVEC4_0_ID),
-    // Test case 2: Fold a * vec4(1.0, 1.0, 1.0, 1.0)
+    // Test case 2: Fold a + vec4(0.0, 0.0, 0.0, 0.0)
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_v4double Function\n" +
+            "%3 = OpLoad %v4double %n\n" +
+            "%2 = OpFAdd %v4double %3 %106\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 3),
+    // Test case 3: Fold a - vec4(0.0, 0.0, 0.0, 0.0)
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_v4double Function\n" +
+            "%3 = OpLoad %v4double %n\n" +
+            "%2 = OpFSub %v4double %3 %106\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 3),
+    // Test case 4: Fold a * vec4(1.0, 1.0, 1.0, 1.0)
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -5054,7 +6052,57 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0),
-    // Test case 2: Fold n + 0
+    // Test case 2: Fold n | 0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_uint Function\n" +
+            "%3 = OpLoad %uint %n\n" +
+            "%2 = OpBitwiseOr %uint %3 %uint_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 3),
+    // Test case 3: Fold n ^ 0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_uint Function\n" +
+            "%3 = OpLoad %uint %n\n" +
+            "%2 = OpBitwiseXor %uint %3 %uint_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 3),
+    // Test case 4: Fold n >> 0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_uint Function\n" +
+            "%3 = OpLoad %uint %n\n" +
+            "%2 = OpShiftRightLogical %uint %3 %uint_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 3),
+    // Test case 5: Fold n >> 0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_uint Function\n" +
+            "%3 = OpLoad %uint %n\n" +
+            "%2 = OpShiftRightArithmetic %uint %3 %uint_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 3),
+    // Test case 6: Fold n << 0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_uint Function\n" +
+            "%3 = OpLoad %uint %n\n" +
+            "%2 = OpShiftLeftLogical %uint %3 %uint_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 3),
+    // Test case 7: Fold n + 0
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -5064,7 +6112,37 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 3),
-    // Test case 3: Fold 0 + n
+    // Test case 8: Fold n - 0
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_uint Function\n" +
+            "%3 = OpLoad %uint %n\n" +
+            "%2 = OpISub %uint %3 %uint_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 3),
+    // Test case 9: Fold 0 | n
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_uint Function\n" +
+            "%3 = OpLoad %uint %n\n" +
+            "%2 = OpBitwiseOr %uint %uint_0 %3\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 3),
+    // Test case 10: Fold 0 ^ n
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_uint Function\n" +
+            "%3 = OpLoad %uint %n\n" +
+            "%2 = OpBitwiseXor %uint %uint_0 %3\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 3),
+    // Test case 11: Fold 0 + n
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -5074,7 +6152,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 3),
-    // Test case 4: Don't fold n + (1,0)
+    // Test case 12: Don't fold n + (1,0)
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -5084,7 +6162,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0),
-    // Test case 5: Don't fold (1,0) + n
+    // Test case 13: Don't fold (1,0) + n
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -5094,7 +6172,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0),
-    // Test case 6: Fold n + (0,0)
+    // Test case 14: Fold n + (0,0)
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -5104,7 +6182,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 3),
-    // Test case 7: Fold (0,0) + n
+    // Test case 15: Fold (0,0) + n
     InstructionFoldingCase<uint32_t>(
         Header() + "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
@@ -5114,7 +6192,117 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 3),
-    // Test case 8: Don't fold because of undefined value. Using 4294967295
+    // Test case 16: Fold n | (0,0)
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_v2int Function\n" +
+            "%3 = OpLoad %v2int %n\n" +
+            "%2 = OpBitwiseOr %v2int %3 %v2int_0_0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 3),
+    // Test case 17: Fold (0,0) | n
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_v2int Function\n" +
+            "%3 = OpLoad %v2int %n\n" +
+            "%2 = OpBitwiseOr %v2int %v2int_0_0 %3\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, 3),
+    // Test case 18: Fold 0 >> n
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_uint Function\n" +
+            "%3 = OpLoad %uint %n\n" +
+            "%2 = OpShiftRightLogical %uint %109 %3\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, UINT_0_ID),
+    // Test case 19: Fold 0 >> n
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_uint Function\n" +
+            "%3 = OpLoad %uint %n\n" +
+            "%2 = OpShiftRightArithmetic %uint %109 %3\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, UINT_0_ID),
+    // Test case 20: Fold 0 << n
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_uint Function\n" +
+            "%3 = OpLoad %uint %n\n" +
+            "%2 = OpShiftLeftLogical %uint %109 %3\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, UINT_0_ID),
+    // Test case 21: Fold 0 / n
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_uint Function\n" +
+            "%3 = OpLoad %int %n\n" +
+            "%2 = OpSDiv %int %100 %3\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, INT_0_ID),
+    // Test case 22: Fold 0 / n
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_uint Function\n" +
+            "%3 = OpLoad %uint %n\n" +
+            "%2 = OpUDiv %uint %109 %3\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, UINT_0_ID),
+    // Test case 23: Fold 0 % n
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_uint Function\n" +
+            "%3 = OpLoad %uint %n\n" +
+            "%2 = OpSMod %int %int_0 %3\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, INT_0_ID),
+    // Test case 24: Fold 0 % n
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_uint Function\n" +
+            "%3 = OpLoad %uint %n\n" +
+            "%2 = OpUMod %uint %109 %3\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, UINT_0_ID),
+    // Test case 25: Fold n % 1
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_uint Function\n" +
+            "%3 = OpLoad %int %n\n" +
+            "%2 = OpSMod %int %3 %int_1\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, INT_NULL_ID),
+    // Test case 26: Fold n % 1
+    InstructionFoldingCase<uint32_t>(
+        Header() + "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%n = OpVariable %_ptr_uint Function\n" +
+            "%3 = OpLoad %uint %n\n" +
+            "%2 = OpUMod %uint %3 %uint_1\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        2, UINT_NULL_ID),
+    // Test case 27: Don't fold because of undefined value. Using 4294967295
     // means that entry is undefined. We do not expect it to ever happen, so
     // not worth folding.
     InstructionFoldingCase<uint32_t>(
@@ -5126,7 +6314,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         2, 0),
-    // Test case 9: Don't fold because of undefined value. Using 4294967295
+    // Test case 28: Don't fold because of undefined value. Using 4294967295
     // means that entry is undefined. We do not expect it to ever happen, so
     // not worth folding.
     InstructionFoldingCase<uint32_t>(
@@ -5462,7 +6650,7 @@
   std::unique_ptr<IRContext> context;
   Instruction* inst;
   std::tie(context, inst) =
-      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_1);
+      FoldInstruction(tc.test_body, tc.id_to_fold, SPV_ENV_UNIVERSAL_1_5);
 
   EXPECT_TRUE((inst == nullptr) == (tc.expected_result == 0));
   if (inst != nullptr) {
@@ -5568,7 +6756,8 @@
 
   std::unique_ptr<IRContext> context;
   Instruction* inst;
-  std::tie(context, inst) = FoldInstruction(tc.test_body, tc.id_to_fold,SPV_ENV_UNIVERSAL_1_1);
+  //std::cerr << "[\n" << tc.test_body << "\n]";
+  std::tie(context, inst) = FoldInstruction(tc.test_body, tc.id_to_fold,SPV_ENV_UNIVERSAL_1_5);
 
   EXPECT_EQ(inst != nullptr, tc.expected_result);
   if (inst != nullptr) {
@@ -5586,7 +6775,7 @@
             "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
             "%n = OpVariable %_ptr_int Function\n" +
-            "%3 = OpLoad %uint %n\n" +
+            "%3 = OpLoad %int %n\n" +
             "%2 = OpIAdd %uint %int_0 %3\n" +
             "OpReturn\n" +
             "OpFunctionEnd\n",
@@ -5598,14 +6787,59 @@
             "; CHECK: %2 = OpBitcast [[int]] %3\n" +
             "%main = OpFunction %void None %void_func\n" +
             "%main_lab = OpLabel\n" +
-            "%n = OpVariable %_ptr_int Function\n" +
-            "%3 = OpLoad %int %n\n" +
+            "%n = OpVariable %_ptr_uint Function\n" +
+            "%3 = OpLoad %uint %n\n" +
             "%2 = OpIAdd %int %uint_0 %3\n" +
             "OpReturn\n" +
             "OpFunctionEnd\n",
         2, true)
 ));
 
+INSTANTIATE_TEST_SUITE_P(RedundantBitcastTest, MatchingInstructionFoldingTest,
+  ::testing::Values(
+    // Test case 0: uint32 x; asuint32(x) => x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: %2 = OpCopyObject [[uint]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%3 = OpLoad %uint %n\n" +
+      "%2 = OpBitcast %uint %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      2, true),
+    // Test case 1: uint32 x; asuint32(asint32(x)) => x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: %2 = OpCopyObject [[uint]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%3 = OpLoad %uint %n\n" +
+      "%4 = OpBitcast %int %3\n" +
+      "%2 = OpBitcast %uint %4\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      2, true),
+    // Test case 2: float32 x; asuint32(asint32(x)) => asuint32(x)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: %2 = OpBitcast [[uint]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_float Function\n" +
+      "%3 = OpLoad %float %n\n" +
+      "%4 = OpBitcast %int %3\n" +
+      "%2 = OpBitcast %uint %4\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      2, true)
+  ));
+
 INSTANTIATE_TEST_SUITE_P(MergeNegateTest, MatchingInstructionFoldingTest,
 ::testing::Values(
   // Test case 0: fold consecutive fnegate
@@ -6131,6 +7365,2073 @@
       4, false)
 ));
 
+INSTANTIATE_TEST_SUITE_P(ReassociateCommutiveBitwiseTest, MatchingInstructionFoldingTest,
+  ::testing::Values(
+    // Test case 0: fold (n ^ 248) ^ 31 = n ^ 231
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%uint_248 = OpConstant %uint 248\n" +
+      "%uint_31 = OpConstant %uint 31\n" +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_231:%\\w+]] = OpConstant [[uint]] 231\n" +
+      "; CHECK: %2 = OpBitwiseXor [[uint]] %4 [[uint_231]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseXor %uint %4 %uint_248\n" +
+      "%2 = OpBitwiseXor %uint %3 %uint_31\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 1: fold 31 ^ (n ^ 248) = n ^ 231
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%uint_248 = OpConstant %uint 248\n" +
+      "%uint_31 = OpConstant %uint 31\n" +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_231:%\\w+]] = OpConstant [[uint]] 231\n" +
+      "; CHECK: %2 = OpBitwiseXor [[uint]] %4 [[uint_231]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseXor %uint %4 %uint_248\n" +
+      "%2 = OpBitwiseXor %uint %uint_31 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 2: fold (248 ^ n) ^ 31 = n ^ 231
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%uint_248 = OpConstant %uint 248\n" +
+      "%uint_31 = OpConstant %uint 31\n" +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_231:%\\w+]] = OpConstant [[uint]] 231\n" +
+      "; CHECK: %2 = OpBitwiseXor [[uint]] %4 [[uint_231]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseXor %uint %uint_248 %4\n" +
+      "%2 = OpBitwiseXor %uint %3 %uint_31\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 3: fold 31 ^ (248 ^ n) = n ^ 231
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%uint_248 = OpConstant %uint 248\n" +
+      "%uint_31 = OpConstant %uint 31\n" +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_231:%\\w+]] = OpConstant [[uint]] 231\n" +
+      "; CHECK: %2 = OpBitwiseXor [[uint]] %4 [[uint_231]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseXor %uint %uint_248 %4\n" +
+      "%2 = OpBitwiseXor %uint %uint_31 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 4: fold 3 | (n | 193) = n | 195
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%uint_193 = OpConstant %uint 193\n" +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_195:%\\w+]] = OpConstant [[uint]] 195\n" +
+      "; CHECK: %2 = OpBitwiseOr [[uint]] %4 [[uint_195]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseOr %uint %4 %uint_193\n" +
+      "%2 = OpBitwiseOr %uint %uint_3 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 5: fold (n | 193) | 3 = n | 195
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%uint_193 = OpConstant %uint 193\n" +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_195:%\\w+]] = OpConstant [[uint]] 195\n" +
+      "; CHECK: %2 = OpBitwiseOr [[uint]] %4 [[uint_195]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseOr %uint %4 %uint_193\n" +
+      "%2 = OpBitwiseOr %uint %3 %uint_3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 6: fold 3 | (193 | n) = n | 195
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%uint_193 = OpConstant %uint 193\n" +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_195:%\\w+]] = OpConstant [[uint]] 195\n" +
+      "; CHECK: %2 = OpBitwiseOr [[uint]] %4 [[uint_195]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseOr %uint %uint_193 %4\n" +
+      "%2 = OpBitwiseOr %uint %uint_3 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 7: fold (193 | n) | 3 = n | 195
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%uint_193 = OpConstant %uint 193\n" +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_195:%\\w+]] = OpConstant [[uint]] 195\n" +
+      "; CHECK: %2 = OpBitwiseOr [[uint]] %4 [[uint_195]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseOr %uint %uint_193 %4\n" +
+      "%2 = OpBitwiseOr %uint %3 %uint_3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 8: fold 65520 & (n & 4095) = n & 4080
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%uint_65520 = OpConstant %uint 65520\n" +
+      "%uint_4095 = OpConstant %uint 4095\n" +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_4080:%\\w+]] = OpConstant [[uint]] 4080\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_4080]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseAnd %uint %4 %uint_4095\n" +
+      "%2 = OpBitwiseAnd %uint %uint_65520 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 9: fold (n & 4095) & 65520 = n & 4080
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%uint_65520 = OpConstant %uint 65520\n" +
+      "%uint_4095 = OpConstant %uint 4095\n" +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_4080:%\\w+]] = OpConstant [[uint]] 4080\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_4080]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseAnd %uint %4 %uint_4095\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_65520\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 10: fold 65520 & (4095 & n) = n & 4080
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%uint_65520 = OpConstant %uint 65520\n" +
+      "%uint_4095 = OpConstant %uint 4095\n" +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_4080:%\\w+]] = OpConstant [[uint]] 4080\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_4080]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseAnd %uint %uint_4095 %4\n" +
+      "%2 = OpBitwiseAnd %uint %uint_65520 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 11: fold (4095 & n) & 65520 = n & 4080
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%uint_65520 = OpConstant %uint 65520\n" +
+      "%uint_4095 = OpConstant %uint 4095\n" +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_4080:%\\w+]] = OpConstant [[uint]] 4080\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_4080]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseAnd %uint %uint_4095 %4\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_65520\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true)
+));
+
+INSTANTIATE_TEST_SUITE_P(ReassociateNestedGenericInt, MatchingInstructionFoldingTest,
+  ::testing::Values(
+    // Test case 0:
+    // (a * 3) * (b * 8) = 24 * (a * b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_24:%\\w+]] = OpConstant [[uint]] 24\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpIMul [[uint]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpIMul [[uint]] [[uint_24]] [[rhs]]\n" +
+      "%C0 = OpConstant %uint 3\n" +
+      "%C1 = OpConstant %uint 8\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_uint Function\n" +
+      "%a = OpLoad %uint %var\n" +
+      "%b = OpLoad %uint %var\n" +
+      "%lhs = OpIMul %uint %a %C0\n" +
+      "%rhs = OpIMul %uint %b %C1\n" +
+      "%4 = OpIMul %uint %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 1:
+    // (a * 34) * (5 * b) = 170 * (a * b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_170:%\\w+]] = OpConstant [[uint]] 170\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpIMul [[uint]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpIMul [[uint]] [[uint_170]] [[rhs]]\n" +
+      "%C0 = OpConstant %uint 34\n" +
+      "%C1 = OpConstant %uint 5\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_uint Function\n" +
+      "%a = OpLoad %uint %var\n" +
+      "%b = OpLoad %uint %var\n" +
+      "%lhs = OpIMul %uint %a %C0\n" +
+      "%rhs = OpIMul %uint %C1 %b\n" +
+      "%4 = OpIMul %uint %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 2:
+    // (42 * a) * (b * 7) = 24 * (a * b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_294:%\\w+]] = OpConstant [[uint]] 294\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpIMul [[uint]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpIMul [[uint]] [[uint_294]] [[rhs]]\n" +
+      "%C0 = OpConstant %uint 42\n" +
+      "%C1 = OpConstant %uint 7\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_uint Function\n" +
+      "%a = OpLoad %uint %var\n" +
+      "%b = OpLoad %uint %var\n" +
+      "%lhs = OpIMul %uint %C0 %a\n" +
+      "%rhs = OpIMul %uint %b %C1\n" +
+      "%4 = OpIMul %uint %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 3:
+    // (5 * a) * (14 * b) = 70 * (a * b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_70:%\\w+]] = OpConstant [[uint]] 70\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpIMul [[uint]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpIMul [[uint]] [[uint_70]] [[rhs]]\n" +
+      "%C0 = OpConstant %uint 5\n" +
+      "%C1 = OpConstant %uint 14\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_uint Function\n" +
+      "%a = OpLoad %uint %var\n" +
+      "%b = OpLoad %uint %var\n" +
+      "%lhs = OpIMul %uint %C0 %a\n" +
+      "%rhs = OpIMul %uint %C1 %b\n" +
+      "%4 = OpIMul %uint %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 4:
+    // (a ^ 3) ^ (b ^ 8) = 11 ^ (a ^ b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_11:%\\w+]] = OpConstant [[uint]] 11\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpBitwiseXor [[uint]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpBitwiseXor [[uint]] [[uint_11]] [[rhs]]\n" +
+      "%C0 = OpConstant %uint 3\n" +
+      "%C1 = OpConstant %uint 8\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_uint Function\n" +
+      "%a = OpLoad %uint %var\n" +
+      "%b = OpLoad %uint %var\n" +
+      "%lhs = OpBitwiseXor %uint %a %C0\n" +
+      "%rhs = OpBitwiseXor %uint %b %C1\n" +
+      "%4 = OpBitwiseXor %uint %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 5:
+    // (a ^ 34) ^ (5 ^ b) = 39 ^ (a ^ b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_39:%\\w+]] = OpConstant [[uint]] 39\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpBitwiseXor [[uint]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpBitwiseXor [[uint]] [[uint_39]] [[rhs]]\n" +
+      "%C0 = OpConstant %uint 34\n" +
+      "%C1 = OpConstant %uint 5\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_uint Function\n" +
+      "%a = OpLoad %uint %var\n" +
+      "%b = OpLoad %uint %var\n" +
+      "%lhs = OpBitwiseXor %uint %a %C0\n" +
+      "%rhs = OpBitwiseXor %uint %C1 %b\n" +
+      "%4 = OpBitwiseXor %uint %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 6:
+    // (42 ^ a) ^ (b ^ 7) = 45 ^ (a ^ b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_45:%\\w+]] = OpConstant [[uint]] 45\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpBitwiseXor [[uint]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpBitwiseXor [[uint]] [[uint_45]] [[rhs]]\n" +
+      "%C0 = OpConstant %uint 42\n" +
+      "%C1 = OpConstant %uint 7\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_uint Function\n" +
+      "%a = OpLoad %uint %var\n" +
+      "%b = OpLoad %uint %var\n" +
+      "%lhs = OpBitwiseXor %uint %C0 %a\n" +
+      "%rhs = OpBitwiseXor %uint %b %C1\n" +
+      "%4 = OpBitwiseXor %uint %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 7:
+    // (5 ^ a) ^ (14 ^ b) = 11 ^ (a ^ b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_11:%\\w+]] = OpConstant [[uint]] 11\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpBitwiseXor [[uint]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpBitwiseXor [[uint]] [[uint_11]] [[rhs]]\n" +
+      "%C0 = OpConstant %uint 5\n" +
+      "%C1 = OpConstant %uint 14\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_uint Function\n" +
+      "%a = OpLoad %uint %var\n" +
+      "%b = OpLoad %uint %var\n" +
+      "%lhs = OpBitwiseXor %uint %C0 %a\n" +
+      "%rhs = OpBitwiseXor %uint %C1 %b\n" +
+      "%4 = OpBitwiseXor %uint %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 8:
+    // (a | 3) | (b | 8) = 11 | (a | b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_11:%\\w+]] = OpConstant [[uint]] 11\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpBitwiseOr [[uint]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpBitwiseOr [[uint]] [[uint_11]] [[rhs]]\n" +
+      "%C0 = OpConstant %uint 3\n" +
+      "%C1 = OpConstant %uint 8\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_uint Function\n" +
+      "%a = OpLoad %uint %var\n" +
+      "%b = OpLoad %uint %var\n" +
+      "%lhs = OpBitwiseOr %uint %a %C0\n" +
+      "%rhs = OpBitwiseOr %uint %b %C1\n" +
+      "%4 = OpBitwiseOr %uint %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 9:
+    // (a | 34) | (5 | b) = 39 | (a | b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_39:%\\w+]] = OpConstant [[uint]] 39\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpBitwiseOr [[uint]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpBitwiseOr [[uint]] [[uint_39]] [[rhs]]\n" +
+      "%C0 = OpConstant %uint 34\n" +
+      "%C1 = OpConstant %uint 5\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_uint Function\n" +
+      "%a = OpLoad %uint %var\n" +
+      "%b = OpLoad %uint %var\n" +
+      "%lhs = OpBitwiseOr %uint %a %C0\n" +
+      "%rhs = OpBitwiseOr %uint %C1 %b\n" +
+      "%4 = OpBitwiseOr %uint %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 10:
+    // (42 | a) | (b | 7) = 47 | (a | b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_47:%\\w+]] = OpConstant [[uint]] 47\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpBitwiseOr [[uint]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpBitwiseOr [[uint]] [[uint_47]] [[rhs]]\n" +
+      "%C0 = OpConstant %uint 42\n" +
+      "%C1 = OpConstant %uint 7\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_uint Function\n" +
+      "%a = OpLoad %uint %var\n" +
+      "%b = OpLoad %uint %var\n" +
+      "%lhs = OpBitwiseOr %uint %C0 %a\n" +
+      "%rhs = OpBitwiseOr %uint %b %C1\n" +
+      "%4 = OpBitwiseOr %uint %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 11:
+    // (5 | a) | (14 | b) = 15 | (a | b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_15:%\\w+]] = OpConstant [[uint]] 15\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpBitwiseOr [[uint]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpBitwiseOr [[uint]] [[uint_15]] [[rhs]]\n" +
+      "%C0 = OpConstant %uint 5\n" +
+      "%C1 = OpConstant %uint 14\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_uint Function\n" +
+      "%a = OpLoad %uint %var\n" +
+      "%b = OpLoad %uint %var\n" +
+      "%lhs = OpBitwiseOr %uint %C0 %a\n" +
+      "%rhs = OpBitwiseOr %uint %C1 %b\n" +
+      "%4 = OpBitwiseOr %uint %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 12:
+    // (a & 12) & (b & 24) = 8 & (a & b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_8:%\\w+]] = OpConstant [[uint]] 8\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpBitwiseAnd [[uint]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpBitwiseAnd [[uint]] [[uint_8]] [[rhs]]\n" +
+      "%C0 = OpConstant %uint 12\n" +
+      "%C1 = OpConstant %uint 24\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_uint Function\n" +
+      "%a = OpLoad %uint %var\n" +
+      "%b = OpLoad %uint %var\n" +
+      "%lhs = OpBitwiseAnd %uint %a %C0\n" +
+      "%rhs = OpBitwiseAnd %uint %b %C1\n" +
+      "%4 = OpBitwiseAnd %uint %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 13:
+    // (a & 34) & (6 & b) = 2 & (a & b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_2:%\\w+]] = OpConstant [[uint]] 2\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpBitwiseAnd [[uint]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpBitwiseAnd [[uint]] [[uint_2]] [[rhs]]\n" +
+      "%C0 = OpConstant %uint 34\n" +
+      "%C1 = OpConstant %uint 6\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_uint Function\n" +
+      "%a = OpLoad %uint %var\n" +
+      "%b = OpLoad %uint %var\n" +
+      "%lhs = OpBitwiseAnd %uint %a %C0\n" +
+      "%rhs = OpBitwiseAnd %uint %C1 %b\n" +
+      "%4 = OpBitwiseAnd %uint %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 14:
+    // (43 & a) & (b & 15) = 11 & (a & b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_11:%\\w+]] = OpConstant [[uint]] 11\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpBitwiseAnd [[uint]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpBitwiseAnd [[uint]] [[uint_11]] [[rhs]]\n" +
+      "%C0 = OpConstant %uint 43\n" +
+      "%C1 = OpConstant %uint 15\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_uint Function\n" +
+      "%a = OpLoad %uint %var\n" +
+      "%b = OpLoad %uint %var\n" +
+      "%lhs = OpBitwiseAnd %uint %C0 %a\n" +
+      "%rhs = OpBitwiseAnd %uint %b %C1\n" +
+      "%4 = OpBitwiseAnd %uint %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 15:
+    // (5 & a) & (14 & b) = 4 & (a & b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_4:%\\w+]] = OpConstant [[uint]] 4\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[uint]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpBitwiseAnd [[uint]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpBitwiseAnd [[uint]] [[uint_4]] [[rhs]]\n" +
+      "%C0 = OpConstant %uint 5\n" +
+      "%C1 = OpConstant %uint 14\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_uint Function\n" +
+      "%a = OpLoad %uint %var\n" +
+      "%b = OpLoad %uint %var\n" +
+      "%lhs = OpBitwiseAnd %uint %C0 %a\n" +
+      "%rhs = OpBitwiseAnd %uint %C1 %b\n" +
+      "%4 = OpBitwiseAnd %uint %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true)
+));
+
+INSTANTIATE_TEST_SUITE_P(ReassociateNestedMulDivFloat, MatchingInstructionFoldingTest,
+  ::testing::Values(
+    // Test case 0:
+    // (a * 9) * (b * 12) = 108 * (a * b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_108:%\\w+]] = OpConstant [[float]] 108\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_108]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 9\n" +
+      "%C1 = OpConstant %float 12\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFMul %float %a %C0\n" +
+      "%rhs = OpFMul %float %b %C1\n" +
+      "%4 = OpFMul %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 1:
+    // (a * 24) * (b / 10) = 2.4 * (a * b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_2p4:%\\w+]] = OpConstant [[float]] 2.4\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_2p4]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 24\n" +
+      "%C1 = OpConstant %float 10\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFMul %float %a %C0\n" +
+      "%rhs = OpFDiv %float %b %C1\n" +
+      "%4 = OpFMul %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 2:
+    // (a * 102) * (4 * b) = 408 * (a * b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_408:%\\w+]] = OpConstant [[float]] 408\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_408]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 102\n" +
+      "%C1 = OpConstant %float 4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFMul %float %a %C0\n" +
+      "%rhs = OpFMul %float %C1 %b\n" +
+      "%4 = OpFMul %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 3:
+    // (a * 37) * (8 / b) = 296 * (a / b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_296:%\\w+]] = OpConstant [[float]] 296\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_296]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 37\n" +
+      "%C1 = OpConstant %float 8\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFMul %float %a %C0\n" +
+      "%rhs = OpFDiv %float %C1 %b\n" +
+      "%4 = OpFMul %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 4:
+    // (a * 11) / (b * 2) = 5.5 * (a / b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_5p5:%\\w+]] = OpConstant [[float]] 5.5\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_5p5]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 11\n" +
+      "%C1 = OpConstant %float 2\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFMul %float %a %C0\n" +
+      "%rhs = OpFMul %float %b %C1\n" +
+      "%4 = OpFDiv %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 5:
+    // (a * 8) / (b / 53) = 424 * (a / b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_424:%\\w+]] = OpConstant [[float]] 424\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_424]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 8\n" +
+      "%C1 = OpConstant %float 53\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFMul %float %a %C0\n" +
+      "%rhs = OpFDiv %float %b %C1\n" +
+      "%4 = OpFDiv %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 6:
+    // (a * 13) / (5 * b) = 2.6 * (a / b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_2p6:%\\w+]] = OpConstant [[float]] 2.6\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_2p6]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 13\n" +
+      "%C1 = OpConstant %float 5\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFMul %float %a %C0\n" +
+      "%rhs = OpFMul %float %C1 %b\n" +
+      "%4 = OpFDiv %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 7:
+    // (a * 21) / (2 / b) = 10.5 * (a * b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_10p5:%\\w+]] = OpConstant [[float]] 10.5\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_10p5]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 21\n" +
+      "%C1 = OpConstant %float 2\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFMul %float %a %C0\n" +
+      "%rhs = OpFDiv %float %C1 %b\n" +
+      "%4 = OpFDiv %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 8:
+    // (9 * a) * (b * 12) = 108 * (a * b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_108:%\\w+]] = OpConstant [[float]] 108\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_108]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 9\n" +
+      "%C1 = OpConstant %float 12\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFMul %float %C0 %a\n" +
+      "%rhs = OpFMul %float %b %C1\n" +
+      "%4 = OpFMul %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 9:
+    // (24 * a) * (b / 4) = 6 * (a * b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_6:%\\w+]] = OpConstant [[float]] 6\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_6]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 24\n" +
+      "%C1 = OpConstant %float 4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFMul %float %C0 %a\n" +
+      "%rhs = OpFDiv %float %b %C1\n" +
+      "%4 = OpFMul %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 10:
+    // (102 * a) * (4 * b) = 408 * (a * b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_408:%\\w+]] = OpConstant [[float]] 408\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_408]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 102\n" +
+      "%C1 = OpConstant %float 4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFMul %float %C0 %a\n" +
+      "%rhs = OpFMul %float %C1 %b\n" +
+      "%4 = OpFMul %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 11:
+    // (37 * a) * (8 / b) = 296 * (a / b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_296:%\\w+]] = OpConstant [[float]] 296\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_296]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 37\n" +
+      "%C1 = OpConstant %float 8\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFMul %float %C0 %a\n" +
+      "%rhs = OpFDiv %float %C1 %b\n" +
+      "%4 = OpFMul %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 12:
+    // (11 * a) / (b * 8) = 1.375 * (a / b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_1p375:%\\w+]] = OpConstant [[float]] 1.375\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_1p375]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 11\n" +
+      "%C1 = OpConstant %float 8\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFMul %float %C0 %a\n" +
+      "%rhs = OpFMul %float %b %C1\n" +
+      "%4 = OpFDiv %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 13:
+    // (8 * a) / (b / 53) = 424 * (a / b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_424:%\\w+]] = OpConstant [[float]] 424\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_424]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 8\n" +
+      "%C1 = OpConstant %float 53\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFMul %float %C0 %a\n" +
+      "%rhs = OpFDiv %float %b %C1\n" +
+      "%4 = OpFDiv %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 14:
+    // (20 * a) / (10 * b) = 2 * (a / b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_2]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 20\n" +
+      "%C1 = OpConstant %float 10\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFMul %float %C0 %a\n" +
+      "%rhs = OpFMul %float %C1 %b\n" +
+      "%4 = OpFDiv %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 15:
+    // (21 * a) / (3 / b) = 7 * (a * b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_7:%\\w+]] = OpConstant [[float]] 7\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_7]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 21\n" +
+      "%C1 = OpConstant %float 3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFMul %float %C0 %a\n" +
+      "%rhs = OpFDiv %float %C1 %b\n" +
+      "%4 = OpFDiv %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 16:
+    // (a / 12) * (b * 9) = 0.75 * (a * b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_0p75:%\\w+]] = OpConstant [[float]] 0.75\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_0p75]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 12\n" +
+      "%C1 = OpConstant %float 9\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFDiv %float %a %C0\n" +
+      "%rhs = OpFMul %float %b %C1\n" +
+      "%4 = OpFMul %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 17:
+    // (a / 0.5) * (b / 0.2) = 10 * (a * b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_10:%\\w+]] = OpConstant [[float]] 10\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_10]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 0.5\n" +
+      "%C1 = OpConstant %float 0.2\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFDiv %float %a %C0\n" +
+      "%rhs = OpFDiv %float %b %C1\n" +
+      "%4 = OpFMul %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 18:
+    // (a / 4) * (102 * b) = 25.5 * (a * b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_25p5:%\\w+]] = OpConstant [[float]] 25.5\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_25p5]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 4\n" +
+      "%C1 = OpConstant %float 102\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFDiv %float %a %C0\n" +
+      "%rhs = OpFMul %float %C1 %b\n" +
+      "%4 = OpFMul %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 19:
+    // (a / 8) * (37 / b) = 4.625 * (a / b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_4p625:%\\w+]] = OpConstant [[float]] 4.625\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_4p625]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 8\n" +
+      "%C1 = OpConstant %float 37\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFDiv %float %a %C0\n" +
+      "%rhs = OpFDiv %float %C1 %b\n" +
+      "%4 = OpFMul %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 20:
+    // (a / 10) / (b * 2) = 0.05 * (a / b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_0p05:%\\w+]] = OpConstant [[float]] 0.05\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_0p05]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 10\n" +
+      "%C1 = OpConstant %float 2\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFDiv %float %a %C0\n" +
+      "%rhs = OpFMul %float %b %C1\n" +
+      "%4 = OpFDiv %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 21:
+    // (a / 8) / (b / 53) = 6.625 * (a / b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_6p625:%\\w+]] = OpConstant [[float]] 6.625\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_6p625]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 8\n" +
+      "%C1 = OpConstant %float 53\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFDiv %float %a %C0\n" +
+      "%rhs = OpFDiv %float %b %C1\n" +
+      "%4 = OpFDiv %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 22:
+    // (a / 4) / (8 * b) = 0.03125 * (a / b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_0p03125:%\\w+]] = OpConstant [[float]] 0.03125\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_0p03125]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 4\n" +
+      "%C1 = OpConstant %float 8\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFDiv %float %a %C0\n" +
+      "%rhs = OpFMul %float %C1 %b\n" +
+      "%4 = OpFDiv %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 23:
+    // (a / 2) / (0.25 / b) = 2 * (a * b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_2]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 2\n" +
+      "%C1 = OpConstant %float 0.25\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFDiv %float %a %C0\n" +
+      "%rhs = OpFDiv %float %C1 %b\n" +
+      "%4 = OpFDiv %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 24:
+    // (9 / a) * (b * 12) = 108 * (b / a)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_108:%\\w+]] = OpConstant [[float]] 108\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[b]] [[a]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_108]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 9\n" +
+      "%C1 = OpConstant %float 12\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFDiv %float %C0 %a\n" +
+      "%rhs = OpFMul %float %b %C1\n" +
+      "%4 = OpFMul %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 25:
+    // (24 / a) * (b / 10) = 2.4 * (b / a)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_2p4:%\\w+]] = OpConstant [[float]] 2.4\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[b]] [[a]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_2p4]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 24\n" +
+      "%C1 = OpConstant %float 10\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFDiv %float %C0 %a\n" +
+      "%rhs = OpFDiv %float %b %C1\n" +
+      "%4 = OpFMul %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 26:
+    // (102 / a) * (4 * b) = 408 * (b / a)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_408:%\\w+]] = OpConstant [[float]] 408\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[b]] [[a]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_408]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 102\n" +
+      "%C1 = OpConstant %float 4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFDiv %float %C0 %a\n" +
+      "%rhs = OpFMul %float %C1 %b\n" +
+      "%4 = OpFMul %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 27:
+    // (37 / a) * (8 / b) = 296 / (a * b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_296:%\\w+]] = OpConstant [[float]] 296\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFDiv [[float]] [[float_296]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 37\n" +
+      "%C1 = OpConstant %float 8\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFDiv %float %C0 %a\n" +
+      "%rhs = OpFDiv %float %C1 %b\n" +
+      "%4 = OpFMul %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 28:
+    // (11 / a) / (b * 8) = 1.375 / (a * b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_1p375:%\\w+]] = OpConstant [[float]] 1.375\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFDiv [[float]] [[float_1p375]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 11\n" +
+      "%C1 = OpConstant %float 8\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFDiv %float %C0 %a\n" +
+      "%rhs = OpFMul %float %b %C1\n" +
+      "%4 = OpFDiv %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 29:
+    // (8 / a) / (b / 53) = 424 / (a * b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_424:%\\w+]] = OpConstant [[float]] 424\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFDiv [[float]] [[float_424]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 8\n" +
+      "%C1 = OpConstant %float 53\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFDiv %float %C0 %a\n" +
+      "%rhs = OpFDiv %float %b %C1\n" +
+      "%4 = OpFDiv %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 30:
+    // (13 / a) / (10 * b) = 1.3 / (a * b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_1p3:%\\w+]] = OpConstant [[float]] 1.3\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFMul [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFDiv [[float]] [[float_1p3]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 13\n" +
+      "%C1 = OpConstant %float 10\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFDiv %float %C0 %a\n" +
+      "%rhs = OpFMul %float %C1 %b\n" +
+      "%4 = OpFDiv %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 31:
+    // (21 / a) / (2 / b) = 10.5 * (b / a)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_10p5:%\\w+]] = OpConstant [[float]] 10.5\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFDiv [[float]] [[b]] [[a]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[float_10p5]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 21\n" +
+      "%C1 = OpConstant %float 2\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFDiv %float %C0 %a\n" +
+      "%rhs = OpFDiv %float %C1 %b\n" +
+      "%4 = OpFDiv %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true)
+  ));
+
+INSTANTIATE_TEST_SUITE_P(ReassociateNestedAddSubTest, MatchingInstructionFoldingTest,
+  ::testing::Values(
+    // Test case 0:
+    // (a + 9) + (b + 12) = 21 + (a + b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_21:%\\w+]] = OpConstant [[float]] 21\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_21]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 9\n" +
+      "%C1 = OpConstant %float 12\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFAdd %float %a %C0\n" +
+      "%rhs = OpFAdd %float %b %C1\n" +
+      "%4 = OpFAdd %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 1:
+    // (a + 24) + (b - 11) = 13 + (a + b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_13:%\\w+]] = OpConstant [[float]] 13\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_13]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 24\n" +
+      "%C1 = OpConstant %float 11\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFAdd %float %a %C0\n" +
+      "%rhs = OpFSub %float %b %C1\n" +
+      "%4 = OpFAdd %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 2:
+    // (a + 102) + (4 + b) = 106 + (a + b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_106:%\\w+]] = OpConstant [[float]] 106\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_106]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 102\n" +
+      "%C1 = OpConstant %float 4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFAdd %float %a %C0\n" +
+      "%rhs = OpFAdd %float %C1 %b\n" +
+      "%4 = OpFAdd %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 3:
+    // (a + 37) + (8 - b) = 45 + (a - b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_45:%\\w+]] = OpConstant [[float]] 45\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_45]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 37\n" +
+      "%C1 = OpConstant %float 8\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFAdd %float %a %C0\n" +
+      "%rhs = OpFSub %float %C1 %b\n" +
+      "%4 = OpFAdd %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 4:
+    // (a + 11) - (b + 9) = 2 + (a - b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_2]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 11\n" +
+      "%C1 = OpConstant %float 9\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFAdd %float %a %C0\n" +
+      "%rhs = OpFAdd %float %b %C1\n" +
+      "%4 = OpFSub %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 5:
+    // (a + 8) - (b - 53) = 61 + (a - b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_61:%\\w+]] = OpConstant [[float]] 61\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_61]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 8\n" +
+      "%C1 = OpConstant %float 53\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFAdd %float %a %C0\n" +
+      "%rhs = OpFSub %float %b %C1\n" +
+      "%4 = OpFSub %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 6:
+    // (a + 13) - (12 + b) = 1 + (a - b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_1:%\\w+]] = OpConstant [[float]] 1\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_1]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 13\n" +
+      "%C1 = OpConstant %float 12\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFAdd %float %a %C0\n" +
+      "%rhs = OpFAdd %float %C1 %b\n" +
+      "%4 = OpFSub %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 7:
+    // (a + 2) - (21 - b) = -19 + (a + b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_n19:%\\w+]] = OpConstant [[float]] -19\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_n19]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 2\n" +
+      "%C1 = OpConstant %float 21\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFAdd %float %a %C0\n" +
+      "%rhs = OpFSub %float %C1 %b\n" +
+      "%4 = OpFSub %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 8:
+    // (9 + a) + (b + 12) = 21 + (a + b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_21:%\\w+]] = OpConstant [[float]] 21\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_21]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 9\n" +
+      "%C1 = OpConstant %float 12\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFAdd %float %C0 %a\n" +
+      "%rhs = OpFAdd %float %b %C1\n" +
+      "%4 = OpFAdd %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 9:
+    // (24 + a) + (b - 11) = 13 + (a + b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_13:%\\w+]] = OpConstant [[float]] 13\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_13]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 24\n" +
+      "%C1 = OpConstant %float 11\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFAdd %float %C0 %a\n" +
+      "%rhs = OpFSub %float %b %C1\n" +
+      "%4 = OpFAdd %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 10:
+    // (102 + a) + (4 + b) = 106 + (a + b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_106:%\\w+]] = OpConstant [[float]] 106\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_106]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 102\n" +
+      "%C1 = OpConstant %float 4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFAdd %float %C0 %a\n" +
+      "%rhs = OpFAdd %float %C1 %b\n" +
+      "%4 = OpFAdd %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 11:
+    // (37 + a) + (8 - b) = 45 + (a - b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_45:%\\w+]] = OpConstant [[float]] 45\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_45]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 37\n" +
+      "%C1 = OpConstant %float 8\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFAdd %float %C0 %a\n" +
+      "%rhs = OpFSub %float %C1 %b\n" +
+      "%4 = OpFAdd %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 12:
+    // (11 + a) - (b + 9) = 2 + (a - b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_2]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 11\n" +
+      "%C1 = OpConstant %float 9\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFAdd %float %C0 %a\n" +
+      "%rhs = OpFAdd %float %b %C1\n" +
+      "%4 = OpFSub %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 13:
+    // (8 + a) - (b - 53) = 61 + (a - b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_61:%\\w+]] = OpConstant [[float]] 61\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_61]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 8\n" +
+      "%C1 = OpConstant %float 53\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFAdd %float %C0 %a\n" +
+      "%rhs = OpFSub %float %b %C1\n" +
+      "%4 = OpFSub %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 14:
+    // (13 + a) - (12 + b) = 1 + (a - b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_1:%\\w+]] = OpConstant [[float]] 1\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_1]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 13\n" +
+      "%C1 = OpConstant %float 12\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFAdd %float %C0 %a\n" +
+      "%rhs = OpFAdd %float %C1 %b\n" +
+      "%4 = OpFSub %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 15:
+    // (2 + a) - (21 - b) = -19 + (a + b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_n19:%\\w+]] = OpConstant [[float]] -19\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_n19]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 2\n" +
+      "%C1 = OpConstant %float 21\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFAdd %float %C0 %a\n" +
+      "%rhs = OpFSub %float %C1 %b\n" +
+      "%4 = OpFSub %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 16:
+    // (a - 9) + (b + 12) = 3 + (a + b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_3:%\\w+]] = OpConstant [[float]] 3\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_3]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 9\n" +
+      "%C1 = OpConstant %float 12\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFSub %float %a %C0\n" +
+      "%rhs = OpFAdd %float %b %C1\n" +
+      "%4 = OpFAdd %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 17:
+    // (a - 24) + (b - 11) = -35 + (a + b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_n35:%\\w+]] = OpConstant [[float]] -35\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_n35]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 24\n" +
+      "%C1 = OpConstant %float 11\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFSub %float %a %C0\n" +
+      "%rhs = OpFSub %float %b %C1\n" +
+      "%4 = OpFAdd %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 18:
+    // (a - 102) + (4 + b) = -98 + (a + b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_n98:%\\w+]] = OpConstant [[float]] -98\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_n98]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 102\n" +
+      "%C1 = OpConstant %float 4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFSub %float %a %C0\n" +
+      "%rhs = OpFAdd %float %C1 %b\n" +
+      "%4 = OpFAdd %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 19:
+    // (a - 37) + (8 - b) = -29 + (a - b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_n29:%\\w+]] = OpConstant [[float]] -29\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_n29]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 37\n" +
+      "%C1 = OpConstant %float 8\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFSub %float %a %C0\n" +
+      "%rhs = OpFSub %float %C1 %b\n" +
+      "%4 = OpFAdd %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 20:
+    // (a - 11) - (b + 10) = -21 + (a - b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_n21:%\\w+]] = OpConstant [[float]] -21\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_n21]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 11\n" +
+      "%C1 = OpConstant %float 10\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFSub %float %a %C0\n" +
+      "%rhs = OpFAdd %float %b %C1\n" +
+      "%4 = OpFSub %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 21:
+    // (a - 8) - (b - 53) = 45 + (a - b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_45:%\\w+]] = OpConstant [[float]] 45\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_45]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 8\n" +
+      "%C1 = OpConstant %float 53\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFSub %float %a %C0\n" +
+      "%rhs = OpFSub %float %b %C1\n" +
+      "%4 = OpFSub %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 22:
+    // (a - 13) - (12 + b) = -25 + (a - b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_n25:%\\w+]] = OpConstant [[float]] -25\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_n25]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 13\n" +
+      "%C1 = OpConstant %float 12\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFSub %float %a %C0\n" +
+      "%rhs = OpFAdd %float %C1 %b\n" +
+      "%4 = OpFSub %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 23:
+    // (a - 2) - (21 - b) = -23 + (a + b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_n23:%\\w+]] = OpConstant [[float]] -23\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_n23]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 2\n" +
+      "%C1 = OpConstant %float 21\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFSub %float %a %C0\n" +
+      "%rhs = OpFSub %float %C1 %b\n" +
+      "%4 = OpFSub %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 24:
+    // (9 - a) + (b + 12) = 21 + (b - a)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_21:%\\w+]] = OpConstant [[float]] 21\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[b]] [[a]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_21]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 9\n" +
+      "%C1 = OpConstant %float 12\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFSub %float %C0 %a\n" +
+      "%rhs = OpFAdd %float %b %C1\n" +
+      "%4 = OpFAdd %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 25:
+    // (24 - a) + (b - 11) = 13 + (b - a)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_13:%\\w+]] = OpConstant [[float]] 13\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[b]] [[a]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_13]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 24\n" +
+      "%C1 = OpConstant %float 11\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFSub %float %C0 %a\n" +
+      "%rhs = OpFSub %float %b %C1\n" +
+      "%4 = OpFAdd %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 26:
+    // (102 - a) + (4 + b) = 106 + (b - a)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_106:%\\w+]] = OpConstant [[float]] 106\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[b]] [[a]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_106]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 102\n" +
+      "%C1 = OpConstant %float 4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFSub %float %C0 %a\n" +
+      "%rhs = OpFAdd %float %C1 %b\n" +
+      "%4 = OpFAdd %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 27:
+    // (37 - a) + (8 - b) = 45 - (a + b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_45:%\\w+]] = OpConstant [[float]] 45\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFSub [[float]] [[float_45]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 37\n" +
+      "%C1 = OpConstant %float 8\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFSub %float %C0 %a\n" +
+      "%rhs = OpFSub %float %C1 %b\n" +
+      "%4 = OpFAdd %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 28:
+    // (11 - a) - (b + 9) = 2 - (a + b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFSub [[float]] [[float_2]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 11\n" +
+      "%C1 = OpConstant %float 9\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFSub %float %C0 %a\n" +
+      "%rhs = OpFAdd %float %b %C1\n" +
+      "%4 = OpFSub %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 29:
+    // (8 - a) - (b - 53) = 61 - (a + b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_61:%\\w+]] = OpConstant [[float]] 61\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFSub [[float]] [[float_61]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 8\n" +
+      "%C1 = OpConstant %float 53\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFSub %float %C0 %a\n" +
+      "%rhs = OpFSub %float %b %C1\n" +
+      "%4 = OpFSub %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 30:
+    // (13 - a) - (12 + b) = 1 - (a + b)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_1:%\\w+]] = OpConstant [[float]] 1\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFAdd [[float]] [[a]] [[b]]\n" +
+      "; CHECK: %4 = OpFSub [[float]] [[float_1]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 13\n" +
+      "%C1 = OpConstant %float 12\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFSub %float %C0 %a\n" +
+      "%rhs = OpFAdd %float %C1 %b\n" +
+      "%4 = OpFSub %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true),
+    // Test case 31:
+    // (2 - a) - (21 - b) = -19 + (b - a)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float_n19:%\\w+]] = OpConstant [[float]] -19\n" +
+      "; CHECK: [[a:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[b:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[rhs:%\\w+]] = OpFSub [[float]] [[b]] [[a]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[float_n19]] [[rhs]]\n" +
+      "%C0 = OpConstant %float 2\n" +
+      "%C1 = OpConstant %float 21\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%a = OpLoad %float %var\n" +
+      "%b = OpLoad %float %var\n" +
+      "%lhs = OpFSub %float %C0 %a\n" +
+      "%rhs = OpFSub %float %C1 %b\n" +
+      "%4 = OpFSub %float %lhs %rhs\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      4, true)
+  ));
+
 INSTANTIATE_TEST_SUITE_P(ReciprocalFDivTest, MatchingInstructionFoldingTest,
 ::testing::Values(
   // Test case 0: scalar reicprocal
@@ -6727,6 +10028,103 @@
       "%4 = OpIMul %long %3 %long_10\n" +
       "OpReturn\n" +
       "OpFunctionEnd\n",
+    4, true),
+  // Test case 31: merge fmul with two negatives
+  // (-x) * (-y) = x * y
+  InstructionFoldingCase<bool>(
+    Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[x:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[y:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: %4 = OpFMul [[float]] [[x]] [[y]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%x = OpLoad %float %var\n" +
+      "%y = OpLoad %float %var\n" +
+     "%nx = OpFNegate %float %x\n" +
+     "%ny = OpFNegate %float %y\n" +
+      "%4 = OpFMul %float %nx %ny\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+    4, true),
+  // Test case 32: merge OpVectorTimesScalar with two negatives
+  // (-x) * (-y) = x * y
+  InstructionFoldingCase<bool>(
+    Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[float2:%\\w+]] = OpTypeVector [[float]] 2\n" +
+      "; CHECK: [[x:%\\w+]] = OpLoad [[float2]]\n" +
+      "; CHECK: [[y:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: %4 = OpVectorTimesScalar [[float2]] [[x]] [[y]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+ "%var_f2 = OpVariable %_ptr_v2float Function\n" +
+    "%var = OpVariable %_ptr_float Function\n" +
+      "%x = OpLoad %v2float %var_f2\n" +
+      "%y = OpLoad %float %var\n" +
+     "%nx = OpFNegate %v2float %x\n" +
+     "%ny = OpFNegate %float %y\n" +
+      "%4 = OpVectorTimesScalar %v2float %nx %ny\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+    4, true),
+  // Test case 33: merge smul with two negatives
+  // (-x) * (-y) = x * y
+  InstructionFoldingCase<bool>(
+    Header() +
+      "; CHECK: [[long:%\\w+]] = OpTypeInt 64 1\n" +
+      "; CHECK: [[x:%\\w+]] = OpLoad [[long]]\n" +
+      "; CHECK: [[y:%\\w+]] = OpLoad [[long]]\n" +
+      "; CHECK: %4 = OpIMul [[long]] [[x]] [[y]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_long Function\n" +
+      "%x = OpLoad %long %var\n" +
+      "%y = OpLoad %long %var\n" +
+     "%nx = OpSNegate %long %x\n" +
+     "%ny = OpSNegate %long %y\n" +
+      "%4 = OpIMul %long %nx %ny\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+    4, true),
+  // Test case 34: merge fdiv with two negatives
+  // (-x) / (-y) = x / y
+  InstructionFoldingCase<bool>(
+    Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[x:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[y:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: %4 = OpFDiv [[float]] [[x]] [[y]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_float Function\n" +
+      "%x = OpLoad %float %var\n" +
+      "%y = OpLoad %float %var\n" +
+     "%nx = OpFNegate %float %x\n" +
+     "%ny = OpFNegate %float %y\n" +
+      "%4 = OpFDiv %float %nx %ny\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+    4, true),
+  // Test case 35: merge sdiv with two negatives
+  // (-x) / (-y) = x / y
+  InstructionFoldingCase<bool>(
+    Header() +
+      "; CHECK: [[long:%\\w+]] = OpTypeInt 64 1\n" +
+      "; CHECK: [[x:%\\w+]] = OpLoad [[long]]\n" +
+      "; CHECK: [[y:%\\w+]] = OpLoad [[long]]\n" +
+      "; CHECK: %4 = OpSDiv [[long]] [[x]] [[y]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var = OpVariable %_ptr_long Function\n" +
+      "%x = OpLoad %long %var\n" +
+      "%y = OpLoad %long %var\n" +
+     "%nx = OpSNegate %long %x\n" +
+     "%ny = OpSNegate %long %y\n" +
+      "%4 = OpSDiv %long %nx %ny\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
     4, true)
 ));
 
@@ -6994,73 +10392,1057 @@
     4, false)
 ));
 
+INSTANTIATE_TEST_SUITE_P(RedundantAndOrXorTest, MatchingInstructionFoldingTest,
+  ::testing::Values(
+    // Test case 0: Fold
+    // 1 & (n | 2) = n & 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseOr %uint %4 %uint_2\n" +
+      "%2 = OpBitwiseAnd %uint %uint_1 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 1: Fold
+    // 1 & (2 | n) = n & 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseOr %uint %uint_2 %4\n" +
+      "%2 = OpBitwiseAnd %uint %uint_1 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 2: Fold
+    // (n | 2) & 1 = n & 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseOr %uint %4 %uint_2\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_1\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 3: Fold
+    // (2 | n) & 1 = n & 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseOr %uint %uint_2 %4\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_1\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 4: Fold
+    // 1 & (n | 3) = 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" +
+      "; CHECK: %2 = OpCopyObject [[uint]] [[uint_1]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseOr %uint %4 %uint_3\n" +
+      "%2 = OpBitwiseAnd %uint %uint_1 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 5: Fold
+    // 1 & (3 | n) = 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" +
+      "; CHECK: %2 = OpCopyObject [[uint]] [[uint_1]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseOr %uint %uint_3 %4\n" +
+      "%2 = OpBitwiseAnd %uint %uint_1 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 6: Fold
+    // (n | 3) & 1 = 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" +
+      "; CHECK: %2 = OpCopyObject [[uint]] [[uint_1]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseOr %uint %4 %uint_3\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_1\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 7: Fold
+    // (3 | n) & 1 = 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" +
+      "; CHECK: %2 = OpCopyObject [[uint]] [[uint_1]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseOr %uint %uint_3 %4\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_1\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 8: Do not fold
+    // 3 & (n | 1)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseOr %uint %4 %uint_1\n" +
+      "%2 = OpBitwiseAnd %uint %uint_3 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, false),
+
+    // Test case 9: Do not fold
+    // 3 & (1 | n)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseOr %uint %uint_1 %4\n" +
+      "%2 = OpBitwiseAnd %uint %uint_3 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, false),
+
+    // Test case 10: Do not fold
+    // (n | 1) & 3
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseOr %uint %4 %uint_1\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, false),
+
+    // Test case 11: Do not fold
+    // (1 | n) & 3
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseOr %uint %uint_1 %4\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, false),
+
+    // Test case 12: Fold
+    // 1 & (n ^ 2) = n & 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseXor %uint %4 %uint_2\n" +
+      "%2 = OpBitwiseAnd %uint %uint_1 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 13: Fold
+    // 1 & (2 ^ n) = n & 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseXor %uint %uint_2 %4\n" +
+      "%2 = OpBitwiseAnd %uint %uint_1 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 14: Fold
+    // (n ^ 2) & 1 = n & 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseXor %uint %4 %uint_2\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_1\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 15: Fold
+    // (2 ^ n) & 1 = n & 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseXor %uint %uint_2 %4\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_1\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 16: Do not fold
+    // 1 & (n ^ 3)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseXor %uint %4 %uint_3\n" +
+      "%2 = OpBitwiseAnd %uint %uint_1 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, false),
+
+    // Test case 17: Do not fold
+    // 1 & (3 ^ n)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseXor %uint %uint_3 %4\n" +
+      "%2 = OpBitwiseAnd %uint %uint_1 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, false),
+
+    // Test case 18: Do not fold
+    // (n ^ 3) & 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseXor %uint %4 %uint_3\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_1\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, false),
+
+    // Test case 19: Do not fold
+    // (3 ^ n) & 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseXor %uint %uint_3 %4\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_1\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, false),
+
+    // Test case 20: Fold
+    // 0b101 & (n | 0b10101) = 0b101
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_5:%\\w+]] = OpConstant [[uint]] 5\n" +
+      "; CHECK: %2 = OpCopyObject [[uint]] [[uint_5]]\n" +
+      "%uint_5 = OpConstant %uint 5\n" +
+      "%uint_21 = OpConstant %uint 21\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseOr %uint %4 %uint_21\n" +
+      "%2 = OpBitwiseAnd %uint %uint_5 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 21: Do not fold
+    // 0b101 & (n ^ 0b10101)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%uint_5 = OpConstant %uint 5\n" +
+      "%uint_21 = OpConstant %uint 21\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseXor %uint %4 %uint_21\n" +
+      "%2 = OpBitwiseAnd %uint %uint_5 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, false),
+
+    // Test case 22: Fold
+    // 0b101 & (n ^ 0b1010) = n & 0b101
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_5:%\\w+]] = OpConstant [[uint]] 5\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_5]]\n" +
+      "%uint_5 = OpConstant %uint 5\n" +
+      "%uint_10 = OpConstant %uint 10\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseXor %uint %4 %uint_10\n" +
+      "%2 = OpBitwiseAnd %uint %uint_5 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 23: Fold
+    // 0b101 & (n | 0b1010) = n & 0b101
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_5:%\\w+]] = OpConstant [[uint]] 5\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_5]]\n" +
+      "%uint_5 = OpConstant %uint 5\n" +
+      "%uint_10 = OpConstant %uint 10\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseOr %uint %4 %uint_10\n" +
+      "%2 = OpBitwiseAnd %uint %uint_5 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 24: Fold
+    // 0b11000 & (n ^ 0b00111) = n & 0b11000
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_24:%\\w+]] = OpConstant [[uint]] 24\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_24]]\n" +
+      "%uint_24 = OpConstant %uint 24\n" +
+      "%uint_7 = OpConstant %uint 7\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseXor %uint %4 %uint_7\n" +
+      "%2 = OpBitwiseAnd %uint %uint_24 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+      // Test case 25: Fold
+      // 0b11000 & (n | 0b00111) = n & 0b11000
+      InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_24:%\\w+]] = OpConstant [[uint]] 24\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_24]]\n" +
+      "%uint_24 = OpConstant %uint 24\n" +
+      "%uint_7 = OpConstant %uint 7\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpBitwiseOr %uint %4 %uint_7\n" +
+      "%2 = OpBitwiseAnd %uint %uint_24 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true)
+  ));
+
+INSTANTIATE_TEST_SUITE_P(RedundantAndAddSubTest, MatchingInstructionFoldingTest,
+  ::testing::Values(
+    // Test case 0: Fold
+    // 1 & (n + 2) = n & 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpIAdd %uint %4 %uint_2\n" +
+      "%2 = OpBitwiseAnd %uint %uint_1 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 1: Fold
+    // 1 & (2 + n) = n & 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpIAdd %uint %uint_2 %4\n" +
+      "%2 = OpBitwiseAnd %uint %uint_1 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 2: Fold
+    // (n + 2) & 1 = n & 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpIAdd %uint %4 %uint_2\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_1\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 3: Fold
+    // (2 + n) & 1 = n & 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpIAdd %uint %uint_2 %4\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_1\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 4: Fold
+    // 1 & (n - 2) = n & 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpISub %uint %4 %uint_2\n" +
+      "%2 = OpBitwiseAnd %uint %uint_1 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 5: Do not fold
+    // 1 & (2 - n)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpISub %uint %uint_2 %4\n" +
+      "%2 = OpBitwiseAnd %uint %uint_1 %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, false),
+
+    // Test case 6: Fold
+    // (n - 2) & 1 = n & 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_1]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpISub %uint %4 %uint_2\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_1\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+    // Test case 7: Do not fold
+    // (2 - n) & 1
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpISub %uint %uint_2 %4\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_1\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, false),
+
+    // Test case 8: Do not fold
+    // 1 & (n + 1)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpIAdd %uint %4 %uint_1\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_1\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, false),
+
+    // Test case 9: Do not fold
+    // 1 & (n - 1)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpISub %uint %4 %uint_1\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_1\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, false),
+
+    // Test case 10: Do not fold
+    // 2 & (n + 1)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpIAdd %uint %4 %uint_1\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_2\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, false),
+
+    // Test case 11: Do not fold
+    // 2 & (n - 1)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpISub %uint %4 %uint_1\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_2\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, false),
+
+    // Test case 12: Do not fold
+    // 0b10 & (n + 0b101)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "%uint_5 = OpConstant %uint 5\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpIAdd %uint %4 %uint_5\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_2\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, false),
+
+    // Test case 13: Fold
+    // 0b10010 & (n + 0b11100000) = n & 0b10010
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+      "; CHECK: [[uint_18:%\\w+]] = OpConstant [[uint]] 18\n" +
+      "; CHECK: %2 = OpBitwiseAnd [[uint]] %4 [[uint_18]]\n" +
+      "%uint_18 = OpConstant %uint 18\n" +
+      "%uint_224 = OpConstant %uint 224\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%4 = OpLoad %uint %n\n" +
+      "%3 = OpIAdd %uint %4 %uint_224\n" +
+      "%2 = OpBitwiseAnd %uint %3 %uint_18\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+
+      // Testcase 14: Do not fold
+      // 0b10000 & (n + 0b11100001)
+      InstructionFoldingCase<bool>(
+        Header() +
+        "%uint_16 = OpConstant %uint 16\n" +
+        "%uint_225 = OpConstant %uint 225\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_uint Function\n" +
+        "%4 = OpLoad %uint %n\n" +
+        "%3 = OpIAdd %uint %4 %uint_225\n" +
+        "%2 = OpBitwiseAnd %uint %3 %uint_16\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, false)
+  ));
+
+  INSTANTIATE_TEST_SUITE_P(RedundantAndShiftTest, GeneralInstructionFoldingTest,
+    ::testing::Values(
+      // Test case 0: Fold
+      // 1 & (n << 1) = 0
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_uint Function\n" +
+        "%4 = OpLoad %uint %n\n" +
+        "%3 = OpShiftLeftLogical %uint %4 %uint_1\n" +
+        "%2 = OpBitwiseAnd %uint %uint_1 %3\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, UINT_NULL_ID),
+
+      // Test case 1: Fold
+      // (n << 1) & 1 = 0
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_uint Function\n" +
+        "%4 = OpLoad %uint %n\n" +
+        "%3 = OpShiftLeftLogical %uint %4 %uint_1\n" +
+        "%2 = OpBitwiseAnd %uint %3 %uint_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, UINT_NULL_ID),
+
+      // Test case 2: Do not fold
+      // 3 & (n << 1)
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_uint Function\n" +
+        "%4 = OpLoad %uint %n\n" +
+        "%3 = OpShiftLeftLogical %uint %4 %uint_1\n" +
+        "%2 = OpBitwiseAnd %uint %uint_3 %3\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, 0),
+
+      // Test case 3: Do not fold
+      // (n << 1) & 3
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_uint Function\n" +
+        "%4 = OpLoad %uint %n\n" +
+        "%3 = OpShiftLeftLogical %uint %4 %uint_1\n" +
+        "%2 = OpBitwiseAnd %uint %3 %uint_3\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, 0),
+
+      // Test case 4: Fold
+      // 0x80000000 & (n >> 1) = 0
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%uint_2147483648 = OpConstant %uint 2147483648\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_uint Function\n" +
+        "%4 = OpLoad %uint %n\n" +
+        "%3 = OpShiftRightLogical %uint %4 %uint_1\n" +
+        "%2 = OpBitwiseAnd %uint %uint_2147483648 %3\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, UINT_NULL_ID),
+
+      // Test case 5: Fold
+      // (n >> 1) & 0x80000000 = 0
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%uint_2147483648 = OpConstant %uint 2147483648\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_uint Function\n" +
+        "%4 = OpLoad %uint %n\n" +
+        "%3 = OpShiftRightLogical %uint %4 %uint_1\n" +
+        "%2 = OpBitwiseAnd %uint %3 %uint_2147483648\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, UINT_NULL_ID),
+
+      // Test case 6: Do not fold
+      // (n >> 1) & 0xc0000000
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%uint_3221225472 = OpConstant %uint 3221225472\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_uint Function\n" +
+        "%4 = OpLoad %uint %n\n" +
+        "%3 = OpShiftRightLogical %uint %4 %uint_1\n" +
+        "%2 = OpBitwiseAnd %uint %3 %uint_3221225472\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, 0),
+
+      // Test case 7: Do not fold
+      // 0xc0000000 & (n >> 1)
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%uint_3221225472 = OpConstant %uint 3221225472\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_uint Function\n" +
+        "%4 = OpLoad %uint %n\n" +
+        "%3 = OpShiftRightLogical %uint %4 %uint_1\n" +
+        "%2 = OpBitwiseAnd %uint %uint_3221225472 %3\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, 0),
+
+      // Test case 8: Fold
+      // 0b1111 & (n << 4) = 0
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%uint_15 = OpConstant %uint 15\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_uint Function\n" +
+        "%4 = OpLoad %uint %n\n" +
+        "%3 = OpShiftLeftLogical %uint %4 %uint_4\n" +
+        "%2 = OpBitwiseAnd %uint %uint_15 %3\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, UINT_NULL_ID),
+
+      // Test case 9: Fold
+      // 0b1000 & (n << 4) = 0
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%uint_8 = OpConstant %uint 8\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_uint Function\n" +
+        "%4 = OpLoad %uint %n\n" +
+        "%3 = OpShiftLeftLogical %uint %4 %uint_4\n" +
+        "%2 = OpBitwiseAnd %uint %uint_8 %3\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, UINT_NULL_ID),
+
+      // Test case 10: Do not fold
+      // 0b1111 & (n << 3)
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%uint_15 = OpConstant %uint 15\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_uint Function\n" +
+        "%4 = OpLoad %uint %n\n" +
+        "%3 = OpShiftLeftLogical %uint %4 %uint_3\n" +
+        "%2 = OpBitwiseAnd %uint %uint_15 %3\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, 0),
+
+      // Test case 11: Do not fold
+      // 0b1000 & (n << 3)
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%uint_8 = OpConstant %uint 8\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_uint Function\n" +
+        "%4 = OpLoad %uint %n\n" +
+        "%3 = OpShiftLeftLogical %uint %4 %uint_3\n" +
+        "%2 = OpBitwiseAnd %uint %uint_8 %3\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, 0),
+
+      // Test case 12: Fold
+      // 0xf0000000 & (n >> 4) = 0
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%uint_4026531840 = OpConstant %uint 4026531840\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_uint Function\n" +
+        "%4 = OpLoad %uint %n\n" +
+        "%3 = OpShiftRightLogical %uint %4 %uint_4\n" +
+        "%2 = OpBitwiseAnd %uint %uint_4026531840 %3\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, UINT_NULL_ID),
+
+      // Test case 13: Do not fold
+      // 0xf0000000 & (n >> 3)
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%uint_4026531840 = OpConstant %uint 4026531840\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_uint Function\n" +
+        "%4 = OpLoad %uint %n\n" +
+        "%3 = OpShiftRightLogical %uint %4 %uint_3\n" +
+        "%2 = OpBitwiseAnd %uint %uint_4026531840 %3\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, 0),
+
+      // Test case 14: Do not fold
+      // 0xf0000001 & (n >> 4)
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%uint_4026531841 = OpConstant %uint 4026531841\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_uint Function\n" +
+        "%4 = OpLoad %uint %n\n" +
+        "%3 = OpShiftRightLogical %uint %4 %uint_4\n" +
+        "%2 = OpBitwiseAnd %uint %uint_4026531841 %3\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, 0),
+
+      // Test case 15: Fold (mixed-width shift amount)
+      // 1u64 & (n << 1u32) = 0
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_ulong Function\n" +
+        "%4 = OpLoad %ulong %n\n" +
+        "%3 = OpShiftLeftLogical %ulong %4 %uint_1\n" +
+        "%2 = OpBitwiseAnd %ulong %ulong_1 %3\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, ULONG_NULL_ID),
+
+      // Test case 16: Fold (8-bit base, 32-bit shift amount)
+      // 1u8 & (n << 1u32) = 0
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_ubyte Function\n" +
+        "%4 = OpLoad %ubyte %n\n" +
+        "%3 = OpShiftLeftLogical %ubyte %4 %uint_1\n" +
+        "%2 = OpBitwiseAnd %ubyte %ubyte_1 %3\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, UBYTE_NULL_ID),
+
+      // Test case 17: Fold (16-bit base, 32-bit shift amount)
+      // 1u16 & (n << 1u32) = 0
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_ushort Function\n" +
+        "%4 = OpLoad %ushort %n\n" +
+        "%3 = OpShiftLeftLogical %ushort %4 %uint_1\n" +
+        "%2 = OpBitwiseAnd %ushort %ushort_1 %3\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, USHORT_NULL_ID),
+
+      // Test case 18: Fold (vector, mixed-width shift amount)
+      // <1,0>u16 & (n << <1,0>u32) = 0
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_v2ushort Function\n" +
+        "%4 = OpLoad %v2ushort %n\n" +
+        "%3 = OpShiftLeftLogical %v2ushort %4 %v2uint_1_null\n" +
+        "%2 = OpBitwiseAnd %v2ushort %v2ushort_1_null %3\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, V2USHORT_NULL_ID),
+
+      // Test case 19: Fold (8-bit base, 32-bit shift amount, right shift)
+      // 0x80u8 & (n >> 1u32) = 0
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%ubyte_128 = OpConstant %ubyte 128\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_ubyte Function\n" +
+        "%4 = OpLoad %ubyte %n\n" +
+        "%3 = OpShiftRightLogical %ubyte %4 %uint_1\n" +
+        "%2 = OpBitwiseAnd %ubyte %ubyte_128 %3\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, UBYTE_NULL_ID),
+
+      // Test case 20: Fold (vector, mixed-width shift amount, right shift)
+      // <0x8000,0>u16 & (n >> <1,0>u32) = 0
+      InstructionFoldingCase<uint32_t>(
+        Header() +
+        "%ushort_32768 = OpConstant %ushort 32768\n" +
+        "%v2ushort_32768_0 = OpConstantComposite %v2ushort %ushort_32768 %ushort_0\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_v2ushort Function\n" +
+        "%4 = OpLoad %v2ushort %n\n" +
+        "%3 = OpShiftRightLogical %v2ushort %4 %v2uint_1_null\n" +
+        "%2 = OpBitwiseAnd %v2ushort %v2ushort_32768_0 %3\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, V2USHORT_NULL_ID)
+    ));
+
 INSTANTIATE_TEST_SUITE_P(MergeAddTest, MatchingInstructionFoldingTest,
 ::testing::Values(
   // Test case 0: merge add of negate
-  // (-x) + 2 = 2 - x
+  // (-x) + y = y - x
   InstructionFoldingCase<bool>(
     Header() +
       "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
-      "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" +
-      "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" +
-      "; CHECK: %4 = OpFSub [[float]] [[float_2]] [[ld]]\n" +
+      "; CHECK: [[x:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[y:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: %4 = OpFSub [[float]] [[y]] [[x]]\n" +
       "%main = OpFunction %void None %void_func\n" +
       "%main_lab = OpLabel\n" +
       "%var = OpVariable %_ptr_float Function\n" +
-      "%2 = OpLoad %float %var\n" +
-      "%3 = OpFNegate %float %2\n" +
-      "%4 = OpFAdd %float %3 %float_2\n" +
+      "%x = OpLoad %float %var\n" +
+      "%y = OpLoad %float %var\n" +
+     "%nx = OpFNegate %float %x\n" +
+      "%4 = OpFAdd %float %nx %y\n" +
       "OpReturn\n" +
       "OpFunctionEnd\n",
     4, true),
   // Test case 1: merge add of negate
-  // 2 + (-x) = 2 - x
+  // y + (-x) = y - x
   InstructionFoldingCase<bool>(
     Header() +
       "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
-      "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" +
-      "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" +
-      "; CHECK: %4 = OpFSub [[float]] [[float_2]] [[ld]]\n" +
+      "; CHECK: [[x:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[y:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: %4 = OpFSub [[float]] [[y]] [[x]]\n" +
       "%main = OpFunction %void None %void_func\n" +
       "%main_lab = OpLabel\n" +
       "%var = OpVariable %_ptr_float Function\n" +
-      "%2 = OpLoad %float %var\n" +
-      "%3 = OpSNegate %float %2\n" +
-      "%4 = OpIAdd %float %float_2 %3\n" +
+      "%x = OpLoad %float %var\n" +
+      "%y = OpLoad %float %var\n" +
+     "%nx = OpFNegate %float %x\n" +
+      "%4 = OpFAdd %float %y %nx\n" +
       "OpReturn\n" +
       "OpFunctionEnd\n",
     4, true),
   // Test case 2: merge add of negate
-  // (-x) + 2 = 2 - x
+  // (-x) + y = y - x
   InstructionFoldingCase<bool>(
     Header() +
       "; CHECK: [[long:%\\w+]] = OpTypeInt 64 1\n" +
-      "; CHECK: [[long_2:%\\w+]] = OpConstant [[long]] 2\n" +
-      "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" +
-      "; CHECK: %4 = OpISub [[long]] [[long_2]] [[ld]]\n" +
+      "; CHECK: [[x:%\\w+]] = OpLoad [[long]]\n" +
+      "; CHECK: [[y:%\\w+]] = OpLoad [[long]]\n" +
+      "; CHECK: %4 = OpISub [[long]] [[y]] [[x]]\n" +
       "%main = OpFunction %void None %void_func\n" +
       "%main_lab = OpLabel\n" +
       "%var = OpVariable %_ptr_long Function\n" +
-      "%2 = OpLoad %long %var\n" +
-      "%3 = OpSNegate %long %2\n" +
-      "%4 = OpIAdd %long %3 %long_2\n" +
+      "%x = OpLoad %long %var\n" +
+      "%y = OpLoad %long %var\n" +
+      "%nx = OpSNegate %long %x\n" +
+      "%4 = OpIAdd %long %nx %y\n" +
       "OpReturn\n" +
       "OpFunctionEnd\n",
     4, true),
   // Test case 3: merge add of negate
-  // 2 + (-x) = 2 - x
+  // y + (-x) = y - x
   InstructionFoldingCase<bool>(
     Header() +
       "; CHECK: [[long:%\\w+]] = OpTypeInt 64 1\n" +
-      "; CHECK: [[long_2:%\\w+]] = OpConstant [[long]] 2\n" +
-      "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" +
-      "; CHECK: %4 = OpISub [[long]] [[long_2]] [[ld]]\n" +
+      "; CHECK: [[x:%\\w+]] = OpLoad [[long]]\n" +
+      "; CHECK: [[y:%\\w+]] = OpLoad [[long]]\n" +
+      "; CHECK: %4 = OpISub [[long]] [[y]] [[x]]\n" +
       "%main = OpFunction %void None %void_func\n" +
       "%main_lab = OpLabel\n" +
       "%var = OpVariable %_ptr_long Function\n" +
-      "%2 = OpLoad %long %var\n" +
-      "%3 = OpSNegate %long %2\n" +
-      "%4 = OpIAdd %long %long_2 %3\n" +
+      "%x = OpLoad %long %var\n" +
+      "%y = OpLoad %long %var\n" +
+      "%nx = OpSNegate %long %x\n" +
+      "%4 = OpIAdd %long %y %nx\n" +
       "OpReturn\n" +
       "OpFunctionEnd\n",
     4, true),
@@ -7430,6 +11812,94 @@
     9, true)
 ));
 
+INSTANTIATE_TEST_SUITE_P(FactorSubMul, MatchingInstructionFoldingTest,
+::testing::Values(
+    // Test case 0: factor of sub of muls
+    // (a * b) - (a * c) => a * (b - c)
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+      "; CHECK: [[newsub:%\\w+]] = OpFSub [[float]] %4 %5\n" +
+      "; CHECK: %9 = OpFMul [[float]] %6 [[newsub]]\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%var0 = OpVariable %_ptr_float Function\n" +
+      "%var1 = OpVariable %_ptr_float Function\n" +
+      "%var2 = OpVariable %_ptr_float Function\n" +
+      "%4 = OpLoad %float %var0\n" +
+      "%5 = OpLoad %float %var1\n" +
+      "%6 = OpLoad %float %var2\n" +
+      "%7 = OpFMul %float %6 %4\n" +
+      "%8 = OpFMul %float %6 %5\n" +
+      "%9 = OpFSub %float %7 %8\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd\n",
+      9, true),
+  // Test case 1: factor of sub of muls
+  // (b * a) - (a * c) => a * (b - c)
+  InstructionFoldingCase<bool>(
+    Header() +
+    "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+    "; CHECK: [[newsub:%\\w+]] = OpFSub [[float]] %4 %5\n" +
+    "; CHECK: %9 = OpFMul [[float]] %6 [[newsub]]\n" +
+    "%main = OpFunction %void None %void_func\n" +
+    "%main_lab = OpLabel\n" +
+    "%var0 = OpVariable %_ptr_float Function\n" +
+    "%var1 = OpVariable %_ptr_float Function\n" +
+    "%var2 = OpVariable %_ptr_float Function\n" +
+    "%4 = OpLoad %float %var0\n" +
+    "%5 = OpLoad %float %var1\n" +
+    "%6 = OpLoad %float %var2\n" +
+    "%7 = OpFMul %float %4 %6\n" +
+    "%8 = OpFMul %float %6 %5\n" +
+    "%9 = OpFSub %float %7 %8\n" +
+    "OpReturn\n" +
+    "OpFunctionEnd\n",
+    9, true),
+  // Test case 2: factor of sub of muls
+  // (a * b) - (c * a) => a * (b - c)
+  InstructionFoldingCase<bool>(
+    Header() +
+    "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+    "; CHECK: [[newsub:%\\w+]] = OpFSub [[float]] %4 %5\n" +
+    "; CHECK: %9 = OpFMul [[float]] %6 [[newsub]]\n" +
+    "%main = OpFunction %void None %void_func\n" +
+    "%main_lab = OpLabel\n" +
+    "%var0 = OpVariable %_ptr_float Function\n" +
+    "%var1 = OpVariable %_ptr_float Function\n" +
+    "%var2 = OpVariable %_ptr_float Function\n" +
+    "%4 = OpLoad %float %var0\n" +
+    "%5 = OpLoad %float %var1\n" +
+    "%6 = OpLoad %float %var2\n" +
+    "%7 = OpFMul %float %6 %4\n" +
+    "%8 = OpFMul %float %5 %6\n" +
+    "%9 = OpFSub %float %7 %8\n" +
+    "OpReturn\n" +
+    "OpFunctionEnd\n",
+    9, true),
+  // Test case 3: factor of sub of muls
+  // (b * a) - (c * a) => a * (b - c)
+  InstructionFoldingCase<bool>(
+    Header() +
+    "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+    "; CHECK: [[newsub:%\\w+]] = OpFSub [[float]] %4 %5\n" +
+    "; CHECK: %9 = OpFMul [[float]] %6 [[newsub]]\n" +
+    "%main = OpFunction %void None %void_func\n" +
+    "%main_lab = OpLabel\n" +
+    "%var0 = OpVariable %_ptr_float Function\n" +
+    "%var1 = OpVariable %_ptr_float Function\n" +
+    "%var2 = OpVariable %_ptr_float Function\n" +
+    "%4 = OpLoad %float %var0\n" +
+    "%5 = OpLoad %float %var1\n" +
+    "%6 = OpLoad %float %var2\n" +
+    "%7 = OpFMul %float %4 %6\n" +
+    "%8 = OpFMul %float %5 %6\n" +
+    "%9 = OpFSub %float %7 %8\n" +
+    "OpReturn\n" +
+    "OpFunctionEnd\n",
+    9, true)
+));
+
 INSTANTIATE_TEST_SUITE_P(MergeSubTest, MatchingInstructionFoldingTest,
 ::testing::Values(
   // Test case 0: merge sub of negate
@@ -7450,19 +11920,20 @@
       "OpFunctionEnd\n",
     4, true),
   // Test case 1: merge sub of negate
-  // 2 - (-x) = x + 2
+  // y - (-x) = y + x
   InstructionFoldingCase<bool>(
     Header() +
       "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
-      "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" +
-      "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" +
-      "; CHECK: %4 = OpFAdd [[float]] [[ld]] [[float_2]]\n" +
+      "; CHECK: [[x:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: [[y:%\\w+]] = OpLoad [[float]]\n" +
+      "; CHECK: %4 = OpFAdd [[float]] [[y]] [[x]]\n" +
       "%main = OpFunction %void None %void_func\n" +
       "%main_lab = OpLabel\n" +
       "%var = OpVariable %_ptr_float Function\n" +
-      "%2 = OpLoad %float %var\n" +
-      "%3 = OpFNegate %float %2\n" +
-      "%4 = OpFSub %float %float_2 %3\n" +
+      "%x = OpLoad %float %var\n" +
+      "%y = OpLoad %float %var\n" +
+     "%nx = OpFNegate %float %x\n" +
+      "%4 = OpFSub %float %y %nx\n" +
       "OpReturn\n" +
       "OpFunctionEnd\n",
     4, true),
@@ -7484,19 +11955,20 @@
       "OpFunctionEnd\n",
     4, true),
   // Test case 3: merge sub of negate
-  // 2 - (-x) = x + 2
+  // y - (-x) = y + x
   InstructionFoldingCase<bool>(
     Header() +
       "; CHECK: [[long:%\\w+]] = OpTypeInt 64 1\n" +
-      "; CHECK: [[long_2:%\\w+]] = OpConstant [[long]] 2\n" +
-      "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" +
-      "; CHECK: %4 = OpIAdd [[long]] [[ld]] [[long_2]]\n" +
+      "; CHECK: [[x:%\\w+]] = OpLoad [[long]]\n" +
+      "; CHECK: [[y:%\\w+]] = OpLoad [[long]]\n" +
+      "; CHECK: %4 = OpIAdd [[long]] [[y]] [[x]]\n" +
       "%main = OpFunction %void None %void_func\n" +
       "%main_lab = OpLabel\n" +
       "%var = OpVariable %_ptr_long Function\n" +
-      "%2 = OpLoad %long %var\n" +
-      "%3 = OpSNegate %long %2\n" +
-      "%4 = OpISub %long %long_2 %3\n" +
+      "%x = OpLoad %long %var\n" +
+      "%y = OpLoad %long %var\n" +
+      "%nx = OpSNegate %long %x\n" +
+      "%4 = OpISub %long %y %nx\n" +
       "OpReturn\n" +
       "OpFunctionEnd\n",
     4, true),
@@ -7811,9 +12283,1845 @@
           "%4 = OpSelect %v2int %v2bool_false_true %2 %3\n" +
           "OpReturn\n" +
           "OpFunctionEnd",
-      4, true)
+      4, true),
+  // Test case 7: Fold select with different type of zeros for both sides
+  InstructionFoldingCase<bool>(
+      Header() +
+          "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+          "; CHECK: [[float_null:%\\w+]] = OpConstantNull [[float]]\n" +
+          "; CHECK: %2 = OpCopyObject [[float]] [[float_null]]\n" +
+          "%main = OpFunction %void None %void_func\n" +
+          "%main_lab = OpLabel\n" +
+          "%n = OpVariable %_ptr_bool Function\n" +
+          "%load = OpLoad %bool %n\n" +
+          "%2 = OpSelect %float %load %float_null %float_0\n" +
+          "OpReturn\n" +
+          "OpFunctionEnd",
+      2, true)
 ));
 
+INSTANTIATE_TEST_SUITE_P(FoldConstantBooleanSelectTest, MatchingInstructionFoldingTest,
+  ::testing::Values(
+    // Test case 0:
+    // x ? true : false = x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%2 = OpSelect %bool %3 %true %false\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 1:
+    // x ? false : true = !x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%2 = OpSelect %bool %3 %false %true\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 2:
+    // x ? true : null_false = x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%2 = OpSelect %bool %3 %true %bool_null\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 3:
+    // x ? null_false : true = !x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%2 = OpSelect %bool %3 %bool_null %true\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true)
+  ));
+
+INSTANTIATE_TEST_SUITE_P(RedundantLogicalAndTest, MatchingInstructionFoldingTest,
+  ::testing::Values(
+    // Test case 0:
+    // x && true = x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%2 = OpLogicalAnd %bool %3 %true\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 1:
+    // true && x = x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%2 = OpLogicalAnd %bool %true %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true)
+  ));
+
+INSTANTIATE_TEST_SUITE_P(RedundantLogicalOrTest, MatchingInstructionFoldingTest,
+  ::testing::Values(
+    // Test case 0:
+    // x || false = x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%2 = OpLogicalOr %bool %3 %false\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 1:
+    // false || x = x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%2 = OpLogicalOr %bool %false %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 2:
+    // x || null_false = x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%2 = OpLogicalOr %bool %3 %bool_null\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 3:
+    // null_false || x = x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%2 = OpLogicalOr %bool %bool_null %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true)
+  ));
+
+INSTANTIATE_TEST_SUITE_P(RedundantLogicalNotTest, MatchingInstructionFoldingTest,
+  ::testing::Values(
+    // Test case 0:
+    // !!x = x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%4 = OpLogicalNot %bool %3\n" +
+      "%2 = OpLogicalNot %bool %4\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true)
+  ));
+
+INSTANTIATE_TEST_SUITE_P(RedundantLogicalEqualTest, MatchingInstructionFoldingTest,
+  ::testing::Values(
+    // Test case 0:
+    // x == true = x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%2 = OpLogicalEqual %bool %3 %true\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 1:
+    // true == x = x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%2 = OpLogicalEqual %bool %true %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 2:
+    // x == false = !x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%2 = OpLogicalEqual %bool %3 %false\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 3:
+    // false == x = !x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%2 = OpLogicalEqual %bool %false %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 4:
+    // x != true = !x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%2 = OpLogicalNotEqual %bool %3 %true\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 5:
+    // true != x = !x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%2 = OpLogicalNotEqual %bool %true %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 6:
+    // x != false = x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%2 = OpLogicalNotEqual %bool %3 %false\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 7:
+    // false != x = x
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%2 = OpLogicalNotEqual %bool %false %3\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true)
+  ));
+
+INSTANTIATE_TEST_SUITE_P(FoldLogicalNotComparisonTest, MatchingInstructionFoldingTest,
+  ::testing::Values(
+    // Test case 0:
+    // !(a == b) = (a != b) [OpIEqual => OpINotEqual]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpINotEqual [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_int Function\n" +
+      "%m = OpVariable %_ptr_int Function\n" +
+      "%3 = OpLoad %int %n\n" +
+      "%4 = OpLoad %int %m\n" +
+      "%5 = OpIEqual %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 1:
+    // !(a != b) = (a == b) [OpINotEqual => OpIEqual]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpIEqual [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_int Function\n" +
+      "%m = OpVariable %_ptr_int Function\n" +
+      "%3 = OpLoad %int %n\n" +
+      "%4 = OpLoad %int %m\n" +
+      "%5 = OpINotEqual %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 2:
+    // !(a == b) = (a != b) [OpFOrdEqual => OpFUnordNotEqual]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpFUnordNotEqual [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_float Function\n" +
+      "%m = OpVariable %_ptr_float Function\n" +
+      "%3 = OpLoad %float %n\n" +
+      "%4 = OpLoad %float %m\n" +
+      "%5 = OpFOrdEqual %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 3:
+    // !(a != b) = (a == b) [OpFUnordNotEqual => OpFOrdEqual]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpFOrdEqual [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_float Function\n" +
+      "%m = OpVariable %_ptr_float Function\n" +
+      "%3 = OpLoad %float %n\n" +
+      "%4 = OpLoad %float %m\n" +
+      "%5 = OpFUnordNotEqual %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 4:
+    // !(a == b) = (a != b) [OpFUnordEqual => OpFOrdNotEqual]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpFOrdNotEqual [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_float Function\n" +
+      "%m = OpVariable %_ptr_float Function\n" +
+      "%3 = OpLoad %float %n\n" +
+      "%4 = OpLoad %float %m\n" +
+      "%5 = OpFUnordEqual %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 5:
+    // !(a != b) = (a == b) [OpFOrdNotEqual => OpFUnordEqual]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpFUnordEqual [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_float Function\n" +
+      "%m = OpVariable %_ptr_float Function\n" +
+      "%3 = OpLoad %float %n\n" +
+      "%4 = OpLoad %float %m\n" +
+      "%5 = OpFOrdNotEqual %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 6:
+    // !(a == b) = (a != b) [OpLogicalEqual => OpLogicalNotEqual]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpLogicalNotEqual [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%m = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%4 = OpLoad %bool %m\n" +
+      "%5 = OpLogicalEqual %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 7:
+    // !(a != b) = (a == b) [OpLogicalNotEqual => OpLogicalEqual]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpLogicalEqual [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_bool Function\n" +
+      "%m = OpVariable %_ptr_bool Function\n" +
+      "%3 = OpLoad %bool %n\n" +
+      "%4 = OpLoad %bool %m\n" +
+      "%5 = OpLogicalNotEqual %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 8:
+    // !(a > b) = (a <= b) [OpUGreaterThan => OpULessThanEqual]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpULessThanEqual [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%m = OpVariable %_ptr_uint Function\n" +
+      "%3 = OpLoad %uint %n\n" +
+      "%4 = OpLoad %uint %m\n" +
+      "%5 = OpUGreaterThan %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 9:
+    // !(a <= b) = (a > b) [OpULessThanEqual => OpUGreaterThan]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpUGreaterThan [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%m = OpVariable %_ptr_uint Function\n" +
+      "%3 = OpLoad %uint %n\n" +
+      "%4 = OpLoad %uint %m\n" +
+      "%5 = OpULessThanEqual %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 10:
+    // !(a > b) = (a <= b) [OpSGreaterThan => OpSLessThanEqual]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpSLessThanEqual [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_int Function\n" +
+      "%m = OpVariable %_ptr_int Function\n" +
+      "%3 = OpLoad %int %n\n" +
+      "%4 = OpLoad %int %m\n" +
+      "%5 = OpSGreaterThan %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 11:
+    // !(a <= b) = (a > b) [OpSLessThanEqual => OpSGreaterThan]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpSGreaterThan [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_int Function\n" +
+      "%m = OpVariable %_ptr_int Function\n" +
+      "%3 = OpLoad %int %n\n" +
+      "%4 = OpLoad %int %m\n" +
+      "%5 = OpSLessThanEqual %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 12:
+    // !(a > b) = (a <= b) [OpFOrdGreaterThan => OpFUnordLessThanEqual]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpFUnordLessThanEqual [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_float Function\n" +
+      "%m = OpVariable %_ptr_float Function\n" +
+      "%3 = OpLoad %float %n\n" +
+      "%4 = OpLoad %float %m\n" +
+      "%5 = OpFOrdGreaterThan %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 13:
+    // !(a <= b) = (a > b) [OpFUnordLessThanEqual => OpFOrdGreaterThan]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpFOrdGreaterThan [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_float Function\n" +
+      "%m = OpVariable %_ptr_float Function\n" +
+      "%3 = OpLoad %float %n\n" +
+      "%4 = OpLoad %float %m\n" +
+      "%5 = OpFUnordLessThanEqual %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 14:
+    // !(a > b) = (a <= b) [OpFUnordGreaterThan => OpFOrdLessThanEqual]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpFOrdLessThanEqual [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_float Function\n" +
+      "%m = OpVariable %_ptr_float Function\n" +
+      "%3 = OpLoad %float %n\n" +
+      "%4 = OpLoad %float %m\n" +
+      "%5 = OpFUnordGreaterThan %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 15:
+    // !(a <= b) = (a > b) [OpFOrdLessThanEqual => OpFUnordGreaterThan]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpFUnordGreaterThan [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_float Function\n" +
+      "%m = OpVariable %_ptr_float Function\n" +
+      "%3 = OpLoad %float %n\n" +
+      "%4 = OpLoad %float %m\n" +
+      "%5 = OpFOrdLessThanEqual %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 16:
+    // !(a < b) = (a >= b) [OpULessThan => OpUGreaterThanEqual]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpUGreaterThanEqual [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%m = OpVariable %_ptr_uint Function\n" +
+      "%3 = OpLoad %uint %n\n" +
+      "%4 = OpLoad %uint %m\n" +
+      "%5 = OpULessThan %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 17:
+    // !(a >= b) = (a < b) [OpUGreaterThanEqual => OpULessThan]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpULessThan [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_uint Function\n" +
+      "%m = OpVariable %_ptr_uint Function\n" +
+      "%3 = OpLoad %uint %n\n" +
+      "%4 = OpLoad %uint %m\n" +
+      "%5 = OpUGreaterThanEqual %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 18:
+    // !(a < b) = (a >= b) [OpSLessThan => OpSGreaterThanEqual]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpSGreaterThanEqual [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_int Function\n" +
+      "%m = OpVariable %_ptr_int Function\n" +
+      "%3 = OpLoad %int %n\n" +
+      "%4 = OpLoad %int %m\n" +
+      "%5 = OpSLessThan %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 19:
+    // !(a >= b) = (a < b) [OpSGreaterThanEqual => OpSLessThan]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpSLessThan [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_int Function\n" +
+      "%m = OpVariable %_ptr_int Function\n" +
+      "%3 = OpLoad %int %n\n" +
+      "%4 = OpLoad %int %m\n" +
+      "%5 = OpSGreaterThanEqual %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 20:
+    // !(a < b) = (a >= b) [OpFOrdLessThan => OpFUnordGreaterThanEqual]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpFUnordGreaterThanEqual [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_float Function\n" +
+      "%m = OpVariable %_ptr_float Function\n" +
+      "%3 = OpLoad %float %n\n" +
+      "%4 = OpLoad %float %m\n" +
+      "%5 = OpFOrdLessThan %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 21:
+    // !(a >= b) = (a < b) [OpFUnordGreaterThanEqual => OpFOrdLessThan]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpFOrdLessThan [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_float Function\n" +
+      "%m = OpVariable %_ptr_float Function\n" +
+      "%3 = OpLoad %float %n\n" +
+      "%4 = OpLoad %float %m\n" +
+      "%5 = OpFUnordGreaterThanEqual %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 22:
+    // !(a < b) = (a >= b) [OpFUnordLessThan => OpFOrdGreaterThanEqual]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpFOrdGreaterThanEqual [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_float Function\n" +
+      "%m = OpVariable %_ptr_float Function\n" +
+      "%3 = OpLoad %float %n\n" +
+      "%4 = OpLoad %float %m\n" +
+      "%5 = OpFUnordLessThan %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true),
+    // Test case 23:
+    // !(a >= b) = (a < b) [OpFOrdGreaterThanEqual => OpFUnordLessThan]
+    InstructionFoldingCase<bool>(
+      Header() +
+      "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+      "; CHECK: %2 = OpFUnordLessThan [[bool]] %3 %4\n" +
+      "%main = OpFunction %void None %void_func\n" +
+      "%main_lab = OpLabel\n" +
+      "%n = OpVariable %_ptr_float Function\n" +
+      "%m = OpVariable %_ptr_float Function\n" +
+      "%3 = OpLoad %float %n\n" +
+      "%4 = OpLoad %float %m\n" +
+      "%5 = OpFOrdGreaterThanEqual %bool %3 %4\n" +
+      "%2 = OpLogicalNot %bool %5\n" +
+      "OpReturn\n" +
+      "OpFunctionEnd",
+      2, true)
+  ));
+
+  INSTANTIATE_TEST_SUITE_P(MergeBinaryComparisonSelectTest, MatchingInstructionFoldingTest,
+    ::testing::Values(
+      // Test case 0:
+      // [OpLogicalEqual] ((a ? [true, false] : [false, true] == [true, false])) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: [[v2bool:%\\w+]] = OpTypeVector [[bool]] 2\n" +
+        "; CHECK: %2 = OpCopyObject [[v2bool]] %3\n" +
+        "%_ptr_v2bool = OpTypePointer Function %v2bool\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_v2bool Function\n" +
+        "%3 = OpLoad %v2bool %n\n" +
+        "%4 = OpSelect %v2bool %3 %v2bool_true_false %v2bool_false_true\n" +
+        "%2 = OpLogicalEqual %v2bool %4 %v2bool_true_false\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 1:
+      // [OpLogicalEqual] ((a ? [true, false] : [false, true] == [false, true])) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: [[v2bool:%\\w+]] = OpTypeVector [[bool]] 2\n" +
+        "; CHECK: %2 = OpLogicalNot [[v2bool]] %3\n" +
+        "%_ptr_v2bool = OpTypePointer Function %v2bool\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_v2bool Function\n" +
+        "%3 = OpLoad %v2bool %n\n" +
+        "%4 = OpSelect %v2bool %3 %v2bool_true_false %v2bool_false_true\n" +
+        "%2 = OpLogicalEqual %v2bool %4 %v2bool_false_true\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 2:
+      // [OpLogicalNotEqual] ((a ? [true, false] : [false, true] != [true, false])) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: [[v2bool:%\\w+]] = OpTypeVector [[bool]] 2\n" +
+        "; CHECK: %2 = OpLogicalNot [[v2bool]] %3\n" +
+        "%_ptr_v2bool = OpTypePointer Function %v2bool\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_v2bool Function\n" +
+        "%3 = OpLoad %v2bool %n\n" +
+        "%4 = OpSelect %v2bool %3 %v2bool_true_false %v2bool_false_true\n" +
+        "%2 = OpLogicalNotEqual %v2bool %4 %v2bool_true_false\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 3:
+      // [OpLogicalNotEqual] ((a ? [true, false] : [false, true]) != [false, true]) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: [[v2bool:%\\w+]] = OpTypeVector [[bool]] 2\n" +
+        "; CHECK: %2 = OpCopyObject [[v2bool]] %3\n" +
+        "%_ptr_v2bool = OpTypePointer Function %v2bool\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_v2bool Function\n" +
+        "%3 = OpLoad %v2bool %n\n" +
+        "%4 = OpSelect %v2bool %3 %v2bool_true_false %v2bool_false_true\n" +
+        "%2 = OpLogicalNotEqual %v2bool %4 %v2bool_false_true\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 4:
+      // [OpIEqual] ((a ? 1 : 0) == 1) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_1 %uint_0\n" +
+        "%2 = OpIEqual %bool %4 %uint_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 5:
+      // [OpIEqual] ((a ? 0 : 1) == 1) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_0 %uint_1\n" +
+        "%2 = OpIEqual %bool %4 %uint_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 6:
+      // [OpINotEqual] ((a ? 1 : 0) != 1) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_1 %uint_0\n" +
+        "%2 = OpINotEqual %bool %4 %uint_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 7:
+      // [OpINotEqual] ((a ? 0 : 1) != 1) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_0 %uint_1\n" +
+        "%2 = OpINotEqual %bool %4 %uint_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 8:
+      // [OpUGreaterThan] ((a ? 2 : 0) > 1) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_2 %uint_0\n" +
+        "%2 = OpUGreaterThan %bool %4 %uint_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 9:
+      // [OpUGreaterThan] (1 > (a ? 2 : 0)) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_2 %uint_0\n" +
+        "%2 = OpUGreaterThan %bool %uint_1 %4\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 10:
+      // [OpUGreaterThan] ((a ? 0 : 2) > 1) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_0 %uint_2\n" +
+        "%2 = OpUGreaterThan %bool %4 %uint_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 11:
+      // [OpUGreaterThan] (1 > (a ? 0 : 2)) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_0 %uint_2\n" +
+        "%2 = OpUGreaterThan %bool %uint_1 %4\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 12:
+      // [OpSGreaterThan] ((a ? 2 : 0) > 1) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %int %3 %int_2 %int_0\n" +
+        "%2 = OpSGreaterThan %bool %4 %int_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 13:
+      // [OpSGreaterThan] ((a ? 0 : 2) > 1) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %int %3 %int_0 %int_2\n" +
+        "%2 = OpSGreaterThan %bool %4 %int_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 14:
+      // [OpUGreaterThanEqual] ((a ? 2 : 0) >= 2) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_2 %uint_0\n" +
+        "%2 = OpUGreaterThanEqual %bool %4 %uint_2\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 15:
+      // [OpUGreaterThanEqual] ((a ? 0 : 2) >= 2) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_0 %uint_2\n" +
+        "%2 = OpUGreaterThanEqual %bool %4 %uint_2\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 16:
+      // [OpSGreaterThanEqual] ((a ? 2 : 0) >= 2) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %int %3 %int_2 %int_0\n" +
+        "%2 = OpSGreaterThanEqual %bool %4 %int_2\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 17:
+      // [OpSGreaterThanEqual] ((a ? 0 : 2) >= 2) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %int %3 %int_0 %int_2\n" +
+        "%2 = OpSGreaterThanEqual %bool %4 %int_2\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 18:
+      // [OpULessThan] ((a ? 0 : 2) < 2) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_0 %uint_2\n" +
+        "%2 = OpULessThan %bool %4 %uint_2\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 19:
+      // [OpULessThan] (2 < (a ? 0 : 2)) = false
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: [[false:%\\w+]] = OpConstantFalse [[bool]]\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] [[false]]\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_0 %uint_2\n" +
+        "%2 = OpULessThan %bool %uint_2 %4\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 20:
+      // [OpULessThan] ((a ? 2 : 0) < 2) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_2 %uint_0\n" +
+        "%2 = OpULessThan %bool %4 %uint_2\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 21:
+      // [OpULessThan] (2 > (a ? 2 : 0)) = false
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: [[false:%\\w+]] = OpConstantFalse [[bool]]\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] [[false]]\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_2 %uint_0\n" +
+        "%2 = OpULessThan %bool %uint_2 %4\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 22:
+      // [OpSLessThan] ((a ? 0 : 2) < 2) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %int %3 %int_0 %int_2\n" +
+        "%2 = OpSLessThan %bool %4 %int_2\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 23:
+      // [OpSLessThan] ((a ? 2 : 0) < 2) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %int %3 %int_2 %int_0\n" +
+        "%2 = OpSLessThan %bool %4 %int_2\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 24:
+      // [OpULessThanEqual] ((a ? 0 : 2) <= 1) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_0 %uint_2\n" +
+        "%2 = OpULessThanEqual %bool %4 %uint_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 25:
+      // [OpULessThanEqual] ((a ? 2 : 0) <= 1) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_2 %uint_0\n" +
+        "%2 = OpULessThanEqual %bool %4 %uint_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 26:
+      // [OpSLessThanEqual] ((a ? 0 : 2) <= 1) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %int %3 %int_0 %int_2\n" +
+        "%2 = OpSLessThanEqual %bool %4 %int_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 27:
+      // [OpSLessThanEqual] ((a ? 2 : 0) <= 1) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %int %3 %int_2 %int_0\n" +
+        "%2 = OpSLessThanEqual %bool %4 %int_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 28:
+      // [OpFUnordEqual] ((a ? 1 : 0) == 1) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_1 %float_0\n" +
+        "%2 = OpFUnordEqual %bool %4 %float_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 29:
+      // [OpFUnordEqual] ((a ? 0 : 1) == 1) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_0 %float_1\n" +
+        "%2 = OpFUnordEqual %bool %4 %float_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 30:
+      // [OpFUnordNotEqual] ((a ? 1 : 0) != 1) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_1 %float_0\n" +
+        "%2 = OpFUnordNotEqual %bool %4 %float_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 31:
+      // [OpFUnordNotEqual] ((a ? 0 : 1) != 1) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_0 %float_1\n" +
+        "%2 = OpFUnordNotEqual %bool %4 %float_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 32:
+      // [OpFOrdEqual] ((a ? 1 : 0) == 1) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_1 %float_0\n" +
+        "%2 = OpFOrdEqual %bool %4 %float_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 33:
+      // [OpFOrdEqual] ((a ? 0 : 1) == 1) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_0 %float_1\n" +
+        "%2 = OpFOrdEqual %bool %4 %float_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 34:
+      // [OpFOrdNotEqual] ((a ? 1 : 0) != 1) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_1 %float_0\n" +
+        "%2 = OpFOrdNotEqual %bool %4 %float_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 35:
+      // [OpFOrdNotEqual] ((a ? 0 : 1) != 1) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_0 %float_1\n" +
+        "%2 = OpFOrdNotEqual %bool %4 %float_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 36:
+      // [OpFOrdGreaterThan] ((a ? 2 : 0) > 1) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_2 %float_0\n" +
+        "%2 = OpFOrdGreaterThan %bool %4 %float_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 37:
+      // [OpFOrdGreaterThan] ((a ? 0 : 2) > 1) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_0 %float_2\n" +
+        "%2 = OpFOrdGreaterThan %bool %4 %float_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 38:
+      // [OpFUnordGreaterThan] ((a ? 2 : 0) > 1) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_2 %float_0\n" +
+        "%2 = OpFUnordGreaterThan %bool %4 %float_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 39:
+      // [OpFUnordGreaterThan] ((a ? 0 : 2) > 1) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_0 %float_2\n" +
+        "%2 = OpFUnordGreaterThan %bool %4 %float_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 40:
+      // [OpFOrdGreaterThanEqual] ((a ? 2 : 0) >= 2) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_2 %float_0\n" +
+        "%2 = OpFOrdGreaterThanEqual %bool %4 %float_2\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 41:
+      // [OpFOrdGreaterThanEqual] ((a ? 0 : 2) >= 2) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_0 %float_2\n" +
+        "%2 = OpFOrdGreaterThanEqual %bool %4 %float_2\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 42:
+      // [OpFUnordGreaterThanEqual] ((a ? 2 : 0) >= 2) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_2 %float_0\n" +
+        "%2 = OpFUnordGreaterThanEqual %bool %4 %float_2\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 43:
+      // [OpFUnordGreaterThanEqual] ((a ? 0 : 2) >= 2) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_0 %float_2\n" +
+        "%2 = OpFUnordGreaterThanEqual %bool %4 %float_2\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 44:
+      // [OpFUnordLessThan] ((a ? 0 : 2) < 2) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_0 %float_2\n" +
+        "%2 = OpFUnordLessThan %bool %4 %float_2\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 45:
+      // [OpFUnordLessThan] ((a ? 2 : 0) < 2) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_2 %float_0\n" +
+        "%2 = OpFUnordLessThan %bool %4 %float_2\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 46:
+      // [OpFOrdLessThan] ((a ? 0 : 2) < 2) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_0 %float_2\n" +
+        "%2 = OpFOrdLessThan %bool %4 %float_2\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 47:
+      // [OpFOrdLessThan] ((a ? 2 : 0) < 2) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_2 %float_0\n" +
+        "%2 = OpFOrdLessThan %bool %4 %float_2\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 48:
+      // [OpFUnordLessThanEqual] ((a ? 0 : 2) <= 1) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_0 %float_2\n" +
+        "%2 = OpFUnordLessThanEqual %bool %4 %float_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 49:
+      // [OpFUnordLessThanEqual] ((a ? 2 : 0) <= 1) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_2 %float_0\n" +
+        "%2 = OpFUnordLessThanEqual %bool %4 %float_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 50:
+      // [OpFOrdLessThanEqual] ((a ? 0 : 2) <= 1) = a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpCopyObject [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_0 %float_2\n" +
+        "%2 = OpFOrdLessThanEqual %bool %4 %float_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 51:
+      // [OpFOrdLessThanEqual] ((a ? 2 : 0) <= 1) = !a
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[bool:%\\w+]] = OpTypeBool\n" +
+        "; CHECK: %2 = OpLogicalNot [[bool]] %3\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_2 %float_0\n" +
+        "%2 = OpFOrdLessThanEqual %bool %4 %float_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 52:
+      // [OpIAdd] (1 + (a ? 2 : 0)) = (a ? 3 : 1)
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
+        "; CHECK: [[int_1:%\\w+]] = OpConstant [[int]] 1\n" +
+        "; CHECK: [[int_3:%\\w+]] = OpConstant [[int]] 3\n" +
+        "; CHECK: %2 = OpSelect [[int]] %3 [[int_3]] [[int_1]]\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %int %3 %int_2 %int_0\n" +
+        "%2 = OpIAdd %int %int_1 %4\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 53:
+      // [OpFAdd] ((a ? 1 : 2) + 1) = (a ? 2 : 3)
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+        "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" +
+        "; CHECK: [[float_3:%\\w+]] = OpConstant [[float]] 3\n" +
+        "; CHECK: %2 = OpSelect [[float]] %3 [[float_2]] [[float_3]]\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_1 %float_2\n" +
+        "%2 = OpFAdd %float %4 %float_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 54:
+      // [OpISub] (1 - (a ? 2 : 0)) = (a ? -1 : 1)
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
+        "; CHECK: [[int_1:%\\w+]] = OpConstant [[int]] 1\n" +
+        "; CHECK: [[int_n1:%\\w+]] = OpConstant [[int]] -1\n" +
+        "; CHECK: %2 = OpSelect [[int]] %3 [[int_n1]] [[int_1]]\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %int %3 %int_2 %int_0\n" +
+        "%2 = OpISub %int %int_1 %4\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 55:
+      // [OpISub] ((a ? 2 : 0) - 1) = (a ? 1 : -1)
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
+        "; CHECK: [[int_1:%\\w+]] = OpConstant [[int]] 1\n" +
+        "; CHECK: [[int_n1:%\\w+]] = OpConstant [[int]] -1\n" +
+        "; CHECK: %2 = OpSelect [[int]] %3 [[int_1]] [[int_n1]]\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %int %3 %int_2 %int_0\n" +
+        "%2 = OpISub %int %4 %int_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 56:
+      // [OpFSub] (1 - (a ? 1 : 4)) = (a ? 0 : -3)
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+        "; CHECK: [[float_0:%\\w+]] = OpConstant [[float]] 0\n" +
+        "; CHECK: [[float_n3:%\\w+]] = OpConstant [[float]] -3\n" +
+        "; CHECK: %2 = OpSelect [[float]] %3 [[float_0]] [[float_n3]]\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_1 %float_4\n" +
+        "%2 = OpFSub %float %float_1 %4\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 57:
+      // [OpFSub] ((a ? 1 : 2) - 1) = (a ? 0 : 1)
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+        "; CHECK: [[float_0:%\\w+]] = OpConstant [[float]] 0\n" +
+        "; CHECK: [[float_1:%\\w+]] = OpConstant [[float]] 1\n" +
+        "; CHECK: %2 = OpSelect [[float]] %3 [[float_0]] [[float_1]]\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_1 %float_2\n" +
+        "%2 = OpFSub %float %4 %float_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 58:
+      // [OpIMul] ((a ? 2 : 1) * 2) = (a ? 4 : 2)
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
+        "; CHECK: [[int_2:%\\w+]] = OpConstant [[int]] 2\n" +
+        "; CHECK: [[int_4:%\\w+]] = OpConstant [[int]] 4\n" +
+        "; CHECK: %2 = OpSelect [[int]] %3 [[int_4]] [[int_2]]\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %int %3 %int_2 %int_1\n" +
+        "%2 = OpIMul %int %4 %int_2\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 59:
+      // [OpFMul] (2 * (a ? 1 : 2)) = (a ? 2 : 4)
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+        "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" +
+        "; CHECK: [[float_4:%\\w+]] = OpConstant [[float]] 4\n" +
+        "; CHECK: %2 = OpSelect [[float]] %3 [[float_2]] [[float_4]]\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_1 %float_2\n" +
+        "%2 = OpFMul %float %float_2 %4\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 60:
+      // [OpUDiv] ((a ? 10 : 5) / 5) = (a ? 2 : 1)
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+        "; CHECK: [[uint_1:%\\w+]] = OpConstant [[uint]] 1\n" +
+        "; CHECK: [[uint_2:%\\w+]] = OpConstant [[uint]] 2\n" +
+        "; CHECK: %2 = OpSelect [[uint]] %3 [[uint_2]] [[uint_1]]\n" +
+        "%uint_5 = OpConstant %uint 5\n" +
+        "%uint_10 = OpConstant %uint 10\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_10 %uint_5\n" +
+        "%2 = OpUDiv %uint %4 %uint_5\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 61:
+      // [OpSDiv] ((a ? 15 : 10) / 5) = (a ? 3 : 2)
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
+        "; CHECK: [[int_2:%\\w+]] = OpConstant [[int]] 2\n" +
+        "; CHECK: [[int_3:%\\w+]] = OpConstant [[int]] 3\n" +
+        "; CHECK: %2 = OpSelect [[int]] %3 [[int_3]] [[int_2]]\n" +
+        "%int_5 = OpConstant %int 5\n" +
+        "%int_15 = OpConstant %int 15\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %int %3 %int_15 %int_10\n" +
+        "%2 = OpSDiv %int %4 %int_5\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 62:
+      // [OpFDiv] ((a ? 8 : 16) / 4) = (a ? 2 : 4)
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+        "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" +
+        "; CHECK: [[float_4:%\\w+]] = OpConstant [[float]] 4\n" +
+        "; CHECK: %2 = OpSelect [[float]] %3 [[float_2]] [[float_4]]\n" +
+        "%float_8 = OpConstant %float 8\n" +
+        "%float_16 = OpConstant %float 16\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %float %3 %float_8 %float_16\n" +
+        "%2 = OpFDiv %float %4 %float_4\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 63:
+      // [OpVectorTimesScalar] ((a ? [1,2] : [2,1]) * 4) = (a ? [4,8] : [8,4])
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" +
+        "; CHECK: [[v2float:%\\w+]] = OpTypeVector [[float]] 2\n" +
+        "; CHECK: [[float_4:%\\w+]] = OpConstant [[float]] 4\n" +
+        "; CHECK: [[float_8:%\\w+]] = OpConstant [[float]] 8\n" +
+        "; CHECK: [[v2float_4_8:%\\w+]] = OpConstantComposite [[v2float]] [[float_4]] [[float_8]]\n" +
+        "; CHECK: [[v2float_8_4:%\\w+]] = OpConstantComposite [[v2float]] [[float_8]] [[float_4]]\n" +
+        "; CHECK: %2 = OpSelect [[v2float]] %3 [[v2float_4_8]] [[v2float_8_4]]\n" +
+        "%v2float_1_2 = OpConstantComposite %v2float %float_1 %float_2\n" +
+        "%v2float_2_1 = OpConstantComposite %v2float %float_2 %float_1\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %v2float %3 %v2float_1_2 %v2float_2_1\n" +
+        "%2 = OpVectorTimesScalar %v2float %4 %float_4\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 64:
+      // [OpShiftRightLogical] ((a ? 32 : 16) >> 2) = (a ? 8 : 4)
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+        "; CHECK: [[uint_4:%\\w+]] = OpConstant [[uint]] 4\n" +
+        "; CHECK: [[uint_8:%\\w+]] = OpConstant [[uint]] 8\n" +
+        "; CHECK: %2 = OpSelect [[uint]] %3 [[uint_8]] [[uint_4]]\n" +
+        "%uint_16 = OpConstant %uint 16\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_32 %uint_16\n" +
+        "%2 = OpShiftRightLogical %uint %4 %uint_2\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 65:
+      // [OpShiftRightArithmetic] ((a ? 16 : 32) >> 1) = (a ? 8 : 16)
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+        "; CHECK: [[uint_16:%\\w+]] = OpConstant [[uint]] 16\n" +
+        "; CHECK: [[uint_8:%\\w+]] = OpConstant [[uint]] 8\n" +
+        "; CHECK: %2 = OpSelect [[uint]] %3 [[uint_8]] [[uint_16]]\n" +
+        "%uint_16 = OpConstant %uint 16\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_16 %uint_32\n" +
+        "%2 = OpShiftRightArithmetic %uint %4 %uint_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 66:
+      // [OpShiftLeftLogical] ((a ? 7 : 3) << 1) = (a ? 14 : 6)
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+        "; CHECK: [[uint_14:%\\w+]] = OpConstant [[uint]] 14\n" +
+        "; CHECK: [[uint_6:%\\w+]] = OpConstant [[uint]] 6\n" +
+        "; CHECK: %2 = OpSelect [[uint]] %3 [[uint_14]] [[uint_6]]\n" +
+        "%uint_7 = OpConstant %uint 7\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_7 %uint_3\n" +
+        "%2 = OpShiftLeftLogical %uint %4 %uint_1\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 67:
+      // [OpBitwiseXor] (19 ^ (a ? 65 : 17)) = (a ? 82 : 2)
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+        "; CHECK: [[uint_2:%\\w+]] = OpConstant [[uint]] 2\n" +
+        "; CHECK: [[uint_82:%\\w+]] = OpConstant [[uint]] 82\n" +
+        "; CHECK: %2 = OpSelect [[uint]] %3 [[uint_82]] [[uint_2]]\n" +
+        "%uint_65 = OpConstant %uint 65\n" +
+        "%uint_17 = OpConstant %uint 17\n" +
+        "%uint_19 = OpConstant %uint 19\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_65 %uint_17\n" +
+        "%2 = OpBitwiseXor %uint %uint_19 %4\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 68:
+      // [OpBitwiseOr] ((a ? 12 : 13) | 20) = (a ? 28 : 29)
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+        "; CHECK: [[uint_28:%\\w+]] = OpConstant [[uint]] 28\n" +
+        "; CHECK: [[uint_29:%\\w+]] = OpConstant [[uint]] 29\n" +
+        "; CHECK: %2 = OpSelect [[uint]] %3 [[uint_28]] [[uint_29]]\n" +
+        "%uint_12 = OpConstant %uint 12\n" +
+        "%uint_13 = OpConstant %uint 13\n" +
+        "%uint_20 = OpConstant %uint 20\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_12 %uint_13\n" +
+        "%2 = OpBitwiseOr %uint %4 %uint_20\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true),
+      // Test case 69:
+      // [OpBitwiseAnd] (12 & (a ? 53 : 58)) = (a ? 4 : 8)
+      InstructionFoldingCase<bool>(
+        Header() +
+        "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" +
+        "; CHECK: [[uint_4:%\\w+]] = OpConstant [[uint]] 4\n" +
+        "; CHECK: [[uint_8:%\\w+]] = OpConstant [[uint]] 8\n" +
+        "; CHECK: %2 = OpSelect [[uint]] %3 [[uint_4]] [[uint_8]]\n" +
+        "%uint_53 = OpConstant %uint 53\n" +
+        "%uint_58 = OpConstant %uint 58\n" +
+        "%uint_12 = OpConstant %uint 12\n" +
+        "%main = OpFunction %void None %void_func\n" +
+        "%main_lab = OpLabel\n" +
+        "%n = OpVariable %_ptr_bool Function\n" +
+        "%3 = OpLoad %bool %n\n" +
+        "%4 = OpSelect %uint %3 %uint_53 %uint_58\n" +
+        "%2 = OpBitwiseAnd %uint %uint_12 %4\n" +
+        "OpReturn\n" +
+        "OpFunctionEnd",
+        2, true)
+  ));
+
 INSTANTIATE_TEST_SUITE_P(CompositeExtractOrInsertMatchingTest, MatchingInstructionFoldingTest,
 ::testing::Values(
     // Test case 0: Extracting from result of consecutive shuffles of differing
@@ -7940,7 +14248,22 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         5, false),
-    // Test case 7: Extracting the undefined literal value from a vector
+    // Test case 7: Don't fold: Using fmix feeding extract with half type in
+    // the a position.
+    InstructionFoldingCase<bool>(
+        Header() +
+            "%main = OpFunction %void None %void_func\n" +
+            "%main_lab = OpLabel\n" +
+            "%m = OpVariable %_ptr_v4half Function\n" +
+            "%n = OpVariable %_ptr_v4half Function\n" +
+            "%2 = OpLoad %v4half %m\n" +
+            "%3 = OpLoad %v4half %n\n" +
+            "%4 = OpExtInst %v4half %1 FMix %2 %3 %v4half_0_1_0_0\n" +
+            "%5 = OpCompositeExtract %half %4 0\n" +
+            "OpReturn\n" +
+            "OpFunctionEnd",
+        5, false),
+    // Test case 8: Extracting the undefined literal value from a vector
     // shuffle.
     InstructionFoldingCase<bool>(
         Header() +
@@ -7955,7 +14278,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         4, true),
-    // Test case 8: Inserting every element of a vector turns into a composite construct.
+    // Test case 9: Inserting every element of a vector turns into a composite construct.
     InstructionFoldingCase<bool>(
         Header() +
             "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
@@ -7974,7 +14297,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         5, true),
-    // Test case 9: Inserting every element of a vector turns into a composite construct in a different order.
+    // Test case 10: Inserting every element of a vector turns into a composite construct in a different order.
     InstructionFoldingCase<bool>(
         Header() +
             "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
@@ -7993,7 +14316,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         5, true),
-    // Test case 10: Check multiple inserts to the same position are handled correctly.
+    // Test case 11: Check multiple inserts to the same position are handled correctly.
     InstructionFoldingCase<bool>(
         Header() +
             "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
@@ -8013,7 +14336,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         6, true),
-    // Test case 11: The last indexes are 0 and 1, but they have different first indexes.  This should not be folded.
+    // Test case 12: The last indexes are 0 and 1, but they have different first indexes.  This should not be folded.
     InstructionFoldingCase<bool>(
         Header() +
             "%main = OpFunction %void None %void_func\n" +
@@ -8023,7 +14346,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         3, false),
-    // Test case 12: Don't fold when there is a partial insertion.
+    // Test case 13: Don't fold when there is a partial insertion.
     InstructionFoldingCase<bool>(
         Header() +
             "%main = OpFunction %void None %void_func\n" +
@@ -8034,7 +14357,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         4, false),
-    // Test case 13: Insert into a column of a matrix
+    // Test case 14: Insert into a column of a matrix
     InstructionFoldingCase<bool>(
         Header() +
             "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
@@ -8053,7 +14376,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         3, true),
-    // Test case 14: Insert all elements of the matrix.
+    // Test case 15: Insert all elements of the matrix.
     InstructionFoldingCase<bool>(
         Header() +
             "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
@@ -8076,7 +14399,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         5, true),
-    // Test case 15: Replace construct with extract when reconstructing a member
+    // Test case 16: Replace construct with extract when reconstructing a member
     // of another object.
     InstructionFoldingCase<bool>(
         Header() +
@@ -8093,7 +14416,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         5, true),
-    // Test case 16: Don't fold when type cannot be deduced to a constant.
+    // Test case 17: Don't fold when type cannot be deduced to a constant.
     InstructionFoldingCase<bool>(
         Header() +
             "%main = OpFunction %void None %void_func\n" +
@@ -8102,7 +14425,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         4, false),
-    // Test case 17: Don't fold when index into composite is out of bounds.
+    // Test case 18: Don't fold when index into composite is out of bounds.
     InstructionFoldingCase<bool>(
 	Header() +
             "%main = OpFunction %void None %void_func\n" +
@@ -8111,7 +14434,7 @@
 	    "OpReturn\n" +
 	    "OpFunctionEnd",
 	4, false),
-    // Test case 18: Fold when every element of an array is inserted.
+    // Test case 19: Fold when every element of an array is inserted.
     InstructionFoldingCase<bool>(
         Header() +
             "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" +
@@ -8128,7 +14451,7 @@
             "OpReturn\n" +
             "OpFunctionEnd",
         5, true),
-    // Test case 19: Don't fold for isomorphic structs
+    // Test case 20: Don't fold for isomorphic structs
     InstructionFoldingCase<bool>(
         Header() +
             "%structA = OpTypeStruct %ulong\n" +
@@ -8141,7 +14464,226 @@
             "%4 = OpCompositeConstruct %structB %3\n" +
             "OpReturn\n" +
             "OpFunctionEnd",
-        4, false)
+        4, false),
+    // Test case 21: Fold OpCopyLogical feeding extract.
+    InstructionFoldingCase<bool>(
+        Header() + R"(
+; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
+; CHECK: [[struct_type1:%\w+]] = OpTypeStruct [[uint]] [[uint]]
+; CHECK: [[struct_type2:%\w+]] = OpTypeStruct [[uint]] [[uint]]
+; CHECK: [[var:%\w+]] = OpVariable
+; CHECK: [[ld:%\w+]] = OpLoad [[struct_type1]] [[var]]
+; CHECK: [[ex:%\w+]] = OpCompositeExtract [[uint]] [[ld]] 0
+; CHECK: %13 = OpCopyObject [[uint]] [[ex]]
+    %struct1 = OpTypeStruct %uint %uint
+    %struct2 = OpTypeStruct %uint %uint
+%_ptr_StorageBuffer_struct1 = OpTypePointer StorageBuffer %struct1
+%var1 = OpVariable %_ptr_StorageBuffer_struct1 StorageBuffer
+       %main = OpFunction %void None %void_func
+          %4 = OpLabel
+         %11 = OpLoad %struct1 %var1
+         %12 = OpCopyLogical %struct2 %11
+         %13 = OpCompositeExtract %uint %12 0
+               OpReturn
+               OpFunctionEnd
+        )",
+        13, true),
+    // Test case 22: Fold OpCopyLogical feeding extract with struct result.
+    InstructionFoldingCase<bool>(
+        Header() + R"(
+; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
+; CHECK: [[struct_type1:%\w+]] = OpTypeStruct [[uint]]
+; CHECK: [[struct_type2:%\w+]] = OpTypeStruct [[uint]]
+; CHECK: [[struct_type3:%\w+]] = OpTypeStruct [[struct_type1]]
+; CHECK: [[struct_type4:%\w+]] = OpTypeStruct [[struct_type2]]
+; CHECK: [[var:%\w+]] = OpVariable
+; CHECK: [[ld:%\w+]] = OpLoad [[struct_type3]] [[var]]
+; CHECK: [[ex:%\w+]] = OpCompositeExtract [[struct_type1]] [[ld]] 0
+; CHECK: %13 = OpCopyLogical [[struct_type2]] [[ex]]
+    %struct1 = OpTypeStruct %uint
+    %struct2 = OpTypeStruct %uint
+    %struct3 = OpTypeStruct %struct1
+    %struct4 = OpTypeStruct %struct2
+%_ptr_StorageBuffer_struct3 = OpTypePointer StorageBuffer %struct3
+%var1 = OpVariable %_ptr_StorageBuffer_struct3 StorageBuffer
+       %main = OpFunction %void None %void_func
+          %4 = OpLabel
+         %11 = OpLoad %struct3 %var1
+         %12 = OpCopyLogical %struct4 %11
+         %13 = OpCompositeExtract %struct2 %12 0
+               OpReturn
+               OpFunctionEnd
+        )",
+        13, true),
+    // Test case 23: Fold OpCopyLogical feeding extract, even if multiple uses.
+    InstructionFoldingCase<bool>(
+        Header() + R"(
+; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
+; CHECK: [[struct_type1:%\w+]] = OpTypeStruct [[uint]] [[uint]]
+; CHECK: [[struct_type2:%\w+]] = OpTypeStruct [[uint]] [[uint]]
+; CHECK: [[var:%\w+]] = OpVariable
+; CHECK: [[ld:%\w+]] = OpLoad [[struct_type1]] [[var]]
+; CHECK: [[ex:%\w+]] = OpCompositeExtract [[uint]] [[ld]] 0
+; CHECK: %13 = OpCopyObject [[uint]] [[ex]]
+    %struct1 = OpTypeStruct %uint %uint
+    %struct2 = OpTypeStruct %uint %uint
+%_ptr_StorageBuffer_struct1 = OpTypePointer StorageBuffer %struct1
+%var1 = OpVariable %_ptr_StorageBuffer_struct1 StorageBuffer
+       %main = OpFunction %void None %void_func
+          %4 = OpLabel
+         %11 = OpLoad %struct1 %var1
+         %12 = OpCopyLogical %struct2 %11
+         %13 = OpCompositeExtract %uint %12 0
+         %14 = OpCompositeExtract %uint %12 1
+               OpReturn
+               OpFunctionEnd
+        )",
+        13, true),
+    // Test case 24: LoadFeedingExtract
+    InstructionFoldingCase<bool>(
+        Header() + R"(
+; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
+; CHECK: [[uint_1:%\w+]] = OpConstant [[uint]] 1
+; CHECK: [[struct_type:%\w+]] = OpTypeStruct [[uint]] [[uint]]
+; CHECK: [[var:%\w+]] = OpVariable
+; CHECK: [[int_ptr:%\w+]] = OpTypePointer StorageBuffer [[uint]]
+; CHECK: [[ac:%\w+]] = OpAccessChain [[int_ptr]] [[var]] [[uint_1]]
+; CHECK: [[new_ld:%\w+]] = OpLoad [[uint]] [[ac]]
+; CHECK: OpCopyObject [[uint]] [[new_ld]]
+%struct1 = OpTypeStruct %uint %uint
+%_ptr_StorageBuffer_struct = OpTypePointer StorageBuffer %struct1
+%var1 = OpVariable %_ptr_StorageBuffer_struct StorageBuffer
+       %main = OpFunction %void None %void_func
+          %4 = OpLabel
+         %11 = OpLoad %struct1 %var1
+         %13 = OpCompositeExtract %uint %11 1
+               OpReturn
+               OpFunctionEnd
+        )",
+        13, true),
+    // Test case 25: Fold multiple extracts
+    InstructionFoldingCase<bool>(
+        Header() + R"(
+; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
+; CHECK: [[uint_0:%\w+]] = OpConstant [[uint]] 0
+; CHECK: [[struct_type:%\w+]] = OpTypeStruct [[uint]] [[uint]]
+; CHECK: [[var:%\w+]] = OpVariable
+; CHECK: [[int_ptr:%\w+]] = OpTypePointer StorageBuffer [[uint]]
+; CHECK: [[ac:%\w+]] = OpAccessChain [[int_ptr]] [[var]] [[uint_0]]
+; CHECK: [[new_ld:%\w+]] = OpLoad [[uint]] [[ac]]
+; CHECK: OpCopyObject [[uint]] [[new_ld]]
+%struct1 = OpTypeStruct %uint %uint
+%_ptr_StorageBuffer_struct = OpTypePointer StorageBuffer %struct1
+%var1 = OpVariable %_ptr_StorageBuffer_struct StorageBuffer
+       %main = OpFunction %void None %void_func
+          %4 = OpLabel
+         %11 = OpLoad %struct1 %var1
+         %13 = OpCompositeExtract %uint %11 0
+         %14 = OpCompositeExtract %uint %11 1
+               OpReturn
+               OpFunctionEnd
+        )",
+        13, true),
+    // Test case 26: Don't fold function scope load.
+    InstructionFoldingCase<bool>(
+        Header() + R"(
+%struct1 = OpTypeStruct %uint %uint
+%_ptr_Function_struct = OpTypePointer Function %struct1
+       %main = OpFunction %void None %void_func
+          %4 = OpLabel
+       %var1 = OpVariable %_ptr_Function_struct Function
+         %11 = OpLoad %struct1 %var1
+         %13 = OpCompositeExtract %uint %11 0
+               OpReturn
+               OpFunctionEnd
+        )",
+        13, false),
+    // Test case 27: Don't fold volatile load feeding extract.
+    InstructionFoldingCase<bool>(
+        Header() + R"(
+%struct1 = OpTypeStruct %uint %uint
+%_ptr_StorageBuffer_struct = OpTypePointer StorageBuffer %struct1
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%3 = OpVariable %_ptr_StorageBuffer_struct StorageBuffer
+       %main = OpFunction %void None %void_func
+          %4 = OpLabel
+         %11 = OpLoad %struct1 %3 Volatile
+         %13 = OpCompositeExtract %uint %11 0
+               OpReturn
+               OpFunctionEnd
+        )",
+        13, false),
+    // Test case 28: Fold with Aligned memory operand.
+    InstructionFoldingCase<bool>(
+        Header() + R"(
+; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
+; CHECK: [[uint_1:%\w+]] = OpConstant [[uint]] 1
+; CHECK: [[struct_type:%\w+]] = OpTypeStruct [[uint]] [[uint]]
+; CHECK: [[var:%\w+]] = OpVariable
+; CHECK: [[int_ptr:%\w+]] = OpTypePointer StorageBuffer [[uint]]
+; CHECK: [[ac:%\w+]] = OpAccessChain [[int_ptr]] [[var]] [[uint_1]]
+; CHECK: [[new_ld:%\w+]] = OpLoad [[uint]] [[ac]] Aligned 4
+; CHECK: OpCopyObject [[uint]] [[new_ld]]
+               OpDecorate %struct1 Offset 0
+               OpMemberDecorate %struct1 1 Offset 4
+%struct1 = OpTypeStruct %uint %uint
+%_ptr_StorageBuffer_struct = OpTypePointer StorageBuffer %struct1
+%var1 = OpVariable %_ptr_StorageBuffer_struct StorageBuffer
+       %main = OpFunction %void None %void_func
+          %4 = OpLabel
+         %11 = OpLoad %struct1 %var1 Aligned 16
+         %13 = OpCompositeExtract %uint %11 1
+               OpReturn
+               OpFunctionEnd
+        )",
+        13, true),
+    // Test case 29: Fold with Aligned and Nontemporal memory operands.
+    InstructionFoldingCase<bool>(
+        Header() + R"(
+; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
+; CHECK: [[uint_1:%\w+]] = OpConstant [[uint]] 1
+; CHECK: [[struct_type:%\w+]] = OpTypeStruct [[uint]] [[uint]]
+; CHECK: [[var:%\w+]] = OpVariable
+; CHECK: [[int_ptr:%\w+]] = OpTypePointer StorageBuffer [[uint]]
+; CHECK: [[ac:%\w+]] = OpAccessChain [[int_ptr]] [[var]] [[uint_1]]
+; CHECK: [[new_ld:%\w+]] = OpLoad [[uint]] [[ac]] Aligned|Nontemporal 4
+; CHECK: OpCopyObject [[uint]] [[new_ld]]
+               OpDecorate %struct1 Offset 0
+               OpMemberDecorate %struct1 1 Offset 4
+%struct1 = OpTypeStruct %uint %uint
+%_ptr_StorageBuffer_struct = OpTypePointer StorageBuffer %struct1
+%var1 = OpVariable %_ptr_StorageBuffer_struct StorageBuffer
+       %main = OpFunction %void None %void_func
+          %4 = OpLabel
+         %11 = OpLoad %struct1 %var1 Aligned|Nontemporal 16
+         %13 = OpCompositeExtract %uint %11 1
+               OpReturn
+               OpFunctionEnd
+        )",
+        13, true),
+    // Test case 30: Fold with MakePointerVisible memory operand and scope.
+    InstructionFoldingCase<bool>(
+        Header() + R"(
+; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
+; CHECK: [[struct_type:%\w+]] = OpTypeStruct [[uint]] [[uint]]
+; CHECK: [[var:%\w+]] = OpVariable
+; CHECK: [[int_ptr:%\w+]] = OpTypePointer StorageBuffer [[uint]]
+; CHECK: [[ac:%\w+]] = OpAccessChain [[int_ptr]] [[var]] [[idx:%\w+]]
+; CHECK: [[new_ld:%\w+]] = OpLoad [[uint]] [[ac]] MakePointerVisible [[idx]]
+; CHECK: OpCopyObject [[uint]] [[new_ld]]
+               OpDecorate %struct1 Offset 0
+               OpMemberDecorate %struct1 1 Offset 4
+%struct1 = OpTypeStruct %uint %uint
+%_ptr_StorageBuffer_struct = OpTypePointer StorageBuffer %struct1
+%var1 = OpVariable %_ptr_StorageBuffer_struct StorageBuffer
+       %main = OpFunction %void None %void_func
+          %4 = OpLabel
+         %11 = OpLoad %struct1 %var1 MakePointerVisible %uint_1
+         %13 = OpCompositeExtract %uint %11 1
+               OpReturn
+               OpFunctionEnd
+        )",
+        13, true)
 ));
 
 INSTANTIATE_TEST_SUITE_P(DotProductMatchingTest, MatchingInstructionFoldingTest,
@@ -8426,7 +14968,7 @@
 
   std::unique_ptr<IRContext> context;
   Instruction* inst;
-  std::tie(context, inst) = FoldInstruction(tc.test_body, tc.id_to_fold,SPV_ENV_UNIVERSAL_1_1);
+  std::tie(context, inst) = FoldInstruction(tc.test_body, tc.id_to_fold,SPV_ENV_UNIVERSAL_1_5);
 
   // Find the instruction to test.
   EXPECT_EQ(inst != nullptr, tc.expected_result);
diff --git a/test/opt/freeze_spec_const_test.cpp b/test/opt/freeze_spec_const_test.cpp
index 1ccaa3e..3295a12 100644
--- a/test/opt/freeze_spec_const_test.cpp
+++ b/test/opt/freeze_spec_const_test.cpp
@@ -172,6 +172,52 @@
                                                      /* skip_nop = */ true);
 }
 
+TEST_F(FreezeSpecConstantValueRemoveDecorationTest, ConstantData) {
+  std::vector<const char*> text = {
+      // clang-format off
+               "OpCapability Shader",
+               "OpCapability ConstantDataKHR",
+               "OpExtension \"SPV_KHR_constant_data\"",
+               "OpMemoryModel Logical GLSL450",
+               "OpEntryPoint GLCompute %uint_1 \"main\"",
+               "OpExecutionMode %2 LocalSize 1 1 1",
+               "OpDecorate %uint_1 SpecId 1",
+               "OpDecorate %3 SpecId 2",
+       "%void = OpTypeVoid",
+       "%uint = OpTypeInt 32 0",
+     "%uint_1 = OpSpecConstant %uint 1",
+"%_arr_uint_2 = OpTypeArray %uint %2",
+          "%3 = OpSpecConstantDataKHR %_arr_uint_2 1",
+          "%7 = OpTypeFunction %void",
+          "%8 = OpFunction %void None %7",
+          "%9 = OpLabel",
+               "OpReturn",
+               "OpFunctionEnd",
+
+      // clang-format on
+  };
+  std::string expected_disassembly = SelectiveJoin(text, [](const char* line) {
+    return std::string(line).find("SpecId") != std::string::npos;
+  });
+  std::vector<std::pair<const char*, const char*>> replacement_pairs = {
+      {"%uint_1 = OpSpecConstant %uint 1", "%uint_1 = OpConstant %uint 1"},
+      {"%3 = OpSpecConstantDataKHR %_arr_uint_2 1",
+       "%3 = OpConstantDataKHR %_arr_uint_2 1"},
+  };
+  for (auto& p : replacement_pairs) {
+    EXPECT_TRUE(FindAndReplace(&expected_disassembly, p.first, p.second))
+        << "text:\n"
+        << expected_disassembly << "\n"
+        << "find_str:\n"
+        << p.first << "\n"
+        << "replace_str:\n"
+        << p.second << "\n";
+  }
+  SinglePassRunAndCheck<FreezeSpecConstantValuePass>(JoinAllInsts(text),
+                                                     expected_disassembly,
+                                                     /* skip_nop = */ true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/graphics_robust_access_test.cpp b/test/opt/graphics_robust_access_test.cpp
index a1a3b7d..20fb7e3 100644
--- a/test/opt/graphics_robust_access_test.cpp
+++ b/test/opt/graphics_robust_access_test.cpp
@@ -110,6 +110,7 @@
   OpCapability Shader
   OpMemoryModel Logical Simple
   OpEntryPoint GLCompute %main "main"
+  OpExecutionMode %main LocalSize 16 1 1
 )";
 }
 
@@ -228,6 +229,7 @@
 //   TODO(dneto): RuntimeArray
 
 TEST_F(GraphicsRobustAccessTest, ACVectorLeastInboundConstantUntouched) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"(
@@ -244,6 +246,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACVectorMostInboundConstantUntouched) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"(
@@ -260,6 +263,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACVectorExcessConstantClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"(
@@ -276,6 +280,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACVectorNegativeConstantClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"(
@@ -295,6 +300,7 @@
 // Like the previous test, but ensures the pass knows how to modify an index
 // which does not come first in the access chain.
 TEST_F(GraphicsRobustAccessTest, ACVectorInArrayNegativeConstantClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"(
@@ -315,6 +321,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACVectorGeneralClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt() << R"(
@@ -335,6 +342,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACVectorGeneralShortClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   // Show that signed 16 bit integers are clamped as well.
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
@@ -360,6 +368,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACVectorGeneralUShortClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   // Show that unsigned 16 bit integers are clamped as well.
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
@@ -385,6 +394,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACVectorGeneralLongClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   // Show that signed 64 bit integers are clamped as well.
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
@@ -410,6 +420,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACVectorGeneralULongClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   // Show that unsigned 64 bit integers are clamped as well.
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
@@ -435,6 +446,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACMatrixLeastInboundConstantUntouched) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
@@ -454,6 +466,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACMatrixMostInboundConstantUntouched) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
@@ -473,6 +486,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACMatrixExcessConstantClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
@@ -492,6 +506,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACMatrixNegativeConstantClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
@@ -511,6 +526,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACMatrixGeneralClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
@@ -534,6 +550,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACArrayLeastInboundConstantUntouched) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
@@ -551,6 +568,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACArrayMostInboundConstantUntouched) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
@@ -568,6 +586,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
@@ -590,6 +609,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralShortIndexUIntBoundsClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   // Index is signed short, array bounds overflows the index type.
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
@@ -615,6 +635,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralUShortIndexIntBoundsClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   // Index is unsigned short, array bounds overflows the index type.
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
@@ -640,6 +661,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralUIntIndexShortBoundsClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   // Signed int index i is wider than the array bounds type.
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
@@ -664,6 +686,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralIntIndexUShortBoundsClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   // Unsigned int index i is wider than the array bounds type.
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
@@ -688,6 +711,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralLongIndexUIntBoundsClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   // Signed long index i is wider than the array bounds type.
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
@@ -712,6 +736,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACArrayGeneralULongIndexIntBoundsClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   // Unsigned long index i is wider than the array bounds type.
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
@@ -737,6 +762,7 @@
 
 TEST_F(GraphicsRobustAccessTest,
        ACArrayGeneralShortIndeArrayBiggerThanShortMaxClipsToShortIntMax) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << "OpCapability Int16\n"
@@ -761,6 +787,7 @@
 
 TEST_F(GraphicsRobustAccessTest,
        ACArrayGeneralIntIndexArrayBiggerThanIntMaxClipsToSignedIntMax) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt()
@@ -784,6 +811,7 @@
 
 TEST_F(GraphicsRobustAccessTest,
        ACArrayGeneralLongIndexArrayBiggerThanLongMaxClipsToSignedLongMax) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << "OpCapability Int64\n"
@@ -811,6 +839,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACArraySpecIdSizedAlwaysClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC({"spec200"}) << R"(
@@ -837,6 +866,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACStructLeastUntouched) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
@@ -853,6 +883,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACStructMostUntouched) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC() << TypesVoid() << TypesInt()
@@ -869,6 +900,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACStructSpecConstantFail) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC({"struct", "spec200"})
@@ -891,6 +923,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACStructFloatConstantFail) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC({"struct"}) <<
@@ -911,6 +944,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACStructNonConstantFail) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC({"struct", "i"}) <<
@@ -932,6 +966,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACStructExcessFail) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC({"struct", "i"}) << TypesVoid() << TypesInt()
@@ -951,6 +986,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACStructNegativeFail) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC({"struct", "i"}) << TypesVoid() << TypesInt()
@@ -970,6 +1006,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACRTArrayLeastInboundClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC() << "OpDecorate %rtarr ArrayStride 4 "
@@ -997,6 +1034,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralShortIndexClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << "OpCapability Int16\n"
@@ -1028,6 +1066,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralUShortIndexClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << "OpCapability Int16\n"
@@ -1059,6 +1098,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralIntIndexClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC({"i"}) << "OpDecorate %rtarr ArrayStride 4 "
@@ -1087,6 +1127,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralUIntIndexClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << ShaderPreambleAC({"i"}) << "OpDecorate %rtarr ArrayStride 4 "
@@ -1115,6 +1156,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralLongIndexClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << "OpCapability Int64" << ShaderPreambleAC({"i"})
@@ -1144,6 +1186,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralULongIndexClamped) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
     shaders << "OpCapability Int64" << ShaderPreambleAC({"i"})
@@ -1173,6 +1216,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACRTArrayStructVectorElem) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   // The point of this test is that the access chain can have indices past the
   // index into the runtime array.  For good measure, the index into the final
   // struct is out of bounds.  We have to clamp that index too.
@@ -1213,6 +1257,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACArrayRTArrayStructVectorElem) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   // Now add an additional level of arrays around the Block-decorated struct.
   for (auto* ac : AccessChains()) {
     std::ostringstream shaders;
@@ -1257,6 +1302,7 @@
 }
 
 TEST_F(GraphicsRobustAccessTest, ACSplitACArrayRTArrayStructVectorElem) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   // Split the address calculation across two access chains.  Force
   // the transform to walk up the access chains to find the base variable.
   for (auto* ac : AccessChains()) {
@@ -1318,6 +1364,7 @@
 
 TEST_F(GraphicsRobustAccessTest,
        ACSplitACArrayRTArrayStructVectorElemAcrossBasicBlocks) {
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
   // Split the address calculation across two access chains.  Force
   // the transform to walk up the access chains to find the base variable.
   // This time, put the different access chains in different basic blocks.
diff --git a/test/opt/inline_test.cpp b/test/opt/inline_test.cpp
index ef7ac37..f4785ab 100644
--- a/test/opt/inline_test.cpp
+++ b/test/opt/inline_test.cpp
@@ -3584,8 +3584,8 @@
 
 TEST_F(InlineTest, DebugSimpleHLSLPixelShader) {
   const std::string text = R"(
-; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} 1 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 1 %src_main
-; CHECK: [[lex_blk:%\d+]] = OpExtInst %void [[ext]] DebugLexicalBlock {{%\d+}} 1 47 [[dbg_main]]
+; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} 2 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 1 %src_main
+; CHECK: [[lex_blk:%\d+]] = OpExtInst %void [[ext]] DebugLexicalBlock {{%\d+}} 2 47 [[dbg_main]]
 ; CHECK: %main = OpFunction %void None
 ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_main]]
 ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare {{%\d+}} %param_var_color
@@ -3637,9 +3637,9 @@
          %18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 Float
          %19 = OpExtInst %void %1 DebugTypeVector %18 4
          %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %19 %19
-         %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %src_main
-         %25 = OpExtInst %void %1 DebugLocalVariable %24 %19 %15 1 20 %22 FlagIsLocal 0
-         %26 = OpExtInst %void %1 DebugLexicalBlock %15 1 47 %22
+         %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 2 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %src_main
+         %25 = OpExtInst %void %1 DebugLocalVariable %24 %19 %15 2 20 %22 FlagIsLocal 0
+         %26 = OpExtInst %void %1 DebugLexicalBlock %15 2 47 %22
        %main = OpFunction %void None %27
          %28 = OpLabel
 %param_var_color = OpVariable %_ptr_Function_v4float Function
@@ -3670,8 +3670,8 @@
   // Same as DebugSimpleHLSLPixelShader but for
   // NonSemantic.Shader.DebugInfo.100.
   const std::string text = R"(
-; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} %uint_1 %uint_1 {{%\d+}} {{%\d+}} %uint_3 %uint_1
-; CHECK: [[lex_blk:%\d+]] = OpExtInst %void [[ext]] DebugLexicalBlock {{%\d+}} %uint_1 %uint_47 [[dbg_main]]
+; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} %uint_2 %uint_1 {{%\d+}} {{%\d+}} %uint_3 %uint_1
+; CHECK: [[lex_blk:%\d+]] = OpExtInst %void [[ext]] DebugLexicalBlock {{%\d+}} %uint_2 %uint_47 [[dbg_main]]
 ; CHECK: %main = OpFunction %void None
 ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_main]]
 ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare {{%\d+}} %param_var_color
@@ -3733,9 +3733,9 @@
          %18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 %uint_3 %uint_0
          %19 = OpExtInst %void %1 DebugTypeVector %18 %uint_4
          %20 = OpExtInst %void %1 DebugTypeFunction %uint_3 %19 %19
-         %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 %uint_1 %uint_1 %16 %21 %uint_3 %uint_1
-         %25 = OpExtInst %void %1 DebugLocalVariable %24 %19 %15 %uint_1 %uint_20 %22 %uint_4 %uint_0
-         %26 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_1 %uint_47 %22
+         %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 %uint_2 %uint_1 %16 %21 %uint_3 %uint_1
+         %25 = OpExtInst %void %1 DebugLocalVariable %24 %19 %15 %uint_2 %uint_20 %22 %uint_4 %uint_0
+         %26 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_2 %uint_47 %22
        %main = OpFunction %void None %27
          %28 = OpLabel
 %param_var_color = OpVariable %_ptr_Function_v4float Function
@@ -4433,10 +4433,6 @@
                OpMemoryModel PhysicalStorageBuffer64 GLSL450
                OpEntryPoint GLCompute %1 "main"
                OpExecutionMode %1 LocalSize 8 8 1
-               OpDecorate %_ptr_PhysicalStorageBuffer__struct_5 ArrayStride 8
-               OpMemberDecorate %_struct_3 0 Offset 0
-               OpMemberDecorate %_struct_3 1 Offset 8
-               OpDecorate %_ptr_PhysicalStorageBuffer_int ArrayStride 4
                OpMemberDecorate %_struct_5 0 Offset 0
                OpMemberDecorate %_struct_5 1 Offset 4
                OpDecorate %6 Aliased
@@ -4471,6 +4467,155 @@
   SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
 }
 
+TEST_F(InlineTest, DebugDeclareWithAccessChain) {
+  const std::string text = R"(
+; CHECK: [[EmptyStruct:%[\w]+]] = OpTypeStruct %float
+; CHECK-DAG: [[Struct:%[\w]+]] = OpTypeStruct [[EmptyStruct]]
+; CHECK-DAG: [[PtrType:%[\w]+]] = OpTypePointer Function [[Struct]]
+; CHECK-DAG: [[EmptyPtrType:%[\w]+]] = OpTypePointer Function [[EmptyStruct]]
+               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+               OpExtension "SPV_KHR_relaxed_extended_instruction"
+          %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %2 "computeMain"
+               OpExecutionMode %2 LocalSize 1 1 1
+          %3 = OpString "s.hlsl"
+          %4 = OpString "float"
+          %5 = OpString "source"
+          %6 = OpString "a"
+          %7 = OpString "SomeStruct"
+          %8 = OpString "SomeStruct.getA"
+          %9 = OpString ""
+         %10 = OpString "this"
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+    %uint_32 = OpConstant %uint 32
+      %float = OpTypeFloat 32
+       %void = OpTypeVoid
+     %uint_3 = OpConstant %uint 3
+     %uint_1 = OpConstant %uint 1
+     %uint_4 = OpConstant %uint 4
+     %uint_5 = OpConstant %uint 5
+    %uint_11 = OpConstant %uint 11
+     %uint_8 = OpConstant %uint 8
+   %uint_288 = OpConstant %uint 288
+         %25 = OpTypeFunction %void
+ %_struct_26 = OpTypeStruct %float
+ %_struct_27 = OpTypeStruct %_struct_26
+%_ptr_Function__struct_27 = OpTypePointer Function %_struct_27
+%_ptr_Function__struct_26 = OpTypePointer Function %_struct_26
+%_ptr_Function_float = OpTypePointer Function %float
+         %30 = OpTypeFunction %float %_ptr_Function__struct_26 %_ptr_Function_float
+         %31 = OpUndef %float
+         %32 = OpExtInst %void %1 DebugTypeBasic %4 %uint_32 %uint_3 %uint_0
+         %33 = OpExtInst %void %1 DebugSource %3 %5
+         %34 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %33 %uint_5
+         %35 = OpExtInst %void %1 DebugTypeMember %6 %32 %33 %uint_1 %uint_0 %uint_0 %uint_32 %uint_3
+         %36 = OpExtInstWithForwardRefsKHR %void %1 DebugTypeComposite %7 %uint_1 %33 %uint_1 %uint_0 %34 %7 %uint_32 %uint_3 %35 %37
+         %38 = OpExtInst %void %1 DebugTypeFunction %uint_3 %32 %36
+         %37 = OpExtInst %void %1 DebugFunction %8 %38 %33 %uint_1 %uint_5 %36 %9 %uint_3 %uint_4
+         %39 = OpExtInst %void %1 DebugLocalVariable %10 %36 %33 %uint_1 %uint_5 %37 %uint_288 %uint_1
+         %52 = OpExtInst %void %1 DebugLocalVariable %10 %32 %33 %uint_1 %uint_5 %37 %uint_288 %uint_1
+         %40 = OpExtInst %void %1 DebugExpression
+; CHECK: OpFunction %void None
+; CHECK: [[Var:%[\w]+]] = OpVariable [[PtrType]] Function
+; CHECK: OpExtInst %void {{%[\w+]+}} DebugDeclare {{%[\w+]+}} [[Var]] {{%[\w+]+}} %int_0
+; CHECK: OpExtInst %void {{%[\w+]+}} DebugDeclare {{%[\w+]+}} [[Var]] {{%[\w+]+}} %int_0 %int_0
+          %2 = OpFunction %void None %25
+         %41 = OpLabel
+         %42 = OpVariable %_ptr_Function__struct_27 Function
+         %43 = OpAccessChain %_ptr_Function__struct_26 %42 %int_0
+         %49 = OpAccessChain %_ptr_Function_float %43 %int_0
+         %44 = OpFunctionCall %float %45 %43 %49
+               OpReturn
+               OpFunctionEnd
+; CHECK: OpFunction %float None
+         %45 = OpFunction %float None %30
+         %46 = OpFunctionParameter %_ptr_Function__struct_26
+         %50 = OpFunctionParameter %_ptr_Function_float
+         %47 = OpLabel
+         %48 = OpExtInst %void %1 DebugDeclare %39 %46 %40
+         %51 = OpExtInst %void %1 DebugDeclare %52 %50 %40
+               OpReturnValue %31
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
+}
+
+TEST_F(InlineTest, DebugDeclareWithDynamicAccessChainKilled) {
+  // Check that a DebugDeclare whose Variable operand is remapped to an
+  // OpAccessChain with a non-constant index is killed, because DebugDeclare
+  // Indexes operands must be constant integers.
+  const std::string text = R"(
+; CHECK: OpFunction %void None
+; CHECK-NOT: DebugDeclare
+; CHECK: OpReturn
+               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+               OpExtension "SPV_KHR_relaxed_extended_instruction"
+          %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %2 "main"
+               OpExecutionMode %2 LocalSize 1 1 1
+          %3 = OpString "test.hlsl"
+          %4 = OpString "float"
+          %5 = OpString "source"
+          %6 = OpString "foo"
+          %7 = OpString ""
+          %8 = OpString "this"
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+     %uint_3 = OpConstant %uint 3
+     %uint_4 = OpConstant %uint 4
+     %uint_5 = OpConstant %uint 5
+    %uint_32 = OpConstant %uint 32
+      %float = OpTypeFloat 32
+       %void = OpTypeVoid
+         %20 = OpTypeFunction %void
+%_arr_float_uint_4 = OpTypeArray %float %uint_4
+%_ptr_Function_arr = OpTypePointer Function %_arr_float_uint_4
+%_ptr_Function_float = OpTypePointer Function %float
+%_ptr_Function_int = OpTypePointer Function %int
+         %25 = OpTypeFunction %float %_ptr_Function_float
+         %26 = OpUndef %float
+         %27 = OpExtInst %void %1 DebugTypeBasic %4 %uint_32 %uint_3 %uint_0
+         %28 = OpExtInst %void %1 DebugSource %3 %5
+         %29 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %28 %uint_5
+         %30 = OpExtInst %void %1 DebugTypeFunction %uint_3 %27 %27
+         %31 = OpExtInst %void %1 DebugFunction %6 %30 %28 %uint_1 %uint_5 %29 %7 %uint_3 %uint_4
+         %32 = OpExtInst %void %1 DebugLocalVariable %8 %27 %28 %uint_1 %uint_5 %31 %uint_4 %uint_1
+         %33 = OpExtInst %void %1 DebugExpression
+          %2 = OpFunction %void None %20
+         %34 = OpLabel
+         %35 = OpVariable %_ptr_Function_arr Function
+         %36 = OpVariable %_ptr_Function_int Function
+               OpStore %36 %int_0
+         %37 = OpLoad %int %36
+         %38 = OpAccessChain %_ptr_Function_float %35 %37
+         %39 = OpFunctionCall %float %40 %38
+               OpReturn
+               OpFunctionEnd
+; CHECK: OpFunction %float None
+         %40 = OpFunction %float None %25
+         %41 = OpFunctionParameter %_ptr_Function_float
+         %42 = OpLabel
+         %43 = OpExtInst %void %1 DebugDeclare %32 %41 %33
+               OpReturnValue %26
+               OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    Empty modules
diff --git a/test/opt/instruction_test.cpp b/test/opt/instruction_test.cpp
index 67961eb..fa230c5 100644
--- a/test/opt/instruction_test.cpp
+++ b/test/opt/instruction_test.cpp
@@ -1526,7 +1526,7 @@
   EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
 }
 
-TEST_F(DescriptorTypeTest, GetShader100DebugOpcode) {
+TEST_F(DescriptorTypeTest, GetShaderDebugOpcode) {
   const std::string text = R"(
               OpCapability Shader
          %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
@@ -1541,11 +1541,11 @@
   std::unique_ptr<IRContext> context =
       BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
   Instruction* debug_expression = context->get_def_use_mgr()->GetDef(5);
-  EXPECT_EQ(debug_expression->GetShader100DebugOpcode(),
-            NonSemanticShaderDebugInfo100DebugExpression);
+  EXPECT_EQ(debug_expression->GetShaderDebugOpcode(),
+            NonSemanticShaderDebugInfoDebugExpression);
   Instruction* debug_source = context->get_def_use_mgr()->GetDef(6);
-  EXPECT_EQ(debug_source->GetShader100DebugOpcode(),
-            NonSemanticShaderDebugInfo100DebugSource);
+  EXPECT_EQ(debug_source->GetShaderDebugOpcode(),
+            NonSemanticShaderDebugInfoDebugSource);
 
   // Test that an opcode larger than the max will return Max.  This instruction
   // cannot be in the assembly above because the assembler expects the string
@@ -1553,16 +1553,17 @@
   // file could have an arbitrary number.
   std::unique_ptr<Instruction> past_max(debug_expression->Clone(context.get()));
   const uint32_t kExtInstOpcodeInIndex = 1;
-  uint32_t large_opcode = NonSemanticShaderDebugInfo100InstructionsMax + 2;
+  uint32_t large_opcode =
+      static_cast<uint32_t>(NonSemanticShaderDebugInfoInstructionsMax) + 2u;
   past_max->SetInOperand(kExtInstOpcodeInIndex, {large_opcode});
-  EXPECT_EQ(past_max->GetShader100DebugOpcode(),
-            NonSemanticShaderDebugInfo100InstructionsMax);
+  EXPECT_EQ(past_max->GetShaderDebugOpcode(),
+            NonSemanticShaderDebugInfoInstructionsMax);
 
   // Test that an opcode without a value in the enum, but less than Max returns
   // the same value.
-  uint32_t opcode = NonSemanticShaderDebugInfo100InstructionsMax - 2;
+  uint32_t opcode = NonSemanticShaderDebugInfoInstructionsMax - 2;
   past_max->SetInOperand(kExtInstOpcodeInIndex, {opcode});
-  EXPECT_EQ(past_max->GetShader100DebugOpcode(), opcode);
+  EXPECT_EQ(past_max->GetShaderDebugOpcode(), opcode);
 }
 
 }  // namespace
diff --git a/test/opt/ir_builder.cpp b/test/opt/ir_builder.cpp
index f0cfc18..15d31d9 100644
--- a/test/opt/ir_builder.cpp
+++ b/test/opt/ir_builder.cpp
@@ -256,6 +256,38 @@
   Match(text, context.get());
 }
 
+TEST_F(IRBuilderTest, AddVariable) {
+  // Use Private beacuse its' enun is 7 which is higher
+  // than the ID limit.
+  const std::string text = R"(
+; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
+; CHECK: [[ptr:%\w+]] = OpTypePointer Private [[uint]]
+; CHECK: [[var:%\w+]] = OpVariable [[ptr]] Private
+; CHECK: OpTypeFloat
+OpCapability Kernel
+OpCapability VectorComputeINTEL
+OpCapability Linkage
+OpExtension "SPV_INTEL_vector_compute"
+OpMemoryModel Logical OpenCL
+%1 = OpTypeInt 32 0
+%2 = OpTypePointer Private %1
+%3 = OpTypeFloat 32
+)";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
+  EXPECT_NE(nullptr, context) << text;
+
+  auto* float_ty = context->get_def_use_mgr()->GetDef(3);
+  InstructionBuilder builder(context.get(), float_ty);
+  auto* var = builder.AddVariable(2u, uint32_t(spv::StorageClass::Private));
+  EXPECT_NE(nullptr, var);
+
+  context->get_def_use_mgr()->AnalyzeInstDefUse(var);  // should not assert
+
+  Match(text, context.get());
+}
+
 TEST_F(IRBuilderTest, AddCompositeConstruct) {
   const std::string text = R"(
 ; CHECK: [[uint:%\w+]] = OpTypeInt
diff --git a/test/opt/ir_loader_test.cpp b/test/opt/ir_loader_test.cpp
index 769a25d..6e0aa41 100644
--- a/test/opt/ir_loader_test.cpp
+++ b/test/opt/ir_loader_test.cpp
@@ -1523,6 +1523,74 @@
   });
 }
 
+TEST(IrBuilder, RoundTripSimpleGraphWithBody) {
+  DoRoundTripCheck(R"(OpCapability Shader
+OpCapability TensorsARM
+OpCapability GraphARM
+OpExtension "SPV_ARM_tensors"
+OpExtension "SPV_ARM_graph"
+%1 = OpExtInstImport "TOSA.001000.1"
+OpMemoryModel Logical GLSL450
+OpDecorate %2 DescriptorSet 0
+OpDecorate %2 Binding 0
+OpDecorate %3 DescriptorSet 0
+OpDecorate %3 Binding 1
+%uint = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%uint_2 = OpConstant %uint 2
+%uint_3 = OpConstant %uint 3
+%uint_4 = OpConstant %uint 4
+%uint_5 = OpConstant %uint 5
+%uint_10 = OpConstant %uint 10
+%float_0 = OpConstant %float 0
+%_arr_uint_uint_1 = OpTypeArray %uint %uint_1
+%_arr_uint_uint_4 = OpTypeArray %uint %uint_4
+%16 = OpConstantComposite %_arr_uint_uint_1 %uint_1
+%17 = OpConstantComposite %_arr_uint_uint_1 %uint_2
+%18 = OpConstantComposite %_arr_uint_uint_1 %uint_4
+%19 = OpConstantComposite %_arr_uint_uint_4 %uint_1 %uint_10 %uint_10 %uint_3
+%20 = OpConstantComposite %_arr_uint_uint_4 %uint_1 %uint_5 %uint_5 %uint_3
+%21 = OpTypeTensorARM %float %uint_1 %16
+%22 = OpTypeTensorARM %uint %uint_1 %17
+%23 = OpTypeTensorARM %uint %uint_1 %18
+%24 = OpTypeTensorARM %float %uint_4 %19
+%25 = OpTypeTensorARM %float %uint_4 %20
+%_ptr_UniformConstant_24 = OpTypePointer UniformConstant %24
+%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25
+%28 = OpConstantComposite %23 %uint_3 %uint_3
+%29 = OpConstantComposite %23 %uint_2 %uint_2
+%30 = OpConstantComposite %24 %uint_0 %uint_0 %uint_0 %uint_0
+%31 = OpConstantComposite %22 %float_0
+%32 = OpTypeGraphARM 1 %24 %25
+%2 = OpVariable %_ptr_UniformConstant_24 UniformConstant
+%3 = OpVariable %_ptr_UniformConstant_25 UniformConstant
+OpGraphEntryPointARM %33 "main" %2 %3
+%33 = OpGraphARM %32
+%34 = OpGraphInputARM %24 %uint_0
+%35 = OpExtInst %25 %1 AVG_POOL2D %28 %29 %30 %uint_2 %34 %31 %31
+OpGraphSetOutputARM %35 %uint_0
+OpGraphEndARM
+)");
+}
+
+TEST(IrBuilder, GraphInsideGraph) {
+  DoErrorMessageCheck("%2 = OpGraphARM %1\n%3 = OpGraphARM %2",
+                      "graph inside graph", 2);
+}
+
+TEST(IrBuilder, GraphEndOutsideOfGraph) {
+  DoErrorMessageCheck("OpGraphEndARM\n",
+                      "OpGraphEndARM without corresponding OpGraphARM", 1);
+}
+
+TEST(IrBuilder, GraphUnhandledInstruction) {
+  DoErrorMessageCheck(
+      "%2 = OpGraphARM %1\n%3 = OpGraphInputARM %4 %5\nOpNop\nOpGraphEndARM",
+      "unhandled instruction (opcode 0) inside graph", 3);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/legalize_multidim_array_test.cpp b/test/opt/legalize_multidim_array_test.cpp
new file mode 100644
index 0000000..c7bb6b2
--- /dev/null
+++ b/test/opt/legalize_multidim_array_test.cpp
@@ -0,0 +1,639 @@
+// Copyright (c) 2026 Google LLC
+//
+// 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 <string>
+
+#include "test/opt/assembly_builder.h"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using LegalizeMultidimArrayTest = PassTest<::testing::Test>;
+
+TEST_F(LegalizeMultidimArrayTest, Flatten2DResourceArray) {
+  // HLSL:
+  // Texture2D g_Textures[2][3];
+  // SamplerState g_Sampler;
+  // float4 main(float2 uv : TEXCOORD) : SV_Target {
+  //   return g_Textures[0][1].Sample(g_Sampler, uv);
+  // }
+  const std::string text = R"(
+; CHECK: %uint_6 = OpConstant %uint 6
+; CHECK: %_arr_type_2d_image_uint_6 = OpTypeArray %type_2d_image %uint_6
+; CHECK: %_ptr_UniformConstant__arr_type_2d_image_uint_6 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_6
+; CHECK: %g_Textures = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_6 UniformConstant
+; CHECK: [[mul:%\w+]] = OpIMul %uint %int_0 %uint_3
+; CHECK: [[idx:%\w+]] = OpIAdd %uint [[mul]] %int_1
+; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures [[idx]]
+; CHECK: OpLoad %type_2d_image [[ptr]]
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %in_var_TEXCOORD %out_var_SV_Target
+               OpExecutionMode %main OriginUpperLeft
+               OpSource HLSL 600
+               OpName %type_2d_image "type.2d.image"
+               OpName %g_Textures "g_Textures"
+               OpName %type_sampler "type.sampler"
+               OpName %g_Sampler "g_Sampler"
+               OpName %main "main"
+               OpName %src_main "src.main"
+               OpDecorate %in_var_TEXCOORD Location 0
+               OpDecorate %out_var_SV_Target Location 0
+               OpDecorate %g_Textures DescriptorSet 0
+               OpDecorate %g_Textures Binding 0
+               OpDecorate %g_Sampler DescriptorSet 0
+               OpDecorate %g_Sampler Binding 1
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+     %uint_3 = OpConstant %uint 3
+      %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3
+%_arr__arr_type_2d_image_uint_3_uint_2 = OpTypeArray %_arr_type_2d_image_uint_3 %uint_2
+%_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 = OpTypePointer UniformConstant %_arr__arr_type_2d_image_uint_3_uint_2
+%type_sampler = OpTypeSampler
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+    %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+         %24 = OpTypeFunction %void
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+         %31 = OpTypeFunction %v4float %_ptr_Function_v2float
+%_ptr_UniformConstant__arr_type_2d_image_uint_3 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_3
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%type_sampled_image = OpTypeSampledImage %type_2d_image
+ %g_Textures = OpVariable %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 UniformConstant
+  %g_Sampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
+%in_var_TEXCOORD = OpVariable %_ptr_Input_v2float Input
+%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
+       %main = OpFunction %void None %24
+         %25 = OpLabel
+%param_var_uv = OpVariable %_ptr_Function_v2float Function
+         %28 = OpLoad %v2float %in_var_TEXCOORD
+               OpStore %param_var_uv %28
+         %29 = OpFunctionCall %v4float %src_main %param_var_uv
+               OpStore %out_var_SV_Target %29
+               OpReturn
+               OpFunctionEnd
+   %src_main = OpFunction %v4float None %31
+         %uv = OpFunctionParameter %_ptr_Function_v2float
+   %bb_entry = OpLabel
+         %37 = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures %int_0 %int_1
+         %38 = OpLoad %type_2d_image %37
+         %39 = OpLoad %type_sampler %g_Sampler
+         %40 = OpLoad %v2float %uv
+         %42 = OpSampledImage %type_sampled_image %38 %39
+         %43 = OpImageSampleImplicitLod %v4float %42 %40 None
+               OpReturnValue %43
+               OpFunctionEnd
+  )";
+
+  const std::string expected = R"(
+)";
+
+  SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text,
+                                                   /*do_validation=*/true);
+}
+
+// Test that the pass fails when the access chain is split into multiple access
+// chains. We expect CombineAccessChains to be run before this pass to avoid
+// this.
+TEST_F(LegalizeMultidimArrayTest, IndirectUseViaPartialAccessChain) {
+  // HLSL source approximation:
+  // Texture2D g_Textures[2][3];
+  // ...
+  // Texture2D row[3] = g_Textures[0];
+  // return row[1].Sample(...);
+  //
+  // In SPIR-V, this often looks like:
+  // %ptr_row = OpAccessChain %_ptr_UniformConstant_arr_type_2d_image_uint_3
+  // %g_Textures %int_0 %ptr_tex = OpAccessChain
+  // %_ptr_UniformConstant_type_2d_image %ptr_row %int_1 OpLoad %type_2d_image
+  // %ptr_tex
+
+  const std::string text = R"(
+  ; CHECK: Unable to legalize multidimensional array
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %out_var_SV_Target
+               OpExecutionMode %main OriginUpperLeft
+               OpSource HLSL 600
+               OpName %type_2d_image "type.2d.image"
+               OpName %g_Textures "g_Textures"
+               OpName %main "main"
+               OpDecorate %out_var_SV_Target Location 0
+               OpDecorate %g_Textures DescriptorSet 0
+               OpDecorate %g_Textures Binding 0
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+     %uint_3 = OpConstant %uint 3
+      %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3
+%_arr__arr_type_2d_image_uint_3_uint_2 = OpTypeArray %_arr_type_2d_image_uint_3 %uint_2
+%_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 = OpTypePointer UniformConstant %_arr__arr_type_2d_image_uint_3_uint_2
+%_ptr_UniformConstant__arr_type_2d_image_uint_3 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_3
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+         %24 = OpTypeFunction %void
+ %g_Textures = OpVariable %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 UniformConstant
+%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
+       %main = OpFunction %void None %24
+         %25 = OpLabel
+         %37 = OpAccessChain %_ptr_UniformConstant__arr_type_2d_image_uint_3 %g_Textures %int_0
+         %38 = OpAccessChain %_ptr_UniformConstant_type_2d_image %37 %int_1
+         %39 = OpLoad %type_2d_image %38
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  SinglePassRunAndFail<LegalizeMultidimArrayPass>(text);
+}
+
+TEST_F(LegalizeMultidimArrayTest, Flatten3DResourceArray) {
+  // Texture2D g_Textures[2][3][4];
+  // Access: g_Textures[0][1][2]
+  const std::string text = R"(
+; CHECK: %uint_24 = OpConstant %uint 24
+; CHECK: %_arr_type_2d_image_uint_24 = OpTypeArray %type_2d_image %uint_24
+; CHECK: %_ptr_UniformConstant__arr_type_2d_image_uint_24 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_24
+; CHECK: %g_Textures = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_24 UniformConstant
+; CHECK: [[mul1:%\w+]] = OpIMul %uint %int_0 %uint_12
+; CHECK: [[mul2:%\w+]] = OpIMul %uint %int_1 %uint_4
+; CHECK: [[add1:%\w+]] = OpIAdd %uint [[mul1]] [[mul2]]
+; CHECK: [[final_idx:%\w+]] = OpIAdd %uint [[add1]] %int_2
+; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures [[final_idx]]
+; CHECK: OpLoad %type_2d_image [[ptr]]
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpName %type_2d_image "type.2d.image"
+               OpName %g_Textures "g_Textures"
+               OpDecorate %g_Textures DescriptorSet 0
+               OpDecorate %g_Textures Binding 0
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+      %int_2 = OpConstant %int 2
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_2 = OpConstant %uint 2
+     %uint_3 = OpConstant %uint 3
+     %uint_4 = OpConstant %uint 4
+     %uint_12 = OpConstant %uint 12
+      %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_arr_type_2d_image_uint_4 = OpTypeArray %type_2d_image %uint_4
+%_arr_arr_type_2d_image_uint_4_uint_3 = OpTypeArray %_arr_type_2d_image_uint_4 %uint_3
+%_arr_arr_arr_type_2d_image_uint_4_uint_3_uint_2 = OpTypeArray %_arr_arr_type_2d_image_uint_4_uint_3 %uint_2
+%_ptr_UniformConstant_arr_3d = OpTypePointer UniformConstant %_arr_arr_arr_type_2d_image_uint_4_uint_3_uint_2
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+       %void = OpTypeVoid
+       %main_func = OpTypeFunction %void
+ %g_Textures = OpVariable %_ptr_UniformConstant_arr_3d UniformConstant
+       %main = OpFunction %void None %main_func
+         %label = OpLabel
+         %ptr = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures %int_0 %int_1 %int_2
+         %val = OpLoad %type_2d_image %ptr
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true);
+}
+
+TEST_F(LegalizeMultidimArrayTest, FlattenSamplerArray) {
+  // SamplerState g_Samplers[2][2];
+  const std::string text = R"(
+; CHECK: %uint_4 = OpConstant %uint 4
+; CHECK: %_arr_type_sampler_uint_4 = OpTypeArray %type_sampler %uint_4
+; CHECK: %_ptr_UniformConstant__arr_type_sampler_uint_4 = OpTypePointer UniformConstant %_arr_type_sampler_uint_4
+; CHECK: %g_Samplers = OpVariable %_ptr_UniformConstant__arr_type_sampler_uint_4 UniformConstant
+; CHECK: [[mul:%\w+]] = OpIMul %uint %int_0 %uint_2
+; CHECK: [[idx:%\w+]] = OpIAdd %uint [[mul]] %int_1
+; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %g_Samplers [[idx]]
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpName %type_sampler "type.sampler"
+               OpName %g_Samplers "g_Samplers"
+               OpDecorate %g_Samplers DescriptorSet 0
+               OpDecorate %g_Samplers Binding 0
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+%type_sampler = OpTypeSampler
+%_arr_type_sampler_uint_2 = OpTypeArray %type_sampler %uint_2
+%_arr_arr_type_sampler_uint_2_uint_2 = OpTypeArray %_arr_type_sampler_uint_2 %uint_2
+%_ptr_UniformConstant_arr_2d = OpTypePointer UniformConstant %_arr_arr_type_sampler_uint_2_uint_2
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+       %void = OpTypeVoid
+       %main_func = OpTypeFunction %void
+ %g_Samplers = OpVariable %_ptr_UniformConstant_arr_2d UniformConstant
+       %main = OpFunction %void None %main_func
+         %label = OpLabel
+         %ptr = OpAccessChain %_ptr_UniformConstant_type_sampler %g_Samplers %int_0 %int_1
+         %val = OpLoad %type_sampler %ptr
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true);
+}
+
+TEST_F(LegalizeMultidimArrayTest, FlattenStorageBufferArray) {
+  // struct S { float f; };
+  // S buffers[2][3];
+  // buffers[0][1].f
+  const std::string text = R"(
+; CHECK: %uint_6 = OpConstant %uint 6
+; CHECK: %_arr_S_uint_6 = OpTypeArray %S %uint_6
+; CHECK: %_ptr_StorageBuffer__arr_S_uint_6 = OpTypePointer StorageBuffer %_arr_S_uint_6
+; CHECK: %g_Buffers = OpVariable %_ptr_StorageBuffer__arr_S_uint_6 StorageBuffer
+; CHECK: [[mul:%\w+]] = OpIMul %uint %int_0 %uint_3
+; CHECK: [[idx:%\w+]] = OpIAdd %uint [[mul]] %int_1
+; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_StorageBuffer_float %g_Buffers [[idx]] %int_0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpName %S "S"
+               OpName %g_Buffers "g_Buffers"
+               OpDecorate %g_Buffers DescriptorSet 0
+               OpDecorate %g_Buffers Binding 0
+               OpMemberDecorate %S 0 Offset 0
+               OpDecorate %S Block
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+     %uint_3 = OpConstant %uint 3
+      %float = OpTypeFloat 32
+          %S = OpTypeStruct %float
+%_arr_S_uint_3 = OpTypeArray %S %uint_3
+%_arr__arr_S_uint_3_uint_2 = OpTypeArray %_arr_S_uint_3 %uint_2
+%_ptr_StorageBuffer_arr_2d = OpTypePointer StorageBuffer %_arr__arr_S_uint_3_uint_2
+%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
+       %void = OpTypeVoid
+       %main_func = OpTypeFunction %void
+  %g_Buffers = OpVariable %_ptr_StorageBuffer_arr_2d StorageBuffer
+       %main = OpFunction %void None %main_func
+         %label = OpLabel
+         %ptr = OpAccessChain %_ptr_StorageBuffer_float %g_Buffers %int_0 %int_1 %int_0
+         %val = OpLoad %float %ptr
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true);
+}
+
+TEST_F(LegalizeMultidimArrayTest, FlattenUniformArray) {
+  // Uniform buffer array: MyBlock buffers[2][3];
+  // Access: buffers[0][1].member
+  const std::string text = R"(
+; CHECK: %uint_6 = OpConstant %uint 6
+; CHECK: %_arr_MyBlock_uint_6 = OpTypeArray %MyBlock %uint_6
+; CHECK: %_ptr_Uniform__arr_MyBlock_uint_6 = OpTypePointer Uniform %_arr_MyBlock_uint_6
+; CHECK: %g_Uniforms = OpVariable %_ptr_Uniform__arr_MyBlock_uint_6 Uniform
+; CHECK: [[mul:%\w+]] = OpIMul %uint %int_0 %uint_3
+; CHECK: [[idx:%\w+]] = OpIAdd %uint [[mul]] %int_1
+; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_Uniform_float %g_Uniforms [[idx]] %int_0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpName %MyBlock "MyBlock"
+               OpName %g_Uniforms "g_Uniforms"
+               OpDecorate %g_Uniforms DescriptorSet 0
+               OpDecorate %g_Uniforms Binding 0
+               OpMemberDecorate %MyBlock 0 Offset 0
+               OpDecorate %MyBlock Block
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+     %uint_3 = OpConstant %uint 3
+      %float = OpTypeFloat 32
+    %MyBlock = OpTypeStruct %float
+%_arr_MyBlock_uint_3 = OpTypeArray %MyBlock %uint_3
+%_arr__arr_MyBlock_uint_3_uint_2 = OpTypeArray %_arr_MyBlock_uint_3 %uint_2
+%_ptr_Uniform__arr__arr_MyBlock_uint_3_uint_2 = OpTypePointer Uniform %_arr__arr_MyBlock_uint_3_uint_2
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+       %void = OpTypeVoid
+       %main_func = OpTypeFunction %void
+ %g_Uniforms = OpVariable %_ptr_Uniform__arr__arr_MyBlock_uint_3_uint_2 Uniform
+       %main = OpFunction %void None %main_func
+         %label = OpLabel
+         %ptr = OpAccessChain %_ptr_Uniform_float %g_Uniforms %int_0 %int_1 %int_0
+         %val = OpLoad %float %ptr
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true);
+}
+
+TEST_F(LegalizeMultidimArrayTest, AccessChainThroughCopyObject) {
+  // Texture2D g_Textures[2][3];
+  // %copy = OpCopyObject %ptr_type %g_Textures
+  // %ptr = OpAccessChain %... %copy %int_0 %int_1
+  const std::string text = R"(
+; CHECK: %uint_6 = OpConstant %uint 6
+; CHECK: %_arr_type_2d_image_uint_6 = OpTypeArray %type_2d_image %uint_6
+; CHECK: %_ptr_UniformConstant__arr_type_2d_image_uint_6 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_6
+; CHECK: %g_Textures = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_6 UniformConstant
+; CHECK: [[copy:%\w+]] = OpCopyObject %_ptr_UniformConstant__arr_type_2d_image_uint_6 %g_Textures
+; CHECK: [[mul:%\w+]] = OpIMul %uint %int_0 %uint_3
+; CHECK: [[idx:%\w+]] = OpIAdd %uint [[mul]] %int_1
+; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image [[copy]] [[idx]]
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpName %type_2d_image "type.2d.image"
+               OpName %g_Textures "g_Textures"
+               OpDecorate %g_Textures DescriptorSet 0
+               OpDecorate %g_Textures Binding 0
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+     %uint_3 = OpConstant %uint 3
+      %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3
+%_arr__arr_type_2d_image_uint_3_uint_2 = OpTypeArray %_arr_type_2d_image_uint_3 %uint_2
+%_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 = OpTypePointer UniformConstant %_arr__arr_type_2d_image_uint_3_uint_2
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+       %void = OpTypeVoid
+       %main_func = OpTypeFunction %void
+ %g_Textures = OpVariable %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 UniformConstant
+       %main = OpFunction %void None %main_func
+         %label = OpLabel
+         %copy = OpCopyObject %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 %g_Textures
+         %ptr = OpAccessChain %_ptr_UniformConstant_type_2d_image %copy %int_0 %int_1
+         %val = OpLoad %type_2d_image %ptr
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true);
+}
+
+TEST_F(LegalizeMultidimArrayTest, DynamicIndices) {
+  // Access with non-constant indices.
+  // g_Textures[var_i][var_j]
+  const std::string text = R"(
+; CHECK: [[idx1:%\w+]] = OpLoad %int %idx_var_1
+; CHECK: [[idx2:%\w+]] = OpLoad %int %idx_var_2
+; CHECK: [[mul:%\w+]] = OpIMul %uint [[idx1]] %uint_3
+; CHECK: [[add:%\w+]] = OpIAdd %uint [[mul]] [[idx2]]
+; CHECK: OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures [[add]]
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpName %type_2d_image "type.2d.image"
+               OpName %g_Textures "g_Textures"
+               OpName %idx_var_1 "idx_var_1"
+               OpName %idx_var_2 "idx_var_2"
+               OpDecorate %g_Textures DescriptorSet 0
+               OpDecorate %g_Textures Binding 0
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+     %uint_3 = OpConstant %uint 3
+      %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3
+%_arr__arr_type_2d_image_uint_3_uint_2 = OpTypeArray %_arr_type_2d_image_uint_3 %uint_2
+%_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 = OpTypePointer UniformConstant %_arr__arr_type_2d_image_uint_3_uint_2
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%_ptr_Function_int = OpTypePointer Function %int
+       %void = OpTypeVoid
+       %main_func = OpTypeFunction %void
+ %g_Textures = OpVariable %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 UniformConstant
+       %main = OpFunction %void None %main_func
+         %label = OpLabel
+    %idx_var_1 = OpVariable %_ptr_Function_int Function
+    %idx_var_2 = OpVariable %_ptr_Function_int Function
+         %i = OpLoad %int %idx_var_1
+         %j = OpLoad %int %idx_var_2
+         %ptr = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures %i %j
+         %val = OpLoad %type_2d_image %ptr
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true);
+}
+
+TEST_F(LegalizeMultidimArrayTest, IgnoreFunctionScopeArray) {
+  // Function scope array [2][3] should NOT be legalized.
+  const std::string text = R"(
+; CHECK: %_arr__arr_float_uint_3_uint_2 = OpTypeArray %_arr_float_uint_3 %uint_2
+; CHECK: %_ptr_Function__arr__arr_float_uint_3_uint_2 = OpTypePointer Function %_arr__arr_float_uint_3_uint_2
+; CHECK: %var = OpVariable %_ptr_Function__arr__arr_float_uint_3_uint_2 Function
+; CHECK: OpAccessChain %_ptr_Function_float %var %int_0 %int_1
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpName %var "var"
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+     %uint_3 = OpConstant %uint 3
+      %float = OpTypeFloat 32
+%_arr_float_uint_3 = OpTypeArray %float %uint_3
+%_arr__arr_float_uint_3_uint_2 = OpTypeArray %_arr_float_uint_3 %uint_2
+%_ptr_Function__arr__arr_float_uint_3_uint_2 = OpTypePointer Function %_arr__arr_float_uint_3_uint_2
+%_ptr_Function_float = OpTypePointer Function %float
+       %void = OpTypeVoid
+       %main_func = OpTypeFunction %void
+       %main = OpFunction %void None %main_func
+         %label = OpLabel
+         %var = OpVariable %_ptr_Function__arr__arr_float_uint_3_uint_2 Function
+         %ptr = OpAccessChain %_ptr_Function_float %var %int_0 %int_1
+         %val = OpLoad %float %ptr
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true);
+}
+
+TEST_F(LegalizeMultidimArrayTest, IgnoreWorkgroupScopeArray) {
+  // Workgroup scope array [2][3] should NOT be legalized.
+  const std::string text = R"(
+; CHECK: %_arr__arr_float_uint_3_uint_2 = OpTypeArray %_arr_float_uint_3 %uint_2
+; CHECK: %_ptr_Workgroup__arr__arr_float_uint_3_uint_2 = OpTypePointer Workgroup %_arr__arr_float_uint_3_uint_2
+; CHECK: %var = OpVariable %_ptr_Workgroup__arr__arr_float_uint_3_uint_2 Workgroup
+; CHECK: OpAccessChain %_ptr_Workgroup_float %var %int_0 %int_1
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpName %var "var"
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+     %uint_3 = OpConstant %uint 3
+      %float = OpTypeFloat 32
+%_arr_float_uint_3 = OpTypeArray %float %uint_3
+%_arr__arr_float_uint_3_uint_2 = OpTypeArray %_arr_float_uint_3 %uint_2
+%_ptr_Workgroup__arr__arr_float_uint_3_uint_2 = OpTypePointer Workgroup %_arr__arr_float_uint_3_uint_2
+%_ptr_Workgroup_float = OpTypePointer Workgroup %float
+       %void = OpTypeVoid
+       %main_func = OpTypeFunction %void
+ %var = OpVariable %_ptr_Workgroup__arr__arr_float_uint_3_uint_2 Workgroup
+       %main = OpFunction %void None %main_func
+         %label = OpLabel
+         %ptr = OpAccessChain %_ptr_Workgroup_float %var %int_0 %int_1
+         %val = OpLoad %float %ptr
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true);
+}
+
+TEST_F(LegalizeMultidimArrayTest, MultipleAccessChains) {
+  // Access g_Textures[0][1] and g_Textures[1][2] in the same function.
+  const std::string text = R"(
+; CHECK: [[mul1:%\w+]] = OpIMul %uint %int_0 %uint_3
+; CHECK: [[idx1:%\w+]] = OpIAdd %uint [[mul1]] %int_1
+; CHECK: [[ptr1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures [[idx1]]
+; CHECK: OpLoad %type_2d_image [[ptr1]]
+; CHECK: [[mul2:%\w+]] = OpIMul %uint %int_1 %uint_3
+; CHECK: [[idx2:%\w+]] = OpIAdd %uint [[mul2]] %int_2
+; CHECK: [[ptr2:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures [[idx2]]
+; CHECK: OpLoad %type_2d_image [[ptr2]]
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpName %type_2d_image "type.2d.image"
+               OpName %g_Textures "g_Textures"
+               OpDecorate %g_Textures DescriptorSet 0
+               OpDecorate %g_Textures Binding 0
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+      %int_2 = OpConstant %int 2
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+     %uint_3 = OpConstant %uint 3
+      %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3
+%_arr__arr_type_2d_image_uint_3_uint_2 = OpTypeArray %_arr_type_2d_image_uint_3 %uint_2
+%_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 = OpTypePointer UniformConstant %_arr__arr_type_2d_image_uint_3_uint_2
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+       %void = OpTypeVoid
+       %main_func = OpTypeFunction %void
+ %g_Textures = OpVariable %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 UniformConstant
+       %main = OpFunction %void None %main_func
+         %label = OpLabel
+         %ptr1 = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures %int_0 %int_1
+         %val1 = OpLoad %type_2d_image %ptr1
+         %ptr2 = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures %int_1 %int_2
+         %val2 = OpLoad %type_2d_image %ptr2
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true);
+}
+
+TEST_F(LegalizeMultidimArrayTest, MultipleResources) {
+  // Two different resource arrays:
+  // Texture2D g_Textures[2][3];
+  // SamplerState g_Samplers[2][2];
+  const std::string text = R"(
+; CHECK: %g_Textures = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_6 UniformConstant
+; CHECK: %g_Samplers = OpVariable %_ptr_UniformConstant__arr_type_sampler_uint_4 UniformConstant
+; CHECK: [[mul1:%\w+]] = OpIMul %uint %int_0 %uint_3
+; CHECK: [[idx1:%\w+]] = OpIAdd %uint [[mul1]] %int_1
+; CHECK: [[ptr1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures [[idx1]]
+; CHECK: OpLoad %type_2d_image [[ptr1]]
+; CHECK: [[mul2:%\w+]] = OpIMul %uint %int_1 %uint_2
+; CHECK: [[idx2:%\w+]] = OpIAdd %uint [[mul2]] %int_1
+; CHECK: [[ptr2:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %g_Samplers [[idx2]]
+; CHECK: OpLoad %type_sampler [[ptr2]]
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpName %type_2d_image "type.2d.image"
+               OpName %g_Textures "g_Textures"
+               OpName %type_sampler "type.sampler"
+               OpName %g_Samplers "g_Samplers"
+               OpDecorate %g_Textures DescriptorSet 0
+               OpDecorate %g_Textures Binding 0
+               OpDecorate %g_Samplers DescriptorSet 0
+               OpDecorate %g_Samplers Binding 1
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+     %uint_3 = OpConstant %uint 3
+      %float = OpTypeFloat 32
+%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
+%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3
+%_arr__arr_type_2d_image_uint_3_uint_2 = OpTypeArray %_arr_type_2d_image_uint_3 %uint_2
+%_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 = OpTypePointer UniformConstant %_arr__arr_type_2d_image_uint_3_uint_2
+%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
+%type_sampler = OpTypeSampler
+%_arr_type_sampler_uint_2 = OpTypeArray %type_sampler %uint_2
+%_arr_arr_type_sampler_uint_2_uint_2 = OpTypeArray %_arr_type_sampler_uint_2 %uint_2
+%_ptr_UniformConstant_arr_2d_sampler = OpTypePointer UniformConstant %_arr_arr_type_sampler_uint_2_uint_2
+%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
+       %void = OpTypeVoid
+       %main_func = OpTypeFunction %void
+ %g_Textures = OpVariable %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 UniformConstant
+ %g_Samplers = OpVariable %_ptr_UniformConstant_arr_2d_sampler UniformConstant
+       %main = OpFunction %void None %main_func
+         %label = OpLabel
+         %ptr1 = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures %int_0 %int_1
+         %val1 = OpLoad %type_2d_image %ptr1
+         %ptr2 = OpAccessChain %_ptr_UniformConstant_type_sampler %g_Samplers %int_1 %int_1
+         %val2 = OpLoad %type_sampler %ptr2
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true);
+}
+
+}  // namespace
+}  // namespace opt
+}  // namespace spvtools
\ No newline at end of file
diff --git a/test/opt/local_access_chain_convert_test.cpp b/test/opt/local_access_chain_convert_test.cpp
index b35f3a3..8b12314 100644
--- a/test/opt/local_access_chain_convert_test.cpp
+++ b/test/opt/local_access_chain_convert_test.cpp
@@ -1344,6 +1344,7 @@
 OpFunctionEnd
 )";
 
+  ValidatorOptions()->relax_logical_pointer = true;
   SinglePassRunAndCheck<LocalAccessChainConvertPass>(assembly, assembly, false,
                                                      true);
 }
diff --git a/test/opt/local_single_store_elim_test.cpp b/test/opt/local_single_store_elim_test.cpp
index ffe352e..8fd5c9d 100644
--- a/test/opt/local_single_store_elim_test.cpp
+++ b/test/opt/local_single_store_elim_test.cpp
@@ -24,6 +24,56 @@
 
 using LocalSingleStoreElimTest = PassTest<::testing::Test>;
 
+TEST_F(LocalSingleStoreElimTest, DoSomethingWithExtensions) {
+  const std::string capabilities_and_extensions =
+      R"(OpCapability Shader
+OpExtension "SPV_EXT_fragment_shader_interlock"
+OpExtension "SPV_NV_compute_shader_derivatives"
+OpExtension "SPV_KHR_ray_query"
+OpExtension "SPV_NV_shader_subgroup_partitioned"
+OpExtension "SPV_KHR_ray_tracing"
+OpExtension "SPV_EXT_descriptor_indexing"
+)";
+
+  const std::string before = capabilities_and_extensions +
+                             R"(%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 "main"
+OpExecutionMode %2 OriginUpperLeft
+OpSource GLSL 140
+%void = OpTypeVoid
+%4 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%2 = OpFunction %void None %4
+%8 = OpLabel
+%9 = OpVariable %_ptr_Function_float Function
+OpStore %9 %float_0
+%10 = OpLoad %float %9
+OpReturn
+OpFunctionEnd
+)";
+  const std::string after = capabilities_and_extensions +
+                            R"(%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 "main"
+OpExecutionMode %2 OriginUpperLeft
+OpSource GLSL 140
+%void = OpTypeVoid
+%4 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%float_0 = OpConstant %float 0
+%2 = OpFunction %void None %4
+%8 = OpLabel
+%9 = OpVariable %_ptr_Function_float Function
+OpStore %9 %float_0
+OpReturn
+OpFunctionEnd
+)";
+  SinglePassRunAndCheck<LocalSingleStoreElimPass>(before, after, true, true);
+}
 TEST_F(LocalSingleStoreElimTest, PositiveAndNegative) {
   // Single store to v is optimized. Multiple store to
   // f is not optimized.
diff --git a/test/opt/local_ssa_elim_test.cpp b/test/opt/local_ssa_elim_test.cpp
index 45006ca..4ea0926 100644
--- a/test/opt/local_ssa_elim_test.cpp
+++ b/test/opt/local_ssa_elim_test.cpp
@@ -2978,7 +2978,9 @@
 
 ; CHECK:      OpExtInst %void [[ext]] DebugScope [[dbg_main]]
 ; CHECK:      OpStore %f %float_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugScope [[dbg_bb]]
 ; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_x]] %float_0
+; CHECK-NEXT: OpExtInst %void [[ext]] DebugScope [[dbg_main]]
 ; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] %float_0
 ; CHECK-NEXT: OpStore %i %int_0
 ; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] %int_0
@@ -4223,6 +4225,7 @@
                OpFunctionEnd
 )";
 
+  ValidatorOptions()->relax_logical_pointer = true;
   SinglePassRunAndMatch<SSARewritePass>(text, true);
 }
 
@@ -5434,6 +5437,7 @@
        %1614 = OpLabel
 ;CHECK:      %1614 = OpLabel
 ;CHECK-NEXT: [[phi:%\w+]] = OpPhi 
+;CHECK-NEXT: {{%\w+}} = OpExtInst %void {{%\w+}} DebugScope %179
 ;CHECK-NEXT: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue %233
        %2335 = OpExtInst %void %2 DebugScope %179
        %1795 = OpExtInst %void %2 DebugLine %64 %uint_149 %uint_149 %uint_16 %uint_16
@@ -5456,6 +5460,50 @@
   SinglePassRunAndMatch<SSARewritePass>(text, true);
 }
 
+TEST_F(LocalSSAElimTest, StoreWithLoadedPtr) {
+  const std::string text =
+      R"(OpCapability Shader
+OpCapability VariablePointersStorageBuffer
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %2 "pointer_branch_buffer" %3
+OpExecutionMode %2 LocalSize 8 8 1
+OpDecorate %3 DescriptorSet 0
+OpDecorate %3 Binding 0
+%bool = OpTypeBool
+%uint = OpTypeInt 32 0
+%uint_2 = OpConstant %uint 2
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%_ptr_Function__ptr_StorageBuffer_uint = OpTypePointer Function %_ptr_StorageBuffer_uint
+%3 = OpVariable %_ptr_StorageBuffer_uint StorageBuffer
+%11 = OpUndef %bool
+%2 = OpFunction %void None %8
+%12 = OpLabel
+%13 = OpVariable %_ptr_Function__ptr_StorageBuffer_uint Function
+OpSelectionMerge %14 None
+OpBranchConditional %11 %15 %16
+%15 = OpLabel
+OpBranch %14
+%16 = OpLabel
+OpStore %13 %3
+OpBranch %14
+%14 = OpLabel
+; CHECK: [[phi:%\w+]] = OpPhi %_ptr_StorageBuffer_uint
+; CHECK-NEXT: [[ld:%\w+]] = OpLoad %uint %20
+; CHECK-NEXT: OpIAdd %uint [[ld]] %uint_2
+%17 = OpLoad %_ptr_StorageBuffer_uint %13
+%18 = OpLoad %uint %17
+%19 = OpIAdd %uint %18 %uint_2
+OpReturn
+OpFunctionEnd
+)";
+
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_6);
+  SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
 // TODO(greg-lunarg): Add tests to verify handling of these cases:
 //
 //    No optimization in the presence of
diff --git a/test/opt/loop_optimizations/fusion_legal.cpp b/test/opt/loop_optimizations/fusion_legal.cpp
index ef7daee..cda72a0 100644
--- a/test/opt/loop_optimizations/fusion_legal.cpp
+++ b/test/opt/loop_optimizations/fusion_legal.cpp
@@ -3415,7 +3415,8 @@
       EXPECT_FALSE(fusion.AreCompatible());
     }
 
-    ld.CreatePreHeaderBlocksIfMissing();
+    auto status = ld.CreatePreHeaderBlocksIfMissing();
+    EXPECT_NE(status, LoopDescriptor::Status::Failure);
 
     {
       LoopFusion fusion(context.get(), loops[0], loops[1]);
@@ -3588,7 +3589,8 @@
       EXPECT_FALSE(fusion.AreCompatible());
     }
 
-    ld.CreatePreHeaderBlocksIfMissing();
+    auto status = ld.CreatePreHeaderBlocksIfMissing();
+    EXPECT_NE(status, LoopDescriptor::Status::Failure);
 
     {
       LoopFusion fusion(context.get(), loops[0], loops[1]);
diff --git a/test/opt/loop_optimizations/unroll_simple.cpp b/test/opt/loop_optimizations/unroll_simple.cpp
index b4fd3ea..256b487 100644
--- a/test/opt/loop_optimizations/unroll_simple.cpp
+++ b/test/opt/loop_optimizations/unroll_simple.cpp
@@ -3788,6 +3788,48 @@
   SinglePassRunAndMatch<PartialUnrollerTestPass<2>>(text, true);
 }
 
+TEST_F(PassClassTest, UnrollWithDecorationOnPhi) {
+  // With LocalMultiStoreElimPass
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %2 "main"
+               OpExecutionMode %2 LocalSize 16 16 1
+; CHECK-NOT: OpDecorate {{%\w+}} RelaxedPrecision
+               OpDecorate %4 RelaxedPrecision
+      %float = OpTypeFloat 32
+%float_0_000122070312 = OpConstant %float 0.000122070312
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+       %bool = OpTypeBool
+       %void = OpTypeVoid
+         %12 = OpTypeFunction %void
+          %2 = OpFunction %void None %12
+         %13 = OpLabel
+               OpBranch %14
+         %14 = OpLabel
+          %4 = OpPhi %float %float_0_000122070312 %13 %3 %15
+         %16 = OpPhi %int %int_0 %13 %17 %15
+         %18 = OpSLessThan %bool %16 %int_1
+               OpLoopMerge %19 %15 Unroll
+               OpBranchConditional %18 %15 %19
+         %15 = OpLabel
+; CHECK: [[v:%\w+]] = OpExtInst %float
+          %3 = OpExtInst %float %1 NMax %float_0_000122070312 %float_0_000122070312
+         %17 = OpIAdd %int %16 %int_1
+               OpBranch %14
+         %19 = OpLabel
+; CHECK: OpCopyObject %float [[v]]
+         %20 = OpCopyObject %float %4
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  SinglePassRunAndMatch<LoopUnroller>(text, true);
+}
+
 TEST_F(PassClassTest, DontUnrollInfiteLoop) {
   // This is an infinite loop that because the step is 0.  We want to make sure
   // the unroller does not try to unroll it.
@@ -3822,6 +3864,81 @@
   SinglePassRunAndCheck<LoopUnroller>(text, text, false);
 }
 
+TEST_F(PassClassTest, ApplyDecorationsToClonedInstructions) {
+  const std::string text = R"(
+  ; CHECK: OpDecorate [[ld1:%\w+]] RelaxedPrecision
+  ; CHECK: OpDecorate [[mul1:%\w+]] RelaxedPrecision
+  ; CHECK: OpDecorate [[add1:%\w+]] RelaxedPrecision
+  ; CHECK: OpDecorate [[ld2:%\w+]] RelaxedPrecision
+  ; CHECK: OpDecorate [[mul2:%\w+]] RelaxedPrecision
+  ; CHECK: OpDecorate [[add2:%\w+]] RelaxedPrecision
+  ; CHECK: OpDecorate [[ld3:%\w+]] RelaxedPrecision
+  ; CHECK: OpDecorate [[mul3:%\w+]] RelaxedPrecision
+  ; CHECK: OpDecorate [[add3:%\w+]] RelaxedPrecision
+  
+; CHECK: [[ld1]] = OpLoad %float
+; CHECK: [[mul1]] = OpFMul %float
+; CHECK: [[add1]] = OpFAdd %float
+; CHECK: [[ld2]] = OpLoad %float
+; CHECK: [[mul2]] = OpFMul %float
+; CHECK: [[add2]] = OpFAdd %float
+; CHECK: [[ld3]] = OpLoad %float
+; CHECK: [[mul3]] = OpFMul %float
+; CHECK: [[add3]] = OpFAdd %float
+
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main" %2
+               OpExecutionMode %1 LocalSize 1 1 1
+               OpDecorate %2 DescriptorSet 0
+               OpDecorate %2 Binding 0
+               OpDecorate %_runtimearr_float ArrayStride 4
+               OpMemberDecorate %_struct_4 0 Offset 0
+               OpDecorate %_struct_4 Block
+               OpDecorate %5 RelaxedPrecision
+               OpDecorate %6 RelaxedPrecision
+               OpDecorate %7 RelaxedPrecision
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_3 = OpConstant %int 3
+      %int_1 = OpConstant %int 1
+      %float = OpTypeFloat 32
+%_runtimearr_float = OpTypeRuntimeArray %float
+  %_struct_4 = OpTypeStruct %_runtimearr_float
+%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4
+       %uint = OpTypeInt 32 0
+       %void = OpTypeVoid
+         %16 = OpTypeFunction %void
+       %bool = OpTypeBool
+%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
+          %2 = OpVariable %_ptr_StorageBuffer__struct_4 StorageBuffer
+          %1 = OpFunction %void None %16
+         %19 = OpLabel
+               OpBranch %20
+         %20 = OpLabel
+         %21 = OpPhi %int %int_0 %19 %22 %23
+         %24 = OpSLessThan %bool %21 %int_3
+               OpLoopMerge %25 %23 Unroll
+               OpBranchConditional %24 %26 %25
+         %26 = OpLabel
+         %27 = OpBitcast %uint %21
+         %28 = OpAccessChain %_ptr_StorageBuffer_float %2 %int_0 %27
+          %5 = OpLoad %float %28
+          %6 = OpFMul %float %5 %5
+          %7 = OpFAdd %float %5 %6
+               OpStore %28 %7
+               OpBranch %23
+         %23 = OpLabel
+         %22 = OpIAdd %int %21 %int_1
+               OpBranch %20
+         %25 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  SetTargetEnv(SPV_ENV_UNIVERSAL_1_6);
+  SinglePassRunAndMatch<LoopUnroller>(text, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/optimizer_test.cpp b/test/opt/optimizer_test.cpp
index 0171c09..76457dd 100644
--- a/test/opt/optimizer_test.cpp
+++ b/test/opt/optimizer_test.cpp
@@ -12,12 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "spirv-tools/optimizer.hpp"
+
+#include <sstream>
 #include <string>
 #include <vector>
 
 #include "gmock/gmock.h"
 #include "spirv-tools/libspirv.hpp"
-#include "spirv-tools/optimizer.hpp"
 #include "test/opt/pass_fixture.h"
 
 namespace spvtools {
@@ -388,6 +390,213 @@
       << "Was expecting the result id of DebugScope to have been changed.";
 }
 
+TEST(Optimizer, CheckDefaultPerformancePassesLargeStructScalarization) {
+  std::string start = R"(OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %4 "main" %46 %48
+OpSource GLSL 430
+OpName %4 "main"
+OpDecorate %44 Block
+OpMemberDecorate %44 0 BuiltIn Position
+OpMemberDecorate %44 1 BuiltIn PointSize
+OpMemberDecorate %44 2 BuiltIn ClipDistance
+OpDecorate %48 Location 0
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%7 = OpTypeVector %6 4
+%8 = OpTypePointer Function %7
+%9 = OpTypeStruct %7)";
+
+  // add 200 float members to the struct
+  for (int i = 0; i < 200; i++) {
+    start += " %6";
+  }
+
+  start += R"(
+%10 = OpTypeFunction %9 %8
+%14 = OpTypeFunction %6 %9
+%18 = OpTypePointer Function %9
+%20 = OpTypeInt 32 1
+%21 = OpConstant %20 0
+%24 = OpConstant %20 1
+%25 = OpTypeInt 32 0
+%26 = OpConstant %25 1
+%27 = OpTypePointer Function %6
+%43 = OpTypeArray %6 %26
+%44 = OpTypeStruct %7 %6 %43
+%45 = OpTypePointer Output %44
+%46 = OpVariable %45 Output
+%47 = OpTypePointer Input %7
+%48 = OpVariable %47 Input
+%54 = OpTypePointer Output %7
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+%49 = OpVariable %8 Function
+%50 = OpLoad %7 %48
+OpStore %49 %50
+%51 = OpFunctionCall %9 %12 %49
+%52 = OpFunctionCall %6 %16 %51
+%53 = OpCompositeConstruct %7 %52 %52 %52 %52
+%55 = OpAccessChain %54 %46 %21
+OpStore %55 %53
+OpReturn
+OpFunctionEnd
+%12 = OpFunction %9 None %10
+%11 = OpFunctionParameter %8
+%13 = OpLabel
+%19 = OpVariable %18 Function
+%22 = OpLoad %7 %11
+%23 = OpAccessChain %8 %19 %21
+OpStore %23 %22
+%28 = OpAccessChain %27 %11 %26
+%29 = OpLoad %6 %28
+%30 = OpConvertFToS %20 %29
+%31 = OpAccessChain %27 %19 %21 %30
+%32 = OpLoad %6 %31
+%33 = OpAccessChain %27 %19 %24
+OpStore %33 %32
+%34 = OpLoad %9 %19
+OpReturnValue %34
+OpFunctionEnd
+%16 = OpFunction %6 None %14
+%15 = OpFunctionParameter %9
+%17 = OpLabel
+%37 = OpCompositeExtract %6 %15 1
+%38 = OpConvertFToS %20 %37
+%39 = OpCompositeExtract %7 %15 0
+%40 = OpVectorExtractDynamic %6 %39 %38
+OpReturnValue %40
+OpFunctionEnd)";
+
+  std::vector<uint32_t> binary;
+  SpirvTools tools(SPV_ENV_VULKAN_1_3);
+  tools.Assemble(start, &binary,
+                 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  std::string test_disassembly;
+  std::string default_disassembly;
+
+  {
+    Optimizer opt(SPV_ENV_VULKAN_1_3);
+    opt.RegisterPerformancePasses();
+
+    std::vector<uint32_t> optimized;
+    ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized))
+        << start << "\n";
+
+    tools.Disassemble(optimized.data(), optimized.size(), &default_disassembly,
+                      SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  }
+
+  {
+    // default passes should not benefit from additional scalar replacement
+    Optimizer opt(SPV_ENV_VULKAN_1_3);
+    opt.RegisterPerformancePasses()
+        .RegisterPass(CreateScalarReplacementPass(201))
+        .RegisterPass(CreateAggressiveDCEPass());
+
+    std::vector<uint32_t> optimized;
+    ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized))
+        << start << "\n";
+
+    tools.Disassemble(optimized.data(), optimized.size(), &test_disassembly,
+                      SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  }
+
+  EXPECT_EQ(test_disassembly, default_disassembly);
+}
+
+TEST(Optimizer, KeepDebugBuildIdentifierAfterDCE) {
+  // Test that DebugBuildIdentifier is not removed after DCE.
+  const std::string before = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 8 8 1
+%4 = OpString "8937d8f571cf7b58d86d9d66196024f5d04e3186"
+%7 = OpString ""
+%9 = OpString ""
+OpSource Slang 1
+%19 = OpString ""
+%24 = OpString ""
+%25 = OpString ""
+OpName %main "main"
+%void = OpTypeVoid
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%uint_11 = OpConstant %uint 11
+%uint_5 = OpConstant %uint 5
+%uint_100 = OpConstant %uint 100
+%15 = OpTypeFunction %void
+%uint_6 = OpConstant %uint 6
+%uint_7 = OpConstant %uint 7
+%uint_1 = OpConstant %uint 1
+%uint_2 = OpConstant %uint 2
+%3 = OpExtInst %void %1 DebugBuildIdentifier %4 %uint_0
+%8 = OpExtInst %void %1 DebugSource %9 %7
+%13 = OpExtInst %void %1 DebugCompilationUnit %uint_100 %uint_5 %8 %uint_11
+%17 = OpExtInst %void %1 DebugTypeFunction %uint_0 %void
+%18 = OpExtInst %void %1 DebugFunction %19 %17 %8 %uint_5 %uint_6 %13 %19 %uint_0 %uint_5
+%23 = OpExtInst %void %1 DebugEntryPoint %18 %13 %24 %25
+%main = OpFunction %void None %15
+%16 = OpLabel
+%21 = OpExtInst %void %1 DebugFunctionDefinition %18 %main
+%32 = OpExtInst %void %1 DebugScope %18
+%26 = OpExtInst %void %1 DebugLine %8 %uint_7 %uint_7 %uint_1 %uint_2
+OpReturn
+%33 = OpExtInst %void %1 DebugNoScope
+OpFunctionEnd
+  )";
+
+  std::vector<uint32_t> binary;
+  SpirvTools tools(SPV_ENV_VULKAN_1_3);
+  tools.Assemble(before, &binary,
+                 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  Optimizer opt(SPV_ENV_VULKAN_1_3);
+  opt.RegisterPerformancePasses().RegisterPass(CreateAggressiveDCEPass());
+
+  std::vector<uint32_t> optimized;
+  ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized))
+      << before << "\n";
+
+  std::string after;
+  tools.Disassemble(optimized.data(), optimized.size(), &after,
+                    SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+
+  // Test that the DebugBuildIdentifier is not removed after DCE.
+  size_t dbi_pos = after.find("DebugBuildIdentifier");
+  EXPECT_NE(dbi_pos, std::string::npos)
+      << "Was expecting the DebugBuildIdentifier to have been kept.";
+  std::string string_id;
+  std::string flags_id;
+  if (dbi_pos != std::string::npos) {
+    std::stringstream ss(after.substr(dbi_pos));
+    std::string temp;
+    char percent;
+    ss >> temp;  // Consume "DebugBuildIdentifier"
+    ss >> percent >> string_id;
+    ss >> percent >> flags_id;
+  }
+
+  EXPECT_FALSE(string_id.empty())
+      << "Could not find string id for DebugBuildIdentifier.";
+  EXPECT_FALSE(flags_id.empty())
+      << "Could not find flags id for DebugBuildIdentifier.";
+
+  bool found =
+      (after.find("%" + string_id + " = OpString") != std::string::npos);
+  EXPECT_TRUE(found)
+      << "Was expecting the DebugBuildIdentifier string to have been kept.";
+  found = (after.find("%" + flags_id + " = OpConstant") != std::string::npos);
+  EXPECT_TRUE(found)
+      << "Was expecting the DebugBuildIdentifier constant to have been kept.";
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/pass_fixture.h b/test/opt/pass_fixture.h
index e520821..a578cc4 100644
--- a/test/opt/pass_fixture.h
+++ b/test/opt/pass_fixture.h
@@ -35,6 +35,26 @@
 namespace spvtools {
 namespace opt {
 
+inline std::ostream& operator<<(std::ostream& os,
+                                const effcee::Result::Status ers) {
+  switch (ers) {
+    case effcee::Result::Status::Ok:
+      return os << "effcee::Result::Status::Ok";
+    case effcee::Result::Status::Fail:
+      return os << "effcee::Result::Status::Fail";
+    case effcee::Result::Status::BadOption:
+      return os << "effcee::Result::Status::BadOption";
+    case effcee::Result::Status::NoRules:
+      return os << "effcee::Result::Status::NoRules";
+    case effcee::Result::Status::BadRule:
+      return os << "effcee::Result::Status::BadRule";
+    default:
+      break;
+  }
+  return os << "(invalid effcee::Result::Status " << static_cast<unsigned>(ers)
+            << ")";
+}
+
 // Template class for testing passes. It contains some handy utility methods for
 // running passes and checking results.
 //
@@ -102,23 +122,24 @@
     auto status = Pass::Status::SuccessWithoutChange;
     std::tie(optimized_bin, status) = SinglePassRunToBinary<PassT>(
         assembly, skip_nop, std::forward<Args>(args)...);
-    if (do_validation) {
-      spv_context spvContext = spvContextCreate(env_);
-      spv_diagnostic diagnostic = nullptr;
-      spv_const_binary_t binary = {optimized_bin.data(), optimized_bin.size()};
-      spv_result_t error = spvValidateWithOptions(
-          spvContext, ValidatorOptions(), &binary, &diagnostic);
-      EXPECT_EQ(error, 0);
-      if (error != 0) spvDiagnosticPrint(diagnostic);
-      spvDiagnosticDestroy(diagnostic);
-      spvContextDestroy(spvContext);
-    }
     std::string optimized_asm;
     SpirvTools tools(env_);
     EXPECT_TRUE(
         tools.Disassemble(optimized_bin, &optimized_asm, disassemble_options_))
         << "Disassembling failed for shader:\n"
         << assembly << std::endl;
+    if (do_validation) {
+      spv_context spvContext = spvContextCreate(env_);
+      spv_diagnostic diagnostic = nullptr;
+      spv_const_binary_t binary = {optimized_bin.data(), optimized_bin.size()};
+      spv_result_t error = spvValidateWithOptions(
+          spvContext, ValidatorOptions(), &binary, &diagnostic);
+      EXPECT_EQ(error, 0) << "validation failed for optimized asm:\n"
+                          << optimized_asm;
+      if (error != 0) spvDiagnosticPrint(diagnostic);
+      spvDiagnosticDestroy(diagnostic);
+      spvContextDestroy(spvContext);
+    }
     return std::make_tuple(optimized_asm, status);
   }
 
@@ -199,14 +220,14 @@
   // messages.
   template <typename PassT, typename... Args>
   void SinglePassRunAndFail(const std::string& original, Args&&... args) {
-    context_ = BuildModule(env_, consumer_, original, assemble_options_);
-    EXPECT_NE(nullptr, context()) << "Assembling failed for shader:\n"
-                                  << original << std::endl;
     std::ostringstream errs;
     auto error_consumer = [&errs](spv_message_level_t, const char*,
                                   const spv_position_t&, const char* message) {
       errs << message << std::endl;
     };
+    context_ = BuildModule(env_, error_consumer, original, assemble_options_);
+    EXPECT_NE(nullptr, context()) << "Assembling failed for shader:\n"
+                                  << original << std::endl;
     auto pass = MakeUnique<PassT>(std::forward<Args>(args)...);
     pass->SetMessageConsumer(error_consumer);
     const auto status = pass->Run(context());
@@ -257,6 +278,23 @@
     }
   }
 
+  // Returns the disassembly of the current module. This is useful for
+  // debugging.
+  std::unique_ptr<opt::IRContext> AssembleModule(const std::string& text) {
+    return spvtools::BuildModule(env_, consumer_, text, assemble_options_);
+  }
+
+  // Returns the disassembly of the current module. This is useful for
+  // debugging.
+  std::string Disassemble(opt::Module* m) {
+    std::vector<uint32_t> binary;
+    m->ToBinary(&binary, /* skip_nop = */ false);
+    std::string disassembly;
+    SpirvTools tools(env_);
+    tools.Disassemble(binary, &disassembly, disassemble_options_);
+    return disassembly;
+  }
+
   void SetAssembleOptions(uint32_t assemble_options) {
     assemble_options_ = assemble_options;
   }
diff --git a/test/opt/pass_merge_return_test.cpp b/test/opt/pass_merge_return_test.cpp
index 494f2e9..c005d74 100644
--- a/test/opt/pass_merge_return_test.cpp
+++ b/test/opt/pass_merge_return_test.cpp
@@ -2598,6 +2598,70 @@
   EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
 }
 
+TEST_F(MergeReturnPassTest, DebugFunctionDefinitionStillInEntryBlock) {
+  // Make sure that the DebugFunctionDefinition instruction is still in the
+  // entry block
+  const std::string text =
+      R"(
+; CHECK: OpFunction
+; CHECK: OpLabel
+; CHECK: DebugFunctionDefinition
+; CHECK: OpSelectionMerge
+; CHECK: OpCompositeExtract
+; CHECK: OpUGreaterThan
+        OpCapability Shader
+        OpExtension "SPV_KHR_non_semantic_info"
+        %2 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+        OpMemoryModel Logical GLSL450
+        OpEntryPoint GLCompute %main "main" %entryPointParam_main %gl_GlobalInvocationID
+        %1 = OpString "test"
+        OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
+        OpDecorate %entryPointParam_main Location 0
+        %void = OpTypeVoid
+        %4 = OpExtInst %void %2 DebugSource %1
+        %uint = OpTypeInt 32 0
+        %uint_100 = OpConstant %uint 100
+        %uint_5 = OpConstant %uint 5
+        %uint_11 = OpConstant %uint 11
+        %10 = OpExtInst %void %2 DebugCompilationUnit %uint_100 %uint_5 %4 %uint_11
+        %12 = OpTypeFunction %void
+        %uint_0 = OpConstant %uint 0
+        %14 = OpExtInst %void %2 DebugTypeFunction %uint_0 %void
+        %uint_2 = OpConstant %uint 2
+        %16 = OpExtInst %void %2 DebugFunction %1 %14 %4 %uint_2 %uint_5 %10 %1 %uint_0 %uint_2
+        %v3uint = OpTypeVector %uint 3
+        %_ptr_Input_v3uint = OpTypePointer Input %v3uint
+        %bool = OpTypeBool
+        %uint_3 = OpConstant %uint 3
+        %int = OpTypeInt 32 1
+        %_ptr_Output_int = OpTypePointer Output %int
+        %int_1 = OpConstant %int 1
+        %int_0 = OpConstant %int 0
+        %gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input    ; BuiltIn GlobalInvocationId
+        %entryPointParam_main = OpVariable %_ptr_Output_int Output      ; Location 0
+
+        ; Function main
+        %main = OpFunction %void None %12
+        %13 = OpLabel
+        %20 = OpExtInst %void %2 DebugScope %16
+        %29 = OpLoad %v3uint %gl_GlobalInvocationID
+        %37 = OpCompositeExtract %uint %29 0
+        %39 = OpUGreaterThan %bool %37 %uint_3
+        %19 = OpExtInst %void %2 DebugFunctionDefinition %16 %main
+        OpSelectionMerge %21 None
+        OpBranchConditional %39 %23 %21
+        %21 = OpLabel
+        OpStore %entryPointParam_main %int_1
+        OpReturn
+        %23 = OpLabel
+        OpStore %entryPointParam_main %int_0
+        OpReturn
+        OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<MergeReturnPass>(text, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/pass_remove_duplicates_test.cpp b/test/opt/pass_remove_duplicates_test.cpp
index 131a6b4..9cf420f 100644
--- a/test/opt/pass_remove_duplicates_test.cpp
+++ b/test/opt/pass_remove_duplicates_test.cpp
@@ -639,6 +639,131 @@
   EXPECT_EQ(GetErrorMessage(), "");
 }
 
+TEST_F(RemoveDuplicatesTest, DuplicateExtensions) {
+  const std::string spirv = R"(
+OpExtension "SPV_INTEL_function_variants"
+OpExtension "SPV_INTEL_function_variants"
+)";
+  const std::string after = R"(OpExtension "SPV_INTEL_function_variants"
+)";
+
+  EXPECT_EQ(RunPass(spirv), after);
+  EXPECT_EQ(GetErrorMessage(), "");
+}
+
+TEST_F(RemoveDuplicatesTest, DuplicateConditionalExtensions) {
+  const std::string spirv = R"(
+OpConditionalExtensionINTEL %1 "SPV_INTEL_function_variants"
+OpConditionalExtensionINTEL %1 "SPV_INTEL_function_variants"
+)";
+  const std::string after =
+      R"(OpConditionalExtensionINTEL %1 "SPV_INTEL_function_variants"
+)";
+
+  EXPECT_EQ(RunPass(spirv), after);
+  EXPECT_EQ(GetErrorMessage(), "");
+}
+
+TEST_F(RemoveDuplicatesTest, MixedConditionalExtensions) {
+  const std::string spirv = R"(
+OpConditionalExtensionINTEL %1 "SPV_INTEL_function_variants"
+OpConditionalExtensionINTEL %2 "SPV_INTEL_function_variants"
+OpConditionalExtensionINTEL %2 "SPV_INTEL_inline_assembly"
+OpConditionalExtensionINTEL %2 "SPV_INTEL_inline_assembly"
+OpExtension "SPV_INTEL_function_variants"
+)";
+  const std::string after =
+      R"(OpConditionalExtensionINTEL %1 "SPV_INTEL_function_variants"
+OpConditionalExtensionINTEL %2 "SPV_INTEL_function_variants"
+OpConditionalExtensionINTEL %2 "SPV_INTEL_inline_assembly"
+OpExtension "SPV_INTEL_function_variants"
+)";
+
+  EXPECT_EQ(RunPass(spirv), after);
+  EXPECT_EQ(GetErrorMessage(), "");
+}
+
+TEST_F(RemoveDuplicatesTest, MixedConditionalExtensionsReordered) {
+  const std::string spirv = R"(
+OpConditionalExtensionINTEL %2 "SPV_INTEL_function_variants"
+OpConditionalExtensionINTEL %2 "SPV_INTEL_inline_assembly"
+OpConditionalExtensionINTEL %2 "SPV_INTEL_inline_assembly"
+OpConditionalExtensionINTEL %1 "SPV_INTEL_function_variants"
+OpExtension "SPV_INTEL_function_variants"
+%3 = OpTypeBool
+%1 = OpSpecConstantTrue %3
+%2 = OpSpecConstantFalse %3
+)";
+  const std::string after =
+      R"(OpConditionalExtensionINTEL %1 "SPV_INTEL_function_variants"
+OpConditionalExtensionINTEL %1 "SPV_INTEL_inline_assembly"
+OpConditionalExtensionINTEL %2 "SPV_INTEL_function_variants"
+OpExtension "SPV_INTEL_function_variants"
+%3 = OpTypeBool
+%2 = OpSpecConstantTrue %3
+%1 = OpSpecConstantFalse %3
+)";
+
+  EXPECT_EQ(RunPass(spirv), after);
+  EXPECT_EQ(GetErrorMessage(), "");
+}
+
+TEST_F(RemoveDuplicatesTest, DuplicateConditionalCapabilities) {
+  const std::string spirv = R"(
+OpConditionalCapabilityINTEL %1 Kernel
+OpConditionalCapabilityINTEL %1 Kernel
+)";
+  const std::string after = R"(OpConditionalCapabilityINTEL %1 Kernel
+)";
+
+  EXPECT_EQ(RunPass(spirv), after);
+  EXPECT_EQ(GetErrorMessage(), "");
+}
+
+TEST_F(RemoveDuplicatesTest, MixedConditionalCapabilities) {
+  const std::string spirv = R"(
+OpConditionalCapabilityINTEL %1 Kernel
+OpConditionalCapabilityINTEL %2 Kernel
+OpConditionalCapabilityINTEL %2 Linkage
+OpConditionalCapabilityINTEL %2 Linkage
+OpCapability Kernel
+)";
+  const std::string after =
+      R"(OpConditionalCapabilityINTEL %1 Kernel
+OpConditionalCapabilityINTEL %2 Kernel
+OpConditionalCapabilityINTEL %2 Linkage
+OpCapability Kernel
+)";
+
+  EXPECT_EQ(RunPass(spirv), after);
+  EXPECT_EQ(GetErrorMessage(), "");
+}
+
+TEST_F(RemoveDuplicatesTest, MixedConditionalCapabilitiesReordered) {
+  const std::string spirv = R"(
+OpConditionalCapabilityINTEL %2 Kernel
+OpConditionalCapabilityINTEL %2 Linkage
+OpConditionalCapabilityINTEL %2 Linkage
+OpConditionalCapabilityINTEL %1 Kernel
+OpCapability Kernel
+%3 = OpTypeBool
+%1 = OpSpecConstantTrue %3
+%2 = OpSpecConstantFalse %3
+)";
+  const std::string after =
+      R"(OpConditionalCapabilityINTEL %1 Kernel
+OpConditionalCapabilityINTEL %1 Linkage
+OpConditionalCapabilityINTEL %2 Kernel
+OpCapability Kernel
+%3 = OpTypeBool
+%2 = OpSpecConstantTrue %3
+%1 = OpSpecConstantFalse %3
+)";
+
+  EXPECT_EQ(RunPass(spirv), after);
+  EXPECT_EQ(GetErrorMessage(), "");
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/pass_utils.h b/test/opt/pass_utils.h
index 8968f8a..ddf3b9f 100644
--- a/test/opt/pass_utils.h
+++ b/test/opt/pass_utils.h
@@ -17,6 +17,7 @@
 
 #include <algorithm>
 #include <functional>
+#include <iostream>
 #include <iterator>
 #include <string>
 #include <vector>
@@ -24,10 +25,25 @@
 #include "gtest/gtest.h"
 #include "include/spirv-tools/libspirv.h"
 #include "include/spirv-tools/libspirv.hpp"
+#include "source/opt/pass.h"
 
 namespace spvtools {
 namespace opt {
 
+inline std::ostream& operator<<(std::ostream& os, const Pass::Status ps) {
+  switch (ps) {
+    case Pass::Status::Failure:
+      return os << "Pass::Status::Failure";
+    case Pass::Status::SuccessWithChange:
+      return os << "Pass::Status::SuccessWithChange";
+    case Pass::Status::SuccessWithoutChange:
+      return os << "Pass::Status::SuccessWithoutChange";
+    default:
+      break;
+  }
+  return os << "(invalid Pass::Status " << static_cast<unsigned>(ps) << ")";
+}
+
 struct Message {
   spv_message_level_t level;
   const char* source_file;
diff --git a/test/opt/private_to_local_test.cpp b/test/opt/private_to_local_test.cpp
index f7c37c9..6314d49 100644
--- a/test/opt/private_to_local_test.cpp
+++ b/test/opt/private_to_local_test.cpp
@@ -173,6 +173,43 @@
   SinglePassRunAndMatch<PrivateToLocalPass>(text, false);
 }
 
+TEST_F(PrivateToLocalTest, IgnorePointerToPrivateVariable) {
+  // Should not change because the pointer to the private variable
+  // has been stored inside a function variable.
+  const std::string text = R"(
+               OpCapability Shader
+               OpCapability VariablePointersStorageBuffer
+               OpCapability VariablePointers
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %load_thread_local "load_thread_local" %v
+               OpExecutionMode %load_thread_local LocalSize 8 8 1
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+       %void = OpTypeVoid
+%function_type__1561_ = OpTypeFunction %void
+%_ptr_Private_uint = OpTypePointer Private %uint
+%_ptr_Function__ptr_Private_uint = OpTypePointer Function %_ptr_Private_uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+          %v = OpVariable %_ptr_Private_uint Private %uint_0
+%load_thread_local = OpFunction %void None %function_type__1561_
+         %37 = OpLabel
+         %31 = OpVariable %_ptr_Function__ptr_Private_uint Function
+         %32 = OpVariable %_ptr_Function_uint Function
+         %33 = OpVariable %_ptr_Function_uint Function
+               OpStore %31 %v
+         %35 = OpLoad %uint %v
+               OpStore %32 %35
+         %36 = OpLoad %uint %v
+               OpStore %33 %36
+               OpReturn
+               OpFunctionEnd
+  )";
+  auto result = SinglePassRunAndDisassemble<PrivateToLocalPass>(
+      text, /* skip_nop = */ true, /* do_validation = */ false);
+  EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
+}
+
 TEST_F(PrivateToLocalTest, UsedInTwoFunctions) {
   // Should not change because it is used in multiple functions.
   const std::string text = R"(
diff --git a/test/opt/redundancy_elimination_test.cpp b/test/opt/redundancy_elimination_test.cpp
index eb78497..6af1107 100644
--- a/test/opt/redundancy_elimination_test.cpp
+++ b/test/opt/redundancy_elimination_test.cpp
@@ -84,6 +84,34 @@
   SinglePassRunAndMatch<RedundancyEliminationPass>(text, false);
 }
 
+// Remove a redundant add with flipped arguments.
+TEST_F(RedundancyEliminationTest, RemoveRedundantAddFlipped) {
+  const std::string text = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "main"
+               OpExecutionMode %2 OriginUpperLeft
+               OpSource GLSL 430
+          %3 = OpTypeVoid
+          %4 = OpTypeFunction %3
+          %5 = OpTypeFloat 32
+          %6 = OpTypePointer Function %5
+          %2 = OpFunction %3 None %4
+          %7 = OpLabel
+          %8 = OpVariable %6 Function
+          %9 = OpLoad %5 %8
+         %10 = OpFMul %5 %9 %9
+         %11 = OpFAdd %5 %9 %10
+; CHECK: OpFAdd
+; CHECK-NEXT: OpReturn
+         %12 = OpFAdd %5 %10 %9
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<RedundancyEliminationPass>(text, false);
+}
+
 // Remove a redundant add going through a multiple basic blocks.
 TEST_F(RedundancyEliminationTest, RemoveRedundantAddDiamond) {
   const std::string text = R"(
@@ -359,6 +387,93 @@
   SinglePassRunAndCheck<RedundancyEliminationPass>(text, text, false);
 }
 
+TEST_F(RedundancyEliminationTest, PreserveLoadYieldingImage) {
+  // This is more strict than needed.
+  // This is sufficient to ensure that the load of an image
+  // occurs in the same basic block as its use.
+  const std::string text = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginLowerLeft
+               OpName %main "main"
+               OpName %load_ty "load_ty"
+               OpDecorate %var DescriptorSet 0
+               OpDecorate %var Binding 0
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %load_ty = OpTypeImage %float 2D 0 0 0 2 Rgba32f
+%ptr_load_ty = OpTypePointer UniformConstant %load_ty
+        %var = OpVariable %ptr_load_ty UniformConstant
+       %main = OpFunction %void None %6
+       ; CHECK: OpLoad %load_ty
+       ; CHECK: OpLoad %load_ty
+         %15 = OpLabel
+         %16 = OpLoad %load_ty %var
+         %17 = OpLoad %load_ty %var
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<RedundancyEliminationPass>(text, false);
+}
+
+TEST_F(RedundancyEliminationTest, PreserveLoadYieldingSampler) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginLowerLeft
+               OpName %main "main"
+               OpName %load_ty "load_ty"
+               OpDecorate %var DescriptorSet 0
+               OpDecorate %var Binding 0
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+    %load_ty = OpTypeSampler
+%ptr_load_ty = OpTypePointer UniformConstant %load_ty
+        %var = OpVariable %ptr_load_ty UniformConstant
+       %main = OpFunction %void None %6
+       ; CHECK: OpLoad %load_ty
+       ; CHECK: OpLoad %load_ty
+         %15 = OpLabel
+         %16 = OpLoad %load_ty %var
+         %17 = OpLoad %load_ty %var
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<RedundancyEliminationPass>(text, false);
+}
+
+TEST_F(RedundancyEliminationTest, PreserveLoadYieldingSampledImage) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginLowerLeft
+               OpName %main "main"
+               OpName %load_ty "load_ty"
+               OpDecorate %var DescriptorSet 0
+               OpDecorate %var Binding 0
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+       %i_ty = OpTypeImage %float 2D 0 0 0 2 Rgba32f
+    %load_ty = OpTypeSampledImage %i_ty
+%ptr_load_ty = OpTypePointer UniformConstant %load_ty
+        %var = OpVariable %ptr_load_ty UniformConstant
+       %main = OpFunction %void None %6
+       ; CHECK: OpLoad %load_ty
+       ; CHECK: OpLoad %load_ty
+         %15 = OpLabel
+         %16 = OpLoad %load_ty %var
+         %17 = OpLoad %load_ty %var
+               OpReturn
+               OpFunctionEnd
+  )";
+  SinglePassRunAndMatch<RedundancyEliminationPass>(text, false);
+}
+
 }  // namespace
 }  // namespace opt
-}  // namespace spvtools
\ No newline at end of file
+}  // namespace spvtools
diff --git a/test/opt/remove_unused_interface_variables_test.cpp b/test/opt/remove_unused_interface_variables_test.cpp
index 8bb40f7..bb273e5 100644
--- a/test/opt/remove_unused_interface_variables_test.cpp
+++ b/test/opt/remove_unused_interface_variables_test.cpp
@@ -179,6 +179,50 @@
                                                             true, true);
 }
 
+TEST_F(RemoveUnusedInterfaceVariablesTest,
+       DontEliminateUntypedVariableInterfaceVulkan12) {
+  const std::string spirv = R"(
+                               OpCapability Shader
+                               OpCapability SampledBuffer
+                               OpCapability ImageBuffer
+                               OpCapability Int64
+                               OpCapability DescriptorHeapEXT
+                               OpCapability UntypedPointersKHR
+                               OpExtension "SPV_EXT_descriptor_heap"
+                               OpExtension "SPV_KHR_untyped_pointers"
+                               OpMemoryModel Logical GLSL450
+                               OpEntryPoint Fragment %1 "main" %2
+; CHECK:                       OpEntryPoint Fragment %1 "main" %2
+                               OpExecutionMode %1 OriginUpperLeft
+                               OpDecorate %2 BuiltIn ResourceHeapEXT
+                       %uint = OpTypeInt 32 0
+                      %float = OpTypeFloat 32
+                    %v4float = OpTypeVector %float 4
+                     %uint_0 = OpConstant %uint 0
+                     %uint_1 = OpConstant %uint 1
+       %type_untyped_pointer = OpTypeUntypedPointerKHR UniformConstant
+                       %void = OpTypeVoid
+                         %10 = OpTypeFunction %void
+          %type_buffer_image = OpTypeImage %float Buffer 2 0 0 1 Rgba32f
+        %type_buffer_image_0 = OpTypeImage %float Buffer 2 0 0 2 Rgba32f
+            %type_buffer_ext = OpTypeBufferEXT StorageBuffer
+%_runtimearr_type_buffer_ext = OpTypeRuntimeArray %type_buffer_ext
+                          %2 = OpUntypedVariableKHR %type_untyped_pointer UniformConstant
+                          %1 = OpFunction %void None %10
+                          %3 = OpLabel
+                         %20 = OpUntypedAccessChainKHR %type_untyped_pointer %_runtimearr_type_buffer_ext %2 %uint_0
+                         %24 = OpUntypedAccessChainKHR %type_untyped_pointer %_runtimearr_type_buffer_ext %2 %uint_1
+                         %26 = OpLoad %type_buffer_image %20
+                         %28 = OpImageFetch %v4float %26 %uint_0 None
+                         %29 = OpLoad %type_buffer_image_0 %24
+                               OpImageWrite %29 %uint_0 %28 None
+                               OpReturn
+                               OpFunctionEnd
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_2);
+  SinglePassRunAndMatch<RemoveUnusedInterfaceVariablesPass>(spirv, true);
+}
+
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools
diff --git a/test/opt/resolve_binding_conflicts_pass_test.cpp b/test/opt/resolve_binding_conflicts_pass_test.cpp
new file mode 100644
index 0000000..31378b9
--- /dev/null
+++ b/test/opt/resolve_binding_conflicts_pass_test.cpp
@@ -0,0 +1,864 @@
+// Copyright (c) 2025 Google LLC
+//
+// 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 <iostream>
+#include <ostream>
+
+#include "spirv-tools/optimizer.hpp"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+struct ResolveBindingConflictsTest : public PassTest<::testing::Test> {
+  virtual void SetUp() override {
+    SetTargetEnv(SPV_ENV_VULKAN_1_1);  // allow storage buffer storage class
+                                       // without extension.
+    SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+    SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES |
+                          SPV_BINARY_TO_TEXT_OPTION_INDENT |
+                          SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  }
+};
+
+using StringList = std::vector<std::string>;
+std::string EntryPointDecls(const StringList& names) {
+  std::ostringstream os;
+  for (auto& name : names) {
+    os << "               OpEntryPoint GLCompute %" + name + " \"" + name +
+              "\"\n";
+  }
+  for (auto& name : names) {
+    os << "               OpExecutionMode %" + name + " LocalSize 1 1 1\n";
+  }
+  for (auto& name : names) {
+    os << "               OpName %" + name + " \"" + name + "\"\n";
+  }
+  return os.str();
+}
+
+std::string Preamble(const StringList& names = {"main"}) {
+  return R"(               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+)" + EntryPointDecls(names) +
+         R"(               OpName %voidfn "voidfn"
+               OpName %s_ty "s_ty"
+               OpName %i_ty "i_ty"
+               OpName %si_ty "si_ty"
+               OpName %p_s_ty "p_s_ty"
+               OpName %p_i_ty "p_i_ty"
+               OpName %p_si_ty "p_si_ty"
+               OpName %st_ty "st_ty"
+               OpName %pu_st_ty "pu_st_ty"
+               OpName %pb_st_ty "pb_st_ty"
+)";
+}
+
+std::string BasicTypes() {
+  return R"(               OpDecorate %st_ty Block
+               OpMemberDecorate %st_ty 0 Offset 0
+      %float = OpTypeFloat 32
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_3 = OpConstant %uint 3
+       %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
+       %s_ty = OpTypeSampler
+       %i_ty = OpTypeImage %float 2D 0 0 0 1 Unknown
+      %si_ty = OpTypeSampledImage %i_ty
+     %p_i_ty = OpTypePointer UniformConstant %i_ty
+     %p_s_ty = OpTypePointer UniformConstant %s_ty
+    %p_si_ty = OpTypePointer UniformConstant %si_ty
+      %st_ty = OpTypeStruct %uint
+   %pu_st_ty = OpTypePointer Uniform %st_ty
+   %pb_st_ty = OpTypePointer StorageBuffer %st_ty
+)";
+}
+std::string NoCheck() { return "; CHECK-NOT: nothing to see"; }
+
+TEST_F(ResolveBindingConflictsTest, NoBindings_NoChange) {
+  const std::string kTest = Preamble() + BasicTypes() +
+                            R"(       %main = OpFunction %void None %voidfn
+        %100 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>(
+      kTest + NoCheck(), /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithoutChange);
+  EXPECT_EQ(kTest, disasm);
+}
+
+TEST_F(ResolveBindingConflictsTest, NoConflict_UnusedVars_NoChange) {
+  const std::string kTest = Preamble() +
+                            R"(
+               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+               OpDecorate %101 DescriptorSet 0
+               OpDecorate %101 Binding 0
+               OpDecorate %102 DescriptorSet 0
+               OpDecorate %102 Binding 0
+               OpDecorate %103 DescriptorSet 0
+               OpDecorate %103 Binding 0
+               OpDecorate %104 DescriptorSet 0
+               OpDecorate %104 Binding 0
+
+      ; CHECK: OpDecorate %100 DescriptorSet 0
+      ; CHECK: OpDecorate %100 Binding 0
+      ; CHECK: OpDecorate %101 DescriptorSet 0
+      ; CHECK: OpDecorate %101 Binding 0
+      ; CHECK: OpDecorate %102 DescriptorSet 0
+      ; CHECK: OpDecorate %102 Binding 0
+      ; CHECK: OpDecorate %103 DescriptorSet 0
+      ; CHECK: OpDecorate %103 Binding 0
+      ; CHECK: OpDecorate %104 DescriptorSet 0
+      ; CHECK: OpDecorate %104 Binding 0
+
+)" + BasicTypes() + R"(
+
+ ; Unused variables
+
+        %100 = OpVariable %p_i_ty UniformConstant  ; image
+        %101 = OpVariable %p_s_ty UniformConstant  ; sampler
+        %102 = OpVariable %pu_st_ty Uniform        ; UBO
+        %103 = OpVariable %pb_st_ty StorageBuffer  ; SSBO
+        %104 = OpVariable %p_si_ty UniformConstant ; combined sampled image
+
+       %main = OpFunction %void None %voidfn
+         %10 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithoutChange) << disasm;
+}
+
+TEST_F(ResolveBindingConflictsTest, NoConflict_UsedVars_NoChange) {
+  const std::string kTest = Preamble() +
+                            R"(
+               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+               OpDecorate %101 DescriptorSet 0
+               OpDecorate %101 Binding 1
+               OpDecorate %102 DescriptorSet 0
+               OpDecorate %102 Binding 2
+               OpDecorate %103 DescriptorSet 0
+               OpDecorate %103 Binding 3
+               OpDecorate %110 DescriptorSet 1
+               OpDecorate %110 Binding 0
+
+      ; CHECK: OpDecorate %100 DescriptorSet 0
+      ; CHECK: OpDecorate %100 Binding 0
+      ; CHECK: OpDecorate %101 DescriptorSet 0
+      ; CHECK: OpDecorate %101 Binding 1
+      ; CHECK: OpDecorate %102 DescriptorSet 0
+      ; CHECK: OpDecorate %102 Binding 2
+      ; CHECK: OpDecorate %103 DescriptorSet 0
+      ; CHECK: OpDecorate %103 Binding 3
+      ; CHECK: OpDecorate %110 DescriptorSet 1
+      ; CHECK: OpDecorate %110 Binding 0
+
+)" + BasicTypes() + R"(
+
+        %100 = OpVariable %p_i_ty UniformConstant  ; image
+        %101 = OpVariable %p_s_ty UniformConstant  ; sampler
+        %102 = OpVariable %pu_st_ty Uniform        ; UBO
+        %103 = OpVariable %pb_st_ty StorageBuffer  ; SSBO
+        %110 = OpVariable %p_si_ty UniformConstant ; combined sampled image
+
+       %main = OpFunction %void None %voidfn
+         %10 = OpLabel
+         %11 = OpCopyObject %p_i_ty %100
+         %12 = OpCopyObject %p_s_ty %101
+         %13 = OpCopyObject %pu_st_ty %102
+         %14 = OpCopyObject %pb_st_ty %103
+         %15 = OpCopyObject %p_si_ty %110
+               OpReturn
+               OpFunctionEnd
+)";
+
+  auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithoutChange) << disasm;
+}
+
+TEST_F(ResolveBindingConflictsTest,
+       OneEntryPoint_SamplerFirstConflict_Resolves) {
+  const std::string kTest = Preamble() +
+                            R"(
+               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+               OpDecorate %101 DescriptorSet 0
+               OpDecorate %101 Binding 0
+
+      ; The sampler's binding number is incremented, even when listed first.
+      ; CHECK: OpDecorate %100 DescriptorSet 0
+      ; CHECK: OpDecorate %100 Binding 1
+      ; CHECK: OpDecorate %101 DescriptorSet 0
+      ; CHECK: OpDecorate %101 Binding 0
+
+)" + BasicTypes() + R"(
+
+        %100 = OpVariable %p_s_ty UniformConstant ; sampler listed first
+        %101 = OpVariable %p_i_ty UniformConstant
+
+       %main = OpFunction %void None %voidfn
+         %10 = OpLabel
+         %11 = OpCopyObject %p_s_ty %100
+         %12 = OpCopyObject %p_i_ty %101
+               OpReturn
+               OpFunctionEnd
+)";
+
+  auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(ResolveBindingConflictsTest,
+       OneEntryPoint_SamplerSecondConflict_Resolves) {
+  const std::string kTest = Preamble() +
+                            R"(
+               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+               OpDecorate %101 DescriptorSet 0
+               OpDecorate %101 Binding 0
+
+      ; The sampler's binding number is incremented, even when listed second.
+      ; CHECK: OpDecorate %100 DescriptorSet 0
+      ; CHECK: OpDecorate %100 Binding 0
+      ; CHECK: OpDecorate %101 DescriptorSet 0
+      ; CHECK: OpDecorate %101 Binding 1
+
+)" + BasicTypes() + R"(
+
+        %100 = OpVariable %p_i_ty UniformConstant
+        %101 = OpVariable %p_s_ty UniformConstant ; sampler listed second
+
+       %main = OpFunction %void None %voidfn
+         %10 = OpLabel
+         %11 = OpCopyObject %p_i_ty %100
+         %12 = OpCopyObject %p_s_ty %101
+               OpReturn
+               OpFunctionEnd
+)";
+
+  auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(ResolveBindingConflictsTest, OneEntryPoint_Conflict_Ripples) {
+  const std::string kTest = Preamble() +
+                            R"(
+               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+               OpDecorate %101 DescriptorSet 0
+               OpDecorate %101 Binding 0
+               OpDecorate %102 DescriptorSet 0
+               OpDecorate %102 Binding 1
+               OpDecorate %103 DescriptorSet 0
+               OpDecorate %103 Binding 2
+               OpDecorate %104 DescriptorSet 0
+               OpDecorate %104 Binding 3
+
+      ; The sampler's binding number is incremented, and later
+      ; bindings move out of the way.
+      ; CHECK: OpDecorate %100 DescriptorSet 0
+      ; CHECK: OpDecorate %100 Binding 1
+      ; CHECK: OpDecorate %101 DescriptorSet 0
+      ; CHECK: OpDecorate %101 Binding 0
+      ; CHECK: OpDecorate %102 DescriptorSet 0
+      ; CHECK: OpDecorate %102 Binding 2
+      ; CHECK: OpDecorate %103 DescriptorSet 0
+      ; CHECK: OpDecorate %103 Binding 3
+      ; CHECK: OpDecorate %104 DescriptorSet 0
+      ; CHECK: OpDecorate %104 Binding 4
+
+)" + BasicTypes() + R"(
+
+        %100 = OpVariable %p_s_ty UniformConstant ; sampler comes first
+        %101 = OpVariable %p_i_ty UniformConstant
+        %102 = OpVariable %pu_st_ty Uniform
+        %103 = OpVariable %pb_st_ty StorageBuffer
+        %104 = OpVariable %p_si_ty UniformConstant
+
+       %main = OpFunction %void None %voidfn
+         %10 = OpLabel
+         %11 = OpCopyObject %p_s_ty %100
+         %12 = OpCopyObject %p_i_ty %101
+         %13 = OpCopyObject %pu_st_ty %102
+         %14 = OpCopyObject %pb_st_ty %103
+         %15 = OpCopyObject %p_si_ty %104
+               OpReturn
+               OpFunctionEnd
+)";
+
+  auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(ResolveBindingConflictsTest,
+       OneEntryPoint_Conflict_RippleStopsAtFirstHole) {
+  const std::string kTest = Preamble() +
+                            R"(
+               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+               OpDecorate %101 DescriptorSet 0
+               OpDecorate %101 Binding 0
+               OpDecorate %102 DescriptorSet 0
+               OpDecorate %102 Binding 1
+               ; Leave a hole at (0, 2)
+               OpDecorate %103 DescriptorSet 0
+               OpDecorate %103 Binding 3
+               OpDecorate %104 DescriptorSet 0
+               OpDecorate %104 Binding 4
+
+      ; There was a hole at binding 2.  The ripple stops there.
+      ; CHECK: OpDecorate %100 DescriptorSet 0
+      ; CHECK: OpDecorate %100 Binding 1
+      ; CHECK: OpDecorate %101 DescriptorSet 0
+      ; CHECK: OpDecorate %101 Binding 0
+      ; CHECK: OpDecorate %102 DescriptorSet 0
+      ; CHECK: OpDecorate %102 Binding 2
+      ; CHECK: OpDecorate %103 DescriptorSet 0
+      ; CHECK: OpDecorate %103 Binding 3
+      ; CHECK: OpDecorate %104 DescriptorSet 0
+      ; CHECK: OpDecorate %104 Binding 4
+
+)" + BasicTypes() + R"(
+
+        %100 = OpVariable %p_s_ty UniformConstant ; sampler comes first
+        %101 = OpVariable %p_i_ty UniformConstant
+        %102 = OpVariable %pu_st_ty Uniform
+        %103 = OpVariable %pb_st_ty StorageBuffer
+        %104 = OpVariable %p_si_ty UniformConstant
+
+       %main = OpFunction %void None %voidfn
+         %10 = OpLabel
+         %11 = OpCopyObject %p_s_ty %100
+         %12 = OpCopyObject %p_i_ty %101
+         %13 = OpCopyObject %pu_st_ty %102
+         %14 = OpCopyObject %pb_st_ty %103
+         %15 = OpCopyObject %p_si_ty %104
+               OpReturn
+               OpFunctionEnd
+)";
+
+  auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(ResolveBindingConflictsTest, OneEntryPoint_MultiConflict_Resolves) {
+  const std::string kTest = Preamble() +
+                            R"(
+      ; Two conflicts: at Bindings 0, and 1
+               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+               OpDecorate %101 DescriptorSet 0
+               OpDecorate %101 Binding 0
+               OpDecorate %102 DescriptorSet 0
+               OpDecorate %102 Binding 1
+               OpDecorate %103 DescriptorSet 0
+               OpDecorate %103 Binding 1
+               OpDecorate %104 DescriptorSet 0
+               OpDecorate %104 Binding 2
+
+      ; CHECK: OpDecorate %100 DescriptorSet 0
+      ; CHECK: OpDecorate %100 Binding 1
+      ; CHECK: OpDecorate %101 DescriptorSet 0
+      ; CHECK: OpDecorate %101 Binding 0
+      ; CHECK: OpDecorate %102 DescriptorSet 0
+      ; CHECK: OpDecorate %102 Binding 2
+      ; CHECK: OpDecorate %103 DescriptorSet 0
+      ; CHECK: OpDecorate %103 Binding 3
+      ; CHECK: OpDecorate %104 DescriptorSet 0
+      ; CHECK: OpDecorate %104 Binding 4
+
+)" + BasicTypes() + R"(
+
+        %100 = OpVariable %p_s_ty UniformConstant ; sampler first
+        %101 = OpVariable %p_i_ty UniformConstant
+        %102 = OpVariable %p_i_ty UniformConstant
+        %103 = OpVariable %p_s_ty UniformConstant ; sampler second
+        %104 = OpVariable %pu_st_ty Uniform
+
+       %main = OpFunction %void None %voidfn
+         %10 = OpLabel
+         %11 = OpCopyObject %p_s_ty %100
+         %12 = OpCopyObject %p_i_ty %101
+         %13 = OpCopyObject %p_i_ty %102
+         %14 = OpCopyObject %p_s_ty %103
+         %15 = OpCopyObject %pu_st_ty %104
+               OpReturn
+               OpFunctionEnd
+)";
+
+  auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(ResolveBindingConflictsTest,
+       OneEntryPoint_MultiConflict_ComplexCallGraph_Resolves) {
+  // Check that uses are seen even when used at various points in a complex call
+  // graph.
+  const std::string kTest = Preamble() +
+                            R"(
+               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+               OpDecorate %101 DescriptorSet 0
+               OpDecorate %101 Binding 0
+               OpDecorate %102 DescriptorSet 0
+               OpDecorate %102 Binding 1
+               OpDecorate %103 DescriptorSet 0
+               OpDecorate %103 Binding 1
+               OpDecorate %104 DescriptorSet 0
+               OpDecorate %104 Binding 2
+
+      ; CHECK: OpDecorate %100 DescriptorSet 0
+      ; CHECK: OpDecorate %100 Binding 1
+      ; CHECK: OpDecorate %101 DescriptorSet 0
+      ; CHECK: OpDecorate %101 Binding 0
+      ; CHECK: OpDecorate %102 DescriptorSet 0
+      ; CHECK: OpDecorate %102 Binding 2
+      ; CHECK: OpDecorate %103 DescriptorSet 0
+      ; CHECK: OpDecorate %103 Binding 3
+      ; CHECK: OpDecorate %104 DescriptorSet 0
+      ; CHECK: OpDecorate %104 Binding 4
+
+)" + BasicTypes() + R"(
+
+        %100 = OpVariable %p_s_ty UniformConstant ; used in %200
+        %101 = OpVariable %p_i_ty UniformConstant ; used in %300, %400
+        %102 = OpVariable %p_i_ty UniformConstant ; used in %500
+        %103 = OpVariable %p_s_ty UniformConstant ; used in %400 twice
+        %104 = OpVariable %pu_st_ty Uniform       ; used in %600
+
+        %200 = OpFunction %void None %voidfn
+        %201 = OpLabel
+        %202 = OpCopyObject %p_s_ty %100
+               OpReturn
+               OpFunctionEnd
+
+        %300 = OpFunction %void None %voidfn
+        %301 = OpLabel
+        %302 = OpCopyObject %p_i_ty %101
+               OpReturn
+               OpFunctionEnd
+
+        %400 = OpFunction %void None %voidfn
+        %401 = OpLabel
+        %402 = OpFunctionCall %void %200
+        %403 = OpCopyObject %p_s_ty %103
+        %404 = OpCopyObject %p_i_ty %101
+        %405 = OpCopyObject %p_s_ty %103
+        %406 = OpFunctionCall %void %300
+               OpReturn
+               OpFunctionEnd
+
+        %500 = OpFunction %void None %voidfn
+        %501 = OpLabel
+        %502 = OpFunctionCall %void %400
+        %503 = OpCopyObject %p_i_ty %102
+        %504 = OpFunctionCall %void %300
+               OpReturn
+               OpFunctionEnd
+
+        %600 = OpFunction %void None %voidfn
+        %601 = OpLabel
+        %602 = OpFunctionCall %void %300
+        %603 = OpFunctionCall %void %500
+        %604 = OpCopyObject %pu_st_ty %104
+               OpReturn
+               OpFunctionEnd
+
+       %main = OpFunction %void None %voidfn
+       %1000 = OpLabel
+       %1001 = OpFunctionCall %void %600
+               OpReturn
+               OpFunctionEnd
+)";
+
+  auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(ResolveBindingConflictsTest,
+       MultiEntryPoint_DuplicatConflicts_ResolvesOnlyOnce) {
+  // Before:
+  //
+  //   Binding:   0          1
+  //   Alpha:    %100,%101
+  //   Beta:     %100,%101
+  //
+  // After:
+  //
+  //   Binding:   0          1
+  //   Alpha:    %101       %100
+  //   Beta:     %101       %100
+  const std::string kTest = Preamble({"alpha", "beta"}) +
+                            R"(
+               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+               OpDecorate %101 DescriptorSet 0
+               OpDecorate %101 Binding 0
+
+      ; CHECK: OpDecorate %100 DescriptorSet 0
+      ; CHECK: OpDecorate %100 Binding 1
+      ; CHECK: OpDecorate %101 DescriptorSet 0
+      ; CHECK: OpDecorate %101 Binding 0
+
+)" + BasicTypes() + R"(
+
+        %100 = OpVariable %p_s_ty UniformConstant
+        %101 = OpVariable %p_i_ty UniformConstant
+
+      %alpha = OpFunction %void None %voidfn
+       %1000 = OpLabel
+       %1001 = OpCopyObject %p_s_ty %100
+       %1002 = OpCopyObject %p_i_ty %101
+               OpReturn
+               OpFunctionEnd
+
+       %beta = OpFunction %void None %voidfn
+       %2000 = OpLabel
+       %2001 = OpCopyObject %p_s_ty %100
+       %2002 = OpCopyObject %p_i_ty %101
+               OpReturn
+               OpFunctionEnd
+)";
+
+  auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(ResolveBindingConflictsTest,
+       MultiEntryPoint_IndependentConflicts_Resolves) {
+  // Before:
+  //
+  //   Binding:   0          1
+  //   Alpha:    %100,%101
+  //   Beta:     %102,%103
+  //
+  // After:
+  //
+  //   Binding:   0          1
+  //   Alpha:    %101       %100
+  //   Beta:     %102       %103
+  const std::string kTest = Preamble({"alpha", "beta"}) +
+                            R"(
+               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+               OpDecorate %101 DescriptorSet 0
+               OpDecorate %101 Binding 0
+               OpDecorate %102 DescriptorSet 0
+               OpDecorate %102 Binding 0
+               OpDecorate %103 DescriptorSet 0
+               OpDecorate %103 Binding 0
+
+      ; CHECK: OpDecorate %100 DescriptorSet 0
+      ; CHECK: OpDecorate %100 Binding 1
+      ; CHECK: OpDecorate %101 DescriptorSet 0
+      ; CHECK: OpDecorate %101 Binding 0
+      ; CHECK: OpDecorate %102 DescriptorSet 0
+      ; CHECK: OpDecorate %102 Binding 0
+      ; CHECK: OpDecorate %103 DescriptorSet 0
+      ; CHECK: OpDecorate %103 Binding 1
+
+)" + BasicTypes() + R"(
+
+        %100 = OpVariable %p_s_ty UniformConstant
+        %101 = OpVariable %p_i_ty UniformConstant
+        %102 = OpVariable %p_i_ty UniformConstant
+        %103 = OpVariable %p_s_ty UniformConstant
+
+      %alpha = OpFunction %void None %voidfn
+       %1000 = OpLabel
+       %1001 = OpCopyObject %p_s_ty %100
+       %1002 = OpCopyObject %p_i_ty %101
+               OpReturn
+               OpFunctionEnd
+
+       %beta = OpFunction %void None %voidfn
+       %2000 = OpLabel
+       %2001 = OpCopyObject %p_i_ty %102
+       %2002 = OpCopyObject %p_s_ty %103
+               OpReturn
+               OpFunctionEnd
+)";
+
+  auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(ResolveBindingConflictsTest,
+       MultiEntryPoint_SameVarConflictsAcrossMultiEntryPoints_Resolves) {
+  // A sampler variable is bumped, causing potential conflicts in other shaders.
+  //
+  // Before:
+  //
+  //   Binding:   0          1        2
+  //   Alpha:    %100,%101
+  //   Beta:     %100       %102
+  //   Gamma:    %100                %103
+  //
+  // After:
+  //
+  //   Binding:   0          1        2
+  //   Alpha:    %101       %100
+  //   Beta:                %100     %102
+  //   Gamma:    %100                %103
+  //
+  const std::string kTest = Preamble({"alpha", "beta", "gamma"}) +
+                            R"(
+               OpDecorate %100 DescriptorSet 0  ; The sampler
+               OpDecorate %100 Binding 0
+               OpDecorate %101 DescriptorSet 0
+               OpDecorate %101 Binding 0
+               OpDecorate %102 DescriptorSet 0
+               OpDecorate %102 Binding 1
+               OpDecorate %103 DescriptorSet 0
+               OpDecorate %103 Binding 2
+
+      ; bumped once
+      ; CHECK: OpDecorate %100 DescriptorSet 0
+      ; CHECK: OpDecorate %100 Binding 1
+
+      ; CHECK: OpDecorate %101 DescriptorSet 0
+      ; CHECK: OpDecorate %101 Binding 0
+
+      ; pushed back from bump of %100
+      ; CHECK: OpDecorate %102 DescriptorSet 0
+      ; CHECK: OpDecorate %102 Binding 2
+
+      ; does not need to be bumped
+      ; CHECK: OpDecorate %103 DescriptorSet 0
+      ; CHECK: OpDecorate %103 Binding 2
+
+)" + BasicTypes() + R"(
+
+        %100 = OpVariable %p_s_ty UniformConstant ; used in alpha, beta, gamma
+        %101 = OpVariable %p_i_ty UniformConstant ; used in alpha
+        %102 = OpVariable %pu_st_ty Uniform       ; used in beta
+        %103 = OpVariable %pb_st_ty StorageBuffer ; used in gamma
+
+      %alpha = OpFunction %void None %voidfn
+       %1000 = OpLabel
+       %1001 = OpCopyObject %p_s_ty %100
+       %1002 = OpCopyObject %p_i_ty %101
+               OpReturn
+               OpFunctionEnd
+
+       %beta = OpFunction %void None %voidfn
+       %2000 = OpLabel
+       %2001 = OpCopyObject %p_s_ty %100
+       %2002 = OpCopyObject %pu_st_ty %102
+               OpReturn
+               OpFunctionEnd
+
+      %gamma = OpFunction %void None %voidfn
+       %3000 = OpLabel
+       %3001 = OpCopyObject %p_s_ty %100
+       %3002 = OpCopyObject %pb_st_ty %103
+               OpReturn
+               OpFunctionEnd
+)";
+
+  auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(ResolveBindingConflictsTest, MultiEntryPoint_ConflictCascade_Resolves) {
+  // Before:
+  //
+  //   Binding:   0          1        2        3
+  //   Alpha:    %100,%101
+  //   Beta:     %100       %102
+  //   Gamma:               %102     %103
+  //   Delta:                        %103     %104
+  //
+  // After:
+  //
+  //   Binding:   0          1        2        3        4
+  //   Alpha:    %101       %100
+  //   Beta:                %100     %102
+  //   Gamma:                        %102     %103
+  //   Delta:                                 %103     %104
+  //
+  const std::string kTest = Preamble({"alpha", "beta", "gamma", "delta"}) +
+                            R"(
+               OpDecorate %100 DescriptorSet 0  ; The sampler
+               OpDecorate %100 Binding 0
+               OpDecorate %101 DescriptorSet 0
+               OpDecorate %101 Binding 0
+               OpDecorate %102 DescriptorSet 0
+               OpDecorate %102 Binding 1
+               OpDecorate %103 DescriptorSet 0
+               OpDecorate %103 Binding 2
+               OpDecorate %104 DescriptorSet 0
+               OpDecorate %104 Binding 3
+
+      ; %100 is bumped once:
+      ; CHECK: OpDecorate %100 DescriptorSet 0
+      ; CHECK: OpDecorate %100 Binding 1
+
+      ; CHECK: OpDecorate %101 DescriptorSet 0
+      ; CHECK: OpDecorate %101 Binding 0
+
+      ; pushed back from bump of %100
+      ; CHECK: OpDecorate %102 DescriptorSet 0
+      ; CHECK: OpDecorate %102 Binding 2
+
+      ; pushed back from bump of %102
+      ; CHECK: OpDecorate %103 DescriptorSet 0
+      ; CHECK: OpDecorate %103 Binding 3
+
+      ; pushed back from bump of %103
+      ; CHECK: OpDecorate %104 DescriptorSet 0
+      ; CHECK: OpDecorate %104 Binding 4
+
+)" + BasicTypes() + R"(
+
+        %100 = OpVariable %p_s_ty UniformConstant  ; used in alpha, beta
+        %101 = OpVariable %p_i_ty UniformConstant  ; used in alpha
+        %102 = OpVariable %pu_st_ty Uniform        ; used in beta, gamma
+        %103 = OpVariable %pb_st_ty StorageBuffer  ; used in gamma, delta
+        %104 = OpVariable %p_si_ty UniformConstant ; used delta
+
+      %alpha = OpFunction %void None %voidfn
+       %1000 = OpLabel
+       %1001 = OpCopyObject %p_s_ty %100
+       %1002 = OpCopyObject %p_i_ty %101
+               OpReturn
+               OpFunctionEnd
+
+       %beta = OpFunction %void None %voidfn
+       %2000 = OpLabel
+       %2001 = OpCopyObject %p_s_ty %100
+       %2002 = OpCopyObject %pu_st_ty %102
+               OpReturn
+               OpFunctionEnd
+
+      %gamma = OpFunction %void None %voidfn
+       %3000 = OpLabel
+       %3001 = OpCopyObject %pu_st_ty %102
+       %3002 = OpCopyObject %pb_st_ty %103
+               OpReturn
+               OpFunctionEnd
+
+      %delta = OpFunction %void None %voidfn
+       %4000 = OpLabel
+       %4001 = OpCopyObject %pb_st_ty %103
+       %4002 = OpCopyObject %p_si_ty %104
+               OpReturn
+               OpFunctionEnd
+)";
+
+  auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(ResolveBindingConflictsTest,
+       MultiEntryPoint_ConflictCascade_RevisitEntryPoint) {
+  // Prove that the settling algorithm knows to revisit entry points that
+  // already had all their own conflicts resolved.
+  //
+  // Before:
+  //
+  //   Binding:   0          1       2        3       4
+  //   Alpha:    %100,%101          %103     %104    %105
+  //   Beta:          %101  %102    %103             %105
+  //
+  // After:
+  //
+  //   Binding:    0         1       2        3       4       5
+  //   Alpha:    %100       %101             %103    %104    %105
+  //   Beta:                %101    %102     %103            %105
+  const std::string kTest = Preamble({"alpha", "beta"}) +
+                            R"(
+               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+               OpDecorate %101 DescriptorSet 0 ; the sampler
+               OpDecorate %101 Binding 0
+               OpDecorate %102 DescriptorSet 0
+               OpDecorate %102 Binding 1
+               OpDecorate %103 DescriptorSet 0
+               OpDecorate %103 Binding 2
+               OpDecorate %104 DescriptorSet 0
+               OpDecorate %104 Binding 3
+               OpDecorate %105 DescriptorSet 0
+               OpDecorate %105 Binding 4
+
+      ; CHECK: OpDecorate %100 DescriptorSet 0
+      ; CHECK: OpDecorate %100 Binding 0
+      ; CHECK: OpDecorate %101 DescriptorSet 0
+      ; CHECK: OpDecorate %101 Binding 1
+      ; CHECK: OpDecorate %102 DescriptorSet 0
+      ; CHECK: OpDecorate %102 Binding 2
+      ; CHECK: OpDecorate %103 DescriptorSet 0
+      ; CHECK: OpDecorate %103 Binding 3
+      ; CHECK: OpDecorate %104 DescriptorSet 0
+      ; CHECK: OpDecorate %104 Binding 4
+      ; CHECK: OpDecorate %105 DescriptorSet 0
+      ; CHECK: OpDecorate %105 Binding 5
+
+)" + BasicTypes() + R"(
+
+        %100 = OpVariable %p_i_ty UniformConstant
+        %101 = OpVariable %p_s_ty UniformConstant
+        %102 = OpVariable %pu_st_ty Uniform
+        %103 = OpVariable %pb_st_ty StorageBuffer
+        %104 = OpVariable %p_si_ty UniformConstant
+        %105 = OpVariable %p_s_ty UniformConstant
+
+      %alpha = OpFunction %void None %voidfn
+       %1000 = OpLabel
+       %1001 = OpCopyObject %p_i_ty %100
+       %1002 = OpCopyObject %p_s_ty %101
+       %1003 = OpCopyObject %pb_st_ty %103
+       %1004 = OpCopyObject %p_si_ty %104
+       %1005 = OpCopyObject %p_s_ty %105
+               OpReturn
+               OpFunctionEnd
+
+       %beta = OpFunction %void None %voidfn
+       %2000 = OpLabel
+       %2001 = OpCopyObject %p_s_ty %101
+       %2002 = OpCopyObject %pu_st_ty %102
+       %2003 = OpCopyObject %pb_st_ty %103
+       %2004 = OpCopyObject %p_s_ty %105
+               OpReturn
+               OpFunctionEnd
+)";
+
+  auto [disasm, status] = SinglePassRunAndMatch<ResolveBindingConflictsPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+}  // namespace
+}  // namespace opt
+}  // namespace spvtools
diff --git a/test/opt/scalar_replacement_test.cpp b/test/opt/scalar_replacement_test.cpp
index 0ba285b..0256ac2 100644
--- a/test/opt/scalar_replacement_test.cpp
+++ b/test/opt/scalar_replacement_test.cpp
@@ -27,7 +27,7 @@
 
 TEST_F(ScalarReplacementPassName, Default) {
   auto srp = ScalarReplacementPass();
-  EXPECT_STREQ(srp.name(), "scalar-replacement=100");
+  EXPECT_STREQ(srp.name(), "scalar-replacement=0");
 }
 
 TEST_F(ScalarReplacementPassName, Large) {
@@ -1863,6 +1863,7 @@
 OpFunctionEnd
 )";
 
+  ValidatorOptions()->relax_logical_pointer = true;
   auto result =
       SinglePassRunAndDisassemble<ScalarReplacementPass>(text, true, true, 0);
   EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
diff --git a/test/opt/set_spec_const_default_value_test.cpp b/test/opt/set_spec_const_default_value_test.cpp
index 10f805b..ae0ddf0 100644
--- a/test/opt/set_spec_const_default_value_test.cpp
+++ b/test/opt/set_spec_const_default_value_test.cpp
@@ -70,7 +70,7 @@
         {"100:1024   \n \r\t \t \v \f ", true,
          SpecIdToValueStrMap{{100, "1024"}}},
         // 6. maximum spec id
-        {"4294967295:0", true, SpecIdToValueStrMap{{4294967295, "0"}}},
+        {"4294967295:0", true, SpecIdToValueStrMap{{4294967295u, "0"}}},
         // 7. minimum spec id
         {"0:100", true, SpecIdToValueStrMap{{0, "100"}}},
         // 8. random content without spaces are allowed
@@ -1027,6 +1027,114 @@
             "%3 = OpSpecConstant %uchar 0\n"
             "%4 = OpSpecConstant %uchar 214\n",
         },
+        // 23. ConstantData - no spec provided
+        {
+            // code
+            "OpDecorate %1 SpecId 100\n"
+            "OpDecorate %2 SpecId 101\n"
+            "%uint = OpTypeInt 32 0\n"
+            "%2 = OpSpecConstant %uint 1\n"
+            "%_arr_uint_2 = OpTypeArray %uint %2\n"
+            "%1 = OpSpecConstantDataKHR %_arr_uint_2 0\n",
+            // default values
+            SpecIdToValueBitPatternMap{{888, {0x01020304}}},
+            // expected
+            "OpDecorate %1 SpecId 100\n"
+            "OpDecorate %2 SpecId 101\n"
+            "%uint = OpTypeInt 32 0\n"
+            "%2 = OpSpecConstant %uint 1\n"
+            "%_arr_uint_2 = OpTypeArray %uint %2\n"
+            "%1 = OpSpecConstantDataKHR %_arr_uint_2 0\n",
+        },
+        // 24. ConstantData - only data - uint32
+        {
+            // code
+            "OpDecorate %1 SpecId 100\n"
+            "%uint = OpTypeInt 32 0\n"
+            "%uint_1 = OpConstant %uint 1\n"
+            "%_arr_uint_uint_1 = OpTypeArray %uint %uint_1\n"
+            "%1 = OpSpecConstantDataKHR %_arr_uint_uint_1 0\n",
+            // default values
+            SpecIdToValueBitPatternMap{{100, {0x01020304}}},
+            // expected
+            "OpDecorate %1 SpecId 100\n"
+            "%uint = OpTypeInt 32 0\n"
+            "%uint_1 = OpConstant %uint 1\n"
+            "%_arr_uint_uint_1 = OpTypeArray %uint %uint_1\n"
+            "%1 = OpSpecConstantDataKHR %_arr_uint_uint_1 16909060\n",
+        },
+        // 25. ConstantData - only data - int8
+        {
+            // code
+            "OpDecorate %1 SpecId 100\n"
+            "%char = OpTypeInt 8 1\n"
+            "%char_1 = OpConstant %char 1\n"
+            "%_arr_char_char_1 = OpTypeArray %char %char_1\n"
+            "%1 = OpSpecConstantDataKHR %_arr_char_char_1 0\n",
+            // default values
+            SpecIdToValueBitPatternMap{{100, {0x01020304}}},
+            // expected
+            "OpDecorate %1 SpecId 100\n"
+            "%char = OpTypeInt 8 1\n"
+            "%char_1 = OpConstant %char 1\n"
+            "%_arr_char_char_1 = OpTypeArray %char %char_1\n"
+            "%1 = OpSpecConstantDataKHR %_arr_char_char_1 16909060\n",
+        },
+        // 26. ConstantData - only data - uint64
+        {
+            // code
+            "OpDecorate %1 SpecId 100\n"
+            "%ulong = OpTypeInt 64 0\n"
+            "%ulong_1 = OpConstant %ulong 1\n"
+            "%_arr_ulong_ulong_1 = OpTypeArray %ulong %ulong_1\n"
+            "%1 = OpSpecConstantDataKHR %_arr_ulong_ulong_1 0 0\n",
+            // default values
+            SpecIdToValueBitPatternMap{{100, {0x01020304, 0x1}}},
+            // expected
+            "OpDecorate %1 SpecId 100\n"
+            "%ulong = OpTypeInt 64 0\n"
+            "%ulong_1 = OpConstant %ulong 1\n"
+            "%_arr_ulong_ulong_1 = OpTypeArray %ulong %ulong_1\n"
+            "%1 = OpSpecConstantDataKHR %_arr_ulong_ulong_1 16909060 1\n",
+        },
+        // 27. ConstantData - expand length - uint32
+        {
+            // code
+            "OpDecorate %1 SpecId 100\n"
+            "OpDecorate %2 SpecId 101\n"
+            "%uint = OpTypeInt 32 0\n"
+            "%2 = OpSpecConstant %uint 1\n"
+            "%_arr_uint_2 = OpTypeArray %uint %2\n"
+            "%1 = OpSpecConstantDataKHR %_arr_uint_2 0\n",
+            // default values
+            SpecIdToValueBitPatternMap{{100, {0x1, 0x2, 0x3, 0x4}}, {101, {4}}},
+            // expected
+            "OpDecorate %1 SpecId 100\n"
+            "OpDecorate %2 SpecId 101\n"
+            "%uint = OpTypeInt 32 0\n"
+            "%2 = OpSpecConstant %uint 4\n"
+            "%_arr_uint_2 = OpTypeArray %uint %2\n"
+            "%1 = OpSpecConstantDataKHR %_arr_uint_2 1 2 3 4\n",
+        },
+        // 28. ConstantData - shrink length - uint32
+        {
+            // code
+            "OpDecorate %1 SpecId 100\n"
+            "OpDecorate %2 SpecId 101\n"
+            "%uint = OpTypeInt 32 0\n"
+            "%2 = OpSpecConstant %uint 4\n"
+            "%_arr_uint_2 = OpTypeArray %uint %2\n"
+            "%1 = OpSpecConstantDataKHR %_arr_uint_2 1 2 3 4\n",
+            // default values
+            SpecIdToValueBitPatternMap{{100, {0x5, 0x6}}, {101, {2}}},
+            // expected
+            "OpDecorate %1 SpecId 100\n"
+            "OpDecorate %2 SpecId 101\n"
+            "%uint = OpTypeInt 32 0\n"
+            "%2 = OpSpecConstant %uint 2\n"
+            "%_arr_uint_2 = OpTypeArray %uint %2\n"
+            "%1 = OpSpecConstantDataKHR %_arr_uint_2 5 6\n",
+        },
     }));
 
 INSTANTIATE_TEST_SUITE_P(
@@ -1162,6 +1270,40 @@
             "%2 = OpSpecConstant %ulong 200\n"
             "%3 = OpSpecConstant %double 3.141592653\n",
         },
+        // 7. ConstantData - expand length
+        {
+            // code
+            "OpDecorate %1 SpecId 100\n"
+            "%uint = OpTypeInt 32 0\n"
+            "%uint_1 = OpConstant %uint 1\n"
+            "%_arr_uint_uint_1 = OpTypeArray %uint %uint_1\n"
+            "%1 = OpSpecConstantDataKHR %_arr_uint_uint_1 0\n",
+            // default values
+            SpecIdToValueBitPatternMap{{100, {0x01020304, 0x0, 0x1, 0x2}}},
+            // expected
+            "OpDecorate %1 SpecId 100\n"
+            "%uint = OpTypeInt 32 0\n"
+            "%uint_1 = OpConstant %uint 1\n"
+            "%_arr_uint_uint_1 = OpTypeArray %uint %uint_1\n"
+            "%1 = OpSpecConstantDataKHR %_arr_uint_uint_1 16909060 0 1 2\n",
+        },
+        // 8. ConstantData - shrink length
+        {
+            // code
+            "OpDecorate %1 SpecId 100\n"
+            "%uint = OpTypeInt 32 0\n"
+            "%uint_4 = OpConstant %uint 4\n"
+            "%_arr_uint_uint_4 = OpTypeArray %uint %uint_4\n"
+            "%1 = OpSpecConstantDataKHR %_arr_uint_uint_4 0 1 2 3\n",
+            // default values
+            SpecIdToValueBitPatternMap{{100, {4, 5}}},
+            // expected
+            "OpDecorate %1 SpecId 100\n"
+            "%uint = OpTypeInt 32 0\n"
+            "%uint_4 = OpConstant %uint 4\n"
+            "%_arr_uint_uint_4 = OpTypeArray %uint %uint_4\n"
+            "%1 = OpSpecConstantDataKHR %_arr_uint_uint_4 4 5\n",
+        },
     }));
 
 }  // namespace
diff --git a/test/opt/split_combined_image_sampler_pass_test.cpp b/test/opt/split_combined_image_sampler_pass_test.cpp
new file mode 100644
index 0000000..d6ad6bb
--- /dev/null
+++ b/test/opt/split_combined_image_sampler_pass_test.cpp
@@ -0,0 +1,1933 @@
+// Copyright (c) 2025 Google LLC
+//
+// 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 <array>
+#include <iostream>
+#include <ostream>
+
+#include "spirv-tools/optimizer.hpp"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+struct SplitCombinedImageSamplerPassTest : public PassTest<::testing::Test> {
+  virtual void SetUp() override {
+    SetTargetEnv(SPV_ENV_VULKAN_1_0);
+    SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+    SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES |
+                          SPV_BINARY_TO_TEXT_OPTION_INDENT |
+                          SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  }
+};
+
+struct TypeCase {
+  const char* glsl_type;
+  const char* image_type_decl;
+};
+std::ostream& operator<<(std::ostream& os, const TypeCase& tc) {
+  os << tc.glsl_type;
+  return os;
+}
+
+struct SplitCombinedImageSamplerPassTypeCaseTest
+    : public PassTest<::testing::TestWithParam<TypeCase>> {
+  virtual void SetUp() override {
+    SetTargetEnv(SPV_ENV_VULKAN_1_0);
+    SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+    SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES |
+                          SPV_BINARY_TO_TEXT_OPTION_INDENT |
+                          SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  }
+};
+
+std::vector<TypeCase> ImageTypeCases() {
+  return std::vector<TypeCase>{
+      {"sampler2D", "OpTypeImage %float 2D 0 0 0 1 Unknown"},
+      {"sampler2DShadow", "OpTypeImage %float 2D 1 0 0 1 Unknown"},
+      {"sampler2DArray", "OpTypeImage %float 2D 0 1 0 1 Unknown"},
+      {"sampler2DArrayShadow", "OpTypeImage %float 2D 1 1 0 1 Unknown"},
+      {"sampler2DMS", "OpTypeImage %float 2D 0 0 1 1 Unknown"},
+      {"sampler2DMSArray", "OpTypeImage %float 2D 0 1 1 1 Unknown"},
+      {"sampler3D", "OpTypeImage %float 3D 0 0 0 1 Unknown"},
+      {"samplerCube", "OpTypeImage %float Cube 0 0 0 1 Unknown"},
+      {"samplerCubeShadow", "OpTypeImage %float Cube 1 0 0 1 Unknown"},
+      {"samplerCubeArray", "OpTypeImage %float Cube 0 1 0 1 Unknown"},
+      {"samplerCubeArrayShadow", "OpTypeImage %float Cube 1 1 0 1 Unknown"},
+      {"isampler2D", "OpTypeImage %int 2D 0 0 0 1 Unknown"},
+      {"isampler2DShadow", "OpTypeImage %int 2D 1 0 0 1 Unknown"},
+      {"isampler2DArray", "OpTypeImage %int 2D 0 1 0 1 Unknown"},
+      {"isampler2DArrayShadow", "OpTypeImage %int 2D 1 1 0 1 Unknown"},
+      {"isampler2DMS", "OpTypeImage %int 2D 0 0 1 1 Unknown"},
+      {"isampler2DMSArray", "OpTypeImage %int 2D 0 1 1 1 Unknown"},
+      {"isampler3D", "OpTypeImage %int 3D 0 0 0 1 Unknown"},
+      {"isamplerCube", "OpTypeImage %int Cube 0 0 0 1 Unknown"},
+      {"isamplerCubeShadow", "OpTypeImage %int Cube 1 0 0 1 Unknown"},
+      {"isamplerCubeArray", "OpTypeImage %int Cube 0 1 0 1 Unknown"},
+      {"isamplerCubeArrayShadow", "OpTypeImage %int Cube 1 1 0 1 Unknown"},
+      {"usampler2D", "OpTypeImage %uint 2D 0 0 0 1 Unknown"},
+      {"usampler2DShadow", "OpTypeImage %uint 2D 1 0 0 1 Unknown"},
+      {"usampler2DArray", "OpTypeImage %uint 2D 0 1 0 1 Unknown"},
+      {"usampler2DArrayShadow", "OpTypeImage %uint 2D 1 1 0 1 Unknown"},
+      {"usampler2DMS", "OpTypeImage %uint 2D 0 0 1 1 Unknown"},
+      {"usampler2DMSArray", "OpTypeImage %uint 2D 0 1 1 1 Unknown"},
+      {"usampler3D", "OpTypeImage %uint 3D 0 0 0 1 Unknown"},
+      {"usamplerCube", "OpTypeImage %uint Cube 0 0 0 1 Unknown"},
+      {"usamplerCubeShadow", "OpTypeImage %uint Cube 1 0 0 1 Unknown"},
+      {"usamplerCubeArray", "OpTypeImage %uint Cube 0 1 0 1 Unknown"},
+      {"usamplerCubeArrayShadow", "OpTypeImage %uint Cube 1 1 0 1 Unknown"},
+  };
+}
+
+std::string Preamble(const std::string shader_interface = "") {
+  return R"(               OpCapability Shader
+               OpCapability RuntimeDescriptorArray
+               OpExtension "SPV_EXT_descriptor_indexing"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main")" +
+         shader_interface + R"(
+               OpExecutionMode %main LocalSize 1 1 1
+               OpName %main "main"
+               OpName %main_0 "main_0"
+               OpName %voidfn "voidfn"
+)";
+}
+
+std::string PreambleFragment(const std::string shader_interface = "") {
+  return R"(               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main")" +
+         shader_interface + R"(
+               OpExecutionMode %main OriginUpperLeft
+               OpName %main "main"
+               OpName %main_0 "main_0"
+               OpName %voidfn "voidfn"
+)";
+}
+
+std::string BasicTypes() {
+  return R"(      %float = OpTypeFloat 32
+       %uint = OpTypeInt 32 0
+        %int = OpTypeInt 32 1
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+     %uint_3 = OpConstant %uint 3
+    %float_0 = OpConstant %float 0
+    %v2float = OpTypeVector %float 2
+    %v3float = OpTypeVector %float 3
+    %v4float = OpTypeVector %float 4
+         %13 = OpConstantNull %v2float
+         %14 = OpConstantNull %v3float
+         %15 = OpConstantNull %v4float
+       %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
+)";
+}
+std::string Main() {
+  return R"(
+       %main = OpFunction %void None %voidfn
+     %main_0 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+}
+std::string NoCheck() { return "; CHECK-NOT: nothing to see"; }
+
+TEST_F(SplitCombinedImageSamplerPassTest, SamplerOnly_NoChange) {
+  const std::string kTest = Preamble() +
+                            R"(               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+)" + BasicTypes() + R"(         %10 = OpTypeSampler
+%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10
+        %100 = OpVariable %_ptr_UniformConstant_10 UniformConstant
+       %main = OpFunction %void None %voidfn
+     %main_0 = OpLabel
+          %6 = OpLoad %10 %100
+               OpReturn
+               OpFunctionEnd
+)";
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest + NoCheck(), /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithoutChange)
+      << "status" << kTest << "\n -> \n"
+      << disasm;
+  EXPECT_EQ(disasm, kTest) << "disasm";
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest, ImageOnly_NoChange) {
+  const std::string kTest = Preamble() +
+                            R"(               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+)" + BasicTypes() + R"(         %10 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10
+        %100 = OpVariable %_ptr_UniformConstant_10 UniformConstant
+       %main = OpFunction %void None %voidfn
+     %main_0 = OpLabel
+          %6 = OpLoad %10 %100
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SCOPED_TRACE("image only");
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest + NoCheck(), /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithoutChange);
+  EXPECT_EQ(disasm, kTest);
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest, PtrSampledImageOnly_DeletesPtrType) {
+  const std::string kTest = Preamble() + BasicTypes() + R"(
+  ; CHECK: OpCapability Shader
+  ; CHECK-NOT: OpTypePointer UniformConstant
+  ; CHECK: OpFunction %void
+        %100 = OpTypeImage %float 2D 0 0 0 1 Unknown
+        %101 = OpTypeSampledImage %100
+        %102 = OpTypePointer UniformConstant %101
+       %main = OpFunction %void None %voidfn
+     %main_0 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest + NoCheck(), /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << "status";
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest,
+       PtrArraySampledImageOnly_DeletesPtrType) {
+  const std::string kTest = Preamble() + BasicTypes() + R"(
+  ; CHECK: OpCapability Shader
+  ; CHECK-NOT: OpTypePointer UniformConstant
+  ; CHECK: OpFunction %void
+        %100 = OpTypeImage %float 2D 0 0 0 1 Unknown
+        %101 = OpTypeSampledImage %100
+        %103 = OpTypeArray %101 %uint_1
+        %104 = OpTypePointer UniformConstant %103
+       %main = OpFunction %void None %voidfn
+     %main_0 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest + NoCheck(), /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << "status";
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest,
+       PtrRtArraySampledImageOnly_DeletesPtrType) {
+  const std::string kTest = Preamble() + BasicTypes() + R"(
+  ; CHECK: OpCapability Shader
+  ; CHECK-NOT: OpTypePointer UniformConstant
+  ; CHECK: OpFunction %void
+        %100 = OpTypeImage %float 2D 0 0 0 1 Unknown
+        %101 = OpTypeSampledImage %100
+        %103 = OpTypeRuntimeArray %101
+        %104 = OpTypePointer UniformConstant %103
+       %main = OpFunction %void None %voidfn
+     %main_0 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest + NoCheck(), /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << "status";
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest,
+       Combined_NoSampler_CreatedBeforeSampledImage) {
+  // No OpTypeSampler to begin with.
+  const std::string kTest = Preamble() +
+                            R"(               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+
+     ; A sampler type is created and placed at the start of types.
+     ; CHECK: OpDecorate %{{\d+}} Binding 0
+     ; CHECK: OpDecorate %{{\d+}} Binding 0
+     ; CHECK-NOT: TypeSampledImage
+     ; CHECK: TypeSampler
+     ; CHECK: TypeSampledImage
+
+)" + BasicTypes() + R"( %10 = OpTypeImage %float 2D 0 0 0 1 Unknown
+         %11 = OpTypeSampledImage %10
+%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
+
+        %100 = OpVariable %_ptr_UniformConstant_11 UniformConstant
+       %main = OpFunction %void None %voidfn
+     %main_0 = OpLabel
+          %6 = OpLoad %11 %100
+               OpReturn
+               OpFunctionEnd
+)";
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest, Combined_SynthesizeVarNames) {
+  // Also tests binding info is copied to both variables.
+  const std::string kTest = Preamble() +
+                            R"(
+               OpName %orig_var "orig_var"
+               OpDecorate %orig_var DescriptorSet 0
+               OpDecorate %orig_var Binding 0
+
+     ; The combined image variable is replaced by an image variable and a sampler variable.
+
+     ; CHECK: OpCapability
+     ; The original name is deleted
+     ; CHECK-NOT: OpName %orig_var "
+     ; CHECK: OpName %orig_var_image "orig_var_image"
+     ; CHECK: OpName %orig_var_sampler "orig_var_sampler"
+     ; CHECK-NOT: OpName %orig_var "
+
+     ; CHECK: OpDecorate %orig_var_image DescriptorSet 0
+     ; CHECK: OpDecorate %orig_var_sampler DescriptorSet 0
+     ; CHECK: OpDecorate %orig_var_image Binding 0
+     ; CHECK: OpDecorate %orig_var_sampler Binding 0
+
+     ; CHECK: %10 = OpTypeImage %
+     ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10
+     ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler
+     ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]]
+
+
+     ; CHECK-NOT: %orig_var = OpVariable
+     ; CHECK-DAG: %orig_var_sampler = OpVariable %[[sampler_ptr_ty]] UniformConstant
+     ; CHECK-DAG: %orig_var_image = OpVariable %[[image_ptr_ty]] UniformConstant
+     ; CHECK: = OpFunction
+
+)" + BasicTypes() + R"(
+        %10 = OpTypeImage %float 2D 0 0 0 1 Unknown
+         %11 = OpTypeSampledImage %10
+%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
+
+   %orig_var = OpVariable %_ptr_UniformConstant_11 UniformConstant
+       %main = OpFunction %void None %voidfn
+     %main_0 = OpLabel
+        %101 = OpLoad %11 %orig_var
+               OpReturn
+               OpFunctionEnd
+)";
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_P(SplitCombinedImageSamplerPassTypeCaseTest, Combined_RemapLoad) {
+  // Also tests binding info is copied to both variables.
+  const std::string kTest = Preamble() +
+                            R"(
+               OpName %combined "combined"
+               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+
+     ; CHECK: OpName
+     ; CHECK-NOT: OpDecorate %100
+     ; CHECK: OpDecorate %[[image_var:\d+]] DescriptorSet 0
+     ; CHECK: OpDecorate %[[sampler_var:\d+]] DescriptorSet 0
+     ; CHECK: OpDecorate %[[image_var]] Binding 0
+     ; CHECK: OpDecorate %[[sampler_var]] Binding 0
+
+     ; CHECK: %10 = OpTypeImage %
+     ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10
+     ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler
+     ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]]
+
+     ; The combined image variable is replaced by an image variable and a sampler variable.
+
+     ; CHECK-NOT: %100 = OpVariable
+     ; CHECK-DAG: %[[sampler_var]] = OpVariable %[[sampler_ptr_ty]] UniformConstant
+     ; CHECK-DAG: %[[image_var]] = OpVariable %[[image_ptr_ty]] UniformConstant
+     ; CHECK: = OpFunction
+
+     ; The load of the combined image+sampler is replaced by a two loads, then
+     ; a combination operation.
+     ; CHECK: %[[im:\d+]] = OpLoad %10 %[[image_var]]
+     ; CHECK: %[[s:\d+]] = OpLoad %[[sampler_ty]] %[[sampler_var]]
+     ; CHECK: %combined = OpSampledImage %11 %[[im]] %[[s]]
+
+)" + BasicTypes() +
+                            " %10 = " + GetParam().image_type_decl + R"(
+         %11 = OpTypeSampledImage %10
+%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
+
+        %100 = OpVariable %_ptr_UniformConstant_11 UniformConstant
+       %main = OpFunction %void None %voidfn
+     %main_0 = OpLabel
+   %combined = OpLoad %11 %100
+
+     ; Uses of the combined image sampler are preserved.
+     ; CHECK: OpCopyObject %11 %combined
+
+          %7 = OpCopyObject %11 %combined
+               OpReturn
+               OpFunctionEnd
+)";
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_P(SplitCombinedImageSamplerPassTypeCaseTest,
+       Combined_RemapLoad_RelaxedPrecisionOnVarCopied) {
+  // All decorations on the variable are copied. In this case, RelaxedPrecision
+  const std::string kTest = Preamble() +
+                            R"(
+               OpName %combined "combined"
+               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+               OpDecorate %100 RelaxedPrecision
+
+     ; CHECK: OpName
+     ; CHECK-NOT: OpDecorate %100
+     ; CHECK: OpDecorate %[[image_var:\d+]] DescriptorSet 0
+     ; CHECK: OpDecorate %[[sampler_var:\d+]] DescriptorSet 0
+     ; CHECK: OpDecorate %[[image_var]] Binding 0
+     ; CHECK: OpDecorate %[[sampler_var]] Binding 0
+     ; CHECK: OpDecorate %[[image_var:\d+]] RelaxedPrecision
+     ; CHECK: OpDecorate %[[sampler_var:\d+]] RelaxedPrecision
+
+     ; CHECK: %10 = OpTypeImage %
+     ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10
+     ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler
+     ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]]
+
+     ; The combined image variable is replaced by an image variable and a sampler variable.
+
+     ; CHECK-NOT: %100 = OpVariable
+     ; CHECK-NOT: OpVariable _ptr_UniformConstant_11
+     ; CHECK-DAG: %[[sampler_var]] = OpVariable %[[sampler_ptr_ty]] UniformConstant
+     ; CHECK-DAG: %[[image_var]] = OpVariable %[[image_ptr_ty]] UniformConstant
+     ; CHECK: = OpFunction
+
+     ; The load of the combined image+sampler is replaced by a two loads, then
+     ; a combination operation.
+     ; CHECK: %[[im:\d+]] = OpLoad %10 %[[image_var]]
+     ; CHECK: %[[s:\d+]] = OpLoad %[[sampler_ty]] %[[sampler_var]]
+     ; CHECK: %combined = OpSampledImage %11 %[[im]] %[[s]]
+
+               %bool = OpTypeBool ; location marker
+)" + BasicTypes() +
+                            " %10 = " + GetParam().image_type_decl + R"(
+         %11 = OpTypeSampledImage %10
+%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
+
+        %100 = OpVariable %_ptr_UniformConstant_11 UniformConstant
+       %main = OpFunction %void None %voidfn
+     %main_0 = OpLabel
+   %combined = OpLoad %11 %100
+
+     ; Uses of the combined image sampler are preserved.
+     ; CHECK: OpCopyObject %11 %combined
+
+          %7 = OpCopyObject %11 %combined
+               OpReturn
+               OpFunctionEnd
+)";
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_P(SplitCombinedImageSamplerPassTypeCaseTest,
+       Combined_RemapLoad_RelaxedPrecisionOnLoadCopied) {
+  // Copy decorations form an OpLoad that is replaced.
+  const std::string kTest = Preamble() +
+                            R"(
+               OpName %combined "combined"
+               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+               OpDecorate %combined RelaxedPrecision
+
+     ; CHECK: OpName
+     ; CHECK-NOT: OpDecorate %100
+     ; CHECK: OpDecorate %[[image_var:\d+]] DescriptorSet 0
+     ; CHECK: OpDecorate %[[sampler_var:\d+]] DescriptorSet 0
+     ; CHECK: OpDecorate %[[image_var]] Binding 0
+     ; CHECK: OpDecorate %[[sampler_var]] Binding 0
+
+     ; This is what we are checking in this test.
+     ; CHECK: OpDecorate %[[im:\d+]] RelaxedPrecision
+     ; CHECK: OpDecorate %[[s:\d+]] RelaxedPrecision
+
+     ; CHECK: %10 = OpTypeImage %
+     ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10
+     ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler
+     ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]]
+
+     ; The combined image variable is replaced by an image variable and a sampler variable.
+
+     ; CHECK-NOT: %100 = OpVariable
+     ; CHECK-NOT: OpVariable _ptr_UniformConstant_11
+     ; CHECK-DAG: %[[sampler_var]] = OpVariable %[[sampler_ptr_ty]] UniformConstant
+     ; CHECK-DAG: %[[image_var]] = OpVariable %[[image_ptr_ty]] UniformConstant
+     ; CHECK: = OpFunction
+
+     ; The load of the combined image+sampler is replaced by a two loads, then
+     ; a combination operation. The new loads get the same decorations that the
+     ; original load had.
+     ; CHECK: %[[im]] = OpLoad %10 %[[image_var]]
+     ; CHECK: %[[s]] = OpLoad %[[sampler_ty]] %[[sampler_var]]
+     ; CHECK: %combined = OpSampledImage %11 %[[im]] %[[s]]
+
+               %bool = OpTypeBool ; location marker
+)" + BasicTypes() +
+                            " %10 = " + GetParam().image_type_decl + R"(
+         %11 = OpTypeSampledImage %10
+%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
+
+        %100 = OpVariable %_ptr_UniformConstant_11 UniformConstant
+       %main = OpFunction %void None %voidfn
+     %main_0 = OpLabel
+   %combined = OpLoad %11 %100
+
+     ; Uses of the combined image sampler are preserved.
+     ; CHECK: OpCopyObject %11 %combined
+
+          %7 = OpCopyObject %11 %combined
+               OpReturn
+               OpFunctionEnd
+)";
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_P(SplitCombinedImageSamplerPassTypeCaseTest,
+       Combined_DeletesCopyObjectOfPtr) {
+  // OpCopyObject is deleted, and its uses updated.
+  const std::string kTest = Preamble() +
+                            R"(
+               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+
+     ; CHECK: OpName
+     ; CHECK-NOT: OpDecorate %100
+     ; CHECK: OpDecorate %[[image_var:\d+]] DescriptorSet 0
+     ; CHECK: OpDecorate %[[sampler_var:\d+]] DescriptorSet 0
+     ; CHECK: OpDecorate %[[image_var]] Binding 0
+     ; CHECK: OpDecorate %[[sampler_var]] Binding 0
+
+     ; CHECK: %10 = OpTypeImage %
+     ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10
+     ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler
+     ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]]
+
+     ; The combined image variable is replaced by an image variable and a sampler variable.
+
+     ; CHECK-NOT: %100 = OpVariable
+     ; CHECK-DAG: %[[sampler_var]] = OpVariable %[[sampler_ptr_ty]] UniformConstant
+     ; CHECK-DAG: %[[image_var]] = OpVariable %[[image_ptr_ty]] UniformConstant
+     ; CHECK: = OpFunction
+
+
+)" + BasicTypes() +
+                            " %10 = " + GetParam().image_type_decl + R"(
+         %11 = OpTypeSampledImage %10
+%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
+
+        %100 = OpVariable %_ptr_UniformConstant_11 UniformConstant
+       %main = OpFunction %void None %voidfn
+     %main_0 = OpLabel
+        %101 = OpCopyObject %_ptr_UniformConstant_11 %100
+        %102 = OpLoad %11 %101
+        %103 = OpCopyObject %_ptr_UniformConstant_11 %101
+        %104 = OpCopyObject %11 %102 ;; this copy survives
+               OpReturn
+               OpFunctionEnd
+
+     ; The OpCopyObject instructions are removed.
+     ; The load of the combined image+sampler is replaced by a two loads, then
+     ; a combination operation. The only OpCopyObject that remains is the copy
+     ; of the copy of the sampled image value.
+     ; CHECK: %[[im:\d+]] = OpLoad %10 %[[image_var]]
+     ; CHECK: %[[s:\d+]] = OpLoad %[[sampler_ty]] %[[sampler_var]]
+     ; CHECK: %[[si:\d+]] = OpSampledImage %11 %[[im]] %[[s]]
+     ; CHECK-NEXT: OpCopyObject %11 %[[si]]
+     ; CHECK-NEXT: OpReturn
+)";
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_P(SplitCombinedImageSamplerPassTypeCaseTest, ArrayCombined_RemapLoad) {
+  const std::string kTest = Preamble() +
+                            R"(
+               OpName %combined "combined"
+               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+
+     ; CHECK: OpName
+     ; CHECK-NOT: OpDecorate %100
+     ; CHECK: OpDecorate %[[image_var:\d+]] DescriptorSet 0
+     ; CHECK: OpDecorate %[[sampler_var:\d+]] DescriptorSet 0
+     ; CHECK: OpDecorate %[[image_var]] Binding 0
+     ; CHECK: OpDecorate %[[sampler_var]] Binding 0
+
+     ; CHECK: %10 = OpTypeImage %
+     ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10
+     ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler
+     ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]]
+
+     ; The combined image variable is replaced by an image variable and a sampler variable.
+
+     ; CHECK: %[[array_image_ty:\w+]] = OpTypeArray %10 %uint_3
+     ; CHECK: %[[ptr_array_image_ty:\w+]] = OpTypePointer UniformConstant %[[array_image_ty]]
+
+     ; CHECK: %[[array_sampler_ty:\w+]] = OpTypeArray %[[sampler_ty]] %uint_3
+     ; CHECK: %[[ptr_array_sampler_ty:\w+]] = OpTypePointer UniformConstant %[[array_sampler_ty]]
+
+     ; CHECK-NOT: %100 = OpVariable
+     ; CHECK-DAG: %[[sampler_var]] = OpVariable %[[ptr_array_sampler_ty]] UniformConstant
+     ; CHECK-DAG: %[[image_var]] = OpVariable %[[ptr_array_image_ty]] UniformConstant
+     ; CHECK: = OpFunction
+
+     ; The access chain and load is replaced by two access chains, two loads, then
+     ; a combine operation.
+     ; CHECK: %[[ptr_im:\d+]] = OpAccessChain %[[image_ptr_ty]] %[[image_var]] %uint_1
+     ; CHECK: %[[ptr_s:\d+]] = OpAccessChain %[[sampler_ptr_ty]] %[[sampler_var]] %uint_1
+     ; CHECK: %[[im:\d+]] = OpLoad %10 %[[ptr_im]]
+     ; CHECK: %[[s:\d+]] = OpLoad %[[sampler_ty]] %[[ptr_s]]
+     ; CHECK: %combined = OpSampledImage %11 %[[im]] %[[s]]
+
+)" + BasicTypes() +
+                            " %10 = " + GetParam().image_type_decl + R"(
+         %11 = OpTypeSampledImage %10
+%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
+         %12 = OpTypeArray %11 %uint_3
+%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12
+
+        %100 = OpVariable %_ptr_UniformConstant_12 UniformConstant
+       %main = OpFunction %void None %voidfn
+     %main_0 = OpLabel
+        %ptr = OpAccessChain %_ptr_UniformConstant_11 %100 %uint_1
+   %combined = OpLoad %11 %ptr
+
+     ; Uses of the combined image sampler are preserved.
+     ; CHECK: OpCopyObject %11 %combined
+
+          %7 = OpCopyObject %11 %combined
+               OpReturn
+               OpFunctionEnd
+)";
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_P(SplitCombinedImageSamplerPassTypeCaseTest, RtArrayCombined_RemapLoad) {
+  const std::string kTest = Preamble() +
+                            R"(
+               OpName %combined "combined"
+               OpDecorate %100 DescriptorSet 0
+               OpDecorate %100 Binding 0
+
+     ; CHECK: OpName
+     ; CHECK-NOT: OpDecorate %100
+     ; CHECK: OpDecorate %[[image_var:\d+]] DescriptorSet 0
+     ; CHECK: OpDecorate %[[sampler_var:\d+]] DescriptorSet 0
+     ; CHECK: OpDecorate %[[image_var]] Binding 0
+     ; CHECK: OpDecorate %[[sampler_var]] Binding 0
+
+     ; CHECK: %10 = OpTypeImage %
+     ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10
+     ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler
+     ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]]
+
+     ; The combined image variable is replaced by an image variable and a sampler variable.
+
+     ; CHECK: %[[array_image_ty:\w+]] = OpTypeRuntimeArray %10
+     ; CHECK: %[[ptr_array_image_ty:\w+]] = OpTypePointer UniformConstant %[[array_image_ty]]
+
+     ; CHECK: %[[array_sampler_ty:\w+]] = OpTypeRuntimeArray %[[sampler_ty]]
+     ; CHECK: %[[ptr_array_sampler_ty:\w+]] = OpTypePointer UniformConstant %[[array_sampler_ty]]
+
+     ; CHECK-NOT: %100 = OpVariable
+     ; CHECK-DAG: %[[sampler_var]] = OpVariable %[[ptr_array_sampler_ty]] UniformConstant
+     ; CHECK-DAG: %[[image_var]] = OpVariable %[[ptr_array_image_ty]] UniformConstant
+     ; CHECK: = OpFunction
+
+     ; The access chain and load is replaced by two access chains, two loads, then
+     ; a combine operation.
+     ; CHECK: %[[ptr_im:\d+]] = OpAccessChain %[[image_ptr_ty]] %[[image_var]] %uint_1
+     ; CHECK: %[[ptr_s:\d+]] = OpAccessChain %[[sampler_ptr_ty]] %[[sampler_var]] %uint_1
+     ; CHECK: %[[im:\d+]] = OpLoad %10 %[[ptr_im]]
+     ; CHECK: %[[s:\d+]] = OpLoad %[[sampler_ty]] %[[ptr_s]]
+     ; CHECK: %combined = OpSampledImage %11 %[[im]] %[[s]]
+
+)" + BasicTypes() +
+                            " %10 = " + GetParam().image_type_decl + R"(
+         %11 = OpTypeSampledImage %10
+%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
+         %12 = OpTypeRuntimeArray %11
+%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12
+
+        %100 = OpVariable %_ptr_UniformConstant_12 UniformConstant
+       %main = OpFunction %void None %voidfn
+     %main_0 = OpLabel
+        %ptr = OpAccessChain %_ptr_UniformConstant_11 %100 %uint_1
+   %combined = OpLoad %11 %ptr
+
+     ; Uses of the combined image sampler are preserved.
+     ; CHECK: OpCopyObject %11 %combined
+
+          %7 = OpCopyObject %11 %combined
+               OpReturn
+               OpFunctionEnd
+)";
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+INSTANTIATE_TEST_SUITE_P(AllCombinedTypes,
+                         SplitCombinedImageSamplerPassTypeCaseTest,
+                         ::testing::ValuesIn(ImageTypeCases()));
+
+// Remap entry point
+
+struct EntryPointRemapCase {
+  const spv_target_env environment = SPV_ENV_VULKAN_1_0;
+  const char* initial_interface = "";
+  const char* expected_interface = nullptr;
+};
+
+std::ostream& operator<<(std::ostream& os, const EntryPointRemapCase& eprc) {
+  os << "(env " << spvLogStringForEnv(eprc.environment) << ", init "
+     << eprc.initial_interface << " -> expect " << eprc.expected_interface
+     << ")";
+  return os;
+}
+
+struct SplitCombinedImageSamplerPassEntryPointRemapTest
+    : public PassTest<::testing::TestWithParam<EntryPointRemapCase>> {
+  virtual void SetUp() override {
+    SetTargetEnv(GetParam().environment);
+    SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+    SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES |
+                          SPV_BINARY_TO_TEXT_OPTION_INDENT |
+                          SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  }
+};
+
+std::vector<EntryPointRemapCase> EntryPointInterfaceCases() {
+  return std::vector<EntryPointRemapCase>{
+      {SPV_ENV_VULKAN_1_0, " %in_var %out_var", " %in_var %out_var"},
+      {SPV_ENV_VULKAN_1_4, " %combined_var",
+       " %combined_var_image %combined_var_sampler"},
+      {SPV_ENV_VULKAN_1_4, " %combined_var %in_var %out_var",
+       " %combined_var_image %in_var %out_var %combined_var_sampler"},
+      {SPV_ENV_VULKAN_1_4, " %in_var %combined_var %out_var",
+       " %in_var %combined_var_image %out_var %combined_var_sampler"},
+      {SPV_ENV_VULKAN_1_4, " %in_var %out_var %combined_var",
+       " %in_var %out_var %combined_var_image %combined_var_sampler"},
+  };
+}
+
+TEST_P(SplitCombinedImageSamplerPassEntryPointRemapTest,
+       EntryPoint_Combined_UsedInShader) {
+  const std::string kTest = PreambleFragment(GetParam().initial_interface) +
+                            R"(
+               OpName %combined "combined"
+               OpName %combined_var "combined_var"
+               OpName %in_var "in_var"
+               OpName %out_var "out_var"
+               OpDecorate %combined_var DescriptorSet 0
+               OpDecorate %combined_var Binding 0
+               OpDecorate %in_var BuiltIn FragCoord
+               OpDecorate %out_var Location 0
+
+; CHECK: OpEntryPoint Fragment %main "main")" +
+                            GetParam().expected_interface + R"(
+; These clauses ensure the expected interface is the whole interface.
+; CHECK-NOT: %{{\d+}}
+; CHECK-NOT: %in_var
+; CHECK-NOT: %out_var
+; CHECK-NOT: %combined_var
+; CHECK: OpExecutionMode %main OriginUpperLeft
+
+     ; Check the var names, tracing up through the types.
+     ; CHECK: %10 = OpTypeImage %float 2D 0 0 0 1 Unknown
+     ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10
+     ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler
+     ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]]
+     ; The combined image variable is replaced by an image variable and a sampler variable.
+     ; CHECK-DAG: %combined_var_sampler = OpVariable %[[sampler_ptr_ty]] UniformConstant
+     ; CHECK-DAG: %combined_var_image = OpVariable %[[image_ptr_ty]] UniformConstant
+     ; CHECK: = OpFunction
+
+               %bool = OpTypeBool
+)" + BasicTypes() + R"(         %10 = OpTypeImage %float 2D 0 0 0 1 Unknown
+         %11 = OpTypeSampledImage %10
+%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
+     %in_ptr_v4f = OpTypePointer Input %v4float
+     %in_var = OpVariable %in_ptr_v4f Input
+    %out_ptr_v4f = OpTypePointer Output %v4float
+    %out_var = OpVariable %out_ptr_v4f Output
+
+%combined_var = OpVariable %_ptr_UniformConstant_11 UniformConstant
+       %main = OpFunction %void None %voidfn
+       ;CHECK:  %main_0 = OpLabel
+       ;CHECK: OpLoad
+
+     %main_0 = OpLabel
+   %combined = OpLoad %11 %combined_var
+               OpReturn
+               OpFunctionEnd
+)";
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_P(SplitCombinedImageSamplerPassEntryPointRemapTest,
+       EntryPoint_Combined_UsedOnlyInEntryPointInstruction) {
+  // If the combined var is in the interface, that is enough to trigger
+  // its replacement. Otherwise the entry point interface is untouched
+  // when the combined var is not otherwise used.
+  const bool combined_var_in_interface =
+      std::string(GetParam().initial_interface).find("%combined_var") !=
+      std::string::npos;
+  if (combined_var_in_interface) {
+    const std::string kTest = PreambleFragment(GetParam().initial_interface) +
+                              R"(
+                 OpName %combined_var "combined_var"
+                 OpName %in_var "in_var"
+                 OpName %out_var "out_var"
+                 OpDecorate %combined_var DescriptorSet 0
+                 OpDecorate %combined_var Binding 0
+                 OpDecorate %in_var BuiltIn FragCoord
+                 OpDecorate %out_var Location 0
+
+  ; CHECK: OpEntryPoint Fragment %main "main")" +
+                              GetParam().expected_interface + R"(
+  ; These clauses ensure the expected interface is the whole interface.
+  ; CHECK-NOT: %{{\d+}}
+  ; CHECK-NOT: %in_var
+  ; CHECK-NOT: %out_var
+  ; CHECK-NOT: %combined_var
+  ; CHECK: OpExecutionMode %main OriginUpperLeft
+
+                 %bool = OpTypeBool
+  )" + BasicTypes() + R"(         %10 = OpTypeImage %float 2D 0 0 0 1 Unknown
+           %11 = OpTypeSampledImage %10
+  %_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
+       %in_ptr_v4f = OpTypePointer Input %v4float
+       %in_var = OpVariable %in_ptr_v4f Input
+      %out_ptr_v4f = OpTypePointer Output %v4float
+      %out_var = OpVariable %out_ptr_v4f Output
+
+  ; %combined_var is not used!
+  %combined_var = OpVariable %_ptr_UniformConstant_11 UniformConstant
+         %main = OpFunction %void None %voidfn
+       %main_0 = OpLabel
+                 OpReturn
+                 OpFunctionEnd
+  )";
+    auto [disasm, status] =
+        SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+            kTest, /* do_validation= */ true);
+    EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+  }
+}
+
+TEST_P(SplitCombinedImageSamplerPassEntryPointRemapTest,
+       EntryPoint_Combined_Unused) {
+  // If the combined var is in the interface, that is enough to trigger
+  // its replacement. Otherwise the entry point interface is untouched
+  // when the combined var is not otherwise used.
+  const bool combined_var_in_interface =
+      std::string(GetParam().initial_interface).find("%combined_var") !=
+      std::string::npos;
+  if (!combined_var_in_interface) {
+    const std::string kTest = PreambleFragment(GetParam().initial_interface) +
+                              R"(
+  ; CHECK: OpEntryPoint Fragment %main "main")" +
+                              GetParam().initial_interface  // Note this is the
+                                                            // intial interface
+                              + R"(
+  ; These clauses ensure the expected interface is the whole interface.
+  ; CHECK-NOT: %{{\d+}}
+  ; CHECK-NOT: %in_var
+  ; CHECK-NOT: %out_var
+  ; CHECK-NOT: %combined_var
+  ; CHECK: OpExecutionMode %main OriginUpperLeft
+
+  ; The variable disappears.
+  ; CHECK-NOT: %combined_var =
+  ; CHECK: OpFunctionEnd
+                 OpName %combined_var "combined_var"
+                 OpName %in_var "in_var"
+                 OpName %out_var "out_var"
+                 OpDecorate %combined_var DescriptorSet 0
+                 OpDecorate %combined_var Binding 0
+                 OpDecorate %in_var BuiltIn FragCoord
+                 OpDecorate %out_var Location 0
+
+
+                 %bool = OpTypeBool
+  )" + BasicTypes() + R"(         %10 = OpTypeImage %float 2D 0 0 0 1 Unknown
+           %11 = OpTypeSampledImage %10
+  %_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
+       %in_ptr_v4f = OpTypePointer Input %v4float
+       %in_var = OpVariable %in_ptr_v4f Input
+      %out_ptr_v4f = OpTypePointer Output %v4float
+      %out_var = OpVariable %out_ptr_v4f Output
+
+  ; %combined_var is not used!
+  %combined_var = OpVariable %_ptr_UniformConstant_11 UniformConstant
+         %main = OpFunction %void None %voidfn
+       %main_0 = OpLabel
+                 OpReturn
+                 OpFunctionEnd
+)";
+    auto [disasm, status] =
+        SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+            kTest, /* do_validation= */ true);
+    EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(EntryPointRemap,
+                         SplitCombinedImageSamplerPassEntryPointRemapTest,
+                         ::testing::ValuesIn(EntryPointInterfaceCases()));
+
+// Remap function types
+
+struct FunctionTypeCase {
+  const char* initial_type_params = "";
+  const char* expected_type_params = "";
+};
+
+std::ostream& operator<<(std::ostream& os, const FunctionTypeCase& ftc) {
+  os << "(init " << ftc.initial_type_params << " -> expect "
+     << ftc.expected_type_params << ")";
+  return os;
+}
+
+struct SplitCombinedImageSamplerPassFunctionTypeTest
+    : public PassTest<::testing::TestWithParam<FunctionTypeCase>> {
+  virtual void SetUp() override {
+    SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+    SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES |
+                          SPV_BINARY_TO_TEXT_OPTION_INDENT |
+                          SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  }
+};
+
+std::vector<FunctionTypeCase> FunctionTypeCases() {
+  return std::vector<FunctionTypeCase>{
+      {"", ""},
+      {" %image_ty", " %image_ty"},
+      {" %sampler_ty", " %sampler_ty"},
+      {" %sampled_image_ty", " %image_ty %sampler_ty"},
+      {" %uint %sampled_image_ty %float",
+       " %uint %image_ty %sampler_ty %float"},
+      {" %ptr_sampled_image_ty",
+       " %_ptr_UniformConstant_image_ty %_ptr_UniformConstant_sampler_ty"},
+      {" %uint %ptr_sampled_image_ty %float",
+       " %uint %_ptr_UniformConstant_image_ty %_ptr_UniformConstant_sampler_ty "
+       "%float"},
+      {" %uint %ptr_sampled_image_ty %ptr_sampled_image_ty %float",
+       " %uint %_ptr_UniformConstant_image_ty %_ptr_UniformConstant_sampler_ty "
+       "%_ptr_UniformConstant_image_ty %_ptr_UniformConstant_sampler_ty "
+       "%float"},
+  };
+}
+
+TEST_P(SplitCombinedImageSamplerPassFunctionTypeTest,
+       ReplaceCombinedImageSamplersOnly) {
+  const std::string kTest = Preamble() + +R"(
+       OpName %f_ty "f_ty"
+       OpName %sampler_ty "sampler_ty"
+       OpName %image_ty "image_ty"
+       OpName %sampled_image_ty "sampled_image_ty"
+       OpName %ptr_sampled_image_ty "sampled_image_ty"
+
+  )" + BasicTypes() + R"(
+
+ %sampler_ty = OpTypeSampler
+   %image_ty = OpTypeImage %float 2D 0 0 0 1 Unknown
+ %sampled_image_ty = OpTypeSampledImage %image_ty
+ %ptr_sampled_image_ty = OpTypePointer UniformConstant %sampled_image_ty
+
+       %f_ty = OpTypeFunction %float)" +
+                            GetParam().initial_type_params + R"(
+
+  ; CHECK: %f_ty = OpTypeFunction %float)" +
+                            GetParam().expected_type_params + R"(
+)" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_P(SplitCombinedImageSamplerPassFunctionTypeTest, AvoidDuplicateType) {
+  // SPIR-V does not allow duplicate non-aggregate types. That includes function
+  // types.  Test that when replacing function type parameters would cause a
+  // collision, that the original function type is replaced with the new one.
+  const std::string initial_params(GetParam().initial_type_params);
+  const std::string expected_params(GetParam().expected_type_params);
+  const std::string kTest = Preamble() + +R"(
+       OpName %sampler_ty "sampler_ty"
+       OpName %image_ty "image_ty"
+       OpName %sampled_image_ty "sampled_image_ty"
+       OpName %_ptr_UniformConstant_sampler_ty "_ptr_UniformConstant_sampler_ty"
+       OpName %_ptr_UniformConstant_image_ty "_ptr_UniformConstant_image_ty"
+       OpName %ptr_sampled_image_ty "sampled_image_ty"
+       OpName %dest_ty "dest_ty"
+
+  )" + BasicTypes() + R"(
+
+ %sampler_ty = OpTypeSampler
+   %image_ty = OpTypeImage %float 2D 0 0 0 1 Unknown
+ %sampled_image_ty = OpTypeSampledImage %image_ty
+ %ptr_sampled_image_ty = OpTypePointer UniformConstant %sampled_image_ty
+ %_ptr_UniformConstant_image_ty = OpTypePointer UniformConstant %image_ty
+ %_ptr_UniformConstant_sampler_ty = OpTypePointer UniformConstant %sampler_ty
+
+        %100 = OpTypeFunction %float)" +
+                            initial_params + R"(
+    %dest_ty = OpTypeFunction %float)" +
+                            expected_params + R"(
+
+  ; CHECK: OpTypeSampler
+  ; CHECK-NOT: %100 =
+  ; CHECK: %dest_ty = OpTypeFunction %float)" +
+                            expected_params + R"(
+  ; CHECK-NOT: %100 =
+  ; CHECK: %main = OpFunction
+)" + Main();
+  // The original source is invalid if initial and expected params are the same,
+  // because the type is already duplicated.
+  // Only test when they are different.
+  if (initial_params != expected_params) {
+    auto [disasm, status] =
+        SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+            kTest, /* do_validation= */ true);
+    EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(FunctionTypeRemap,
+                         SplitCombinedImageSamplerPassFunctionTypeTest,
+                         ::testing::ValuesIn(FunctionTypeCases()));
+
+// Test array and runtime-array cases for function type replacement.
+
+TEST_F(SplitCombinedImageSamplerPassTest, FunctionType_ReplaceSampledImageArg) {
+  // The original module has a sampled image type, used only as a function
+  // parameter.  We still want to replace it.  But no other sampled-image types
+  // exist. This proves that the pass needs a sampled_image_used_as_param_
+  // state variable.
+  const std::string kTest = Preamble() + +R"(
+       OpName %f_ty "f_ty"
+       OpName %sampler_ty "sampler_ty"
+       OpName %image_ty "image_ty"
+       OpName %sampled_image_ty "sampled_image_ty"
+
+  )" + BasicTypes() + R"(
+
+ %sampler_ty = OpTypeSampler
+   %image_ty = OpTypeImage %float 2D 0 0 0 1 Unknown
+ %sampled_image_ty = OpTypeSampledImage %image_ty
+
+       %f_ty = OpTypeFunction %float %sampled_image_ty %float
+  ; CHECK: %f_ty = OpTypeFunction %float %image_ty %sampler_ty %float
+)" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest, FunctionType_ReplaceArrayArg) {
+  const std::string kTest = Preamble() + +R"(
+       OpName %f_ty "f_ty"
+       OpName %sampler_ty "sampler_ty"
+       OpName %image_ty "image_ty"
+       OpName %sampled_image_ty "sampled_image_ty"
+       OpName %ptr_array_si_ty "ptr_array_si_ty"
+
+  )" + BasicTypes() + R"(
+
+ %sampler_ty = OpTypeSampler
+   %image_ty = OpTypeImage %float 2D 0 0 0 1 Unknown
+ %sampled_image_ty = OpTypeSampledImage %image_ty
+   %array_si_ty = OpTypeArray %sampled_image_ty %uint_3
+ %ptr_array_si_ty = OpTypePointer UniformConstant %array_si_ty
+
+  ; CHECK: %[[array_i_ty:\w+]] = OpTypeArray %image_ty %uint_3
+  ; CHECK: %[[ptr_array_i_ty:\w+]] = OpTypePointer UniformConstant %[[array_i_ty]]
+  ; CHECK: %[[array_s_ty:\w+]] = OpTypeArray %sampler_ty %uint_3
+  ; CHECK: %[[ptr_array_s_ty:\w+]] = OpTypePointer UniformConstant %[[array_s_ty]]
+
+       %f_ty = OpTypeFunction %float %uint %ptr_array_si_ty %float
+  ; CHECK: %f_ty = OpTypeFunction %float %uint %[[ptr_array_i_ty]] %[[ptr_array_s_ty]] %float
+)" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest, FunctionType_ReplaceRtArrayArg) {
+  const std::string kTest = Preamble() + +R"(
+       OpName %f_ty "f_ty"
+       OpName %sampler_ty "sampler_ty"
+       OpName %image_ty "image_ty"
+       OpName %sampled_image_ty "sampled_image_ty"
+       OpName %ptr_array_si_ty "ptr_array_si_ty"
+
+  )" + BasicTypes() + R"(
+
+ %sampler_ty = OpTypeSampler
+   %image_ty = OpTypeImage %float 2D 0 0 0 1 Unknown
+ %sampled_image_ty = OpTypeSampledImage %image_ty
+   %array_si_ty = OpTypeRuntimeArray %sampled_image_ty
+ %ptr_array_si_ty = OpTypePointer UniformConstant %array_si_ty
+
+  ; CHECK: %[[array_i_ty:\w+]] = OpTypeRuntimeArray %image_ty
+  ; CHECK: %[[ptr_array_i_ty:\w+]] = OpTypePointer UniformConstant %[[array_i_ty]]
+  ; CHECK: %[[array_s_ty:\w+]] = OpTypeRuntimeArray %sampler_ty
+  ; CHECK: %[[ptr_array_s_ty:\w+]] = OpTypePointer UniformConstant %[[array_s_ty]]
+
+       %f_ty = OpTypeFunction %float %uint %ptr_array_si_ty %float
+  ; CHECK: %f_ty = OpTypeFunction %float %uint %[[ptr_array_i_ty]] %[[ptr_array_s_ty]] %float
+)" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+// Remap function bodies
+
+std::string NamedITypes() {
+  return R"(
+      OpName %f "f"
+      OpName %f_ty "f_ty"
+      OpName %i_ty "i_ty"
+      OpName %s_ty "s_ty"
+      OpName %p_i_ty "p_i_ty"
+      OpName %p_s_ty "p_s_ty"
+)";
+}
+
+std::string NamedCombinedTypes() {
+  return R"(
+      OpName %si_ty "si_ty"
+      OpName %p_si_ty "p_si_ty"
+      OpName %array_si_ty "array_si_ty"
+      OpName %rtarray_si_ty "rtarray_si_ty"
+      OpName %p_array_si_ty "p_array_si_ty"
+      OpName %p_rtarray_si_ty "p_rtarray_si_ty"
+)";
+}
+
+std::string NamedCaller() {
+  return R"(
+      OpName %caller_ty     "caller_ty"
+      OpName %caller        "caller"
+      OpName %caller_entry  "caller_entry"
+      OpName %caller_call   "caller_call"
+      OpName %caller_arg    "caller_arg"
+)";
+}
+
+std::string ITypes() {
+  return R"(
+      %i_ty = OpTypeImage %float 2D 0 0 0 1 Unknown
+      %s_ty = OpTypeSampler
+      %p_i_ty = OpTypePointer UniformConstant %i_ty
+      %p_s_ty = OpTypePointer UniformConstant %s_ty
+)";
+}
+
+std::string CombinedTypes() {
+  return R"(
+      %si_ty = OpTypeSampledImage %i_ty
+      %p_si_ty = OpTypePointer UniformConstant %si_ty
+      %array_si_ty = OpTypeArray %si_ty %uint_3
+      %p_array_si_ty = OpTypePointer UniformConstant %array_si_ty
+      %rtarray_si_ty = OpTypeRuntimeArray %si_ty
+      %p_rtarray_si_ty = OpTypePointer UniformConstant %rtarray_si_ty
+)";
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest, FunctionBody_ScalarNoChange) {
+  const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() +
+                            BasicTypes() + ITypes() + CombinedTypes() + R"(
+
+      ; CHECK: %f_ty = OpTypeFunction %float %i_ty %s_ty %p_i_ty %p_s_ty
+      %f_ty = OpTypeFunction %float %i_ty %s_ty %p_i_ty %p_s_ty
+
+      ; CHECK: %f = OpFunction %float None %f_ty
+      ; CHECK-NEXT: OpFunctionParameter %i_ty
+      ; CHECK-NEXT: OpFunctionParameter %s_ty
+      ; CHECK-NEXT: OpFunctionParameter %p_i_ty
+      ; CHECK-NEXT: OpFunctionParameter %p_s_ty
+      ; CHECK-NEXT: OpLabel
+      %f = OpFunction %float None %f_ty
+      %100 = OpFunctionParameter %i_ty
+      %101 = OpFunctionParameter %s_ty
+      %102 = OpFunctionParameter %p_i_ty
+      %103 = OpFunctionParameter %p_s_ty
+      %110 = OpLabel
+      OpReturnValue %float_0
+      OpFunctionEnd
+      )" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest,
+       FunctionBody_SampledImage_OpImageSample) {
+  const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() +
+                            BasicTypes() + ITypes() + CombinedTypes() + R"(
+
+      ; CHECK: %f_ty = OpTypeFunction %v4float %uint %i_ty %s_ty %float
+      %f_ty = OpTypeFunction %v4float %uint %si_ty %float
+
+      ; CHECK: %f = OpFunction %v4float None %f_ty
+      ; CHECK: OpFunctionParameter %uint
+      ; CHECK-NEXT: %[[i:\w+]] = OpFunctionParameter %i_ty
+      ; CHECK-NEXT: %[[s:\w+]] = OpFunctionParameter %s_ty
+      ; CHECK-NEXT: OpFunctionParameter %float
+      ; CHECK-NEXT: OpLabel
+      ; CHECK-NEXT: %[[si:\w+]] = OpSampledImage %si_ty %[[i]] %[[s]]
+      ; CHECK-NEXT: %200 = OpImageSampleExplicitLod %v4float %[[si]] %13 Lod %float_0
+      ; CHECK-NEXT: OpReturnValue %200
+
+      %f = OpFunction %v4float None %f_ty
+      %100 = OpFunctionParameter %uint
+      %101 = OpFunctionParameter %si_ty ; replace this
+      %110 = OpFunctionParameter %float
+      %120 = OpLabel
+      %200 = OpImageSampleExplicitLod %v4float %101 %13 Lod %float_0
+      OpReturnValue %200
+      OpFunctionEnd
+
+      )" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest, FunctionBody_SampledImage_OpImage) {
+  const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() +
+                            BasicTypes() + ITypes() + CombinedTypes() + R"(
+
+      ; CHECK: %f_ty = OpTypeFunction %void %uint %i_ty %s_ty %float
+      %f_ty = OpTypeFunction %void %uint %si_ty %float
+
+      ; CHECK: %f = OpFunction %void None %f_ty
+      ; CHECK: OpFunctionParameter %uint
+      ; CHECK-NEXT: %[[i:\w+]] = OpFunctionParameter %i_ty
+      ; CHECK-NEXT: %[[s:\w+]] = OpFunctionParameter %s_ty
+      ; CHECK-NEXT: OpFunctionParameter %float
+      ; CHECK-NEXT: OpLabel
+      ; CHECK-NEXT: %[[si:\w+]] = OpSampledImage %si_ty %[[i]] %[[s]]
+      ; CHECK-NEXT: %200 = OpImage %i_ty %[[si]]
+      ; CHECK-NEXT: OpReturn
+
+      %f = OpFunction %void None %f_ty
+      %100 = OpFunctionParameter %uint
+      %101 = OpFunctionParameter %si_ty ; replace this
+      %110 = OpFunctionParameter %float
+      %120 = OpLabel
+      %200 = OpImage %i_ty %101
+      OpReturn
+      OpFunctionEnd
+
+      )" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest, FunctionBody_PtrSampledImage) {
+  const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() +
+                            BasicTypes() + ITypes() + CombinedTypes() + R"(
+
+      ; CHECK: %f_ty = OpTypeFunction %v4float %uint %p_i_ty %p_s_ty %float
+      %f_ty = OpTypeFunction %v4float %uint %p_si_ty %float
+
+      ; CHECK: %f = OpFunction %v4float None %f_ty
+      ; CHECK-NEXT: OpFunctionParameter %uint
+      ; CHECK-NEXT: %[[pi:\w+]] = OpFunctionParameter %p_i_ty
+      ; CHECK-NEXT: %[[ps:\w+]] = OpFunctionParameter %p_s_ty
+      ; CHECK-NEXT: OpFunctionParameter %float
+      ; CHECK-NEXT: OpLabel
+      ; CHECK-NEXT: %[[i:\w+]] = OpLoad %i_ty %[[pi]]
+      ; CHECK-NEXT: %[[s:\w+]] = OpLoad %s_ty %[[ps]]
+      ; CHECK-NEXT: %[[si:\w+]] = OpSampledImage %si_ty %[[i]] %[[s]]
+      ; CHECK-NEXT: %200 = OpImageSampleExplicitLod %v4float %[[si]] %13 Lod %float_0
+      ; CHECK-NEXT: OpReturnValue %200
+
+      %f = OpFunction %v4float None %f_ty
+      %100 = OpFunctionParameter %uint
+      %101 = OpFunctionParameter %p_si_ty ; replace this
+      %110 = OpFunctionParameter %float
+      %120 = OpLabel
+      %121 = OpLoad %si_ty %101
+      %200 = OpImageSampleExplicitLod %v4float %121 %13 Lod %float_0
+      OpReturnValue %200
+      OpFunctionEnd
+      )" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest,
+       FunctionCall_NoImageOrSampler_NoChange) {
+  const std::string kTest = Preamble() + NamedITypes() + NamedCaller() +
+                            BasicTypes() + ITypes() + CombinedTypes() + R"(
+
+      ; CHECK: %f_ty = OpTypeFunction %void %uint %float
+      %f_ty = OpTypeFunction %void %uint %float
+      %caller_ty = OpTypeFunction %float  ; make it return non-void otherwise it's just like main
+
+      ; The called function does not change
+      ; CHECK: %f = OpFunction %void None %f_ty
+      ; CHECK-NEXT: = OpFunctionParameter %uint
+      ; CHECK-NEXT: = OpFunctionParameter %float
+      ; CHECK-NEXT: = OpLabel
+      ; CHECK-NEXT: OpReturn
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %f = OpFunction %void None %f_ty
+      %100 = OpFunctionParameter %uint
+      %101 = OpFunctionParameter %float
+      %110 = OpLabel
+      OpReturn
+      OpFunctionEnd
+
+      ; The caller does not change
+      ; CHECK: %caller = OpFunction %float None %caller_ty
+      ; CHECK-NEXT: %caller_entry = OpLabel
+      ; CHECK-NEXT: %caller_arg = OpCopyObject %uint %uint_0
+      ; CHECK-NEXT: OpFunctionCall %void %f %caller_arg %float_0
+      ; CHECK-NEXT: OpReturnValue %float_0
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %caller = OpFunction %float None %caller_ty
+%caller_entry = OpLabel
+  %caller_arg = OpCopyObject %uint %uint_0
+ %caller_call = OpFunctionCall %void %f %caller_arg %float_0
+                OpReturnValue %float_0
+                OpFunctionEnd
+
+      )" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  // We still get a success-with-change result because the boilerplate included
+  // combined types, which were removed.
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest, FunctionCall_Image_NoChange) {
+  const std::string kTest = Preamble() + NamedITypes() + NamedCaller() +
+                            BasicTypes() + ITypes() + R"(
+
+      ; CHECK: %f_ty = OpTypeFunction %void %i_ty
+      %f_ty = OpTypeFunction %void %i_ty
+      %caller_ty = OpTypeFunction %float %i_ty
+
+      ; The called function does not change
+      ; CHECK: %f = OpFunction %void None %f_ty
+      ; CHECK-NEXT: = OpFunctionParameter %i_ty
+      ; CHECK-NEXT: = OpLabel
+      ; CHECK-NEXT: OpReturn
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %f = OpFunction %void None %f_ty
+      %100 = OpFunctionParameter %i_ty
+      %110 = OpLabel
+      OpReturn
+      OpFunctionEnd
+
+      ; The caller does not change
+      ; CHECK: %caller = OpFunction %float None %caller_ty
+      ; CHECK-NEXT: %caller_arg = OpFunctionParameter %i_ty
+      ; CHECK-NEXT: %caller_entry = OpLabel
+      ; CHECK-NEXT: OpFunctionCall %void %f %caller_arg
+      ; CHECK-NEXT: OpReturnValue %float_0
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %caller = OpFunction %float None %caller_ty
+  %caller_arg = OpFunctionParameter %i_ty
+%caller_entry = OpLabel
+ %caller_call = OpFunctionCall %void %f %caller_arg
+                OpReturnValue %float_0
+                OpFunctionEnd
+
+      )" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithoutChange) << disasm;
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest, FunctionCall_Sampler_NoChange) {
+  const std::string kTest = Preamble() + NamedITypes() + NamedCaller() +
+                            BasicTypes() + ITypes() + R"(
+
+      ; CHECK: %f_ty = OpTypeFunction %void %s_ty
+      %f_ty = OpTypeFunction %void %s_ty
+      %caller_ty = OpTypeFunction %float %s_ty
+
+      ; The called function does not change
+      ; CHECK: %f = OpFunction %void None %f_ty
+      ; CHECK-NEXT: = OpFunctionParameter %s_ty
+      ; CHECK-NEXT: = OpLabel
+      ; CHECK-NEXT: OpReturn
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %f = OpFunction %void None %f_ty
+      %100 = OpFunctionParameter %s_ty
+      %110 = OpLabel
+      OpReturn
+      OpFunctionEnd
+
+      ; The caller does not change
+      ; CHECK: %caller = OpFunction %float None %caller_ty
+      ; CHECK-NEXT: %caller_arg = OpFunctionParameter %s_ty
+      ; CHECK-NEXT: %caller_entry = OpLabel
+      ; CHECK-NEXT: OpFunctionCall %void %f %caller_arg
+      ; CHECK-NEXT: OpReturnValue %float_0
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %caller = OpFunction %float None %caller_ty
+  %caller_arg = OpFunctionParameter %s_ty
+%caller_entry = OpLabel
+ %caller_call = OpFunctionCall %void %f %caller_arg
+                OpReturnValue %float_0
+                OpFunctionEnd
+
+      )" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithoutChange) << disasm;
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest, FunctionCall_PtrImage_NoChange) {
+  const std::string kTest = Preamble() + NamedITypes() + NamedCaller() +
+                            BasicTypes() + ITypes() + R"(
+
+      ; CHECK: %f_ty = OpTypeFunction %void %p_i_ty
+      %f_ty = OpTypeFunction %void %p_i_ty
+      %caller_ty = OpTypeFunction %float %p_i_ty
+
+      ; The called function does not change
+      ; CHECK: %f = OpFunction %void None %f_ty
+      ; CHECK-NEXT: = OpFunctionParameter %p_i_ty
+      ; CHECK-NEXT: = OpLabel
+      ; CHECK-NEXT: OpReturn
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %f = OpFunction %void None %f_ty
+      %100 = OpFunctionParameter %p_i_ty
+      %110 = OpLabel
+      OpReturn
+      OpFunctionEnd
+
+      ; The caller does not change
+      ; CHECK: %caller = OpFunction %float None %caller_ty
+      ; CHECK-NEXT: %caller_arg = OpFunctionParameter %p_i_ty
+      ; CHECK-NEXT: %caller_entry = OpLabel
+      ; CHECK-NEXT: OpFunctionCall %void %f %caller_arg
+      ; CHECK-NEXT: OpReturnValue %float_0
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %caller = OpFunction %float None %caller_ty
+  %caller_arg = OpFunctionParameter %p_i_ty
+%caller_entry = OpLabel
+ %caller_call = OpFunctionCall %void %f %caller_arg
+                OpReturnValue %float_0
+                OpFunctionEnd
+
+      )" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithoutChange) << disasm;
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest, FunctionCall_PtrSampler_NoChange) {
+  const std::string kTest = Preamble() + NamedITypes() + NamedCaller() +
+                            BasicTypes() + ITypes() + R"(
+
+      ; CHECK: %f_ty = OpTypeFunction %void %p_s_ty
+      %f_ty = OpTypeFunction %void %p_s_ty
+      %caller_ty = OpTypeFunction %float %p_s_ty
+
+      ; The called function does not change
+      ; CHECK: %f = OpFunction %void None %f_ty
+      ; CHECK-NEXT: = OpFunctionParameter %p_s_ty
+      ; CHECK-NEXT: = OpLabel
+      ; CHECK-NEXT: OpReturn
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %f = OpFunction %void None %f_ty
+      %100 = OpFunctionParameter %p_s_ty
+      %110 = OpLabel
+      OpReturn
+      OpFunctionEnd
+
+      ; The caller does not change
+      ; CHECK: %caller = OpFunction %float None %caller_ty
+      ; CHECK-NEXT: %caller_arg = OpFunctionParameter %p_s_ty
+      ; CHECK-NEXT: %caller_entry = OpLabel
+      ; CHECK-NEXT: OpFunctionCall %void %f %caller_arg
+      ; CHECK-NEXT: OpReturnValue %float_0
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %caller = OpFunction %float None %caller_ty
+  %caller_arg = OpFunctionParameter %p_s_ty
+%caller_entry = OpLabel
+ %caller_call = OpFunctionCall %void %f %caller_arg
+                OpReturnValue %float_0
+                OpFunctionEnd
+
+      )" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithoutChange) << disasm;
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest, FunctionCall_SampledImage_Split) {
+  const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() +
+                            NamedCaller() + BasicTypes() + ITypes() +
+                            CombinedTypes() + R"(
+
+      ; CHECK: %f_ty = OpTypeFunction %void %i_ty %s_ty
+      %f_ty = OpTypeFunction %void %si_ty
+      %caller_ty = OpTypeFunction %float %si_ty
+
+      ; Call function arg is split. We've checked these details in other tests.
+      ; CHECK: %f = OpFunction %void None %f_ty
+      ; CHECK-NEXT: %[[callee_i:\w+]] = OpFunctionParameter %i_ty
+      ; CHECK-NEXT: %[[callee_s:\w+]] = OpFunctionParameter %s_ty
+      ; CHECK-NEXT: = OpLabel
+      ; CHECK-NEXT: OpReturn
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %f = OpFunction %void None %f_ty
+      %100 = OpFunctionParameter %si_ty
+      %110 = OpLabel
+      OpReturn
+      OpFunctionEnd
+
+      ; CHECK: %caller = OpFunction %float None %caller_ty
+      ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %i_ty
+      ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %s_ty
+      ; CHECK-NEXT: %caller_entry = OpLabel
+      ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i]] %[[caller_s]]
+      ; CHECK-NEXT: OpReturnValue %float_0
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %caller = OpFunction %float None %caller_ty
+  %caller_arg = OpFunctionParameter %si_ty
+%caller_entry = OpLabel
+ %caller_call = OpFunctionCall %void %f %caller_arg
+                OpReturnValue %float_0
+                OpFunctionEnd
+
+      )" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest,
+       FunctionCall_SampledImageDuplicatedArg_Split) {
+  const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() +
+                            NamedCaller() + BasicTypes() + ITypes() +
+                            CombinedTypes() + R"(
+
+      ; CHECK: %f_ty = OpTypeFunction %void %i_ty %s_ty %i_ty %s_ty
+      %f_ty = OpTypeFunction %void %si_ty %si_ty
+      %caller_ty = OpTypeFunction %float %si_ty
+
+      ; Call function arg is split. We've checked these details in other tests.
+      ; CHECK: %f = OpFunction %void None %f_ty
+      ; CHECK-NEXT: %[[callee_i_0:\w+]] = OpFunctionParameter %i_ty
+      ; CHECK-NEXT: %[[callee_s_0:\w+]] = OpFunctionParameter %s_ty
+      ; CHECK-NEXT: %[[callee_i_1:\w+]] = OpFunctionParameter %i_ty
+      ; CHECK-NEXT: %[[callee_s_1:\w+]] = OpFunctionParameter %s_ty
+      ; CHECK-NEXT: = OpLabel
+      ; CHECK-NEXT: OpReturn
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %f = OpFunction %void None %f_ty
+      %100 = OpFunctionParameter %si_ty
+      %101 = OpFunctionParameter %si_ty
+      %110 = OpLabel
+      OpReturn
+      OpFunctionEnd
+
+      ; CHECK: %caller = OpFunction %float None %caller_ty
+      ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %i_ty
+      ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %s_ty
+      ; CHECK-NEXT: %caller_entry = OpLabel
+      ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i]] %[[caller_s]] %[[caller_i]] %[[caller_s]]
+      ; CHECK-NEXT: OpReturnValue %float_0
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %caller = OpFunction %float None %caller_ty
+  %caller_arg = OpFunctionParameter %si_ty
+%caller_entry = OpLabel
+ %caller_call = OpFunctionCall %void %f %caller_arg %caller_arg
+                OpReturnValue %float_0
+                OpFunctionEnd
+
+      )" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest,
+       FunctionCall_SampledImageTwoDistinct_Split) {
+  const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() +
+                            NamedCaller() + BasicTypes() + ITypes() +
+                            CombinedTypes() + R"(
+
+      ; CHECK: %f_ty = OpTypeFunction %void %i_ty %s_ty %i_ty %s_ty
+      %f_ty = OpTypeFunction %void %si_ty %si_ty
+      %caller_ty = OpTypeFunction %float %si_ty %si_ty
+
+      ; Call function arg is split. We've checked these details in other tests.
+      ; CHECK: %f = OpFunction %void None %f_ty
+      ; CHECK-NEXT: %[[callee_i_0:\w+]] = OpFunctionParameter %i_ty
+      ; CHECK-NEXT: %[[callee_s_0:\w+]] = OpFunctionParameter %s_ty
+      ; CHECK-NEXT: %[[callee_i_1:\w+]] = OpFunctionParameter %i_ty
+      ; CHECK-NEXT: %[[callee_s_1:\w+]] = OpFunctionParameter %s_ty
+      ; CHECK-NEXT: = OpLabel
+      ; CHECK-NEXT: OpReturn
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %f = OpFunction %void None %f_ty
+      %100 = OpFunctionParameter %si_ty
+      %101 = OpFunctionParameter %si_ty
+      %110 = OpLabel
+      OpReturn
+      OpFunctionEnd
+
+      ; CHECK: %caller = OpFunction %float None %caller_ty
+      ; CHECK-NEXT: %[[caller_i_0:\w+]] = OpFunctionParameter %i_ty
+      ; CHECK-NEXT: %[[caller_s_0:\w+]] = OpFunctionParameter %s_ty
+      ; CHECK-NEXT: %[[caller_i_1:\w+]] = OpFunctionParameter %i_ty
+      ; CHECK-NEXT: %[[caller_s_1:\w+]] = OpFunctionParameter %s_ty
+      ; CHECK-NEXT: %caller_entry = OpLabel
+      ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i_0]] %[[caller_s_0]] %[[caller_i_1]] %[[caller_s_1]]
+      ; CHECK-NEXT: OpReturnValue %float_0
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %caller = OpFunction %float None %caller_ty
+  %caller_arg = OpFunctionParameter %si_ty
+         %201 = OpFunctionParameter %si_ty
+%caller_entry = OpLabel
+ %caller_call = OpFunctionCall %void %f %caller_arg %201
+                OpReturnValue %float_0
+                OpFunctionEnd
+
+      )" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest,
+       FunctionCall_SampledImageAndCopy_Split) {
+  const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() +
+                            NamedCaller() + BasicTypes() + ITypes() +
+                            CombinedTypes() + R"(
+
+      ; CHECK: %f_ty = OpTypeFunction %void %i_ty %s_ty %i_ty %s_ty
+      %f_ty = OpTypeFunction %void %si_ty %si_ty
+      ; CHECK: %caller_ty = OpTypeFunction %float %i_ty %s_ty
+      %caller_ty = OpTypeFunction %float %si_ty
+
+      ; Call function arg is split. We've checked these details in other tests.
+      ; CHECK: %f = OpFunction %void None %f_ty
+      ; CHECK-NEXT: %[[callee_i_0:\w+]] = OpFunctionParameter %i_ty
+      ; CHECK-NEXT: %[[callee_s_0:\w+]] = OpFunctionParameter %s_ty
+      ; CHECK-NEXT: %[[callee_i_1:\w+]] = OpFunctionParameter %i_ty
+      ; CHECK-NEXT: %[[callee_s_1:\w+]] = OpFunctionParameter %s_ty
+      ; CHECK-NEXT: = OpLabel
+      ; CHECK-NEXT: OpReturn
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %f = OpFunction %void None %f_ty
+      %100 = OpFunctionParameter %si_ty
+      %101 = OpFunctionParameter %si_ty
+      %110 = OpLabel
+      OpReturn
+      OpFunctionEnd
+
+      ; CHECK: %caller = OpFunction %float None %caller_ty
+      ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %i_ty
+      ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %s_ty
+      ; CHECK-NEXT: %caller_entry = OpLabel
+      ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i]] %[[caller_s]] %[[caller_i]] %[[caller_s]]
+      ; CHECK-NEXT: OpReturnValue %float_0
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %caller = OpFunction %float None %caller_ty
+  %caller_arg = OpFunctionParameter %si_ty
+%caller_entry = OpLabel
+        %copy = OpCopyObject %si_ty %caller_arg
+ %caller_call = OpFunctionCall %void %f %caller_arg %copy
+                OpReturnValue %float_0
+                OpFunctionEnd
+
+      )" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest,
+       FunctionCall_SampledImageSurrounded_Split) {
+  // Test indexing by surrounding the sampled image parameter with other
+  // arguments that should not be touched.
+  const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() +
+                            NamedCaller() + BasicTypes() + ITypes() +
+                            CombinedTypes() + R"(
+
+      ; CHECK: %f_ty = OpTypeFunction %void %float %i_ty %s_ty %uint
+      %f_ty = OpTypeFunction %void %float %si_ty %uint
+      ; CHECK: %caller_ty = OpTypeFunction %float %uint %i_ty %s_ty %float
+      %caller_ty = OpTypeFunction %float %uint %si_ty %float
+
+      ; Call function arg is split. We've checked these details in other tests.
+      ; CHECK: %f = OpFunction %void None %f_ty
+      ; CHECK-NEXT: %[[callee_f:\w+]] = OpFunctionParameter %float
+      ; CHECK-NEXT: %[[callee_i:\w+]] = OpFunctionParameter %i_ty
+      ; CHECK-NEXT: %[[callee_s:\w+]] = OpFunctionParameter %s_ty
+      ; CHECK-NEXT: %[[callee_u:\w+]] = OpFunctionParameter %uint
+      ; CHECK-NEXT: = OpLabel
+      ; CHECK-NEXT: OpReturn
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %f = OpFunction %void None %f_ty
+       %99 = OpFunctionParameter %float
+      %100 = OpFunctionParameter %si_ty
+      %101 = OpFunctionParameter %uint
+      %110 = OpLabel
+      OpReturn
+      OpFunctionEnd
+
+      ; CHECK: %caller = OpFunction %float None %caller_ty
+      ; CHECK-NEXT: %[[u_param:\w+]] = OpFunctionParameter %uint
+      ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %i_ty
+      ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %s_ty
+      ; CHECK-NEXT: %[[f_param:\w+]] = OpFunctionParameter %float
+      ; CHECK-NEXT: %caller_entry = OpLabel
+      ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[f_param]] %[[caller_i]] %[[caller_s]] %[[u_param]]
+      ; CHECK-NEXT: OpReturnValue %float_0
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %caller = OpFunction %float None %caller_ty
+         %200 = OpFunctionParameter %uint
+  %caller_arg = OpFunctionParameter %si_ty
+         %201 = OpFunctionParameter %float
+%caller_entry = OpLabel
+ %caller_call = OpFunctionCall %void %f %201 %caller_arg %200
+                OpReturnValue %float_0
+                OpFunctionEnd
+
+      )" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest, FunctionCall_PtrSampledImage_Split) {
+  const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() +
+                            NamedCaller() + BasicTypes() + ITypes() +
+                            CombinedTypes() + R"(
+
+      ; CHECK: %f_ty = OpTypeFunction %void %p_i_ty %p_s_ty
+      %f_ty = OpTypeFunction %void %p_si_ty
+      %caller_ty = OpTypeFunction %float %p_si_ty
+
+      ; Call function arg is split. We've checked these details in other tests.
+      ; CHECK: %f = OpFunction %void None %f_ty
+      ; CHECK-NEXT: %[[callee_i:\w+]] = OpFunctionParameter %p_i_ty
+      ; CHECK-NEXT: %[[callee_s:\w+]] = OpFunctionParameter %p_s_ty
+      ; CHECK-NEXT: = OpLabel
+      ; CHECK-NEXT: OpReturn
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %f = OpFunction %void None %f_ty
+      %100 = OpFunctionParameter %p_si_ty
+      %110 = OpLabel
+      OpReturn
+      OpFunctionEnd
+
+      ; CHECK: %caller = OpFunction %float None %caller_ty
+      ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %p_i_ty
+      ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %p_s_ty
+      ; CHECK-NEXT: %caller_entry = OpLabel
+      ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i]] %[[caller_s]]
+      ; CHECK-NEXT: OpReturnValue %float_0
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %caller = OpFunction %float None %caller_ty
+  %caller_arg = OpFunctionParameter %p_si_ty
+%caller_entry = OpLabel
+ %caller_call = OpFunctionCall %void %f %caller_arg
+                OpReturnValue %float_0
+                OpFunctionEnd
+
+      )" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest,
+       FunctionCall_PtrArraySampledImage_Split) {
+  const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() +
+                            NamedCaller() + BasicTypes() + ITypes() +
+                            CombinedTypes() + R"(
+
+      ; CHECK: %[[array_i_ty:\w+]] = OpTypeArray %i_ty %uint_3
+      ; CHECK: %[[p_array_i_ty:\w+]] = OpTypePointer UniformConstant %[[array_i_ty]]
+      ; CHECK: %[[array_s_ty:\w+]] = OpTypeArray %s_ty %uint_3
+      ; CHECK: %[[p_array_s_ty:\w+]] = OpTypePointer UniformConstant %[[array_s_ty]]
+
+      ; CHECK: %f_ty = OpTypeFunction %void %[[p_array_i_ty]] %[[p_array_s_ty]]
+      %f_ty = OpTypeFunction %void %p_array_si_ty
+      %caller_ty = OpTypeFunction %float %p_array_si_ty
+
+      ; Call function arg is split. We've checked these details in other tests.
+      ; CHECK: %f = OpFunction %void None %f_ty
+      ; CHECK-NEXT: %[[callee_i:\w+]] = OpFunctionParameter %[[p_array_i_ty]]
+      ; CHECK-NEXT: %[[callee_s:\w+]] = OpFunctionParameter %[[p_array_s_ty]]
+      ; CHECK-NEXT: = OpLabel
+      ; CHECK-NEXT: OpReturn
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %f = OpFunction %void None %f_ty
+      %100 = OpFunctionParameter %p_array_si_ty
+      %110 = OpLabel
+      OpReturn
+      OpFunctionEnd
+
+      ; CHECK: %caller = OpFunction %float None %caller_ty
+      ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %[[p_array_i_ty]]
+      ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %[[p_array_s_ty]]
+      ; CHECK-NEXT: %caller_entry = OpLabel
+      ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i]] %[[caller_s]]
+      ; CHECK-NEXT: OpReturnValue %float_0
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %caller = OpFunction %float None %caller_ty
+  %caller_arg = OpFunctionParameter %p_array_si_ty
+%caller_entry = OpLabel
+ %caller_call = OpFunctionCall %void %f %caller_arg
+                OpReturnValue %float_0
+                OpFunctionEnd
+
+      )" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+TEST_F(SplitCombinedImageSamplerPassTest,
+       FunctionCall_PtrRtArraySampledImage_Split) {
+  const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() +
+                            NamedCaller() + BasicTypes() + ITypes() +
+                            CombinedTypes() + R"(
+
+      ; CHECK: %[[array_i_ty:\w+]] = OpTypeRuntimeArray %i_ty
+      ; CHECK: %[[p_array_i_ty:\w+]] = OpTypePointer UniformConstant %[[array_i_ty]]
+      ; CHECK: %[[array_s_ty:\w+]] = OpTypeRuntimeArray
+      ; CHECK: %[[p_array_s_ty:\w+]] = OpTypePointer UniformConstant %[[array_s_ty]]
+
+      ; CHECK: %f_ty = OpTypeFunction %void %[[p_array_i_ty]] %[[p_array_s_ty]]
+      %f_ty = OpTypeFunction %void %p_rtarray_si_ty
+      %caller_ty = OpTypeFunction %float %p_rtarray_si_ty
+
+      ; Call function arg is split. We've checked these details in other tests.
+      ; CHECK: %f = OpFunction %void None %f_ty
+      ; CHECK-NEXT: %[[callee_i:\w+]] = OpFunctionParameter %[[p_array_i_ty]]
+      ; CHECK-NEXT: %[[callee_s:\w+]] = OpFunctionParameter %[[p_array_s_ty]]
+      ; CHECK-NEXT: = OpLabel
+      ; CHECK-NEXT: OpReturn
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %f = OpFunction %void None %f_ty
+      %100 = OpFunctionParameter %p_rtarray_si_ty
+      %110 = OpLabel
+      OpReturn
+      OpFunctionEnd
+
+      ; CHECK: %caller = OpFunction %float None %caller_ty
+      ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %[[p_array_i_ty]]
+      ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %[[p_array_s_ty]]
+      ; CHECK-NEXT: %caller_entry = OpLabel
+      ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i]] %[[caller_s]]
+      ; CHECK-NEXT: OpReturnValue %float_0
+      ; CHECK-NEXT: OpFunctionEnd
+
+      %caller = OpFunction %float None %caller_ty
+  %caller_arg = OpFunctionParameter %p_rtarray_si_ty
+%caller_entry = OpLabel
+ %caller_call = OpFunctionCall %void %f %caller_arg
+                OpReturnValue %float_0
+                OpFunctionEnd
+
+      )" + Main();
+
+  auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>(
+      kTest, /* do_validation= */ true);
+  EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
+}
+
+}  // namespace
+}  // namespace opt
+}  // namespace spvtools
diff --git a/test/opt/strip_nonsemantic_info_test.cpp b/test/opt/strip_nonsemantic_info_test.cpp
index 3aacffa..5c3909c 100644
--- a/test/opt/strip_nonsemantic_info_test.cpp
+++ b/test/opt/strip_nonsemantic_info_test.cpp
@@ -224,6 +224,70 @@
   SinglePassRunAndMatch<StripNonSemanticInfoPass>(text, true);
 }
 
+TEST_F(StripNonSemanticInfoTest, StripNonSemanticScopes) {
+  std::string text = R"(
+;CHECK-NOT: OpExtension "SPV_KHR_non_semantic_info
+;CHECK-NOT: OpExtInstImport "NonSemantic.Shader.DebugInfo.100
+;CHECK-NOT: OpExtInst %8 {{%\w+}} DebugInfoNon
+;CHECK-NOT: OpExtInst %8 {{%\w+}} DebugSource
+;CHECK-NOT: OpExtInst %8 {{%\w+}} DebugCompilationUnit
+;CHECK-NOT: OpExtInst %8 {{%\w+}} DebugTypeFunction
+;CHECK-NOT: OpExtInst %8 {{%\w+}} DebugFunction
+;CHECK-NOT: OpExtInst %8 {{%\w+}} DebugScope
+;CHECK-NOT: OpExtInst %8 {{%\w+}} DebugNoScope
+               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+               OpExtension "SPV_KHR_storage_buffer_storage_class"
+          %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %2 "main" %3
+               OpExecutionMode %2 LocalSize 1 1 1
+          %4 = OpString ""
+               OpDecorate %5 ArrayStride 4
+               OpDecorate %6 ArrayStride 4
+               OpDecorate %7 Block
+               OpMemberDecorate %7 0 Offset 0
+               OpDecorate %3 Binding 0
+               OpDecorate %3 DescriptorSet 0
+          %8 = OpTypeVoid
+          %9 = OpTypeInt 32 0
+         %10 = OpConstant %9 100
+         %11 = OpTypeFunction %8
+         %12 = OpTypeInt 32 1
+         %13 = OpConstant %12 0
+         %14 = OpTypeFloat 32
+          %5 = OpTypePointer StorageBuffer %14
+          %6 = OpTypeRuntimeArray %14
+          %7 = OpTypeStruct %6
+         %15 = OpTypePointer StorageBuffer %7
+         %16 = OpConstant %14 0
+          %3 = OpVariable %15 StorageBuffer
+         %17 = OpExtInst %8 %1 DebugInfoNone
+         %18 = OpExtInst %8 %1 DebugSource %4 %4
+         %19 = OpExtInst %8 %1 DebugCompilationUnit %10 %10 %18 %10
+         %20 = OpExtInst %8 %1 DebugTypeFunction %10 %8
+         %21 = OpExtInst %8 %1 DebugFunction %4 %20 %18 %10 %10 %19 %4 %10 %10
+         %22 = OpExtInst %8 %1 DebugEntryPoint %21 %19 %4 %4
+         %23 = OpExtInst %8 %1 DebugTypeBasic %4 %10 %10 %10
+         %24 = OpExtInst %8 %1 DebugTypeArray %23 %10
+         %25 = OpExtInst %8 %1 DebugTypeMember %4 %24 %18 %10 %10 %10 %10 %10
+         %26 = OpExtInst %8 %1 DebugTypeComposite %4 %10 %18 %10 %10 %19 %4 %10 %10 %25
+         %27 = OpExtInst %8 %1 DebugGlobalVariable %4 %26 %18 %10 %10 %19 %4 %3 %10
+          %2 = OpFunction %8 None %11
+         %28 = OpLabel
+         %29 = OpExtInst %8 %1 DebugFunctionDefinition %21 %2
+         %30 = OpExtInst %8 %1 DebugScope %21
+         %31 = OpExtInst %8 %1 DebugLine %18 %10 %10 %10 %10
+         %32 = OpAccessChain %5 %3 %13 %13
+               OpStore %32 %16
+               OpReturn
+         %33 = OpExtInst %8 %1 DebugNoScope
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<StripNonSemanticInfoPass>(text, false);
+}
+
 // Make sure that strip reflect does not remove the debug info (OpString and
 // OpLine).
 TEST_F(StripNonSemanticInfoTest, DontStripDebug) {
diff --git a/test/opt/trim_capabilities_pass_test.cpp b/test/opt/trim_capabilities_pass_test.cpp
index 1569ad7..43271da 100644
--- a/test/opt/trim_capabilities_pass_test.cpp
+++ b/test/opt/trim_capabilities_pass_test.cpp
@@ -1401,6 +1401,244 @@
   EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
 }
 
+TEST_F(TrimCapabilitiesPassTest,
+       StorageBuffer16BitAccess_RemainsSimplePointer_Vulkan1_0) {
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354
+  static_assert(spv::Capability::StorageUniformBufferBlock16 ==
+                spv::Capability::StorageBuffer16BitAccess);
+
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageBuffer16BitAccess
+
+; CHECK:          OpCapability StorageBuffer16BitAccess
+; CHECK-NOT:      OpExtension "SPV_KHR_16bit_storage"
+
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+                      OpDecorate %struct Block
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+            %struct = OpTypeStruct %half
+               %ptr = OpTypePointer StorageBuffer %struct
+                 %1 = OpTypeFunction %void
+                 %2 = OpFunction %void None %1
+                 %3 = OpLabel
+                      OpReturn
+                      OpFunctionEnd
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_0);
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       StorageBuffer16BitAccess_RemainsSimplePointer_Vulkan1_1) {
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354
+  static_assert(spv::Capability::StorageUniformBufferBlock16 ==
+                spv::Capability::StorageBuffer16BitAccess);
+
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageBuffer16BitAccess
+
+; CHECK:          OpCapability StorageBuffer16BitAccess
+; CHECK-NOT:      OpExtension "SPV_KHR_16bit_storage"
+
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+                      OpDecorate %struct Block
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+            %struct = OpTypeStruct %half
+               %ptr = OpTypePointer StorageBuffer %struct
+                 %1 = OpTypeFunction %void
+                 %2 = OpFunction %void None %1
+                 %3 = OpLabel
+                      OpReturn
+                      OpFunctionEnd
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_1);
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(
+    TrimCapabilitiesPassTest,
+    StorageBuffer16BitAccess_RemainsSimplePointerUshortPhysicalStorage_Vulkan1_1) {
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354
+  static_assert(spv::Capability::StorageUniformBufferBlock16 ==
+                spv::Capability::StorageBuffer16BitAccess);
+
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability StorageBuffer16BitAccess
+                      OpCapability PhysicalStorageBufferAddresses
+
+; CHECK:          OpCapability StorageBuffer16BitAccess
+; CHECK:          OpCapability PhysicalStorageBufferAddresses
+; CHECK-NOT:      OpExtension "SPV_KHR_16bit_storage"
+; CHECK-NOT:      OpCapability Int16
+
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+                      OpDecorate %struct Block
+              %void = OpTypeVoid
+            %ushort = OpTypeInt 16 0
+            %struct = OpTypeStruct %ushort
+               %ptr = OpTypePointer PhysicalStorageBuffer %struct
+                 %1 = OpTypeFunction %void
+                 %2 = OpFunction %void None %1
+                 %3 = OpLabel
+                      OpReturn
+                      OpFunctionEnd
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_1);
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(
+    TrimCapabilitiesPassTest,
+    StorageBuffer16BitAccess_RemainsSimplePointerUshortStorageBuffer_Vulkan1_1) {
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354
+  static_assert(spv::Capability::StorageUniformBufferBlock16 ==
+                spv::Capability::StorageBuffer16BitAccess);
+
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability StorageBuffer16BitAccess
+
+; CHECK:          OpCapability StorageBuffer16BitAccess
+; CHECK-NOT:      OpExtension "SPV_KHR_16bit_storage"
+; CHECK-NOT:      OpCapability Int16
+
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+                      OpDecorate %struct Block
+              %void = OpTypeVoid
+            %ushort = OpTypeInt 16 0
+            %struct = OpTypeStruct %ushort
+               %ptr = OpTypePointer StorageBuffer %struct
+                 %1 = OpTypeFunction %void
+                 %2 = OpFunction %void None %1
+                 %3 = OpLabel
+                      OpReturn
+                      OpFunctionEnd
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_1);
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(
+    TrimCapabilitiesPassTest,
+    StorageBuffer16BitAccess_RemainsSimplePointerUshortRecordBuffer_Vulkan1_1) {
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354
+  static_assert(spv::Capability::StorageUniformBufferBlock16 ==
+                spv::Capability::StorageBuffer16BitAccess);
+
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability RayTracingKHR
+                      OpCapability StorageBuffer16BitAccess
+                      OpExtension "SPV_KHR_ray_tracing"
+
+; CHECK-NOT:      OpCapability Int16
+; CHECK:          OpCapability RayTracingKHR
+; CHECK:          OpCapability StorageBuffer16BitAccess
+; CHECK:          OpExtension "SPV_KHR_ray_tracing"
+; CHECK-NOT:      OpExtension "SPV_KHR_16bit_storage"
+
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+                      OpDecorate %struct Block
+              %void = OpTypeVoid
+            %ushort = OpTypeInt 16 0
+            %struct = OpTypeStruct %ushort
+               %ptr = OpTypePointer ShaderRecordBufferKHR %struct
+                 %1 = OpTypeFunction %void
+                 %2 = OpFunction %void None %1
+                 %3 = OpLabel
+                      OpReturn
+                      OpFunctionEnd
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_1);
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       StorageBuffer16BitAccess_TrimRecordBuffer_Vulkan1_1) {
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354
+  static_assert(spv::Capability::StorageUniformBufferBlock16 ==
+                spv::Capability::StorageBuffer16BitAccess);
+
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability RayTracingKHR
+                      OpCapability StorageBuffer16BitAccess
+                      OpExtension "SPV_KHR_ray_tracing"
+
+; CHECK-NOT:      OpCapability RayTracingKHR
+; CHECK-NOT:      OpCapability StorageBuffer16BitAccess
+; CHECK-NOT:      OpExtension "SPV_KHR_ray_tracing"
+
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+                      OpDecorate %struct Block
+              %void = OpTypeVoid
+                 %1 = OpTypeFunction %void
+                 %2 = OpFunction %void None %1
+                 %3 = OpLabel
+                      OpReturn
+                      OpFunctionEnd
+  )";
+  SetTargetEnv(SPV_ENV_VULKAN_1_1);
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest,
+       StorageBuffer16BitAccess_RemovedSimplePointer) {
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/5354
+  static_assert(spv::Capability::StorageUniformBufferBlock16 ==
+                spv::Capability::StorageBuffer16BitAccess);
+
+  const std::string kTest = R"(
+                      OpCapability Shader
+                      OpCapability Float16
+                      OpCapability StorageBuffer16BitAccess
+                      OpExtension "SPV_KHR_16bit_storage"
+
+; CHECK-NOT:          OpCapability StorageBuffer16BitAccess
+; CHECK-NOT:          OpExtension "SPV_KHR_16bit_storage"
+
+                      OpMemoryModel Logical GLSL450
+                      OpEntryPoint GLCompute %2 "main"
+              %void = OpTypeVoid
+              %half = OpTypeFloat 16
+            %struct = OpTypeStruct %half
+               %ptr = OpTypePointer Function %struct
+                 %1 = OpTypeFunction %void
+                 %2 = OpFunction %void None %1
+                 %3 = OpLabel
+                      OpReturn
+                      OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
 TEST_F(TrimCapabilitiesPassTest, FragmentShaderInterlock_RemovedIfNotUsed) {
   const std::string kTest = R"(
                OpCapability Shader
@@ -2762,8 +3000,8 @@
 ; CHECK-NOT:   OpCapability GroupNonUniformArithmetic
                OpCapability GroupNonUniformClustered
 ; CHECK-NOT:   OpCapability GroupNonUniformClustered
-               OpCapability GroupNonUniformPartitionedNV
-; CHECK-NOT:   OpCapability GroupNonUniformPartitionedNV
+               OpCapability GroupNonUniformPartitionedEXT
+; CHECK-NOT:   OpCapability GroupNonUniformPartitionedEXT
                OpCapability GroupNonUniform
 ; CHECK-NOT:   OpCapability GroupNonUniform
                OpExtension "SPV_NV_shader_subgroup_partitioned"
@@ -2792,8 +3030,8 @@
 ; CHECK-NOT:       OpCapability GroupNonUniformArithmetic
                    OpCapability GroupNonUniformClustered
 ; CHECK-NOT:       OpCapability GroupNonUniformClustered
-                   OpCapability GroupNonUniformPartitionedNV
-; CHECK-NOT:       OpCapability GroupNonUniformPartitionedNV
+                   OpCapability GroupNonUniformPartitionedEXT
+; CHECK-NOT:       OpCapability GroupNonUniformPartitionedEXT
                    OpCapability GroupNonUniform
 ; CHECK:           OpCapability GroupNonUniform
                    OpCapability Shader
@@ -2828,8 +3066,8 @@
 ; CHECK-NOT:       OpCapability GroupNonUniformArithmetic
                    OpCapability GroupNonUniformClustered
 ; CHECK-NOT:       OpCapability GroupNonUniformClustered
-                   OpCapability GroupNonUniformPartitionedNV
-; CHECK-NOT:       OpCapability GroupNonUniformPartitionedNV
+                   OpCapability GroupNonUniformPartitionedEXT
+; CHECK-NOT:       OpCapability GroupNonUniformPartitionedEXT
                    OpCapability GroupNonUniform
 ; CHECK-NOT:       OpCapability GroupNonUniform
                    OpExtension "SPV_NV_shader_subgroup_partitioned"
@@ -2864,8 +3102,8 @@
 ; CHECK-NOT:       OpCapability GroupNonUniformArithmetic
                    OpCapability GroupNonUniformClustered
 ; CHECK-NOT:       OpCapability GroupNonUniformClustered
-                   OpCapability GroupNonUniformPartitionedNV
-; CHECK-NOT:       OpCapability GroupNonUniformPartitionedNV
+                   OpCapability GroupNonUniformPartitionedEXT
+; CHECK-NOT:       OpCapability GroupNonUniformPartitionedEXT
                    OpCapability GroupNonUniform
 ; CHECK-NOT:       OpCapability GroupNonUniform
                    OpExtension "SPV_NV_shader_subgroup_partitioned"
@@ -2900,8 +3138,8 @@
 ; CHECK:           OpCapability GroupNonUniformArithmetic
                    OpCapability GroupNonUniformClustered
 ; CHECK-NOT:       OpCapability GroupNonUniformClustered
-                   OpCapability GroupNonUniformPartitionedNV
-; CHECK-NOT:       OpCapability GroupNonUniformPartitionedNV
+                   OpCapability GroupNonUniformPartitionedEXT
+; CHECK-NOT:       OpCapability GroupNonUniformPartitionedEXT
                    OpCapability GroupNonUniform
 ; CHECK-NOT:       OpCapability GroupNonUniform
                    OpExtension "SPV_NV_shader_subgroup_partitioned"
@@ -2937,8 +3175,8 @@
 ; CHECK:           OpCapability GroupNonUniformArithmetic
                    OpCapability GroupNonUniformClustered
 ; CHECK-NOT:       OpCapability GroupNonUniformClustered
-                   OpCapability GroupNonUniformPartitionedNV
-; CHECK-NOT:       OpCapability GroupNonUniformPartitionedNV
+                   OpCapability GroupNonUniformPartitionedEXT
+; CHECK-NOT:       OpCapability GroupNonUniformPartitionedEXT
                    OpCapability GroupNonUniform
 ; CHECK-NOT:       OpCapability GroupNonUniform
                    OpExtension "SPV_NV_shader_subgroup_partitioned"
@@ -2974,8 +3212,8 @@
 ; CHECK:           OpCapability GroupNonUniformArithmetic
                    OpCapability GroupNonUniformClustered
 ; CHECK-NOT:       OpCapability GroupNonUniformClustered
-                   OpCapability GroupNonUniformPartitionedNV
-; CHECK-NOT:       OpCapability GroupNonUniformPartitionedNV
+                   OpCapability GroupNonUniformPartitionedEXT
+; CHECK-NOT:       OpCapability GroupNonUniformPartitionedEXT
                    OpCapability GroupNonUniform
 ; CHECK-NOT:       OpCapability GroupNonUniform
                    OpExtension "SPV_NV_shader_subgroup_partitioned"
@@ -3011,8 +3249,8 @@
 ; CHECK-NOT:       OpCapability GroupNonUniformArithmetic
                    OpCapability GroupNonUniformClustered
 ; CHECK:           OpCapability GroupNonUniformClustered
-                   OpCapability GroupNonUniformPartitionedNV
-; CHECK-NOT:       OpCapability GroupNonUniformPartitionedNV
+                   OpCapability GroupNonUniformPartitionedEXT
+; CHECK-NOT:       OpCapability GroupNonUniformPartitionedEXT
                    OpCapability GroupNonUniform
 ; CHECK-NOT:       OpCapability GroupNonUniform
                    OpExtension "SPV_NV_shader_subgroup_partitioned"
@@ -3068,10 +3306,10 @@
     // clang-format on
 };
 
-using TrimCapabilitiesPassTestSubgroupNV_Unsigned = PassTest<
+using TrimCapabilitiesPassTestSubgroupEXT_Unsigned = PassTest<
     ::testing::TestWithParam<std::tuple<SubgroupTestCase, std::string>>>;
-TEST_P(TrimCapabilitiesPassTestSubgroupNV_Unsigned,
-       GroupNonUniformPartitionedNV_Remains) {
+TEST_P(TrimCapabilitiesPassTestSubgroupEXT_Unsigned,
+       GroupNonUniformPartitionedEXT_Remains) {
   SubgroupTestCase test_case = std::get<0>(GetParam());
   const std::string operation = std::get<1>(GetParam());
 
@@ -3083,8 +3321,8 @@
 ; CHECK-NOT:       OpCapability GroupNonUniformArithmetic
                    OpCapability GroupNonUniformClustered
 ; CHECK-NOT:       OpCapability GroupNonUniformClustered
-                   OpCapability GroupNonUniformPartitionedNV
-; CHECK:           OpCapability GroupNonUniformPartitionedNV
+                   OpCapability GroupNonUniformPartitionedEXT
+; CHECK:           OpCapability GroupNonUniformPartitionedEXT
                    OpCapability GroupNonUniform
 ; CHECK-NOT:       OpCapability GroupNonUniform
                    OpExtension "SPV_NV_shader_subgroup_partitioned"
@@ -3120,14 +3358,14 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(
-    TrimCapabilitiesPassTestSubgroupNV_Unsigned_I,
-    TrimCapabilitiesPassTestSubgroupNV_Unsigned,
+    TrimCapabilitiesPassTestSubgroupEXT_Unsigned_I,
+    TrimCapabilitiesPassTestSubgroupEXT_Unsigned,
     ::testing::Combine(::testing::ValuesIn(kSubgroupTestCases),
-                       ::testing::Values("PartitionedReduceNV",
-                                         "PartitionedInclusiveScanNV",
-                                         "PartitionedExclusiveScanNV")),
+                       ::testing::Values("PartitionedReduceEXT",
+                                         "PartitionedInclusiveScanEXT",
+                                         "PartitionedExclusiveScanEXT")),
     [](const ::testing::TestParamInfo<
-        TrimCapabilitiesPassTestSubgroupNV_Unsigned::ParamType>& info) {
+        TrimCapabilitiesPassTestSubgroupEXT_Unsigned::ParamType>& info) {
       return std::get<0>(info.param).opcode + "_" + std::get<1>(info.param);
     });
 
@@ -3146,8 +3384,8 @@
 ; CHECK:           OpCapability GroupNonUniformArithmetic
                    OpCapability GroupNonUniformClustered
 ; CHECK-NOT:       OpCapability GroupNonUniformClustered
-                   OpCapability GroupNonUniformPartitionedNV
-; CHECK-NOT:       OpCapability GroupNonUniformPartitionedNV
+                   OpCapability GroupNonUniformPartitionedEXT
+; CHECK-NOT:       OpCapability GroupNonUniformPartitionedEXT
                    OpCapability GroupNonUniform
 ; CHECK-NOT:       OpCapability GroupNonUniform
                    OpExtension "SPV_NV_shader_subgroup_partitioned"
@@ -3207,8 +3445,8 @@
 ; CHECK-NOT:       OpCapability GroupNonUniformArithmetic
                    OpCapability GroupNonUniformClustered
 ; CHECK:           OpCapability GroupNonUniformClustered
-                   OpCapability GroupNonUniformPartitionedNV
-; CHECK-NOT:       OpCapability GroupNonUniformPartitionedNV
+                   OpCapability GroupNonUniformPartitionedEXT
+; CHECK-NOT:       OpCapability GroupNonUniformPartitionedEXT
                    OpCapability GroupNonUniform
 ; CHECK-NOT:       OpCapability GroupNonUniform
                    OpExtension "SPV_NV_shader_subgroup_partitioned"
@@ -3305,6 +3543,259 @@
   EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
 }
 
+TEST_F(TrimCapabilitiesPassTest, QuadControlKHR_RemoveIfNotUsed) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability QuadControlKHR
+; CHECK-NOT:   OpCapability QuadControlKHR
+               OpExtension "SPV_KHR_quad_control"
+; CHECK-NOT:   OpExtension "SPV_KHR_quad_control"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %out_var_SV_Target
+               OpExecutionMode %main OriginUpperLeft
+               OpSource HLSL 660
+               OpName %out_var_SV_Target "out.var.SV_Target"
+               OpName %main "main"
+               OpDecorate %out_var_SV_Target Location 0
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
+       %main = OpFunction %void None %7
+          %8 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, QuadControlKHR_RemainsWithQuadAny) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability QuadControlKHR
+; CHECK:       OpCapability QuadControlKHR
+               OpExtension "SPV_KHR_quad_control"
+; CHECK:       OpExtension "SPV_KHR_quad_control"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %out_var_SV_Target
+               OpExecutionMode %main OriginUpperLeft
+               OpSource HLSL 660
+               OpName %out_var_SV_Target "out.var.SV_Target"
+               OpName %main "main"
+               OpDecorate %out_var_SV_Target Location 0
+       %bool = OpTypeBool
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
+       %main = OpFunction %void None %7
+          %8 = OpLabel
+       %true = OpConstantTrue %bool
+         %10 = OpGroupNonUniformQuadAnyKHR %bool %true
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, PhysicalStorageBuffer_RecursiveTypes) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability PhysicalStorageBufferAddresses
+; CHECK:       OpCapability PhysicalStorageBufferAddresses
+               OpExtension "SPV_KHR_physical_storage_buffer"
+; CHECK:       OpExtension "SPV_KHR_physical_storage_buffer"
+               OpMemoryModel PhysicalStorageBuffer64 GLSL450
+               OpEntryPoint Fragment %1 "main"
+               OpExecutionMode %1 OriginUpperLeft
+               OpSource HLSL 600
+               OpMemberDecorate %_struct_2 0 Offset 0
+               OpMemberDecorate %_struct_2 1 Offset 16
+               OpTypeForwardPointer %_ptr_PhysicalStorageBuffer__struct_2 PhysicalStorageBuffer
+        %int = OpTypeInt 32 1
+  %_struct_2 = OpTypeStruct %int %_ptr_PhysicalStorageBuffer__struct_2
+%_ptr_PhysicalStorageBuffer__struct_2 = OpTypePointer PhysicalStorageBuffer %_struct_2
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+          %1 = OpFunction %void None %6
+          %7 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+TEST_F(TrimCapabilitiesPassTest, Geometry_Remains) {
+  const std::string kTest = R"(
+               OpCapability Geometry
+; CHECK:       OpCapability Geometry
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Geometry %gs_main "gs_main" %gl_Position
+               OpExecutionMode %gs_main OutputVertices 3
+               OpExecutionMode %gs_main Invocations 1
+               OpExecutionMode %gs_main Triangles
+               OpExecutionMode %gs_main OutputTriangleStrip
+               OpSource HLSL 660
+               OpName %gs_main "gs_main"
+               OpDecorate %gl_Position BuiltIn Position
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+%gl_Position = OpVariable %_ptr_Output_v4float Output
+    %gs_main = OpFunction %void None %7
+          %8 = OpLabel
+               OpEmitVertex
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
+// FIXME(6277): enable once spirv-opt supports SPV_INTEL_function_variants
+#if 0
+TEST_F(TrimCapabilitiesPassTest, Geometry_RemainsIntel) {
+  const std::string kTest = R"(
+                OpCapability Geometry
+ ; CHECK:       OpCapability Geometry
+                OpCapability SpecConditionalINTEL
+                OpExtension "SPV_INTEL_function_variants"
+                OpMemoryModel Logical GLSL450
+                OpConditionalEntryPointINTEL %false Geometry %gs_main "gs_main"
+                OpExecutionMode %gs_main OutputVertices 3
+                OpExecutionMode %gs_main Invocations 1
+                OpExecutionMode %gs_main Triangles
+                OpExecutionMode %gs_main OutputTriangleStrip
+                OpSource HLSL 660
+                OpName %gs_main "gs_main"
+                OpDecorate %gl_Position BuiltIn Position
+        %bool = OpTypeBool
+       %false = OpSpecConstantFalse %bool
+       %float = OpTypeFloat 32
+     %v4float = OpTypeVector %float 4
+ %_ptr_Output_v4float = OpTypePointer Output %v4float
+        %void = OpTypeVoid
+           %7 = OpTypeFunction %void
+ %gl_Position = OpVariable %_ptr_Output_v4float Output
+     %gs_main = OpFunction %void None %7
+           %8 = OpLabel
+                OpEmitVertex
+                OpReturn
+                OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+#endif
+
+TEST_F(TrimCapabilitiesPassTest, Geometry_Removed) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability Geometry
+; CHECK-NOT:   OpCapability Geometry
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %ps_main "ps_main" %in_var_POSITION %out_var_SV_Target
+               OpExecutionMode %ps_main OriginUpperLeft
+               OpSource HLSL 660
+               OpName %in_var_POSITION "in.var.POSITION"
+               OpName %out_var_SV_Target "out.var.SV_Target"
+               OpName %ps_main "ps_main"
+               OpDecorate %in_var_POSITION Location 0
+               OpDecorate %out_var_SV_Target Location 0
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%in_var_POSITION = OpVariable %_ptr_Input_v4float Input
+%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
+    %ps_main = OpFunction %void None %9
+         %10 = OpLabel
+         %11 = OpLoad %v4float %in_var_POSITION
+               OpStore %out_var_SV_Target %11
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+
+// FIXME(6277): enable once spirv-opt supports SPV_INTEL_function_variants
+#if 0
+TEST_F(TrimCapabilitiesPassTest, Geometry_RemovedIntel) {
+  const std::string kTest = R"(
+               OpCapability Shader
+               OpCapability Geometry
+; CHECK-NOT:   OpCapability Geometry
+               OpCapability SpecConditionalINTEL
+               OpExtension "SPV_INTEL_function_variants"
+               OpMemoryModel Logical GLSL450
+               OpConditionalEntryPointINTEL %false Fragment %ps_main "ps_main" %in_var_POSITION %out_var_SV_Target
+               OpExecutionMode %ps_main OriginUpperLeft
+               OpSource HLSL 660
+               OpName %in_var_POSITION "in.var.POSITION"
+               OpName %out_var_SV_Target "out.var.SV_Target"
+               OpName %ps_main "ps_main"
+               OpDecorate %in_var_POSITION Location 0
+               OpDecorate %out_var_SV_Target Location 0
+       %bool = OpTypeBool
+      %false = OpSpecConstantFalse %bool
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%in_var_POSITION = OpVariable %_ptr_Input_v4float Input
+%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
+    %ps_main = OpFunction %void None %9
+         %10 = OpLabel
+         %11 = OpLoad %v4float %in_var_POSITION
+               OpStore %out_var_SV_Target %11
+               OpReturn
+               OpFunctionEnd
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
+}
+#endif
+
+TEST_F(TrimCapabilitiesPassTest, CheckNop) {
+  const std::string kTest = R"(
+               OpCapability Shader
+; CHECK: OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+          %1 = OpFunction %void None %3
+          %6 = OpLabel
+               OpNop
+               OpReturn
+               OpFunctionEnd;
+  )";
+  const auto result =
+      SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
+  EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
+}
+
 INSTANTIATE_TEST_SUITE_P(
     TrimCapabilitiesPassTestSubgroupClustered_Unsigned_I,
     TrimCapabilitiesPassTestSubgroupClustered_Unsigned,
diff --git a/test/opt/type_manager_test.cpp b/test/opt/type_manager_test.cpp
index 865bfbb..29feaa0 100644
--- a/test/opt/type_manager_test.cpp
+++ b/test/opt/type_manager_test.cpp
@@ -1,4 +1,6 @@
 // Copyright (c) 2016 Google Inc.
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -147,6 +149,7 @@
   types.emplace_back(new Pointer(f32, spv::StorageClass::Input));
   types.emplace_back(new Pointer(sts32f32, spv::StorageClass::Function));
   types.emplace_back(new Pointer(a42f32, spv::StorageClass::Function));
+  types.emplace_back(new Pointer(nullptr, spv::StorageClass::Uniform));
 
   // Function
   types.emplace_back(new Function(voidt, {}));
@@ -174,6 +177,27 @@
   types.emplace_back(new CooperativeMatrixKHR(f32, 8, 8, 8, 1002));
   types.emplace_back(new RayQueryKHR());
   types.emplace_back(new HitObjectNV());
+  types.emplace_back(new HitObjectEXT());
+  types.emplace_back(new CooperativeVectorNV(f32, 16));
+
+  // SPV_AMDX_shader_enqueue
+  types.emplace_back(new NodePayloadArrayAMDX(sts32f32));
+
+  // Tensors
+  types.emplace_back(new TensorARM(f32));
+  auto* tensor_f32 = types.back().get();
+  types.emplace_back(new TensorARM(f32, 4));
+  auto* tensor_f32_ranked = types.back().get();
+  types.emplace_back(new TensorARM(f32, 4, 44));
+  auto* tensor_f32_shaped = types.back().get();
+
+  // BufferEXT (SPV_EXT_descriptor_heap)
+  types.emplace_back(new BufferEXT(spv::StorageClass::StorageBuffer));
+
+  // Graph
+  types.emplace_back(new GraphARM(0, {tensor_f32}));
+  types.emplace_back(new GraphARM(1, {tensor_f32_ranked, tensor_f32_ranked}));
+  types.emplace_back(new GraphARM(1, {tensor_f32_shaped, tensor_f32_shaped}));
 
   types.emplace_back(new TensorLayoutNV(1002, 1000));
   types.emplace_back(new TensorViewNV(1002, 1003, {1000, 1001}));
@@ -243,6 +267,15 @@
     %cm   = OpTypeCooperativeMatrixNV %f64 %id4 %id4 %id4
     %id2    = OpConstant %u32 2
     %cmkhr  = OpTypeCooperativeMatrixKHR %f64 %id4 %id4 %id4 %id2
+    %untyped = OpTypeUntypedPointerKHR Uniform
+    ; ID 43
+    %ts_shape = OpConstantComposite %a5u32 %id4 %id4 %id4 %id4
+    %ts  = OpTypeTensorARM %u32
+    %tsr = OpTypeTensorARM %u32 %id4
+    %tss = OpTypeTensorARM %u32 %id4 %ts_shape
+    %g_noin = OpTypeGraphARM 0 %ts
+    %g_onein = OpTypeGraphARM 1 %tsr %tsr
+    %g_shaped = OpTypeGraphARM 1 %tss %tss
   )";
 
   std::vector<std::pair<uint32_t, std::string>> type_id_strs = {
@@ -282,10 +315,20 @@
       {38, "[sint32, id(34), words(2,34)]"},
       {39, "<float64, 6, 6, 6>"},
       {41, "<float64, 6, 6, 6, 40>"},
+      {42, "untyped_ptr 2*"},  // Include storage class number
+      // Id 43 is OpConstantComposite %a5u32 %id4 %id4 %id4 %id4
+      {44, "tensor<uint32, id(0), id(0)>"},
+      {45, "tensor<uint32, id(6), id(0)>"},
+      {46, "tensor<uint32, id(6), id(43)>"},
+      {47, "graph<0,tensor<uint32, id(0), id(0)>>"},
+      {48,
+       "graph<1,tensor<uint32, id(6), id(0)>,tensor<uint32, id(6), id(0)>>"},
+      {49,
+       "graph<1,tensor<uint32, id(6), id(43)>,tensor<uint32, id(6), id(43)>>"},
   };
 
   std::unique_ptr<IRContext> context =
-      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text);
+      BuildModule(SPV_ENV_UNIVERSAL_1_4, nullptr, text);
   ASSERT_NE(nullptr, context.get());  // It assembled
   TypeManager manager(nullptr, context.get());
 
@@ -1040,8 +1083,11 @@
 ; CHECK: [[uniform_ptr:%\w+]] = OpTypePointer Uniform [[uint]]
 ; CHECK: [[uint2:%\w+]] = OpConstant [[uint]] 2
 ; CHECK: [[uint8:%\w+]] = OpConstant [[uint]] 8
+; CHECK: [[uint4:%\w+]] = OpConstant [[uint]] 4
+; CHECK: [[uint_arr4:%\w+]] = OpTypeArray [[uint]] [[uint4]]
 ; CHECK: [[uint24:%\w+]] = OpConstant [[uint]] 24
 ; CHECK: [[uint42:%\w+]] = OpConstant [[uint]] 42
+; CHECK: [[uint_arr4_44:%\w+]] = OpConstantComposite [[uint_arr4]] [[uint4]] [[uint4]] [[uint4]] [[uint4]]
 ; CHECK: [[uint100:%\w+]] = OpConstant [[uint]] 100
 ; CHECK: [[void:%\w+]] = OpTypeVoid
 ; CHECK: [[bool:%\w+]] = OpTypeBool
@@ -1098,6 +1144,12 @@
 ; CHECK: OpTypeCooperativeMatrixKHR [[f32]] [[uint8]] [[uint8]] [[uint8]] [[uint2]]
 ; CHECK: OpTypeRayQueryKHR
 ; CHECK: OpTypeHitObjectNV
+; CHECK: [[tensor_f32:%\w+]] = OpTypeTensorARM [[f32]]
+; CHECK: [[tensor_f32_ranked:%\w+]] = OpTypeTensorARM [[f32]] [[uint4]]
+; CHECK: [[tensor_f32_shaped:%\w+]] = OpTypeTensorARM [[f32]] [[uint4]] [[uint_arr4_44]]
+; CHECK: OpTypeGraphARM 0 [[tensor_f32]]
+; CHECK: OpTypeGraphARM 1 [[tensor_f32_ranked]] [[tensor_f32_ranked]]
+; CHECK: OpTypeGraphARM 1 [[tensor_f32_shaped]] [[tensor_f32_shaped]]
 OpCapability Shader
 OpCapability Int64
 OpCapability Linkage
@@ -1109,8 +1161,11 @@
 %1001 = OpConstant %uint 1
 %1002 = OpConstant %uint 2
 %8 = OpConstant %uint 8
+%4 = OpConstant %uint 4
+%5 = OpTypeArray %uint %4
 %24 = OpConstant %uint 24
 %42 = OpConstant %uint 42
+%44 = OpConstantComposite %5 %4 %4 %4 %4
 %100 = OpConstant %uint 100
 %1003 = OpConstantFalse %bool
   )";
@@ -1239,6 +1294,35 @@
   EXPECT_EQ(id, 1201);
 }
 
+TEST(TypeManager, AttachLinkageDecoration) {
+  const std::string text = R"(
+      OpCapability Shader
+      OpCapability Linkage
+      OpMemoryModel Logical GLSL450
+      OpDecorate %1000 LinkageAttributes "_1000" Export
+       %800 = OpTypeInt 32 0
+      %1000 = OpTypeStruct %800
+      %1200 = OpTypeStruct %800
+  )";
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_5, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+  TypeManager manager(nullptr, context.get());
+
+  constexpr uint32_t source_id = 1000u;
+  constexpr uint32_t target_id = 1200u;
+  std::vector<Instruction*> decorations =
+      context->get_decoration_mgr()->GetDecorationsFor(source_id, true);
+  Type* type = context->get_type_mgr()->GetType(target_id);
+  for (auto dec : decorations) {
+    manager.AttachDecoration(*dec, type);
+  }
+  EXPECT_FALSE(type->decoration_empty());
+  EXPECT_TRUE(
+      type->HasSameDecorations(context->get_type_mgr()->GetType(source_id)));
+}
+
 }  // namespace
 }  // namespace analysis
 }  // namespace opt
diff --git a/test/opt/types_test.cpp b/test/opt/types_test.cpp
index 4ceeb14..82ddccf 100644
--- a/test/opt/types_test.cpp
+++ b/test/opt/types_test.cpp
@@ -226,6 +226,7 @@
   types.emplace_back(new Pointer(sts32f32, spv::StorageClass::Function));
   types.emplace_back(new Pointer(a42f32, spv::StorageClass::Function));
   types.emplace_back(new Pointer(voidt, spv::StorageClass::Function));
+  types.emplace_back(new Pointer(nullptr, spv::StorageClass::Uniform));
 
   // Function
   types.emplace_back(new Function(voidt, {}));
@@ -331,6 +332,50 @@
   EXPECT_EQ(struct_100xf32.NumberOfComponents(), 100);
 }
 
+TEST(Types, GetByteOffset) {
+  Integer i32(32, true);
+  Float f64(64);
+  Vector v4i32(&i32, 4);
+
+  // Struct: { i32, v4i32, f64 }
+  Struct s1({&i32, &v4i32, &f64});
+  s1.AddMemberDecoration(0, {uint32_t(spv::Decoration::Offset), 0});
+  s1.AddMemberDecoration(1, {uint32_t(spv::Decoration::Offset), 16});
+  s1.AddMemberDecoration(2, {uint32_t(spv::Decoration::Offset), 32});
+
+  EXPECT_EQ(s1.GetByteOffset({0}).value(), 0);
+  EXPECT_EQ(s1.GetByteOffset({1}).value(), 16);
+  EXPECT_EQ(s1.GetByteOffset({2}).value(), 32);
+
+  // Into the vector
+  EXPECT_EQ(s1.GetByteOffset({1, 0}).value(), 16);
+  EXPECT_EQ(s1.GetByteOffset({1, 1}).value(), 20);
+  EXPECT_EQ(s1.GetByteOffset({1, 3}).value(), 28);
+
+  // Array of struct: { i32, v4i32, f64 }[10]
+  Array::LengthInfo len_info{1, {Array::LengthInfo::kConstant, 10}};
+  Array arr(&s1, len_info);
+  arr.AddDecoration({uint32_t(spv::Decoration::ArrayStride), 48});
+
+  // arr[2].v4i32[3]
+  EXPECT_EQ(arr.GetByteOffset({2, 1, 3}).value(), 48 * 2 + 16 + 12);
+
+  // Matrix: 4x4 of f64
+  Vector v4f64(&f64, 4);
+  Matrix m(&v4f64, 4);
+  m.AddDecoration({uint32_t(spv::Decoration::MatrixStride), 32});
+
+  // m[1][2]
+  EXPECT_EQ(m.GetByteOffset({1, 2}).value(), 32 * 1 + 8 * 2);
+
+  // Missing decorations -> returns nullopt
+  Struct s_no_deco({&i32, &f64});
+  EXPECT_FALSE(s_no_deco.GetByteOffset({1}).has_value());
+
+  Array arr_no_deco(&i32, len_info);
+  EXPECT_FALSE(arr_no_deco.GetByteOffset({2}).has_value());
+}
+
 TEST(Types, IntSignedness) {
   std::vector<bool> signednesses = {true, false, false, true};
   std::vector<std::unique_ptr<Integer>> types;
@@ -364,6 +409,20 @@
   }
 }
 
+TEST(Types, FloatFPEncoding) {
+  std::vector<spv::FPEncoding> encodings = {
+      spv::FPEncoding::BFloat16KHR,
+      spv::FPEncoding::Max,
+  };
+  std::vector<std::unique_ptr<Float>> types;
+  for (spv::FPEncoding encoding : encodings) {
+    types.emplace_back(new Float(16, encoding));
+  }
+  for (size_t i = 0; i < encodings.size(); i++) {
+    EXPECT_EQ(encodings[i], types[i]->encoding());
+  }
+}
+
 TEST(Types, VectorElementCount) {
   auto s32 = MakeUnique<Integer>(32, true);
   for (uint32_t c : {2, 3, 4}) {
@@ -443,6 +502,15 @@
   }
 }
 
+TEST(Types, UntypedPointer) {
+  std::unique_ptr<Type> type(new Pointer(nullptr, spv::StorageClass::Uniform));
+  const auto untyped = type->AsPointer();
+  EXPECT_NE(untyped, nullptr);
+  EXPECT_TRUE(untyped->is_untyped());
+  EXPECT_EQ(untyped->pointee_type(), nullptr);
+  EXPECT_EQ(untyped->storage_class(), spv::StorageClass::Uniform);
+}
+
 }  // namespace
 }  // namespace analysis
 }  // namespace opt
diff --git a/test/opt/upgrade_memory_model_test.cpp b/test/opt/upgrade_memory_model_test.cpp
index d213b8b..9c92104 100644
--- a/test/opt/upgrade_memory_model_test.cpp
+++ b/test/opt/upgrade_memory_model_test.cpp
@@ -434,6 +434,394 @@
   SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
 }
 
+TEST_F(UpgradeMemoryModelTest, VariablePointerStoreToFunctionAndPrivate) {
+  const std::string text = R"(
+; CHECK-NOT: OpDecorate {{%\w+}} Coherent
+; CHECK-NOT: OpStore {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer {{%\w+}}
+OpCapability Shader
+OpCapability Linkage
+OpCapability VariablePointersStorageBuffer
+OpExtension "SPV_KHR_variable_pointers"
+OpMemoryModel Logical GLSL450
+OpDecorate %_runtimearr_uint ArrayStride 4
+OpMemberDecorate %_struct_4 0 Offset 0
+OpMemberDecorate %_struct_4 0 Coherent
+OpDecorate %_struct_4 Block
+%void = OpTypeVoid
+%uint = OpTypeInt 32 0
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%_struct_4 = OpTypeStruct %_runtimearr_uint
+%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4
+%_ptr_Function_uint = OpTypePointer Function %uint
+%_ptr_Function__ptr_StorageBuffer__struct_4 = OpTypePointer Function %_ptr_StorageBuffer__struct_4
+%functy = OpTypeFunction %void %_ptr_Function__ptr_StorageBuffer__struct_4 %_ptr_Function_uint
+%_ptr_Private__ptr_StorageBuffer__struct_4 = OpTypePointer Private %_ptr_StorageBuffer__struct_4
+%var_bufPtr_pri = OpVariable %_ptr_Private__ptr_StorageBuffer__struct_4 Private
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%func = OpFunction %void None %functy
+%param_buf = OpFunctionParameter %_ptr_Function__ptr_StorageBuffer__struct_4
+%bb_entry = OpLabel
+%buf = OpLoad %_ptr_StorageBuffer__struct_4 %param_buf
+OpStore %param_buf %buf
+OpStore %var_bufPtr_pri %buf
+OpReturn
+OpFunctionEnd
+)";
+  SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, VariablePointerIndirectLoad) {
+  const std::string text = R"(
+; CHECK-NOT: OpDecorate {{%\w+}} Coherent
+; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
+; CHECK: [[buffer:%\w+]] = OpLoad {{%\w+}} {{%\w+}}
+; CHECK: [[ld_gep:%\w+]] = OpAccessChain {{%\w+}} [[buffer]] {{%\w+}} {{%\w+}}
+; CHECK: OpLoad {{%\w+}} [[ld_gep]] MakePointerVisible|NonPrivatePointer [[scope]]
+OpCapability Shader
+OpCapability Linkage
+OpCapability VariablePointersStorageBuffer
+OpExtension "SPV_KHR_variable_pointers"
+OpMemoryModel Logical GLSL450
+OpDecorate %_runtimearr_uint ArrayStride 4
+OpMemberDecorate %_struct_4 0 Offset 0
+OpMemberDecorate %_struct_4 0 Coherent
+OpDecorate %_struct_4 Block
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%_struct_4 = OpTypeStruct %_runtimearr_uint
+%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4
+%_ptr_Function_uint = OpTypePointer Function %uint
+%_ptr_Function__ptr_StorageBuffer__struct_4 = OpTypePointer Function %_ptr_StorageBuffer__struct_4
+%functy = OpTypeFunction %uint %_ptr_Function__ptr_StorageBuffer__struct_4 %_ptr_Function_uint
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%func = OpFunction %uint None %functy
+%param_buf = OpFunctionParameter %_ptr_Function__ptr_StorageBuffer__struct_4
+%param_offset = OpFunctionParameter %_ptr_Function_uint
+%bb_entry = OpLabel
+%buf = OpLoad %_ptr_StorageBuffer__struct_4 %param_buf
+%offset = OpLoad %uint %param_offset
+%ld_gep = OpAccessChain %_ptr_StorageBuffer_uint %buf %uint_0 %offset
+%data = OpLoad %uint %ld_gep
+OpReturnValue %data
+OpFunctionEnd
+)";
+  SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, VariablePointerIndirectLoadWithPrivateAS) {
+  const std::string text = R"(
+; CHECK-NOT: OpDecorate {{%\w+}} Coherent
+; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
+; CHECK: [[ld_gep:%\w+]] = OpAccessChain {{%\w+}} [[buffer:%\w+]] {{%\w+}} {{%\w+}}
+; CHECK: OpLoad {{%\w+}} [[ld_gep]] MakePointerVisible|NonPrivatePointer [[scope]]
+OpCapability Shader
+OpCapability Linkage
+OpCapability VariablePointersStorageBuffer
+OpExtension "SPV_KHR_variable_pointers"
+OpMemoryModel Logical GLSL450
+OpDecorate %_runtimearr_uint ArrayStride 4
+OpMemberDecorate %_struct_4 0 Offset 0
+OpMemberDecorate %_struct_4 0 Coherent
+OpDecorate %_struct_4 Block
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%_struct_4 = OpTypeStruct %_runtimearr_uint
+%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4
+%_ptr_Function_uint = OpTypePointer Function %uint
+%_ptr_Function__ptr_StorageBuffer__struct_4 = OpTypePointer Function %_ptr_StorageBuffer__struct_4
+%_ptr_Private__ptr_StorageBuffer__struct_4 = OpTypePointer Private %_ptr_StorageBuffer__struct_4
+%var_bufPtr_pri = OpVariable %_ptr_Private__ptr_StorageBuffer__struct_4 Private
+%functy = OpTypeFunction %uint %_ptr_Function__ptr_StorageBuffer__struct_4 %_ptr_Function_uint
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%func = OpFunction %uint None %functy
+%param_buf = OpFunctionParameter %_ptr_Function__ptr_StorageBuffer__struct_4
+%param_offset = OpFunctionParameter %_ptr_Function_uint
+%bb_entry = OpLabel
+%buf0 = OpLoad %_ptr_StorageBuffer__struct_4 %param_buf
+OpStore %var_bufPtr_pri %buf0
+%offset = OpLoad %uint %param_offset
+%buf1 = OpLoad %_ptr_StorageBuffer__struct_4 %var_bufPtr_pri
+%ld_gep = OpAccessChain %_ptr_StorageBuffer_uint %buf1 %uint_0 %offset
+%data = OpLoad %uint %ld_gep
+OpReturnValue %data
+OpFunctionEnd
+)";
+  SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, VariablePointerIndirectStore) {
+  const std::string text = R"(
+; CHECK-NOT: OpDecorate {{%\w+}} Coherent
+; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
+; CHECK: [[st_gep:%\w+]] = OpAccessChain
+; CHECK: OpStore [[st_gep]] {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]]
+OpCapability Shader
+OpCapability Linkage
+OpCapability VariablePointersStorageBuffer
+OpExtension "SPV_KHR_variable_pointers"
+OpMemoryModel Logical GLSL450
+OpDecorate %_runtimearr_uint ArrayStride 4
+OpMemberDecorate %_struct_4 0 Offset 0
+OpMemberDecorate %_struct_4 0 Coherent
+OpDecorate %_struct_4 Block
+%uint = OpTypeInt 32 0
+%void = OpTypeVoid
+%uint_0 = OpConstant %uint 0
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%_struct_4 = OpTypeStruct %_runtimearr_uint
+%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4
+%_ptr_Function_uint = OpTypePointer Function %uint
+%_ptr_Function__ptr_StorageBuffer__struct_4 = OpTypePointer Function %_ptr_StorageBuffer__struct_4
+%functy = OpTypeFunction %void %_ptr_Function__ptr_StorageBuffer__struct_4 %_ptr_Function_uint
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%func = OpFunction %void None %functy
+%param_buf = OpFunctionParameter %_ptr_Function__ptr_StorageBuffer__struct_4
+%param_offset = OpFunctionParameter %_ptr_Function_uint
+%bb_entry = OpLabel
+%buf = OpLoad %_ptr_StorageBuffer__struct_4 %param_buf
+%offset = OpLoad %uint %param_offset
+%st_gep = OpAccessChain %_ptr_StorageBuffer_uint %buf %uint_0 %offset
+OpStore %st_gep %uint_0
+OpReturn
+OpFunctionEnd
+)";
+  SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, VariablePointerIndirectStoreWithPrivateAS) {
+  const std::string text = R"(
+; CHECK-NOT: OpDecorate {{%\w+}} Coherent
+; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
+; CHECK: [[st_gep:%\w+]] = OpAccessChain
+; CHECK: OpStore [[st_gep]] {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]]
+OpCapability Shader
+OpCapability Linkage
+OpCapability VariablePointersStorageBuffer
+OpExtension "SPV_KHR_variable_pointers"
+OpMemoryModel Logical GLSL450
+OpDecorate %_runtimearr_uint ArrayStride 4
+OpMemberDecorate %_struct_4 0 Offset 0
+OpMemberDecorate %_struct_4 0 Coherent
+OpDecorate %_struct_4 Block
+%uint = OpTypeInt 32 0
+%void = OpTypeVoid
+%uint_0 = OpConstant %uint 0
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%_struct_4 = OpTypeStruct %_runtimearr_uint
+%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4
+%_ptr_Function_uint = OpTypePointer Function %uint
+%_ptr_Function__ptr_StorageBuffer__struct_4 = OpTypePointer Function %_ptr_StorageBuffer__struct_4
+%_ptr_Private__ptr_StorageBuffer__struct_4 = OpTypePointer Private %_ptr_StorageBuffer__struct_4
+%var_bufPtr_pri = OpVariable %_ptr_Private__ptr_StorageBuffer__struct_4 Private
+%functy = OpTypeFunction %void %_ptr_Function__ptr_StorageBuffer__struct_4 %_ptr_Function_uint
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%func = OpFunction %void None %functy
+%param_buf = OpFunctionParameter %_ptr_Function__ptr_StorageBuffer__struct_4
+%param_offset = OpFunctionParameter %_ptr_Function_uint
+%bb_entry = OpLabel
+%buf0 = OpLoad %_ptr_StorageBuffer__struct_4 %param_buf
+OpStore %var_bufPtr_pri %buf0
+%offset = OpLoad %uint %param_offset
+%buf1 = OpLoad %_ptr_StorageBuffer__struct_4 %var_bufPtr_pri
+%st_gep = OpAccessChain %_ptr_StorageBuffer_uint %buf1 %uint_0 %offset
+OpStore %st_gep %uint_0
+OpReturn
+OpFunctionEnd
+)";
+  SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, VariablePointerIndirectSelect) {
+  const std::string text = R"(
+; CHECK-NOT: OpDecorate {{%\w+}} Coherent
+; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
+; CHECK: [[buffer:%\w+]] = OpSelect {{%\w+}} {{%\w+}}
+; CHECK: [[ld_gep:%\w+]] = OpAccessChain {{%\w+}} [[buffer]] {{%\w+}} {{%\w+}}
+; CHECK: OpLoad {{%\w+}} [[ld_gep]] MakePointerVisible|NonPrivatePointer [[scope]]
+OpCapability Shader
+OpCapability Linkage
+OpCapability VariablePointersStorageBuffer
+OpExtension "SPV_KHR_variable_pointers"
+OpMemoryModel Logical GLSL450
+OpDecorate %_runtimearr_uint ArrayStride 4
+OpMemberDecorate %_struct_4 0 Offset 0
+OpMemberDecorate %_struct_4 0 Coherent
+OpDecorate %_struct_4 Block
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%_struct_4 = OpTypeStruct %_runtimearr_uint
+%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4
+%_ptr_Function_uint = OpTypePointer Function %uint
+%_ptr_Function__ptr_StorageBuffer__struct_4 = OpTypePointer Function %_ptr_StorageBuffer__struct_4
+%functy = OpTypeFunction %uint %_ptr_Function__ptr_StorageBuffer__struct_4 %_ptr_Function_uint
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%null = OpConstantNull %_ptr_StorageBuffer__struct_4
+%func = OpFunction %uint None %functy
+%param_buf = OpFunctionParameter %_ptr_Function__ptr_StorageBuffer__struct_4
+%param_offset = OpFunctionParameter %_ptr_Function_uint
+%bb_entry = OpLabel
+%buf = OpLoad %_ptr_StorageBuffer__struct_4 %param_buf
+%select = OpSelect %_ptr_StorageBuffer__struct_4 %true %buf %null
+%offset = OpLoad %uint %param_offset
+%ld_gep = OpAccessChain %_ptr_StorageBuffer_uint %select %uint_0 %offset
+%data = OpLoad %uint %ld_gep
+OpReturnValue %data
+OpFunctionEnd
+)";
+  SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, VariablePointerIndirectCopied) {
+  const std::string text = R"(
+; CHECK-NOT: OpDecorate {{%\w+}} Coherent
+; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
+; CHECK: [[buffer:%\w+]] = OpLoad {{%\w+}} {{%\w+}}
+; CHECK: [[ld_gep:%\w+]] = OpAccessChain {{%\w+}} [[buffer]] {{%\w+}} {{%\w+}}
+; CHECK: OpLoad {{%\w+}} [[ld_gep]] MakePointerVisible|NonPrivatePointer [[scope]]
+OpCapability Shader
+OpCapability Linkage
+OpCapability VariablePointersStorageBuffer
+OpExtension "SPV_KHR_variable_pointers"
+OpMemoryModel Logical GLSL450
+OpDecorate %_runtimearr_uint ArrayStride 4
+OpMemberDecorate %_struct_4 0 Offset 0
+OpMemberDecorate %_struct_4 0 Coherent
+OpDecorate %_struct_4 Block
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%_struct_4 = OpTypeStruct %_runtimearr_uint
+%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4
+%_ptr_Function_uint = OpTypePointer Function %uint
+%_ptr_Function__ptr_StorageBuffer__struct_4 = OpTypePointer Function %_ptr_StorageBuffer__struct_4
+%functy = OpTypeFunction %uint %_ptr_Function__ptr_StorageBuffer__struct_4 %_ptr_Function_uint
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%func = OpFunction %uint None %functy
+%param_buf = OpFunctionParameter %_ptr_Function__ptr_StorageBuffer__struct_4
+%param_offset = OpFunctionParameter %_ptr_Function_uint
+%bb_entry = OpLabel
+%copy = OpCopyObject %_ptr_Function__ptr_StorageBuffer__struct_4 %param_buf
+%buf = OpLoad %_ptr_StorageBuffer__struct_4 %copy
+%offset = OpLoad %uint %param_offset
+%ld_gep = OpAccessChain %_ptr_StorageBuffer_uint %buf %uint_0 %offset
+%data = OpLoad %uint %ld_gep
+OpReturnValue %data
+OpFunctionEnd
+)";
+  SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, VariablePointerIndirectPhi) {
+  const std::string text = R"(
+; CHECK-NOT: OpDecorate {{%\w+}} Coherent
+; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
+; CHECK: [[buffer:%\w+]] = OpPhi {{%\w+}} {{%\w+}}
+; CHECK: [[ld_gep:%\w+]] = OpAccessChain {{%\w+}} [[buffer]] {{%\w+}} {{%\w+}}
+; CHECK: OpLoad {{%\w+}} [[ld_gep]] MakePointerVisible|NonPrivatePointer [[scope]]
+OpCapability Shader
+OpCapability Linkage
+OpCapability VariablePointersStorageBuffer
+OpExtension "SPV_KHR_variable_pointers"
+OpMemoryModel Logical GLSL450
+OpDecorate %_runtimearr_uint ArrayStride 4
+OpMemberDecorate %_struct_4 0 Offset 0
+OpMemberDecorate %_struct_4 0 Coherent
+OpDecorate %_struct_4 Block
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%_struct_4 = OpTypeStruct %_runtimearr_uint
+%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4
+%_ptr_Function_uint = OpTypePointer Function %uint
+%_ptr_Function__ptr_StorageBuffer__struct_4 = OpTypePointer Function %_ptr_StorageBuffer__struct_4
+%functy = OpTypeFunction %uint %_ptr_Function__ptr_StorageBuffer__struct_4 %_ptr_Function_uint
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%null = OpConstantNull %_ptr_StorageBuffer__struct_4
+%func = OpFunction %uint None %functy
+%param_buf = OpFunctionParameter %_ptr_Function__ptr_StorageBuffer__struct_4
+%param_offset = OpFunctionParameter %_ptr_Function_uint
+%bb_entry = OpLabel
+%buf = OpLoad %_ptr_StorageBuffer__struct_4 %param_buf
+OpSelectionMerge %bb_end None
+OpBranchConditional %true %bb_then %bb_else
+%bb_then = OpLabel
+OpBranch %bb_end
+%bb_else = OpLabel
+OpBranch %bb_end
+%bb_end = OpLabel
+%phi_buf_ptr = OpPhi %_ptr_StorageBuffer__struct_4 %buf %bb_then %null %bb_else
+%offset = OpLoad %uint %param_offset
+%ld_gep = OpAccessChain %_ptr_StorageBuffer_uint %phi_buf_ptr %uint_0 %offset
+%data = OpLoad %uint %ld_gep
+OpReturnValue %data
+OpFunctionEnd
+)";
+  SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, VariablePointerIndirectFuncCall) {
+  const std::string text = R"(
+; CHECK-NOT: OpDecorate {{%\w+}} Coherent
+; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
+; CHECK: [[buffer:%\w+]] = OpLoad {{%\w+}} {{%\w+}}
+; CHECK: [[ld_gep:%\w+]] = OpAccessChain {{%\w+}} [[buffer]] {{%\w+}} {{%\w+}}
+; CHECK: OpLoad {{%\w+}} [[ld_gep]] MakePointerVisible|NonPrivatePointer [[scope]]
+; CHECK: [[buffer2:%\w+]] = OpLoad {{%\w+}} {{%\w+}} Volatile
+; CHECK: [[ld_gep2:%\w+]] = OpAccessChain {{%\w+}} [[buffer2]] {{%\w+}} {{%\w+}}
+; CHECK: OpLoad {{%\w+}} [[ld_gep2]] Volatile|MakePointerVisible|NonPrivatePointer [[scope]]
+OpCapability Shader
+OpCapability Linkage
+OpCapability VariablePointersStorageBuffer
+OpExtension "SPV_KHR_variable_pointers"
+OpMemoryModel Logical GLSL450
+OpDecorate %_runtimearr_uint ArrayStride 4
+OpMemberDecorate %_struct_4 0 Offset 0
+OpDecorate %_struct_4 Block
+OpDecorate %param_buf Coherent
+OpDecorate %param_buf_1 Coherent
+OpDecorate %param_buf_1 Volatile
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%uint_1024 = OpConstant %uint 1024
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%_struct_4 = OpTypeStruct %_runtimearr_uint
+%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4
+%_ptr_Function_uint = OpTypePointer Function %uint
+%_ptr_Function__ptr_StorageBuffer__struct_4 = OpTypePointer Function %_ptr_StorageBuffer__struct_4
+%functy = OpTypeFunction %uint %_ptr_Function__ptr_StorageBuffer__struct_4 %_ptr_Function_uint
+%local_functy = OpTypeFunction %uint %_ptr_Function__ptr_StorageBuffer__struct_4
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%func = OpFunction %uint None %functy
+%param_buf = OpFunctionParameter %_ptr_Function__ptr_StorageBuffer__struct_4
+%param_offset = OpFunctionParameter %_ptr_Function_uint
+%bb_entry = OpLabel
+%buf = OpLoad %_ptr_StorageBuffer__struct_4 %param_buf
+%offset = OpLoad %uint %param_offset
+%ld_gep = OpAccessChain %_ptr_StorageBuffer_uint %buf %uint_0 %offset
+%data = OpLoad %uint %ld_gep
+%data_2 = OpFunctionCall %uint %local_func %param_buf
+%ret = OpIAdd %uint %data %data_2
+OpReturnValue %ret
+OpFunctionEnd
+%local_func = OpFunction %uint None %local_functy
+%param_buf_1 = OpFunctionParameter %_ptr_Function__ptr_StorageBuffer__struct_4
+%bb_entry_1 = OpLabel
+%buf_1 = OpLoad %_ptr_StorageBuffer__struct_4 %param_buf_1
+%ld_gep_1 = OpAccessChain %_ptr_StorageBuffer_uint %buf_1 %uint_0 %uint_1024
+%data_1 = OpLoad %uint %ld_gep_1
+OpReturnValue %data_1
+OpFunctionEnd
+)";
+  SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
 TEST_F(UpgradeMemoryModelTest, CoherentStructElement) {
   const std::string text = R"(
 ; CHECK-NOT: OpMemberDecorate
@@ -1174,9 +1562,9 @@
 
 TEST_F(UpgradeMemoryModelTest, TessellationControlBarrierNoChange) {
   const std::string text = R"(
-; CHECK: [[none:%\w+]] = OpConstant {{%\w+}} 0
-; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2
-; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[none]]
+; CHECK: [[none:%\w+]] = OpConstant {{%\w+}} 0{{\s*$}}
+; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2{{\s*$}}
+; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[none]]{{\s*$}}
 OpCapability Tessellation
 OpMemoryModel Logical GLSL450
 OpEntryPoint TessellationControl %func "func"
@@ -1195,11 +1583,11 @@
   SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
 }
 
-TEST_F(UpgradeMemoryModelTest, TessellationControlBarrierAddOutput) {
+TEST_F(UpgradeMemoryModelTest, TessellationControlBarrierRelaxedNoChange) {
   const std::string text = R"(
-; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2
-; CHECK: [[output:%\w+]] = OpConstant {{%\w+}} 4096
-; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[output]]
+; CHECK: [[none:%\w+]] = OpConstant {{%\w+}} 0{{\s*$}}
+; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2{{\s*$}}
+; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[none]]{{\s*$}}
 OpCapability Tessellation
 OpMemoryModel Logical GLSL450
 OpEntryPoint TessellationControl %func "func" %var
@@ -1222,25 +1610,52 @@
   SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
 }
 
-TEST_F(UpgradeMemoryModelTest, TessellationMemoryBarrierNoChange) {
+TEST_F(UpgradeMemoryModelTest, TessellationControlBarrierAddOutput) {
   const std::string text = R"(
-; CHECK: [[none:%\w+]] = OpConstant {{%\w+}} 0
-; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2
-; CHECK: OpMemoryBarrier [[workgroup]] [[none]]
+; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2{{\s*$}}
+; CHECK: [[acqrel_workgroup_output:%\w+]] = OpConstant {{%\w+}} 4360{{\s*$}}
+; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[acqrel_workgroup_output]]{{\s*$}}
 OpCapability Tessellation
 OpMemoryModel Logical GLSL450
 OpEntryPoint TessellationControl %func "func" %var
 %void = OpTypeVoid
 %int = OpTypeInt 32 0
-%none = OpConstant %int 0
 %workgroup = OpConstant %int 2
+%acqrel_workgroup = OpConstant %int 264
 %ptr_int_Output = OpTypePointer Output %int
 %var = OpVariable %ptr_int_Output Output
 %func_ty = OpTypeFunction %void
 %func = OpFunction %void None %func_ty
 %1 = OpLabel
 %ld = OpLoad %int %var
-OpMemoryBarrier %workgroup %none
+OpControlBarrier %workgroup %workgroup %acqrel_workgroup
+OpStore %var %ld
+OpReturn
+OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, TessellationMemoryBarrierNoChange) {
+  const std::string text = R"(
+; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2{{\s*$}}
+; CHECK: [[acqrel_workgroup:%\w+]] = OpConstant {{%\w+}} 264{{\s*$}}
+; CHECK: OpMemoryBarrier [[workgroup]] [[acqrel_workgroup]]{{\s*$}}
+OpCapability Tessellation
+OpMemoryModel Logical GLSL450
+OpEntryPoint TessellationControl %func "func" %var
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%workgroup = OpConstant %int 2
+%acqrel_workgroup = OpConstant %int 264
+%ptr_int_Output = OpTypePointer Output %int
+%var = OpVariable %ptr_int_Output Output
+%func_ty = OpTypeFunction %void
+%func = OpFunction %void None %func_ty
+%1 = OpLabel
+%ld = OpLoad %int %var
+OpMemoryBarrier %workgroup %acqrel_workgroup
 OpStore %var %ld
 OpReturn
 OpFunctionEnd
@@ -1251,16 +1666,16 @@
 
 TEST_F(UpgradeMemoryModelTest, TessellationControlBarrierAddOutputSubFunction) {
   const std::string text = R"(
-; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2
-; CHECK: [[output:%\w+]] = OpConstant {{%\w+}} 4096
-; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[output]]
+; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2{{\s*$}}
+; CHECK: [[acqrel_workgroup_output:%\w+]] = OpConstant {{%\w+}} 4360{{\s*$}}
+; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[acqrel_workgroup_output]]{{\s*$}}
 OpCapability Tessellation
 OpMemoryModel Logical GLSL450
 OpEntryPoint TessellationControl %func "func" %var
 %void = OpTypeVoid
 %int = OpTypeInt 32 0
-%none = OpConstant %int 0
 %workgroup = OpConstant %int 2
+%acqrel_workgroup = OpConstant %int 264
 %ptr_int_Output = OpTypePointer Output %int
 %var = OpVariable %ptr_int_Output Output
 %func_ty = OpTypeFunction %void
@@ -1272,7 +1687,7 @@
 %sub_func = OpFunction %void None %func_ty
 %2 = OpLabel
 %ld = OpLoad %int %var
-OpControlBarrier %workgroup %workgroup %none
+OpControlBarrier %workgroup %workgroup %acqrel_workgroup
 OpStore %var %ld
 OpReturn
 OpFunctionEnd
@@ -1284,16 +1699,16 @@
 TEST_F(UpgradeMemoryModelTest,
        TessellationControlBarrierAddOutputDifferentFunctions) {
   const std::string text = R"(
-; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2
-; CHECK: [[output:%\w+]] = OpConstant {{%\w+}} 4096
-; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[output]]
+; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2{{\s*$}}
+; CHECK: [[acqrel_workgroup_output:%\w+]] = OpConstant {{%\w+}} 4360{{\s*$}}
+; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[acqrel_workgroup_output]]{{\s*$}}
 OpCapability Tessellation
 OpMemoryModel Logical GLSL450
 OpEntryPoint TessellationControl %func "func" %var
 %void = OpTypeVoid
 %int = OpTypeInt 32 0
-%none = OpConstant %int 0
 %workgroup = OpConstant %int 2
+%acqrel_workgroup = OpConstant %int 264
 %ptr_int_Output = OpTypePointer Output %int
 %var = OpVariable %ptr_int_Output Output
 %func_ty = OpTypeFunction %void
@@ -1313,7 +1728,7 @@
 OpFunctionEnd
 %barrier_func = OpFunction %void None %func_ty
 %3 = OpLabel
-OpControlBarrier %workgroup %workgroup %none
+OpControlBarrier %workgroup %workgroup %acqrel_workgroup
 OpReturn
 OpFunctionEnd
 %st_func = OpFunction %void None %st_func_ty
@@ -1329,21 +1744,22 @@
 
 TEST_F(UpgradeMemoryModelTest, ChangeControlBarrierMemoryScope) {
   std::string text = R"(
-; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2
-; CHECK: [[queuefamily:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpControlBarrier [[workgroup]] [[queuefamily]]
+; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2{{\s*$}}
+; CHECK: [[acqrel_workgroup:%\w+]] = OpConstant {{%\w+}} 264{{\s*$}}
+; CHECK: [[queuefamily:%\w+]] = OpConstant {{%\w+}} 5{{\s*$}}
+; CHECK: OpControlBarrier [[workgroup]] [[queuefamily]] [[acqrel_workgroup]]{{\s*$}}
 OpCapability Shader
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %func "func"
 %void = OpTypeVoid
 %int = OpTypeInt 32 0
-%none = OpConstant %int 0
 %device = OpConstant %int 1
 %workgroup = OpConstant %int 2
+%acqrel_workgroup = OpConstant %int 264
 %func_ty = OpTypeFunction %void
 %func = OpFunction %void None %func_ty
 %1 = OpLabel
-OpControlBarrier %workgroup %device %none
+OpControlBarrier %workgroup %device %acqrel_workgroup
 OpReturn
 OpFunctionEnd
 )";
@@ -1353,19 +1769,20 @@
 
 TEST_F(UpgradeMemoryModelTest, ChangeMemoryBarrierMemoryScope) {
   std::string text = R"(
-; CHECK: [[queuefamily:%\w+]] = OpConstant {{%\w+}} 5
-; CHECK: OpMemoryBarrier [[queuefamily]]
+; CHECK: [[acqrel_workgroup:%\w+]] = OpConstant {{%\w+}} 264{{\s*$}}
+; CHECK: [[queuefamily:%\w+]] = OpConstant {{%\w+}} 5{{\s*$}}
+; CHECK: OpMemoryBarrier [[queuefamily]] [[acqrel_workgroup]]{{\s*$}}
 OpCapability Shader
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %func "func"
 %void = OpTypeVoid
 %int = OpTypeInt 32 0
-%none = OpConstant %int 0
 %device = OpConstant %int 1
+%acqrel_workgroup = OpConstant %int 264
 %func_ty = OpTypeFunction %void
 %func = OpFunction %void None %func_ty
 %1 = OpLabel
-OpMemoryBarrier %device %none
+OpMemoryBarrier %device %acqrel_workgroup
 OpReturn
 OpFunctionEnd
 )";
diff --git a/test/opt/value_table_test.cpp b/test/opt/value_table_test.cpp
index 3d7aaad..c3be991 100644
--- a/test/opt/value_table_test.cpp
+++ b/test/opt/value_table_test.cpp
@@ -25,7 +25,16 @@
 
 using ::testing::HasSubstr;
 using ::testing::MatchesRegex;
-using ValueTableTest = PassTest<::testing::Test>;
+
+struct ValueTableTest : public PassTest<::testing::Test> {
+  virtual void SetUp() override {
+    SetTargetEnv(SPV_ENV_UNIVERSAL_1_2);
+    SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+    SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES |
+                          SPV_BINARY_TO_TEXT_OPTION_INDENT |
+                          SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+  }
+};
 
 TEST_F(ValueTableTest, SameInstructionSameValue) {
   const std::string text = R"(
@@ -724,9 +733,147 @@
   ValueNumberTable vtable(context.get());
   Instruction* load1 = context->get_def_use_mgr()->GetDef(17);
   Instruction* load2 = context->get_def_use_mgr()->GetDef(18);
+  // Considered the same because the underlying memory is read-only
   EXPECT_EQ(vtable.GetValueNumber(load1), vtable.GetValueNumber(load2));
 }
 
+TEST_F(ValueTableTest, ImageRead_ConsideredDifferent) {
+  // Image reads are considered different because they can be read-write storage
+  // image.
+  const std::string text = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginLowerLeft
+               OpName %main "main"
+               OpName %var "s"
+               OpDecorate %var DescriptorSet 0
+               OpDecorate %var Binding 0
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+      %v2int = OpTypeVector %int 2
+     %v2zero = OpConstantNull %v2int
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+    %v4float = OpTypeVector %float 4
+       %i_ty = OpTypeImage %float 2D 0 0 0 2 Rgba32f
+   %ptr_s_ty = OpTypePointer UniformConstant %i_ty
+        %var = OpVariable %ptr_s_ty UniformConstant
+       %main = OpFunction %void None %6
+         %15 = OpLabel
+         %16 = OpLoad %i_ty %var
+        %100 = OpImageRead %v4float %16 %v2zero
+        %101 = OpImageRead %v4float %16 %v2zero
+               OpReturn
+               OpFunctionEnd
+  )";
+  auto context = AssembleModule(text);
+  ValueNumberTable vtable(context.get());
+  Instruction* read1 = context->get_def_use_mgr()->GetDef(100);
+  Instruction* read2 = context->get_def_use_mgr()->GetDef(101);
+  ASSERT_NE(read1, nullptr);
+  ASSERT_NE(read2, nullptr);
+  EXPECT_NE(vtable.GetValueNumber(read1), vtable.GetValueNumber(read2));
+}
+
+TEST_F(ValueTableTest, LoadSampler_ConsideredDifferent) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginLowerLeft
+               OpName %main "main"
+               OpName %var "var"
+               OpDecorate %var DescriptorSet 0
+               OpDecorate %var Binding 0
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+       %s_ty = OpTypeSampler
+   %ptr_s_ty = OpTypePointer UniformConstant %s_ty
+        %var = OpVariable %ptr_s_ty UniformConstant
+       %main = OpFunction %void None %6
+         %15 = OpLabel
+        %100 = OpLoad %s_ty %var
+        %101 = OpLoad %s_ty %var
+               OpReturn
+               OpFunctionEnd
+  )";
+  auto context = AssembleModule(text);
+  ValueNumberTable vtable(context.get());
+  Instruction* load1 = context->get_def_use_mgr()->GetDef(100);
+  Instruction* load2 = context->get_def_use_mgr()->GetDef(101);
+  ASSERT_NE(load1, nullptr) << Disassemble(context->module());
+  ASSERT_NE(load2, nullptr);
+  EXPECT_NE(vtable.GetValueNumber(load1), vtable.GetValueNumber(load2));
+}
+
+TEST_F(ValueTableTest, LoadImage_ConsideredDifferent) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginLowerLeft
+               OpName %main "main"
+               OpName %var "var"
+               OpDecorate %var DescriptorSet 0
+               OpDecorate %var Binding 0
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+       %i_ty = OpTypeImage %float 2D 0 0 0 1 Unknown
+   %ptr_i_ty = OpTypePointer UniformConstant %i_ty
+        %var = OpVariable %ptr_i_ty UniformConstant
+       %main = OpFunction %void None %6
+         %15 = OpLabel
+        %100 = OpLoad %i_ty %var
+        %101 = OpLoad %i_ty %var
+               OpReturn
+               OpFunctionEnd
+  )";
+  auto context = AssembleModule(text);
+  ValueNumberTable vtable(context.get());
+  Instruction* load1 = context->get_def_use_mgr()->GetDef(100);
+  Instruction* load2 = context->get_def_use_mgr()->GetDef(101);
+  ASSERT_NE(load1, nullptr);
+  ASSERT_NE(load2, nullptr);
+  EXPECT_NE(vtable.GetValueNumber(load1), vtable.GetValueNumber(load2));
+}
+
+TEST_F(ValueTableTest, LoadSampledImage_ConsideredDifferent) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginLowerLeft
+               OpName %main "main"
+               OpName %var "var"
+               OpDecorate %var DescriptorSet 0
+               OpDecorate %var Binding 0
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+       %s_ty = OpTypeSampler
+      %float = OpTypeFloat 32
+       %i_ty = OpTypeImage %float 2D 0 0 0 1 Unknown
+      %si_ty = OpTypeSampledImage %i_ty
+  %ptr_si_ty = OpTypePointer UniformConstant %si_ty
+        %var = OpVariable %ptr_si_ty UniformConstant
+       %main = OpFunction %void None %6
+         %15 = OpLabel
+        %100 = OpLoad %si_ty %var
+        %101 = OpLoad %si_ty %var
+               OpReturn
+               OpFunctionEnd
+  )";
+  auto context = AssembleModule(text);
+  ValueNumberTable vtable(context.get());
+  Instruction* load1 = context->get_def_use_mgr()->GetDef(100);
+  Instruction* load2 = context->get_def_use_mgr()->GetDef(101);
+  ASSERT_NE(load1, nullptr);
+  ASSERT_NE(load2, nullptr);
+  EXPECT_NE(vtable.GetValueNumber(load1), vtable.GetValueNumber(load2));
+}
+
 TEST_F(ValueTableTest, DifferentDebugLocalVariableSameValue) {
   const std::string text = R"(
                OpCapability Shader
diff --git a/test/parse_number_test.cpp b/test/parse_number_test.cpp
index c99205c..40bdd02 100644
--- a/test/parse_number_test.cpp
+++ b/test/parse_number_test.cpp
@@ -898,6 +898,120 @@
   EXPECT_EQ("Invalid 16-bit float literal: -1e400", err_msg);
 }
 
+TEST(ParseAndEncodeFloat8_E4M3, Sample) {
+  EncodeNumberStatus rc = EncodeNumberStatus::kSuccess;
+  std::string err_msg;
+  NumberType type = {8, SPV_NUMBER_FLOATING, SPV_FP_ENCODING_FLOAT8_E4M3};
+
+  // Invalid
+  rc = ParseAndEncodeFloatingPointNumber("", type, AssertEmitFunc, &err_msg);
+  EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc);
+  EXPECT_EQ("Invalid E4M3 float literal: ", err_msg);
+  rc = ParseAndEncodeFloatingPointNumber("0=", type, AssertEmitFunc, &err_msg);
+  EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc);
+  EXPECT_EQ("Invalid E4M3 float literal: 0=", err_msg);
+
+  // Representative samples
+  rc = ParseAndEncodeFloatingPointNumber(
+      "0.0", type, [](uint32_t word) { EXPECT_EQ(0x0u, word); }, nullptr);
+  EXPECT_EQ(EncodeNumberStatus::kSuccess, rc);
+  rc = ParseAndEncodeFloatingPointNumber(
+      "-0.0", type, [](uint32_t word) { EXPECT_EQ(0x80u, word); }, nullptr);
+  EXPECT_EQ(EncodeNumberStatus::kSuccess, rc);
+  rc = ParseAndEncodeFloatingPointNumber(
+      "1.0", type, [](uint32_t word) { EXPECT_EQ(0x38u, word); }, nullptr);
+  EXPECT_EQ(EncodeNumberStatus::kSuccess, rc);
+  rc = ParseAndEncodeFloatingPointNumber(
+      "2.5", type, [](uint32_t word) { EXPECT_EQ(0x42u, word); }, nullptr);
+  EXPECT_EQ(EncodeNumberStatus::kSuccess, rc);
+  rc = ParseAndEncodeFloatingPointNumber(
+      "-0.625", type, [](uint32_t word) { EXPECT_EQ(0xB2u, word); }, nullptr);
+  EXPECT_EQ(EncodeNumberStatus::kSuccess, rc);
+
+  // Overflow
+  rc =
+      ParseAndEncodeFloatingPointNumber("1e38", type, AssertEmitFunc, &err_msg);
+  EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc);
+  EXPECT_EQ("Invalid E4M3 float literal: 1e38", err_msg);
+  rc = ParseAndEncodeFloatingPointNumber("-1e38", type, AssertEmitFunc,
+                                         &err_msg);
+  EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc);
+  EXPECT_EQ("Invalid E4M3 float literal: -1e38", err_msg);
+  rc =
+      ParseAndEncodeFloatingPointNumber("1e40", type, AssertEmitFunc, &err_msg);
+  EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc);
+  EXPECT_EQ("Invalid E4M3 float literal: 1e40", err_msg);
+  rc = ParseAndEncodeFloatingPointNumber("-1e40", type, AssertEmitFunc,
+                                         &err_msg);
+  EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc);
+  EXPECT_EQ("Invalid E4M3 float literal: -1e40", err_msg);
+  rc = ParseAndEncodeFloatingPointNumber("1e400", type, AssertEmitFunc,
+                                         &err_msg);
+  EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc);
+  EXPECT_EQ("Invalid E4M3 float literal: 1e400", err_msg);
+  rc = ParseAndEncodeFloatingPointNumber("-1e400", type, AssertEmitFunc,
+                                         &err_msg);
+  EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc);
+  EXPECT_EQ("Invalid E4M3 float literal: -1e400", err_msg);
+}
+
+TEST(ParseAndEncodeFloat8_E5M2, Sample) {
+  EncodeNumberStatus rc = EncodeNumberStatus::kSuccess;
+  std::string err_msg;
+  NumberType type = {8, SPV_NUMBER_FLOATING, SPV_FP_ENCODING_FLOAT8_E5M2};
+
+  // Invalid
+  rc = ParseAndEncodeFloatingPointNumber("", type, AssertEmitFunc, &err_msg);
+  EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc);
+  EXPECT_EQ("Invalid E5M2 float literal: ", err_msg);
+  rc = ParseAndEncodeFloatingPointNumber("0=", type, AssertEmitFunc, &err_msg);
+  EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc);
+  EXPECT_EQ("Invalid E5M2 float literal: 0=", err_msg);
+
+  // Representative samples
+  rc = ParseAndEncodeFloatingPointNumber(
+      "0.0", type, [](uint32_t word) { EXPECT_EQ(0x0u, word); }, nullptr);
+  EXPECT_EQ(EncodeNumberStatus::kSuccess, rc);
+  rc = ParseAndEncodeFloatingPointNumber(
+      "-0.0", type, [](uint32_t word) { EXPECT_EQ(0x80u, word); }, nullptr);
+  EXPECT_EQ(EncodeNumberStatus::kSuccess, rc);
+  rc = ParseAndEncodeFloatingPointNumber(
+      "1.0", type, [](uint32_t word) { EXPECT_EQ(0x3cu, word); }, nullptr);
+  EXPECT_EQ(EncodeNumberStatus::kSuccess, rc);
+  rc = ParseAndEncodeFloatingPointNumber(
+      "2.5", type, [](uint32_t word) { EXPECT_EQ(0x41u, word); }, nullptr);
+  EXPECT_EQ(EncodeNumberStatus::kSuccess, rc);
+  rc = ParseAndEncodeFloatingPointNumber(
+      "-0.625", type, [](uint32_t word) { EXPECT_EQ(0xB9u, word); }, nullptr);
+  EXPECT_EQ(EncodeNumberStatus::kSuccess, rc);
+
+  // Overflow
+  rc =
+      ParseAndEncodeFloatingPointNumber("1e38", type, AssertEmitFunc, &err_msg);
+  EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc);
+  EXPECT_EQ("Invalid E5M2 float literal: 1e38", err_msg);
+  rc = ParseAndEncodeFloatingPointNumber("-1e38", type, AssertEmitFunc,
+                                         &err_msg);
+  EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc);
+  EXPECT_EQ("Invalid E5M2 float literal: -1e38", err_msg);
+  rc =
+      ParseAndEncodeFloatingPointNumber("1e40", type, AssertEmitFunc, &err_msg);
+  EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc);
+  EXPECT_EQ("Invalid E5M2 float literal: 1e40", err_msg);
+  rc = ParseAndEncodeFloatingPointNumber("-1e40", type, AssertEmitFunc,
+                                         &err_msg);
+  EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc);
+  EXPECT_EQ("Invalid E5M2 float literal: -1e40", err_msg);
+  rc = ParseAndEncodeFloatingPointNumber("1e400", type, AssertEmitFunc,
+                                         &err_msg);
+  EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc);
+  EXPECT_EQ("Invalid E5M2 float literal: 1e400", err_msg);
+  rc = ParseAndEncodeFloatingPointNumber("-1e400", type, AssertEmitFunc,
+                                         &err_msg);
+  EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc);
+  EXPECT_EQ("Invalid E5M2 float literal: -1e400", err_msg);
+}
+
 TEST(ParseAndEncodeFloatingPointNumber, TypeNone) {
   EncodeNumberStatus rc = EncodeNumberStatus::kSuccess;
   std::string err_msg;
diff --git a/test/string_utils_test.cpp b/test/string_utils_test.cpp
index 5851415..b4ce29f 100644
--- a/test/string_utils_test.cpp
+++ b/test/string_utils_test.cpp
@@ -12,10 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <string>
-
-#include "gtest/gtest.h"
 #include "source/util/string_utils.h"
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "source/util/small_vector.h"
 #include "spirv-tools/libspirv.h"
 
 namespace spvtools {
@@ -186,6 +189,50 @@
   EXPECT_EQ("1225th", CardinalToOrdinal(1225));
 }
 
+using MakeVectorMakeStringRoundTripTest = ::testing::TestWithParam<std::string>;
+
+TEST_P(MakeVectorMakeStringRoundTripTest, ViaStdVector) {
+  const std::string str = GetParam();
+  const auto& vec = MakeVector<std::vector<uint32_t>>(str);
+  // Allow for the terminating null byte that must be present.
+  const auto expected_vec_len = (str.size() + 4) / 4;
+  EXPECT_EQ(vec.size(), expected_vec_len);
+  const std::string back_to_string = MakeString(vec);
+  EXPECT_EQ(str, back_to_string);
+}
+
+TEST_P(MakeVectorMakeStringRoundTripTest, ViaSmallVector1) {
+  const std::string str = GetParam();
+  // Allow for the terminating null byte that must be present.
+  const auto expected_vec_len = (str.size() + 4) / 4;
+  const auto& vec = MakeVector<spvtools::utils::SmallVector<uint32_t, 1>>(str);
+  EXPECT_EQ(vec.size(), expected_vec_len);
+  const std::string back_to_string = MakeString(vec);
+  EXPECT_EQ(str, back_to_string);
+}
+
+TEST_P(MakeVectorMakeStringRoundTripTest, ViaSmallVector100) {
+  const std::string str = GetParam();
+  // Allow for the terminating null byte that must be present.
+  const auto expected_vec_len = (str.size() + 4) / 4;
+  const auto& vec =
+      MakeVector<spvtools::utils::SmallVector<uint32_t, 100>>(str);
+  EXPECT_EQ(vec.size(), expected_vec_len);
+  const std::string back_to_string = MakeString(vec);
+  EXPECT_EQ(str, back_to_string);
+}
+
+INSTANTIATE_TEST_SUITE_P(Examples, MakeVectorMakeStringRoundTripTest,
+                         testing::ValuesIn(std::vector<std::string>{
+                             "",
+                             "a",
+                             "bc",
+                             "def",
+                             "ghij",
+                             "klmno",
+                             "dustclouds disappear without a trace",
+                         }));
+
 }  // namespace
 }  // namespace utils
 }  // namespace spvtools
diff --git a/test/target_env_test.cpp b/test/target_env_test.cpp
index faffa8a..05898df 100644
--- a/test/target_env_test.cpp
+++ b/test/target_env_test.cpp
@@ -79,9 +79,16 @@
         {"spv1.1", true, SPV_ENV_UNIVERSAL_1_1},
         {"spv1.2", true, SPV_ENV_UNIVERSAL_1_2},
         {"spv1.3", true, SPV_ENV_UNIVERSAL_1_3},
+        {"spv1.4", true, SPV_ENV_UNIVERSAL_1_4},
+        {"spv1.5", true, SPV_ENV_UNIVERSAL_1_5},
+        {"spv1.6", true, SPV_ENV_UNIVERSAL_1_6},
+        {"spv1.7", false, SPV_ENV_UNIVERSAL_1_3},
         {"vulkan1.0", true, SPV_ENV_VULKAN_1_0},
         {"vulkan1.1", true, SPV_ENV_VULKAN_1_1},
         {"vulkan1.2", true, SPV_ENV_VULKAN_1_2},
+        {"vulkan1.3", true, SPV_ENV_VULKAN_1_3},
+        {"vulkan1.4", true, SPV_ENV_VULKAN_1_4},
+        {"vulkan1.5", false, SPV_ENV_UNIVERSAL_1_0},
         {"opencl2.1", true, SPV_ENV_OPENCL_2_1},
         {"opencl2.2", true, SPV_ENV_OPENCL_2_2},
         {"opengl4.0", true, SPV_ENV_OPENGL_4_0},
@@ -221,6 +228,7 @@
         {"     \t ; Version: 1.1", true, SPV_ENV_UNIVERSAL_1_1},
         // Previous lines
         {"; SPIR-V\n; Version: 1.1", true, SPV_ENV_UNIVERSAL_1_1},
+        {"; -\n; SPIR-V\n; Version: 1.1", true, SPV_ENV_UNIVERSAL_1_1},
 
         // After a non-header line
         {"OpCapability Shader\n; Version: 1.1", false, kSentinelEnv}}));
diff --git a/test/text_to_binary.extension_test.cpp b/test/text_to_binary.extension_test.cpp
index 59f2af9..f57d323 100644
--- a/test/text_to_binary.extension_test.cpp
+++ b/test/text_to_binary.extension_test.cpp
@@ -31,12 +31,12 @@
 
 using spvtest::Concatenate;
 using spvtest::MakeInstruction;
-using utils::MakeVector;
 using spvtest::TextToBinaryTest;
 using ::testing::Combine;
 using ::testing::Eq;
 using ::testing::Values;
 using ::testing::ValuesIn;
+using utils::MakeVector;
 
 // Returns a generator of common Vulkan environment values to be tested.
 std::vector<spv_target_env> CommonVulkanEnvs() {
@@ -70,9 +70,15 @@
               Eq("Import Id is being defined a second time"));
 }
 
-TEST_F(TextToBinaryTest, TooManyArguments) {
+TEST_F(TextToBinaryTest, TooManyArgumentsIdEqualQuote) {
   const std::string input = R"(%opencl = OpExtInstImport "OpenCL.std"
-                               %2 = OpExtInst %float %opencl cos %x %oops")";
+                               %2 = OpExtInst %float %opencl cos %x %oops=")";
+  EXPECT_THAT(CompileFailure(input), Eq("Expected '=', found end of stream."));
+}
+
+TEST_F(TextToBinaryTest, TooManyArgumentsIdEqual) {
+  const std::string input = R"(%opencl = OpExtInstImport "OpenCL.std"
+                               %2 = OpExtInst %float %opencl cos %x %oops=)";
   EXPECT_THAT(CompileFailure(input), Eq("Expected '=', found end of stream."));
 }
 
@@ -852,41 +858,42 @@
             {"OpExtension \"SPV_NV_shader_subgroup_partitioned\"\n",
              MakeInstruction(spv::Op::OpExtension,
                              MakeVector("SPV_NV_shader_subgroup_partitioned"))},
-            {"OpCapability GroupNonUniformPartitionedNV\n",
+            {"OpCapability GroupNonUniformPartitionedEXT\n",
              MakeInstruction(
                  spv::Op::OpCapability,
-                 {(uint32_t)spv::Capability::GroupNonUniformPartitionedNV})},
+                 {(uint32_t)spv::Capability::GroupNonUniformPartitionedEXT})},
             // Check the new capability's token number
-            {"OpCapability GroupNonUniformPartitionedNV\n",
+            {"OpCapability GroupNonUniformPartitionedEXT\n",
              MakeInstruction(spv::Op::OpCapability, {5297})},
-            {"%2 = OpGroupNonUniformPartitionNV %1 %3\n",
-             MakeInstruction(spv::Op::OpGroupNonUniformPartitionNV, {1, 2, 3})},
+            {"%2 = OpGroupNonUniformPartitionEXT %1 %3\n",
+             MakeInstruction(spv::Op::OpGroupNonUniformPartitionEXT,
+                             {1, 2, 3})},
             // Check the new instruction's token number
-            {"%2 = OpGroupNonUniformPartitionNV %1 %3\n",
+            {"%2 = OpGroupNonUniformPartitionEXT %1 %3\n",
              MakeInstruction(static_cast<spv::Op>(5296), {1, 2, 3})},
             // Check the new group operations
-            {"%2 = OpGroupIAdd %1 %3 PartitionedReduceNV %4\n",
+            {"%2 = OpGroupIAdd %1 %3 PartitionedReduceEXT %4\n",
              MakeInstruction(
                  spv::Op::OpGroupIAdd,
-                 {1, 2, 3, (uint32_t)spv::GroupOperation::PartitionedReduceNV,
+                 {1, 2, 3, (uint32_t)spv::GroupOperation::PartitionedReduceEXT,
                   4})},
-            {"%2 = OpGroupIAdd %1 %3 PartitionedReduceNV %4\n",
+            {"%2 = OpGroupIAdd %1 %3 PartitionedReduceEXT %4\n",
              MakeInstruction(spv::Op::OpGroupIAdd, {1, 2, 3, 6, 4})},
-            {"%2 = OpGroupIAdd %1 %3 PartitionedInclusiveScanNV %4\n",
+            {"%2 = OpGroupIAdd %1 %3 PartitionedInclusiveScanEXT %4\n",
              MakeInstruction(
                  spv::Op::OpGroupIAdd,
                  {1, 2, 3,
                   (uint32_t)spv::GroupOperation::PartitionedInclusiveScanNV,
                   4})},
-            {"%2 = OpGroupIAdd %1 %3 PartitionedInclusiveScanNV %4\n",
+            {"%2 = OpGroupIAdd %1 %3 PartitionedInclusiveScanEXT %4\n",
              MakeInstruction(spv::Op::OpGroupIAdd, {1, 2, 3, 7, 4})},
-            {"%2 = OpGroupIAdd %1 %3 PartitionedExclusiveScanNV %4\n",
+            {"%2 = OpGroupIAdd %1 %3 PartitionedExclusiveScanEXT %4\n",
              MakeInstruction(
                  spv::Op::OpGroupIAdd,
                  {1, 2, 3,
-                  (uint32_t)spv::GroupOperation::PartitionedExclusiveScanNV,
+                  (uint32_t)spv::GroupOperation::PartitionedExclusiveScanEXT,
                   4})},
-            {"%2 = OpGroupIAdd %1 %3 PartitionedExclusiveScanNV %4\n",
+            {"%2 = OpGroupIAdd %1 %3 PartitionedExclusiveScanEXT %4\n",
              MakeInstruction(spv::Op::OpGroupIAdd, {1, 2, 3, 8, 4})},
         })));
 
@@ -1376,5 +1383,302 @@
                              {1, 2, 3, 4, 5, 6, 7})},
         })));
 
+// SPV_ARM_tensors
+INSTANTIATE_TEST_SUITE_P(
+    SPV_ARM_tensors, ExtensionRoundTripTest,
+    Combine(
+        Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_0,
+               SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3,
+               SPV_ENV_OPENCL_2_1),
+        ValuesIn(std::vector<AssemblyCase>{
+            {"OpExtension \"SPV_ARM_tensors\"\n",
+             MakeInstruction(spv::Op::OpExtension,
+                             MakeVector("SPV_ARM_tensors"))},
+            {"OpCapability TensorsARM\n",
+             MakeInstruction(spv::Op::OpCapability,
+                             {(uint32_t)spv::Capability::TensorsARM})},
+            {"OpCapability StorageTensorArrayDynamicIndexingARM\n",
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)
+                      spv::Capability::StorageTensorArrayDynamicIndexingARM})},
+            {"OpCapability StorageTensorArrayNonUniformIndexingARM\n",
+             MakeInstruction(spv::Op::OpCapability,
+                             {(uint32_t)spv::Capability::
+                                  StorageTensorArrayNonUniformIndexingARM})},
+            {"%1 = OpTypeTensorARM %2\n",
+             MakeInstruction(spv::Op::OpTypeTensorARM, {1, 2})},
+            {"%1 = OpTypeTensorARM %2 %3\n",
+             MakeInstruction(spv::Op::OpTypeTensorARM, {1, 2, 3})},
+            {"%1 = OpTypeTensorARM %2 %3 %4\n",
+             MakeInstruction(spv::Op::OpTypeTensorARM, {1, 2, 3, 4})},
+            {"%2 = OpTensorReadARM %1 %3 %4\n",
+             MakeInstruction(spv::Op::OpTensorReadARM, {1, 2, 3, 4})},
+            {"%2 = OpTensorReadARM %1 %3 %4 NoneARM\n",
+             MakeInstruction(spv::Op::OpTensorReadARM,
+                             {1, 2, 3, 4,
+                              (uint32_t)spv::TensorOperandsMask::MaskNone})},
+            {"%2 = OpTensorReadARM %1 %3 %4 NontemporalARM\n",
+             MakeInstruction(
+                 spv::Op::OpTensorReadARM,
+                 {1, 2, 3, 4,
+                  (uint32_t)spv::TensorOperandsMask::NontemporalARM})},
+            {"%2 = OpTensorReadARM %1 %3 %4 OutOfBoundsValueARM %5\n",
+             MakeInstruction(
+                 spv::Op::OpTensorReadARM,
+                 {1, 2, 3, 4,
+                  (uint32_t)spv::TensorOperandsMask::OutOfBoundsValueARM, 5})},
+            {"%2 = OpTensorReadARM %1 %3 %4 MakeElementVisibleARM %5\n",
+             MakeInstruction(
+                 spv::Op::OpTensorReadARM,
+                 {1, 2, 3, 4,
+                  (uint32_t)spv::TensorOperandsMask::MakeElementVisibleARM,
+                  5})},
+            {"%2 = OpTensorReadARM %1 %3 %4 NonPrivateElementARM\n",
+             MakeInstruction(
+                 spv::Op::OpTensorReadARM,
+                 {1, 2, 3, 4,
+                  (uint32_t)spv::TensorOperandsMask::NonPrivateElementARM})},
+            {"OpTensorWriteARM %1 %2 %3\n",
+             MakeInstruction(spv::Op::OpTensorWriteARM, {1, 2, 3})},
+            {"OpTensorWriteARM %1 %2 %3 NoneARM\n",
+             MakeInstruction(spv::Op::OpTensorWriteARM,
+                             {1, 2, 3,
+                              (uint32_t)spv::TensorOperandsMask::MaskNone})},
+            {"OpTensorWriteARM %1 %2 %3 NontemporalARM\n",
+             MakeInstruction(
+                 spv::Op::OpTensorWriteARM,
+                 {1, 2, 3, (uint32_t)spv::TensorOperandsMask::NontemporalARM})},
+            {"OpTensorWriteARM %1 %2 %3 MakeElementAvailableARM %4\n",
+             MakeInstruction(
+                 spv::Op::OpTensorWriteARM,
+                 {1, 2, 3,
+                  (uint32_t)spv::TensorOperandsMask::MakeElementAvailableARM,
+                  4})},
+            {"OpTensorWriteARM %1 %2 %3 NonPrivateElementARM\n",
+             MakeInstruction(
+                 spv::Op::OpTensorWriteARM,
+                 {1, 2, 3,
+                  (uint32_t)spv::TensorOperandsMask::NonPrivateElementARM})},
+            {"%2 = OpTensorQuerySizeARM %1 %3 %4\n",
+             MakeInstruction(spv::Op::OpTensorQuerySizeARM, {1, 2, 3, 4})},
+        })));
+
+// SPV_EXT_float8
+INSTANTIATE_TEST_SUITE_P(
+    SPV_EXT_float8, ExtensionRoundTripTest,
+    Combine(
+        Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_0,
+               SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3,
+               SPV_ENV_OPENCL_2_1),
+        ValuesIn(std::vector<AssemblyCase>{
+            {"OpExtension \"SPV_EXT_float8\"\n",
+             MakeInstruction(spv::Op::OpExtension,
+                             MakeVector("SPV_EXT_float8"))},
+            {"OpCapability Float8EXT\n",
+             MakeInstruction(spv::Op::OpCapability,
+                             {(uint32_t)spv::Capability::Float8EXT})},
+            {"OpCapability Float8CooperativeMatrixEXT\n",
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::Float8CooperativeMatrixEXT})},
+            {"%1 = OpTypeFloat 8 Float8E4M3EXT\n",
+             MakeInstruction(spv::Op::OpTypeFloat,
+                             {1, 8, (uint32_t)spv::FPEncoding::Float8E4M3EXT})},
+            {"%1 = OpTypeFloat 8 Float8E5M2EXT\n",
+             MakeInstruction(spv::Op::OpTypeFloat,
+                             {1, 8, (uint32_t)spv::FPEncoding::Float8E5M2EXT})},
+            {"OpDecorate %1 SaturatedToLargestFloat8NormalConversionEXT\n",
+             MakeInstruction(
+                 spv::Op::OpDecorate,
+                 {1,
+                  uint32_t(spv::Decoration::
+                               SaturatedToLargestFloat8NormalConversionEXT)})},
+        })));
+
+// SPV_INTEL_function_variants
+// https://github.com/intel/llvm/blob/sycl/sycl/doc/design/spirv-extensions/SPV_INTEL_function_variants.asciidoc
+INSTANTIATE_TEST_SUITE_P(
+    SPV_INTEL_function_variants, ExtensionRoundTripTest,
+    Combine(
+        Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_6),
+        ValuesIn(std::vector<AssemblyCase>{
+            {"OpExtension \"SPV_INTEL_function_variants\"\n",
+             MakeInstruction(spv::Op::OpExtension,
+                             MakeVector("SPV_INTEL_function_variants"))},
+            {"OpCapability SpecConditionalINTEL\n",
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::SpecConditionalINTEL})},
+            {"OpCapability FunctionVariantsINTEL\n",
+             MakeInstruction(
+                 spv::Op::OpCapability,
+                 {(uint32_t)spv::Capability::FunctionVariantsINTEL})},
+            {"OpDecorate %1 ConditionalINTEL %2\n",
+             MakeInstruction(spv::Op::OpDecorate,
+                             {1, (uint32_t)spv::Decoration::ConditionalINTEL,
+                              2})},
+
+            {"OpConditionalExtensionINTEL %1 \"foo\"\n",
+             MakeInstruction(spv::Op::OpConditionalExtensionINTEL, {1},
+                             MakeVector("foo"))},
+
+            {"OpConditionalEntryPointINTEL %1 Kernel %2 \"foo\"\n",
+             MakeInstruction(spv::Op::OpConditionalEntryPointINTEL,
+                             {1, (uint32_t)spv::ExecutionModel::Kernel, 2},
+                             MakeVector("foo"))},
+
+            {"OpConditionalCapabilityINTEL %1 Kernel\n",
+             MakeInstruction(spv::Op::OpConditionalCapabilityINTEL,
+                             {1, (uint32_t)spv::ExecutionModel::Kernel})},
+
+            {"%2 = OpSpecConstantTargetINTEL %1 42\n",
+             MakeInstruction(spv::Op::OpSpecConstantTargetINTEL, {1, 2, 42})},
+
+            {"%2 = OpSpecConstantTargetINTEL %1 42 99\n",
+             MakeInstruction(spv::Op::OpSpecConstantTargetINTEL,
+                             {1, 2, 42, 99})},
+
+            {"%2 = OpSpecConstantTargetINTEL %1 42 99 108\n",
+             MakeInstruction(spv::Op::OpSpecConstantTargetINTEL,
+                             {1, 2, 42, 99, 108})},
+
+            {"%2 = OpSpecConstantArchitectureINTEL %1 42 99 108 72\n",
+             MakeInstruction(spv::Op::OpSpecConstantArchitectureINTEL,
+                             {1, 2, 42, 99, 108, 72})},
+
+            {"%2 = OpSpecConstantCapabilitiesINTEL %1\n",
+             MakeInstruction(spv::Op::OpSpecConstantCapabilitiesINTEL, {1, 2})},
+
+            {"%2 = OpSpecConstantCapabilitiesINTEL %1 Kernel\n",
+             MakeInstruction(spv::Op::OpSpecConstantCapabilitiesINTEL,
+                             {1, 2, (uint32_t)spv::Capability::Kernel})},
+
+            {"%2 = OpSpecConstantCapabilitiesINTEL %1 Kernel Shader\n",
+             MakeInstruction(spv::Op::OpSpecConstantCapabilitiesINTEL,
+                             {1, 2, (uint32_t)spv::Capability::Kernel,
+                              (uint32_t)spv::Capability::Shader})},
+
+            {"%2 = OpConditionalCopyObjectINTEL %1 %3 %4\n",
+             MakeInstruction(spv::Op::OpConditionalCopyObjectINTEL,
+                             {1, 2, 3, 4})},
+
+            {"%2 = OpConditionalCopyObjectINTEL %1 %3 %4 %5 %6\n",
+             MakeInstruction(spv::Op::OpConditionalCopyObjectINTEL,
+                             {1, 2, 3, 4, 5, 6})},
+
+        })));
+
+// SPV_ARM_graph
+INSTANTIATE_TEST_SUITE_P(
+    SPV_ARM_graph, ExtensionRoundTripTest,
+    Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_6,
+                   SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2,
+                   SPV_ENV_VULKAN_1_3, SPV_ENV_OPENCL_2_1),
+            ValuesIn(std::vector<AssemblyCase>{
+                {"OpExtension \"SPV_ARM_graph\"\n",
+                 MakeInstruction(spv::Op::OpExtension,
+                                 MakeVector("SPV_ARM_graph"))},
+                {"OpCapability GraphARM\n",
+                 MakeInstruction(spv::Op::OpCapability,
+                                 {(uint32_t)spv::Capability::GraphARM})},
+                {"%1 = OpTypeGraphARM 1 %2 %3\n",
+                 MakeInstruction(spv::Op::OpTypeGraphARM, {1, 1, 2, 3})},
+                {"%2 = OpGraphConstantARM %1 42\n",
+                 MakeInstruction(spv::Op::OpGraphConstantARM, {1, 2, 42})},
+                {"%2 = OpGraphARM %1\n",
+                 MakeInstruction(spv::Op::OpGraphARM, {1, 2})},
+                {"OpGraphEndARM\n",
+                 MakeInstruction(spv::Op::OpGraphEndARM, {})},
+                {"%2 = OpGraphInputARM %1 %3\n",
+                 MakeInstruction(spv::Op::OpGraphInputARM, {1, 2, 3})},
+                {"%2 = OpGraphInputARM %1 %3 %4\n",
+                 MakeInstruction(spv::Op::OpGraphInputARM, {1, 2, 3, 4})},
+                {"OpGraphSetOutputARM %1 %2\n",
+                 MakeInstruction(spv::Op::OpGraphSetOutputARM, {1, 2})},
+                {"OpGraphSetOutputARM %1 %2 %3\n",
+                 MakeInstruction(spv::Op::OpGraphSetOutputARM, {1, 2, 3})},
+            })));
+
+// SPV_KHR_abort
+INSTANTIATE_TEST_SUITE_P(
+    SPV_KHR_abort, ExtensionRoundTripTest,
+    Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_6,
+                   SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2,
+                   SPV_ENV_VULKAN_1_3),
+            ValuesIn(std::vector<AssemblyCase>{
+                {"OpExtension \"SPV_KHR_abort\"\n",
+                 MakeInstruction(spv::Op::OpExtension,
+                                 MakeVector("SPV_KHR_abort"))},
+                {"OpCapability AbortKHR\n",
+                 MakeInstruction(spv::Op::OpCapability,
+                                 {(uint32_t)spv::Capability::AbortKHR})},
+                {"OpAbortKHR %1 %2\n",
+                 MakeInstruction(spv::Op::OpAbortKHR, {1, 2})},
+            })));
+
+// SPV_KHR_constant_data
+INSTANTIATE_TEST_SUITE_P(
+    SPV_KHR_constant_data, ExtensionRoundTripTest,
+    Combine(
+        Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_0,
+               SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3),
+        ValuesIn(std::vector<AssemblyCase>{
+            {"OpExtension \"SPV_KHR_constant_data\"\n",
+             MakeInstruction(spv::Op::OpExtension,
+                             MakeVector("SPV_KHR_constant_data"))},
+            {"OpCapability ConstantDataKHR\n",
+             MakeInstruction(spv::Op::OpCapability,
+                             {(uint32_t)spv::Capability::ConstantDataKHR})},
+            {"%2 = OpConstantDataKHR %1 1718578944\n",
+             MakeInstruction(spv::Op::OpConstantDataKHR, {1, 2, 0x666F6F00})},
+            {"%2 = OpSpecConstantDataKHR %1 1718578944\n",
+             MakeInstruction(spv::Op::OpSpecConstantDataKHR,
+                             {1, 2, 0x666F6F00})},
+            {"OpDecorate %1 UTFEncodedKHR\n",
+             MakeInstruction(spv::Op::OpDecorate,
+                             {1, (uint32_t)spv::Decoration::UTFEncodedKHR})},
+
+        })));
+
+TEST_F(TextToBinaryTest, ConstantDataNonUTF) {
+  const std::string source = R"(
+               OpCapability Shader
+               OpCapability ConstantDataKHR
+               OpCapability Int8
+               OpExtension "SPV_KHR_constant_data"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+       %char = OpTypeInt 8 1
+    %uint_20 = OpConstant %uint 20
+ %char_array = OpTypeArray %char %uint_20
+               ; "abcd" where "a" is 0x61 ascii
+       %data = OpConstantDataKHR %char_array 0x10203040
+  %void_func = OpTypeFunction %void
+       %main = OpFunction %void None %void_func
+ %main_label = OpLabel
+               OpReturn
+               OpFunctionEnd
+    )";
+
+  auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
+  spv_binary binary = nullptr;
+  EXPECT_EQ(SPV_SUCCESS, spvTextToBinary(context, source.c_str(), source.size(),
+                                         &binary, nullptr));
+
+  // Opcode for OpConstantDataKHR
+  EXPECT_EQ((binary->code[50] & 0x0ffffu), 5147);
+  EXPECT_EQ(((binary->code[53] & 0xff000000u) >> 24), 0x10);
+  EXPECT_EQ(((binary->code[53] & 0x00ff0000u) >> 16), 0x20);
+  EXPECT_EQ(((binary->code[53] & 0x0000ff00u) >> 8), 0x30);
+  EXPECT_EQ(((binary->code[53] & 0x000000ffu)), 0x40);
+
+  spvBinaryDestroy(binary);
+  spvContextDestroy(context);
+}
+
 }  // namespace
 }  // namespace spvtools
diff --git a/test/text_to_binary.type_declaration_test.cpp b/test/text_to_binary.type_declaration_test.cpp
index 770f298..ff9f654 100644
--- a/test/text_to_binary.type_declaration_test.cpp
+++ b/test/text_to_binary.type_declaration_test.cpp
@@ -278,6 +278,63 @@
               Eq("Expected id to start with %."));
 }
 
+struct FloatEncodingWidthCase {
+  std::string input;
+  bool expect_pass;
+};
+
+using FloatEncodingWidthTest = spvtest::TextToBinaryTestBase<
+    ::testing::TestWithParam<FloatEncodingWidthCase>>;
+
+TEST_P(FloatEncodingWidthTest, Samples) {
+  const auto& param = GetParam();
+  if (param.expect_pass) {
+    CompileSuccessfully(param.input);
+  } else {
+    auto err = CompileFailure(param.input);
+    EXPECT_THAT(err, testing::HasSubstr("Invalid bit width"));
+    EXPECT_THAT(err, testing::HasSubstr("for floating point encoding"));
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    TextToBinaryFloatWidth, FloatEncodingWidthTest,
+    ::testing::ValuesIn(std::vector<FloatEncodingWidthCase>{
+        {"%1 = OpTypeFloat 32", true},
+        {"%1 = OpTypeFloat 64", true},
+        {"%1 = OpTypeFloat 16", true},
+        {"%1 = OpTypeFloat 8", true},
+        // bfloat16
+        {"%1 = OpTypeFloat 0 BFloat16KHR", false},
+        {"%1 = OpTypeFloat 1 BFloat16KHR", false},
+        {"%1 = OpTypeFloat 15 BFloat16KHR", false},
+        {"%1 = OpTypeFloat 16 BFloat16KHR", true},
+        {"%1 = OpTypeFloat 17 BFloat16KHR", false},
+        {"%1 = OpTypeFloat 32 BFloat16KHR", false},
+        {"%1 = OpTypeFloat 64 BFloat16KHR", false},
+        {"%1 = OpTypeFloat 100 BFloat16KHR", false},
+        // fp8 E5M2
+        {"%1 = OpTypeFloat 0 Float8E5M2EXT", false},
+        {"%1 = OpTypeFloat 1 Float8E5M2EXT", false},
+        {"%1 = OpTypeFloat 7 Float8E5M2EXT", false},
+        {"%1 = OpTypeFloat 8 Float8E5M2EXT", true},
+        {"%1 = OpTypeFloat 9 Float8E5M2EXT", false},
+        {"%1 = OpTypeFloat 16 Float8E5M2EXT", false},
+        {"%1 = OpTypeFloat 32 Float8E5M2EXT", false},
+        {"%1 = OpTypeFloat 64 Float8E5M2EXT", false},
+        {"%1 = OpTypeFloat 100 Float8E4M3EXT", false},
+        // fp8 E4M3
+        {"%1 = OpTypeFloat 0 Float8E4M3EXT", false},
+        {"%1 = OpTypeFloat 1 Float8E4M3EXT", false},
+        {"%1 = OpTypeFloat 7 Float8E4M3EXT", false},
+        {"%1 = OpTypeFloat 8 Float8E4M3EXT", true},
+        {"%1 = OpTypeFloat 9 Float8E4M3EXT", false},
+        {"%1 = OpTypeFloat 16 Float8E4M3EXT", false},
+        {"%1 = OpTypeFloat 32 Float8E4M3EXT", false},
+        {"%1 = OpTypeFloat 64 Float8E4M3EXT", false},
+        {"%1 = OpTypeFloat 100 Float8E4M3EXT", false},
+    }));
+
 // TODO(dneto): OpTypeVoid
 // TODO(dneto): OpTypeBool
 // TODO(dneto): OpTypeInt
diff --git a/test/tools/opt/flags.py b/test/tools/opt/flags.py
index 52a43c5..2c7d7ca 100644
--- a/test/tools/opt/flags.py
+++ b/test/tools/opt/flags.py
@@ -113,7 +113,7 @@
       'remove-duplicates',
       'replace-invalid-opcode',
       'ssa-rewrite',
-      'scalar-replacement=100',
+      'scalar-replacement=0',
       'scalar-replacement=42',
       'strength-reduction',
       'strip-debug',
@@ -148,7 +148,7 @@
       'eliminate-local-single-block',
       'eliminate-local-single-store',
       'eliminate-dead-code-aggressive',
-      'scalar-replacement=100',
+      'scalar-replacement=0',
       'convert-local-access-chains',
       'eliminate-local-single-block',
       'eliminate-local-single-store',
@@ -162,7 +162,7 @@
       'redundancy-elimination',
       'combine-access-chains',
       'simplify-instructions',
-      'scalar-replacement=100',
+      'scalar-replacement=0',
       'convert-local-access-chains',
       'eliminate-local-single-block',
       'eliminate-local-single-store',
@@ -259,7 +259,9 @@
       'eliminate-local-single-store',
       'eliminate-dead-code-aggressive',
       'ssa-rewrite',
+      'combine-access-chains',
       'eliminate-dead-code-aggressive',
+      'legalize-multidim-array',
       'ccp',
       'loop-unroll',
       'eliminate-dead-branches',
diff --git a/test/util/CMakeLists.txt b/test/util/CMakeLists.txt
index 20038f7..c5cbc17 100644
--- a/test/util/CMakeLists.txt
+++ b/test/util/CMakeLists.txt
@@ -17,6 +17,8 @@
        bit_vector_test.cpp
        bitutils_test.cpp
        hash_combine_test.cpp
+       index_range_test.cpp
        small_vector_test.cpp
+       span_test.cpp
   LIBS SPIRV-Tools-opt
 )
diff --git a/test/util/index_range_test.cpp b/test/util/index_range_test.cpp
new file mode 100644
index 0000000..0ae3954
--- /dev/null
+++ b/test/util/index_range_test.cpp
@@ -0,0 +1,79 @@
+// Copyright (c) 2025 Google LLC
+//
+// 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/util/index_range.h"
+
+#include "gmock/gmock.h"
+
+namespace spvtools {
+namespace utils {
+namespace {
+
+using IndexRangeTest = ::testing::Test;
+
+using ushort = unsigned short;
+
+TEST(IndexRangeTest, Initialize_Default) {
+  double sentinel_a = 0.0;
+  double sentinel_b = 1.0;
+  const IndexRange<double, unsigned, ushort> ir;
+
+  EXPECT_EQ(ir.first(), unsigned(0));
+  EXPECT_EQ(ir.count(), ushort(0));
+  EXPECT_TRUE(ir.empty());
+
+  auto span_null = ir.apply(nullptr);
+  EXPECT_EQ(span_null.data(), nullptr);
+  EXPECT_EQ(span_null.size(), 0);
+  EXPECT_TRUE(span_null.empty());
+
+  auto span_a = ir.apply(&sentinel_a);
+  EXPECT_EQ(span_a.data(), &sentinel_a);
+  EXPECT_EQ(span_a.size(), 0);
+  EXPECT_TRUE(span_a.empty());
+
+  auto span_b = ir.apply(&sentinel_b);
+  EXPECT_EQ(span_b.data(), &sentinel_b);
+  EXPECT_EQ(span_b.size(), 0);
+  EXPECT_TRUE(span_b.empty());
+}
+
+TEST(IndexRangeTest, Initialize_NonEmpty) {
+  const IndexRange<double, unsigned, ushort> ir(1, 2);
+
+  EXPECT_EQ(ir.first(), unsigned(1));
+  EXPECT_EQ(ir.count(), ushort(2));
+  EXPECT_FALSE(ir.empty());
+
+  auto span_null = ir.apply(nullptr);
+  EXPECT_EQ(span_null.data(), nullptr);
+  EXPECT_EQ(span_null.size(), 0);
+  EXPECT_TRUE(span_null.empty());
+
+  double arr[] = {0.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0};
+
+  auto span_a = ir.apply(arr);
+  EXPECT_EQ(span_a.begin(), arr + 1);
+  EXPECT_EQ(span_a.end(), arr + 3);
+  EXPECT_FALSE(span_a.empty());
+
+  auto span_b = ir.apply(arr + 3);
+  EXPECT_EQ(span_b.begin(), arr + 4);
+  EXPECT_EQ(span_b.end(), arr + 6);
+  EXPECT_FALSE(span_a.empty());
+}
+
+}  // namespace
+}  // namespace utils
+}  // namespace spvtools
diff --git a/test/util/span_test.cpp b/test/util/span_test.cpp
new file mode 100644
index 0000000..cfffa7a
--- /dev/null
+++ b/test/util/span_test.cpp
@@ -0,0 +1,223 @@
+// Copyright (c) 2025 Google LLC
+//
+// 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/util/span.h"
+
+#include "gmock/gmock.h"
+
+namespace spvtools {
+namespace utils {
+namespace {
+
+using SpanTest = ::testing::Test;
+
+TEST(SpanTest, Initialize_Default) {
+  const Span<int> s;
+
+  EXPECT_EQ(s.begin(), nullptr);
+  EXPECT_EQ(s.end(), nullptr);
+  EXPECT_EQ(s.cbegin(), nullptr);
+  EXPECT_EQ(s.cend(), nullptr);
+
+  EXPECT_EQ(s.size(), std::size_t(0));
+  EXPECT_EQ(s.size_bytes(), std::size_t(0));
+  EXPECT_TRUE(s.empty());
+
+  EXPECT_EQ(s.data(), nullptr);
+}
+
+TEST(SpanTest, Initialize_EmptySpan) {
+  int ints[3];
+  int* first = ints + 1;
+  const Span<int> s(first, 0);
+
+  EXPECT_EQ(s.begin(), first);
+  EXPECT_EQ(s.end(), first);
+  EXPECT_EQ(s.begin(), s.end());
+  EXPECT_EQ(s.cbegin(), first);
+  EXPECT_EQ(s.cend(), first);
+  EXPECT_EQ(s.cbegin(), s.cend());
+
+  EXPECT_EQ(s.size(), std::size_t(0));
+  EXPECT_EQ(s.size_bytes(), std::size_t(0));
+  EXPECT_TRUE(s.empty());
+
+  EXPECT_EQ(s.data(), first);
+}
+
+TEST(SpanTest, Initialize_NonemptySpan) {
+  int ints[10] = {0, 10, 20, 30, 40, 50, 60};
+  int* first = ints + 2;
+  const Span<int> s(first, 3);
+
+  EXPECT_EQ(s.begin(), first);
+  EXPECT_EQ(s.end(), first + 3);
+  EXPECT_NE(s.begin(), s.end());
+  EXPECT_EQ(s.cbegin(), first);
+  EXPECT_EQ(s.cend(), first + 3);
+  EXPECT_NE(s.cbegin(), s.cend());
+
+  EXPECT_EQ(s.size(), std::size_t(3));
+  EXPECT_EQ(s.size_bytes(), 3 * sizeof(int));
+  EXPECT_FALSE(s.empty());
+
+  EXPECT_EQ(&(s.front()), first);
+  EXPECT_EQ(s.front(), 20);
+  EXPECT_EQ(&(s.back()), first + 2);
+  EXPECT_EQ(s.back(), 40);
+
+  EXPECT_EQ(s.data(), first);
+  EXPECT_EQ(&s[0], first);
+  EXPECT_EQ(s[0], 20);
+  EXPECT_EQ(&s[1], first + 1);
+  EXPECT_EQ(s[1], 30);
+  EXPECT_EQ(&s[2], first + 2);
+  EXPECT_EQ(s[2], 40);
+  EXPECT_EQ(&s[3], first + 3);
+  EXPECT_EQ(&s[3], s.end());
+}
+
+TEST(SpanTest, Initialize_NonemptySpan_Iterator_PostIncrement) {
+  int ints[10] = {0, 10, 20, 30, 40, 50, 60};
+  int* first = ints + 2;
+  const Span<int> s(first, 3);
+
+  auto iter = s.begin();
+  EXPECT_NE(iter, s.end());
+  EXPECT_EQ(*iter++, 20);
+
+  EXPECT_NE(iter, s.end());
+  EXPECT_EQ(*iter++, 30);
+
+  EXPECT_NE(iter, s.end());
+  EXPECT_EQ(*iter++, 40);
+
+  EXPECT_EQ(iter, s.end());
+}
+
+TEST(SpanTest, Initialize_NonemptySpan_Iterator_PreIncrement) {
+  int ints[10] = {0, 10, 20, 30, 40, 50, 60};
+  int* first = ints + 2;
+  const Span<int> s(first, 3);
+
+  auto iter = s.begin();
+  EXPECT_EQ(*++iter, 30);
+
+  EXPECT_NE(iter, s.end());
+  EXPECT_EQ(*++iter, 40);
+
+  EXPECT_EQ(++iter, s.end());
+}
+
+TEST(SpanTest, Initialize_NonemptySpan_Iterator_PostDecrement) {
+  int ints[10] = {0, 10, 20, 30, 40, 50, 60};
+  int* first = ints + 2;
+  const Span<int> s(first, 3);
+
+  auto iter = s.end();
+  EXPECT_EQ(iter--, s.end());
+  EXPECT_EQ(*iter--, 40);
+
+  EXPECT_NE(iter, s.begin());
+  EXPECT_EQ(*iter--, 30);
+
+  EXPECT_EQ(iter, s.begin());
+  EXPECT_EQ(*iter--, 20);
+
+  EXPECT_NE(iter, s.begin());
+}
+
+TEST(SpanTest, Initialize_NonemptySpan_Iterator_PreDecrement) {
+  int ints[10] = {0, 10, 20, 30, 40, 50, 60};
+  int* first = ints + 2;
+  const Span<int> s(first, 3);
+
+  auto iter = s.end();
+  EXPECT_EQ(*--iter, 40);
+
+  EXPECT_NE(iter, s.begin());
+  EXPECT_EQ(*--iter, 30);
+
+  EXPECT_NE(iter, s.begin());
+  EXPECT_EQ(*--iter, 20);
+
+  EXPECT_EQ(iter, s.begin());
+}
+
+TEST(SpanTest, Subspan_FromNil) {
+  const Span<int> snil(nullptr, 0);
+
+  const auto s0 = snil.subspan(0);
+  const auto s2 = snil.subspan(2);
+
+  EXPECT_EQ(s0.begin(), nullptr);
+  EXPECT_EQ(s0.end(), nullptr);
+  EXPECT_EQ(s0.size(), 0u);
+  EXPECT_TRUE(s0.empty());
+
+  EXPECT_EQ(s2.begin(), nullptr);
+  EXPECT_EQ(s2.end(), nullptr);
+  EXPECT_EQ(s2.size(), 0u);
+  EXPECT_TRUE(s2.empty());
+}
+
+TEST(SpanTest, Subspan_FromEmpty) {
+  int ints[10] = {0, 10, 20, 30, 40, 50, 60};
+  int* first = ints + 2;
+  const Span<int> s(first, 0);
+
+  const auto s0 = s.subspan(0);
+  const auto s2 = s.subspan(2);
+
+  EXPECT_EQ(s0.begin(), nullptr);
+  EXPECT_EQ(s0.end(), nullptr);
+  EXPECT_EQ(s0.size(), 0u);
+  EXPECT_TRUE(s0.empty());
+
+  EXPECT_EQ(s2.begin(), nullptr);
+  EXPECT_EQ(s2.end(), nullptr);
+  EXPECT_EQ(s2.size(), 0u);
+  EXPECT_TRUE(s2.empty());
+}
+
+TEST(SpanTest, Subspan_FromNonEmpty) {
+  int ints[10] = {0, 10, 20, 30, 40, 50, 60};
+  int* first = ints + 2;
+  const Span<int> s(first, 3);
+
+  const auto s0 = s.subspan(0);
+  const auto s2 = s.subspan(2);
+  const auto s3 = s.subspan(3);
+  const auto s4 = s.subspan(4);
+
+  EXPECT_EQ(s0.begin(), s.begin());
+  EXPECT_EQ(s0.end(), s.end());
+  EXPECT_EQ(s0.size(), 3u);
+
+  EXPECT_EQ(s2.begin(), s.begin() + 2);
+  EXPECT_EQ(s2.end(), s.end());
+  EXPECT_EQ(s2.size(), 1u);
+
+  EXPECT_EQ(s3.begin(), nullptr);
+  EXPECT_EQ(s3.end(), nullptr);
+  EXPECT_EQ(s3.size(), 0u);
+
+  EXPECT_EQ(s4.begin(), nullptr);
+  EXPECT_EQ(s4.end(), nullptr);
+  EXPECT_EQ(s4.size(), 0u);
+}
+
+}  // namespace
+}  // namespace utils
+}  // namespace spvtools
diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt
index 9d6f6ea..550a5b2 100644
--- a/test/val/CMakeLists.txt
+++ b/test/val/CMakeLists.txt
@@ -36,9 +36,12 @@
        val_data_test.cpp
        val_decoration_test.cpp
        val_derivatives_test.cpp
+       val_dot_product_test.cpp
        val_entry_point_test.cpp
        val_explicit_reserved_test.cpp
+       val_invalid_type_test.cpp
        val_extensions_test.cpp
+       val_extension_spv_khr_abort_test.cpp
        val_extension_spv_khr_expect_assume_test.cpp
        val_extension_spv_khr_linkonce_odr_test.cpp
        val_extension_spv_khr_subgroup_uniform_control_flow_test.cpp
@@ -47,6 +50,10 @@
        val_extension_spv_khr_terminate_invocation_test.cpp
        val_extension_spv_khr_subgroup_rotate_test.cpp
        val_extension_spv_nv_raw_access_chains.cpp
+       val_extension_spv_intel_arbitrary_precision_integers_test.cpp
+       val_extension_spv_intel_function_variants.cpp
+       val_extension_spv_intel_inline_assembly.cpp
+       val_extension_spv_ext_descriptor_heap.cpp
        val_ext_inst_test.cpp
        val_ext_inst_debug_test.cpp
        ${VAL_TEST_COMMON_SRCS}
@@ -71,19 +78,24 @@
 add_spvtools_unittest(TARGET val_fghijklmnop
   SRCS
        val_function_test.cpp
+       val_graph_test.cpp
+       val_group_test.cpp
        val_id_test.cpp
        val_image_test.cpp
        val_interfaces_test.cpp
        val_layout_test.cpp
        val_literals_test.cpp
+       val_logical_pointers_test.cpp
        val_logicals_test.cpp
        val_memory_test.cpp
+       val_memory_semantics_test.cpp
        val_mesh_shading_test.cpp
        val_misc_test.cpp
        val_modes_test.cpp
        val_non_semantic_test.cpp
        val_non_uniform_test.cpp
        val_opencl_test.cpp
+       val_pipe_test.cpp
        val_primitives_test.cpp
        ${VAL_TEST_COMMON_SRCS}
   LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}
@@ -99,6 +111,7 @@
        val_ssa_test.cpp
        val_state_test.cpp
        val_storage_test.cpp
+       val_tensor_test.cpp
        val_type_unique_test.cpp
        val_validation_state_test.cpp
        val_version_test.cpp
diff --git a/test/val/val_arithmetics_test.cpp b/test/val/val_arithmetics_test.cpp
index 8b2a8d0..445c579 100644
--- a/test/val/val_arithmetics_test.cpp
+++ b/test/val/val_arithmetics_test.cpp
@@ -1166,6 +1166,243 @@
                 "vector size of the right operand: OuterProduct"));
 }
 
+std::string GenerateBFloatCode(const std::string& main_body) {
+  const std::string prefix =
+      R"(
+OpCapability Shader
+OpCapability BFloat16TypeKHR
+OpCapability BFloat16DotProductKHR
+OpCapability BFloat16CooperativeMatrixKHR
+OpExtension "SPV_KHR_bfloat16"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpSource GLSL 450
+OpName %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%bfloat16 = OpTypeFloat 16 BFloat16KHR
+%_ptr_Function_bfloat16 = OpTypePointer Function %bfloat16
+%v2bfloat16 = OpTypeVector %bfloat16 2
+%_ptr_Function_v2bfloat16 = OpTypePointer Function %v2bfloat16
+%main = OpFunction %void None %func
+%main_entry = OpLabel)";
+
+  const std::string suffix =
+      R"(
+OpReturn
+OpFunctionEnd)";
+
+  return prefix + main_body + suffix;
+}
+
+TEST_F(ValidateArithmetics, DotBfloat16) {
+  const std::string body = R"(
+%v1 = OpVariable %_ptr_Function_v2bfloat16 Function
+%v2 = OpVariable %_ptr_Function_v2bfloat16 Function
+%12 = OpLoad %v2bfloat16 %v1
+%14 = OpLoad %v2bfloat16 %v2
+%15 = OpDot %bfloat16 %12 %14
+)";
+
+  CompileSuccessfully(GenerateBFloatCode(body).c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+std::string GenerateFmaCode(const std::string& main_body) {
+  const std::string prefix =
+      R"(
+OpCapability Shader
+OpCapability Int64
+OpCapability Float16
+OpCapability Float64
+OpCapability FMAKHR
+OpExtension "SPV_KHR_fma"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%bool = OpTypeBool
+%f16 = OpTypeFloat 16
+%f32 = OpTypeFloat 32
+%u32 = OpTypeInt 32 0
+%f64 = OpTypeFloat 64
+%f16vec2 = OpTypeVector %f16 2
+%f32vec2 = OpTypeVector %f32 2
+%f64vec2 = OpTypeVector %f64 2
+%f16vec3 = OpTypeVector %f16 3
+%f32vec3 = OpTypeVector %f32 3
+%f64vec3 = OpTypeVector %f64 3
+%f16vec4 = OpTypeVector %f16 4
+%f32vec4 = OpTypeVector %f32 4
+%f64vec4 = OpTypeVector %f64 4
+%u32vec2 = OpTypeVector %u32 2
+
+%f16_1 = OpConstant %f16 1
+%f16_2 = OpConstant %f16 2
+%f16_4 = OpConstant %f16 4
+
+%f32_1 = OpConstant %f32 1
+%f32_2 = OpConstant %f32 2
+%f32_4 = OpConstant %f32 4
+
+%f64_1 = OpConstant %f64 1
+%f64_2 = OpConstant %f64 2
+%f64_4 = OpConstant %f64 4
+
+%u32_1 = OpConstant %u32 1
+%u32_2 = OpConstant %u32 2
+%u32_4 = OpConstant %u32 4
+
+%f16vec2_12 = OpConstantComposite %f16vec2 %f16_1 %f16_2
+%f16vec2_24 = OpConstantComposite %f16vec2 %f16_2 %f16_4
+%f16vec3_124 = OpConstantComposite %f16vec3 %f16_1 %f16_2 %f16_4
+%f16vec4_1241 = OpConstantComposite %f16vec4 %f16_1 %f16_2 %f16_4 %f16_1
+
+%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2
+%f32vec2_24 = OpConstantComposite %f32vec2 %f32_2 %f32_4
+%f32vec3_124 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_4
+%f32vec4_1241 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_4 %f32_1
+
+%f64vec2_12 = OpConstantComposite %f64vec2 %f64_1 %f64_2
+%f64vec2_24 = OpConstantComposite %f64vec2 %f64_2 %f64_4
+%f64vec3_124 = OpConstantComposite %f64vec3 %f64_1 %f64_2 %f64_4
+%f64vec4_1241 = OpConstantComposite %f64vec4 %f64_1 %f64_2 %f64_4 %f64_1
+
+%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2
+%u32vec2_24 = OpConstantComposite %u32vec2 %u32_2 %u32_4
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel)";
+
+  const std::string suffix =
+      R"(
+OpReturn
+OpFunctionEnd)";
+
+  return prefix + main_body + suffix;
+}
+
+TEST_F(ValidateArithmetics, FmaSuccess) {
+  const std::string body = R"(
+%val1 = OpFmaKHR %f16 %f16_1 %f16_2 %f16_4
+%val2 = OpFmaKHR %f32 %f32_1 %f32_2 %f32_4
+%val3 = OpFmaKHR %f64 %f64_1 %f64_2 %f64_4
+%val4 = OpFmaKHR %f16vec2 %f16vec2_12 %f16vec2_24 %f16vec2_12
+%val5 = OpFmaKHR %f32vec2 %f32vec2_12 %f32vec2_24 %f32vec2_12
+%val6 = OpFmaKHR %f64vec2 %f64vec2_12 %f64vec2_24 %f64vec2_12
+%val7 = OpFmaKHR %f16vec3 %f16vec3_124 %f16vec3_124 %f16vec3_124
+%val8 = OpFmaKHR %f32vec3 %f32vec3_124 %f32vec3_124 %f32vec3_124
+%val9 = OpFmaKHR %f64vec3 %f64vec3_124 %f64vec3_124 %f64vec3_124
+%val10 = OpFmaKHR %f16vec4 %f16vec4_1241 %f16vec4_1241 %f16vec4_1241
+%val11 = OpFmaKHR %f32vec4 %f32vec4_1241 %f32vec4_1241 %f32vec4_1241
+%val12 = OpFmaKHR %f64vec4 %f64vec4_1241 %f64vec4_1241 %f64vec4_1241
+)";
+
+  CompileSuccessfully(GenerateFmaCode(body).c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateArithmetics, FmaTypeIdU32) {
+  const std::string body = R"(
+%val = OpFmaKHR %u32 %u32_1 %u32_2 %u32_4
+)";
+
+  CompileSuccessfully(GenerateFmaCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected floating scalar or vector type as Result Type: FmaKHR"));
+}
+
+TEST_F(ValidateArithmetics, FmaTypeIdVec2U32) {
+  const std::string body = R"(
+%val = OpFmaKHR %u32vec2 %u32vec2_12 %u32vec2_24 %u32vec2_12
+)";
+
+  CompileSuccessfully(GenerateFmaCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected floating scalar or vector type as Result Type: FmaKHR"));
+}
+
+TEST_F(ValidateArithmetics, FmaWrongOperand1) {
+  const std::string body = R"(
+%val = OpFmaKHR %f32 %u32_1 %f32_2 %f32_4
+)";
+
+  CompileSuccessfully(GenerateFmaCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected arithmetic operands to be of Result Type: "
+                        "FmaKHR operand index 2"));
+}
+
+TEST_F(ValidateArithmetics, FmaWrongOperand2) {
+  const std::string body = R"(
+%val = OpFmaKHR %f32 %f32_1 %u32_2 %f32_4
+)";
+
+  CompileSuccessfully(GenerateFmaCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected arithmetic operands to be of Result Type: "
+                        "FmaKHR operand index 3"));
+}
+
+TEST_F(ValidateArithmetics, FmaWrongOperand3) {
+  const std::string body = R"(
+%val = OpFmaKHR %f32 %f32_1 %f32_2 %u32_4
+)";
+
+  CompileSuccessfully(GenerateFmaCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected arithmetic operands to be of Result Type: "
+                        "FmaKHR operand index 4"));
+}
+
+TEST_F(ValidateArithmetics, FmaWrongVectorOperand1) {
+  const std::string body = R"(
+%val = OpFmaKHR %f64vec3 %f32vec3_124 %f64vec3_124 %f64vec3_124
+)";
+
+  CompileSuccessfully(GenerateFmaCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected arithmetic operands to be of Result Type: "
+                        "FmaKHR operand index 2"));
+}
+
+TEST_F(ValidateArithmetics, FmaWrongVectorOperand2) {
+  const std::string body = R"(
+%val = OpFmaKHR %f32vec3 %f32vec3_124 %f64vec3_124 %f32vec3_124
+)";
+
+  CompileSuccessfully(GenerateFmaCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected arithmetic operands to be of Result Type: "
+                        "FmaKHR operand index 3"));
+}
+
+TEST_F(ValidateArithmetics, FmaWrongVectorOperand3) {
+  const std::string body = R"(
+%val = OpFmaKHR %f32vec3 %f32vec3_124 %f32vec3_124 %f64vec3_124
+)";
+
+  CompileSuccessfully(GenerateFmaCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected arithmetic operands to be of Result Type: "
+                        "FmaKHR operand index 4"));
+}
+
 std::string GenerateCoopMatCode(const std::string& extra_types,
                                 const std::string& main_body) {
   const std::string prefix =
@@ -1176,6 +1413,7 @@
 OpExtension "SPV_NV_cooperative_matrix"
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 32 1 1
 %void = OpTypeVoid
 %func = OpTypeFunction %void
 %bool = OpTypeBool
@@ -1280,9 +1518,9 @@
 
 TEST_F(ValidateArithmetics, CoopMatScopeFail) {
   const std::string types = R"(
-%device = OpConstant %u32 1
+%workgroup = OpConstant %u32 2
 
-%mat16x16_dv = OpTypeCooperativeMatrixNV %f16 %device %u32_16 %u32_16
+%mat16x16_dv = OpTypeCooperativeMatrixNV %f16 %workgroup %u32_16 %u32_16
 %f16matdv_16x16_1 = OpConstantComposite %mat16x16_dv %f16_1
 )";
 
@@ -1477,11 +1715,13 @@
 OpCapability CooperativeMatrixKHR
 OpCapability CooperativeMatrixReductionsNV
 OpCapability CooperativeMatrixPerElementOperationsNV
+OpCapability VulkanMemoryModel
 OpExtension "SPV_KHR_cooperative_matrix"
 OpExtension "SPV_NV_cooperative_matrix2"
 OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical GLSL450
+OpMemoryModel Logical Vulkan
 OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 32 1 1
 %void = OpTypeVoid
 %func = OpTypeFunction %void
 %bool = OpTypeBool
@@ -1564,8 +1804,9 @@
 %val18 = OpCooperativeMatrixMulAddKHR %u32matC %u32mat_A_1 %u32mat_B_1 %u32mat_C_1
 )";
 
-  CompileSuccessfully(GenerateCoopMatKHRCode("", body).c_str());
-  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+  CompileSuccessfully(GenerateCoopMatKHRCode("", body).c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
 TEST_F(ValidateArithmetics, CoopMatMatrixKHRTimesScalarMismatchFail) {
@@ -1573,8 +1814,10 @@
 %val1 = OpMatrixTimesScalar %f16matA %f16mat_A_1 %f32_1
 )";
 
-  CompileSuccessfully(GenerateCoopMatKHRCode("", body).c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  CompileSuccessfully(GenerateCoopMatKHRCode("", body).c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("Expected scalar operand type to be equal to the component "
@@ -1583,8 +1826,8 @@
 
 TEST_F(ValidateArithmetics, CoopMatKHRScopeFail) {
   const std::string types = R"(
-%device = OpConstant %u32 1
-%mat16x16_dv = OpTypeCooperativeMatrixKHR %f16 %device %u32_16 %u32_16 %useC
+%workgroup = OpConstant %u32 2
+%mat16x16_dv = OpTypeCooperativeMatrixKHR %f16 %workgroup %u32_16 %u32_16 %useC
 %f16matdv_16x16_1 = OpConstantComposite %mat16x16_dv %f16_1
 )";
 
@@ -1592,8 +1835,10 @@
 %val1 = OpFAdd %f16matA %f16matdv_16x16_1 %f16mat_A_1
 )";
 
-  CompileSuccessfully(GenerateCoopMatKHRCode(types, body).c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  CompileSuccessfully(GenerateCoopMatKHRCode(types, body).c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("Expected scopes of Matrix and Result Type to be identical"));
@@ -1609,8 +1854,10 @@
 %val1 = OpCooperativeMatrixMulAddKHR %mat16x4 %f16mat_A_1 %f16mat_B_1 %mat16x4_C_1
 )";
 
-  CompileSuccessfully(GenerateCoopMatKHRCode(types, body).c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  CompileSuccessfully(GenerateCoopMatKHRCode(types, body).c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("Cooperative matrix 'N' mismatch: CooperativeMatrixMulAddKHR"));
@@ -1641,8 +1888,9 @@
 %val5 = OpCooperativeMatrixReduceNV %f16matC8 %f16mat_C_1 Row|Column %reducefunc
 )";
 
-  CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str());
-  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+  CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
 TEST_F(ValidateArithmetics, CoopMat2Reduce2x2DimFail) {
@@ -1662,8 +1910,10 @@
 %val1 = OpCooperativeMatrixReduceNV %f16matC %f16mat_C_1 2x2 %reducefunc
 )";
 
-  CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("For Reduce2x2, result rows/cols must be half of "
                         "matrix rows/cols: CooperativeMatrixReduceNV"));
@@ -1688,8 +1938,10 @@
 %val1 = OpCooperativeMatrixReduceNV %f16matC8x16 %f16mat_C_1 Row %reducefunc
 )";
 
-  CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("For ReduceRow, result rows must match matrix rows: "
                         "CooperativeMatrixReduceNV"));
@@ -1714,8 +1966,10 @@
 %val1 = OpCooperativeMatrixReduceNV %f16matC16x8 %f16mat_C_1 Column %reducefunc
 )";
 
-  CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("For ReduceColumn, result cols must match matrix cols: "
                         "CooperativeMatrixReduceNV"));
@@ -1740,8 +1994,10 @@
 %val1 = OpCooperativeMatrixReduceNV %f16matC8 %f16mat_C_1 Row|Column|2x2 %reducefunc
 )";
 
-  CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Reduce 2x2 must not be used with Row/Column: "
                         "CooperativeMatrixReduceNV"));
@@ -1764,8 +2020,10 @@
 %val1 = OpCooperativeMatrixReduceNV %f16matC %f16mat_C_1 Row|Column %reducefunc
 )";
 
-  CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("CombineFunc return type and parameters must match "
                         "matrix component type: CooperativeMatrixReduceNV"));
@@ -1800,8 +2058,9 @@
 %val2 = OpCooperativeMatrixPerElementOpNV %f16matC %f16mat_C_1 %elemfunc2 %f16_1
 )";
 
-  CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str());
-  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+  CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
 TEST_F(ValidateArithmetics, CoopMat2PerElementOpElemTyFail) {
@@ -1822,8 +2081,9 @@
 %val1 = OpCooperativeMatrixPerElementOpNV %f16matC %f16mat_C_1 %elemfunc
 )";
 
-  CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("must match matrix component type"));
 }
@@ -1846,11 +2106,173 @@
 %val1 = OpCooperativeMatrixPerElementOpNV %f16matC %f16mat_C_1 %elemfunc
 )";
 
-  CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(GenerateCoopMatKHRCode(extra_types, body).c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a 32-bit integer"));
 }
 
+std::string GenerateCoopVecCode(const std::string& extra_types,
+                                const std::string& main_body) {
+  const std::string prefix =
+      R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability CooperativeVectorNV
+OpCapability ReplicatedCompositesEXT
+OpExtension "SPV_NV_cooperative_vector"
+OpExtension "SPV_EXT_replicated_composites"
+%ext_inst = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%bool = OpTypeBool
+%f16 = OpTypeFloat 16
+%f32 = OpTypeFloat 32
+%u32 = OpTypeInt 32 0
+%s32 = OpTypeInt 32 1
+
+%u32_8 = OpConstant %u32 8
+%u32_16 = OpConstant %u32 16
+%u32_4 = OpConstant %u32 4
+%subgroup = OpConstant %u32 3
+
+%f16vec = OpTypeCooperativeVectorNV %f16 %u32_8
+%f16vec4 = OpTypeCooperativeVectorNV %f16 %u32_4
+%u32vec = OpTypeCooperativeVectorNV %u32 %u32_8
+%s32vec = OpTypeCooperativeVectorNV %s32 %u32_8
+
+%f16_1 = OpConstant %f16 1
+%f32_1 = OpConstant %f32 1
+%u32_1 = OpConstant %u32 1
+%s32_1 = OpConstant %s32 1
+
+%f16vec4_1 = OpConstantComposite %f16vec4 %f16_1 %f16_1 %f16_1 %f16_1
+%f16vec_1 = OpConstantComposite %f16vec %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1
+%u32vec_1 = OpConstantComposite %u32vec %u32_1 %u32_1 %u32_1 %u32_1 %u32_1 %u32_1 %u32_1 %u32_1
+%s32vec_1 = OpConstantComposite %s32vec %s32_1 %s32_1 %s32_1 %s32_1 %s32_1 %s32_1 %s32_1 %s32_1
+
+%u32_c1 = OpSpecConstant %u32 1
+%u32_c2 = OpSpecConstant %u32 2
+
+%f16vecc = OpTypeCooperativeVectorNV %f16 %u32_c1
+%f16vecc_1 = OpConstantCompositeReplicateEXT %f16vecc %f16_1
+)";
+
+  const std::string func_begin =
+      R"(
+%main = OpFunction %void None %func
+%main_entry = OpLabel)";
+
+  const std::string suffix =
+      R"(
+OpReturn
+OpFunctionEnd)";
+
+  return prefix + extra_types + func_begin + main_body + suffix;
+}
+
+TEST_F(ValidateArithmetics, CoopVecSuccess) {
+  const std::string body = R"(
+%val1 = OpFAdd %f16vec %f16vec_1 %f16vec_1
+%val2 = OpFSub %f16vec %f16vec_1 %f16vec_1
+%val3 = OpFDiv %f16vec %f16vec_1 %f16vec_1
+%val4 = OpFNegate %f16vec %f16vec_1
+%val5 = OpIAdd %u32vec %u32vec_1 %u32vec_1
+%val6 = OpISub %u32vec %u32vec_1 %u32vec_1
+%val7 = OpUDiv %u32vec %u32vec_1 %u32vec_1
+%val8 = OpIAdd %s32vec %s32vec_1 %s32vec_1
+%val9 = OpISub %s32vec %s32vec_1 %s32vec_1
+%val10 = OpSDiv %s32vec %s32vec_1 %s32vec_1
+%val11 = OpSNegate %s32vec %s32vec_1
+%val12 = OpVectorTimesScalar %f16vec %f16vec_1 %f16_1
+%val13 = OpExtInst %f16vec %ext_inst FMin %f16vec_1 %f16vec_1
+%val14 = OpExtInst %f16vec %ext_inst FMax %f16vec_1 %f16vec_1
+%val15 = OpExtInst %f16vec %ext_inst FClamp %f16vec_1 %f16vec_1 %f16vec_1
+%val16 = OpExtInst %f16vec %ext_inst NClamp %f16vec_1 %f16vec_1 %f16vec_1
+%val17 = OpExtInst %f16vec %ext_inst Step %f16vec_1 %f16vec_1
+%val18 = OpExtInst %f16vec %ext_inst Exp %f16vec_1
+%val19 = OpExtInst %f16vec %ext_inst Log %f16vec_1
+%val20 = OpExtInst %f16vec %ext_inst Tanh %f16vec_1
+%val21 = OpExtInst %f16vec %ext_inst Atan %f16vec_1
+%val22 = OpExtInst %f16vec %ext_inst Fma %f16vec_1 %f16vec_1 %f16vec_1
+%val23 = OpExtInst %u32vec %ext_inst UMin %u32vec_1 %u32vec_1
+%val24 = OpExtInst %u32vec %ext_inst UMax %u32vec_1 %u32vec_1
+%val25 = OpExtInst %u32vec %ext_inst UClamp %u32vec_1 %u32vec_1 %u32vec_1
+%val26 = OpExtInst %s32vec %ext_inst SMin %s32vec_1 %s32vec_1
+%val27 = OpExtInst %s32vec %ext_inst SMax %s32vec_1 %s32vec_1
+%val28 = OpExtInst %s32vec %ext_inst SClamp %s32vec_1 %s32vec_1 %s32vec_1
+%val29 = OpShiftRightLogical %u32vec %u32vec_1 %u32vec_1
+%val30 = OpShiftRightArithmetic %u32vec %u32vec_1 %u32vec_1
+%val31 = OpShiftLeftLogical %u32vec %u32vec_1 %u32vec_1
+%val32 = OpBitwiseOr %u32vec %u32vec_1 %u32vec_1
+%val33 = OpBitwiseXor %u32vec %u32vec_1 %u32vec_1
+%val34 = OpBitwiseAnd %u32vec %u32vec_1 %u32vec_1
+%val35 = OpNot %u32vec %u32vec_1
+)";
+
+  CompileSuccessfully(GenerateCoopVecCode("", body).c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateArithmetics, CoopVecFMulPass) {
+  const std::string body = R"(
+%val1 = OpFMul %f16vec %f16vec_1 %f16vec_1
+)";
+
+  CompileSuccessfully(GenerateCoopVecCode("", body).c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateArithmetics, CoopVecVectorTimesScalarMismatchFail) {
+  const std::string body = R"(
+%val1 = OpVectorTimesScalar %f16vec %f16vec_1 %f32_1
+)";
+
+  CompileSuccessfully(GenerateCoopVecCode("", body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected scalar operand type to be equal to the component "
+                "type of the vector operand: VectorTimesScalar"));
+}
+
+TEST_F(ValidateArithmetics, CoopVecDimFail) {
+  const std::string body = R"(
+%val1 = OpFMul %f16vec %f16vec_1 %f16vec4_1
+)";
+
+  CompileSuccessfully(GenerateCoopVecCode("", body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected number of components to be identical"));
+}
+
+TEST_F(ValidateArithmetics, CoopVecComponentTypeNotScalarNumeric) {
+  const std::string types = R"(
+%bad = OpTypeCooperativeVectorNV %f16vec %u32_8
+)";
+
+  CompileSuccessfully(GenerateCoopVecCode(types, "").c_str());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpTypeVectorIdEXT Component Type <id> "
+                        "'14[%14]' is not a scalar numerical type."));
+}
+
+TEST_F(ValidateArithmetics, CoopVecDimNotConstantInt) {
+  const std::string types = R"(
+%bad = OpTypeCooperativeVectorNV %f16 %f32_1
+)";
+
+  CompileSuccessfully(GenerateCoopVecCode(types, "").c_str());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpTypeVectorIdEXT component count type <id> "
+                        "'7[%float]' is not a 32-bit integer type"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_atomics_test.cpp b/test/val/val_atomics_test.cpp
index 1cec51e..c8727ed 100644
--- a/test/val/val_atomics_test.cpp
+++ b/test/val/val_atomics_test.cpp
@@ -66,6 +66,8 @@
 %acquire_release = OpConstant %u32 8
 %acquire_and_release = OpConstant %u32 6
 %sequentially_consistent = OpConstant %u32 16
+%acquire_uniform_workgroup = OpConstant %u32 322
+%release_uniform_workgroup = OpConstant %u32 324
 %acquire_release_uniform_workgroup = OpConstant %u32 328
 
 %f32_ptr = OpTypePointer Workgroup %f32
@@ -261,7 +263,7 @@
 TEST_F(ValidateAtomics, AtomicLoadInt32VulkanSuccess) {
   const std::string body = R"(
 %val1 = OpAtomicLoad %u32 %u32_var %device %relaxed
-%val2 = OpAtomicLoad %u32 %u32_var %workgroup %acquire
+%val2 = OpAtomicLoad %u32 %u32_var %workgroup %acquire_uniform_workgroup
 %val3 = OpAtomicLoad %u32 %u32_var %invocation %relaxed
 )";
 
@@ -280,8 +282,9 @@
               AnyVUID("VUID-StandaloneSpirv-None-04645"));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("in Vulkan environment, Workgroup Storage Class is limited to "
-                "MeshNV, TaskNV, and GLCompute execution model"));
+      HasSubstr(
+          "in Vulkan environment, Workgroup Storage Class is limited to "
+          "MeshEXT, TaskEXT, MeshNV, TaskNV, and GLCompute execution model"));
 }
 
 TEST_F(ValidateAtomics, AtomicAddIntVulkanWrongType1) {
@@ -694,7 +697,7 @@
 TEST_F(ValidateAtomics, AtomicLoadFloatVulkan) {
   const std::string body = R"(
 %val1 = OpAtomicLoad %f32 %f32_var %device %relaxed
-%val2 = OpAtomicLoad %f32 %f32_var %workgroup %acquire
+%val2 = OpAtomicLoad %f32 %f32_var %workgroup %acquire_uniform_workgroup
 )";
 
   CompileSuccessfully(GenerateShaderComputeCode(body), SPV_ENV_VULKAN_1_0);
@@ -712,8 +715,9 @@
               AnyVUID("VUID-StandaloneSpirv-None-04645"));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("in Vulkan environment, Workgroup Storage Class is limited to "
-                "MeshNV, TaskNV, and GLCompute execution model"));
+      HasSubstr(
+          "in Vulkan environment, Workgroup Storage Class is limited to "
+          "MeshEXT, TaskEXT, MeshNV, TaskNV, and GLCompute execution model"));
 }
 
 TEST_F(ValidateAtomics, AtomicStoreFloatVulkan) {
@@ -737,7 +741,7 @@
 TEST_F(ValidateAtomics, AtomicLoadInt64WithCapabilityVulkanSuccess) {
   const std::string body = R"(
   %val1 = OpAtomicLoad %u64 %u64_var %device %relaxed
-  %val2 = OpAtomicLoad %u64 %u64_var %workgroup %acquire
+  %val2 = OpAtomicLoad %u64 %u64_var %workgroup %acquire_uniform_workgroup
   %val3 = OpAtomicLoad %u64 %u64_var %invocation %relaxed
   )";
 
@@ -781,7 +785,8 @@
               AnyVUID("VUID-StandaloneSpirv-None-04686"));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("AtomicStore: Vulkan spec only allows storage classes for "
+      HasSubstr("AtomicStore: Function is not allowed, the Vulkan spec only "
+                "allows storage classes for "
                 "atomic to be: Uniform, Workgroup, Image, StorageBuffer, "
                 "PhysicalStorageBuffer or TaskPayloadWorkgroupEXT."));
 }
@@ -803,7 +808,7 @@
 // reenable once fixed.
 TEST_F(ValidateAtomics, DISABLED_AtomicLoadVulkanSubgroup) {
   const std::string body = R"(
-%val1 = OpAtomicLoad %u32 %u32_var %subgroup %acquire
+%val1 = OpAtomicLoad %u32 %u32_var %subgroup %acquire_uniform_workgroup
 )";
 
   CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
@@ -813,54 +818,9 @@
                         "limited to Device, Workgroup and Invocation"));
 }
 
-TEST_F(ValidateAtomics, AtomicLoadVulkanRelease) {
-  const std::string body = R"(
-%val1 = OpAtomicLoad %u32 %u32_var %workgroup %release
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-OpAtomicLoad-04731"));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Vulkan spec disallows OpAtomicLoad with Memory Semantics "
-                "Release, AcquireRelease and SequentiallyConsistent"));
-}
-
-TEST_F(ValidateAtomics, AtomicLoadVulkanAcquireRelease) {
-  const std::string body = R"(
-%val1 = OpAtomicLoad %u32 %u32_var %workgroup %acquire_release
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-OpAtomicLoad-04731"));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Vulkan spec disallows OpAtomicLoad with Memory Semantics "
-                "Release, AcquireRelease and SequentiallyConsistent"));
-}
-
-TEST_F(ValidateAtomics, AtomicLoadVulkanSequentiallyConsistent) {
-  const std::string body = R"(
-%val1 = OpAtomicLoad %u32 %u32_var %workgroup %sequentially_consistent
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-OpAtomicLoad-04731"));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Vulkan spec disallows OpAtomicLoad with Memory Semantics "
-                "Release, AcquireRelease and SequentiallyConsistent"));
-}
-
 TEST_F(ValidateAtomics, AtomicLoadVulkanInvocationSemantics) {
   const std::string body = R"(
-%val1 = OpAtomicLoad %u32 %u32_var %invocation %acquire
+%val1 = OpAtomicLoad %u32 %u32_var %invocation %acquire_uniform_workgroup
 )";
 
   CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
@@ -870,7 +830,7 @@
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("AtomicLoad: Vulkan specification requires Memory Semantics to "
-                "be None if used with Invocation Memory Scope"));
+                "be Relaxed if used with Invocation Memory Scope"));
 }
 
 TEST_F(ValidateAtomics, AtomicLoadShaderFloat) {
@@ -1067,7 +1027,7 @@
 
 TEST_F(ValidateAtomics, AtomicStoreVulkanSuccess) {
   const std::string body = R"(
-OpAtomicStore %u32_var %device %release %u32_1
+OpAtomicStore %u32_var %device %release_uniform_workgroup %u32_1
 OpAtomicStore %u32_var %invocation %relaxed %u32_1
 )";
 
@@ -1075,54 +1035,9 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
 }
 
-TEST_F(ValidateAtomics, AtomicStoreVulkanAcquire) {
-  const std::string body = R"(
-OpAtomicStore %u32_var %device %acquire %u32_1
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-OpAtomicStore-04730"));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Vulkan spec disallows OpAtomicStore with Memory Semantics "
-                "Acquire, AcquireRelease and SequentiallyConsistent"));
-}
-
-TEST_F(ValidateAtomics, AtomicStoreVulkanAcquireRelease) {
-  const std::string body = R"(
-OpAtomicStore %u32_var %device %acquire_release %u32_1
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-OpAtomicStore-04730"));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Vulkan spec disallows OpAtomicStore with Memory Semantics "
-                "Acquire, AcquireRelease and SequentiallyConsistent"));
-}
-
-TEST_F(ValidateAtomics, AtomicStoreVulkanSequentiallyConsistent) {
-  const std::string body = R"(
-OpAtomicStore %u32_var %device %sequentially_consistent %u32_1
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-OpAtomicStore-04730"));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Vulkan spec disallows OpAtomicStore with Memory Semantics "
-                "Acquire, AcquireRelease and SequentiallyConsistent"));
-}
-
 TEST_F(ValidateAtomics, AtomicStoreVulkanInvocationSemantics) {
   const std::string body = R"(
-OpAtomicStore %u32_var %invocation %acquire %u32_1
+OpAtomicStore %u32_var %invocation %release_uniform_workgroup %u32_1
 )";
 
   CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
@@ -1132,7 +1047,7 @@
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("AtomicStore: Vulkan specification requires Memory Semantics "
-                "to be None if used with Invocation Memory Scope"));
+                "to be Relaxed if used with Invocation Memory Scope"));
 }
 
 TEST_F(ValidateAtomics, AtomicStoreWrongPointerType) {
@@ -1169,7 +1084,8 @@
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("AtomicStore: storage class must be Function, Workgroup, "
+      HasSubstr("AtomicStore: storage class is Image, but must be Function, "
+                "Workgroup, "
                 "CrossWorkGroup or Generic in the OpenCL environment."));
 }
 
@@ -1181,8 +1097,8 @@
   CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("AtomicStore: storage class forbidden by universal "
-                        "validation rules."));
+              HasSubstr("AtomicStore: Can not be used with storage class "
+                        "UniformConstant by universal validation rules"));
 }
 
 TEST_F(ValidateAtomics, AtomicStoreWrongScopeType) {
@@ -1337,7 +1253,7 @@
 TEST_F(ValidateAtomics, AtomicExchangeVulkanInvocationSemantics) {
   const std::string body = R"(
 OpAtomicStore %u32_var %invocation %relaxed %u32_1
-%val2 = OpAtomicExchange %u32 %u32_var %invocation %acquire %u32_0
+%val2 = OpAtomicExchange %u32 %u32_var %invocation %acquire_uniform_workgroup %u32_0
 )";
 
   CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
@@ -1346,8 +1262,9 @@
               AnyVUID("VUID-StandaloneSpirv-None-04641"));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("AtomicExchange: Vulkan specification requires Memory "
-                "Semantics to be None if used with Invocation Memory Scope"));
+      HasSubstr(
+          "AtomicExchange: Vulkan specification requires Memory Semantics to "
+          "be Relaxed if used with Invocation Memory Scope"));
 }
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeShaderSuccess) {
@@ -1469,9 +1386,10 @@
 
   CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("AtomicCompareExchange: Memory Semantics Release and "
-                        "AcquireRelease cannot be used for operand Unequal"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("AtomicCompareExchange Unequal Memory Semantics must not use "
+                "Release or AcquireRelease memory order"));
 }
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongValueType) {
@@ -1526,7 +1444,7 @@
 TEST_F(ValidateAtomics, AtomicCompareExchangeVulkanInvocationSemanticsEqual) {
   const std::string body = R"(
 OpAtomicStore %u32_var %device %relaxed %u32_1
-%val2 = OpAtomicCompareExchange %u32 %u32_var %invocation %release %relaxed %u32_0 %u32_0
+%val2 = OpAtomicCompareExchange %u32 %u32_var %invocation %release_uniform_workgroup %relaxed %u32_0 %u32_0
 )";
 
   CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
@@ -1535,14 +1453,15 @@
               AnyVUID("VUID-StandaloneSpirv-None-04641"));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("AtomicCompareExchange: Vulkan specification requires Memory "
-                "Semantics to be None if used with Invocation Memory Scope"));
+      HasSubstr(
+          "AtomicCompareExchange: Vulkan specification requires Memory "
+          "Semantics to be Relaxed if used with Invocation Memory Scope"));
 }
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeVulkanInvocationSemanticsUnequal) {
   const std::string body = R"(
 OpAtomicStore %u32_var %device %relaxed %u32_1
-%val2 = OpAtomicCompareExchange %u32 %u32_var %invocation %relaxed %acquire %u32_0 %u32_0
+%val2 = OpAtomicCompareExchange %u32 %u32_var %invocation %acquire_uniform_workgroup %acquire_uniform_workgroup %u32_0 %u32_0
 )";
 
   CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
@@ -1551,8 +1470,9 @@
               AnyVUID("VUID-StandaloneSpirv-None-04641"));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("AtomicCompareExchange: Vulkan specification requires Memory "
-                "Semantics to be None if used with Invocation Memory Scope"));
+      HasSubstr(
+          "AtomicCompareExchange: Vulkan specification requires Memory "
+          "Semantics to be Relaxed if used with Invocation Memory Scope"));
 }
 
 TEST_F(ValidateAtomics, AtomicArithmeticsSuccess) {
@@ -1666,9 +1586,10 @@
 
   CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Memory Semantics Acquire and AcquireRelease cannot be "
-                        "used with AtomicFlagClear"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("AtomicFlagClear: MemorySemantics must not use Acquire or "
+                "AcquireRelease memory order with AtomicFlagClear"));
 }
 
 TEST_F(ValidateAtomics, AtomicFlagClearNotPointer) {
@@ -1743,9 +1664,8 @@
   CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("AtomicIIncrement: Memory Semantics can have at most "
-                        "one of the following bits set: Acquire, Release, "
-                        "AcquireRelease or SequentiallyConsistent"));
+              HasSubstr("AtomicIIncrement: Memory Semantics must have at most "
+                        "one non-relaxed memory order bit set"));
 }
 
 TEST_F(ValidateAtomics, AtomicUniformMemorySemanticsShader) {
@@ -1794,357 +1714,6 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicLoad) {
-  const std::string body = R"(
-%ld = OpAtomicLoad %u32 %u32_var %workgroup %sequentially_consistent
-)";
-
-  const std::string extra = R"(
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
-                      SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("SequentiallyConsistent memory semantics cannot be "
-                        "used with the VulkanKHR memory model."));
-}
-
-TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicStore) {
-  const std::string body = R"(
-OpAtomicStore %u32_var %workgroup %sequentially_consistent %u32_0
-)";
-
-  const std::string extra = R"(
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
-                      SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("SequentiallyConsistent memory semantics cannot be "
-                        "used with the VulkanKHR memory model."));
-}
-
-TEST_F(ValidateAtomics,
-       VulkanMemoryModelBanSequentiallyConsistentAtomicExchange) {
-  const std::string body = R"(
-%ex = OpAtomicExchange %u32 %u32_var %workgroup %sequentially_consistent %u32_0
-)";
-
-  const std::string extra = R"(
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
-                      SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("SequentiallyConsistent memory semantics cannot be "
-                        "used with the VulkanKHR memory model."));
-}
-
-TEST_F(ValidateAtomics,
-       VulkanMemoryModelBanSequentiallyConsistentAtomicCompareExchangeEqual) {
-  const std::string body = R"(
-%ex = OpAtomicCompareExchange %u32 %u32_var %workgroup %sequentially_consistent %relaxed %u32_0 %u32_0
-)";
-
-  const std::string extra = R"(
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
-                      SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("SequentiallyConsistent memory semantics cannot be "
-                        "used with the VulkanKHR memory model."));
-}
-
-TEST_F(ValidateAtomics,
-       VulkanMemoryModelBanSequentiallyConsistentAtomicCompareExchangeUnequal) {
-  const std::string body = R"(
-%ex = OpAtomicCompareExchange %u32 %u32_var %workgroup %relaxed %sequentially_consistent %u32_0 %u32_0
-)";
-
-  const std::string extra = R"(
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
-                      SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("SequentiallyConsistent memory semantics cannot be "
-                        "used with the VulkanKHR memory model."));
-}
-
-TEST_F(ValidateAtomics,
-       VulkanMemoryModelBanSequentiallyConsistentAtomicIIncrement) {
-  const std::string body = R"(
-%inc = OpAtomicIIncrement %u32 %u32_var %workgroup %sequentially_consistent
-)";
-
-  const std::string extra = R"(
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
-                      SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("SequentiallyConsistent memory semantics cannot be "
-                        "used with the VulkanKHR memory model."));
-}
-
-TEST_F(ValidateAtomics,
-       VulkanMemoryModelBanSequentiallyConsistentAtomicIDecrement) {
-  const std::string body = R"(
-%dec = OpAtomicIDecrement %u32 %u32_var %workgroup %sequentially_consistent
-)";
-
-  const std::string extra = R"(
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
-                      SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("SequentiallyConsistent memory semantics cannot be "
-                        "used with the VulkanKHR memory model."));
-}
-
-TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicIAdd) {
-  const std::string body = R"(
-%add = OpAtomicIAdd %u32 %u32_var %workgroup %sequentially_consistent %u32_0
-)";
-
-  const std::string extra = R"(
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
-                      SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("SequentiallyConsistent memory semantics cannot be "
-                        "used with the VulkanKHR memory model."));
-}
-
-TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicISub) {
-  const std::string body = R"(
-%sub = OpAtomicISub %u32 %u32_var %workgroup %sequentially_consistent %u32_0
-)";
-
-  const std::string extra = R"(
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
-                      SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("SequentiallyConsistent memory semantics cannot be "
-                        "used with the VulkanKHR memory model."));
-}
-
-TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicSMin) {
-  const std::string body = R"(
-%min = OpAtomicSMin %u32 %u32_var %workgroup %sequentially_consistent %u32_0
-)";
-
-  const std::string extra = R"(
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
-                      SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("SequentiallyConsistent memory semantics cannot be "
-                        "used with the VulkanKHR memory model."));
-}
-
-TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicUMin) {
-  const std::string body = R"(
-%min = OpAtomicUMin %u32 %u32_var %workgroup %sequentially_consistent %u32_0
-)";
-
-  const std::string extra = R"(
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
-                      SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("SequentiallyConsistent memory semantics cannot be "
-                        "used with the VulkanKHR memory model."));
-}
-
-TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicFMinEXT) {
-  const std::string body = R"(
-%max = OpAtomicFMinEXT %f32 %f32_var %workgroup %sequentially_consistent %f32_0
-)";
-
-  const std::string extra = R"(
-OpCapability VulkanMemoryModelKHR
-OpCapability AtomicFloat32MinMaxEXT
-OpExtension "SPV_KHR_vulkan_memory_model"
-OpExtension "SPV_EXT_shader_atomic_float_min_max"
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
-                      SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("SequentiallyConsistent memory semantics cannot be "
-                        "used with the VulkanKHR memory model."));
-}
-
-TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicSMax) {
-  const std::string body = R"(
-%max = OpAtomicSMax %u32 %u32_var %workgroup %sequentially_consistent %u32_0
-)";
-
-  const std::string extra = R"(
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
-                      SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("SequentiallyConsistent memory semantics cannot be "
-                        "used with the VulkanKHR memory model."));
-}
-
-TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicUMax) {
-  const std::string body = R"(
-%max = OpAtomicUMax %u32 %u32_var %workgroup %sequentially_consistent %u32_0
-)";
-
-  const std::string extra = R"(
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
-                      SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("SequentiallyConsistent memory semantics cannot be "
-                        "used with the VulkanKHR memory model."));
-}
-
-TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicFMaxEXT) {
-  const std::string body = R"(
-%max = OpAtomicFMaxEXT %f32 %f32_var %workgroup %sequentially_consistent %f32_0
-)";
-
-  const std::string extra = R"(
-OpCapability VulkanMemoryModelKHR
-OpCapability AtomicFloat32MinMaxEXT
-OpExtension "SPV_KHR_vulkan_memory_model"
-OpExtension "SPV_EXT_shader_atomic_float_min_max"
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
-                      SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("SequentiallyConsistent memory semantics cannot be "
-                        "used with the VulkanKHR memory model."));
-}
-
-TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicAnd) {
-  const std::string body = R"(
-%and = OpAtomicAnd %u32 %u32_var %workgroup %sequentially_consistent %u32_0
-)";
-
-  const std::string extra = R"(
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
-                      SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("SequentiallyConsistent memory semantics cannot be "
-                        "used with the VulkanKHR memory model."));
-}
-
-TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicOr) {
-  const std::string body = R"(
-%or = OpAtomicOr %u32 %u32_var %workgroup %sequentially_consistent %u32_0
-)";
-
-  const std::string extra = R"(
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
-                      SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("SequentiallyConsistent memory semantics cannot be "
-                        "used with the VulkanKHR memory model."));
-}
-
-TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicXor) {
-  const std::string body = R"(
-%xor = OpAtomicXor %u32 %u32_var %workgroup %sequentially_consistent %u32_0
-)";
-
-  const std::string extra = R"(
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"),
-                      SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("SequentiallyConsistent memory semantics cannot be "
-                        "used with the VulkanKHR memory model."));
-}
-
 TEST_F(ValidateAtomics, OutputMemoryKHRRequiresVulkanMemoryModelKHR) {
   const std::string text = R"(
 OpCapability Shader
@@ -2226,130 +1795,6 @@
                         "capability VulkanMemoryModelKHR"));
 }
 
-TEST_F(ValidateAtomics, MakeAvailableKHRRequiresReleaseSemantics) {
-  const std::string text = R"(
-OpCapability Shader
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical VulkanKHR
-OpEntryPoint Fragment %1 "func"
-OpExecutionMode %1 OriginUpperLeft
-%2 = OpTypeVoid
-%3 = OpTypeInt 32 0
-%semantics = OpConstant %3 8448
-%5 = OpTypeFunction %2
-%workgroup = OpConstant %3 2
-%ptr = OpTypePointer Workgroup %3
-%var = OpVariable %ptr Workgroup
-%1 = OpFunction %2 None %5
-%7 = OpLabel
-OpAtomicStore %var %workgroup %semantics %workgroup
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("AtomicStore: MakeAvailableKHR Memory Semantics also requires "
-                "either Release or AcquireRelease Memory Semantics"));
-}
-
-TEST_F(ValidateAtomics, MakeVisibleKHRRequiresAcquireSemantics) {
-  const std::string text = R"(
-OpCapability Shader
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical VulkanKHR
-OpEntryPoint Fragment %1 "func"
-OpExecutionMode %1 OriginUpperLeft
-%2 = OpTypeVoid
-%3 = OpTypeInt 32 0
-%semantics = OpConstant %3 16640
-%5 = OpTypeFunction %2
-%workgroup = OpConstant %3 2
-%ptr = OpTypePointer Workgroup %3
-%var = OpVariable %ptr Workgroup
-%1 = OpFunction %2 None %5
-%7 = OpLabel
-%ld = OpAtomicLoad %3 %var %workgroup %semantics
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("AtomicLoad: MakeVisibleKHR Memory Semantics also requires "
-                "either Acquire or AcquireRelease Memory Semantics"));
-}
-
-TEST_F(ValidateAtomics, MakeAvailableKHRRequiresStorageSemantics) {
-  const std::string text = R"(
-OpCapability Shader
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical VulkanKHR
-OpEntryPoint Fragment %1 "func"
-OpExecutionMode %1 OriginUpperLeft
-%2 = OpTypeVoid
-%3 = OpTypeInt 32 0
-%semantics = OpConstant %3 8196
-%5 = OpTypeFunction %2
-%workgroup = OpConstant %3 2
-%ptr = OpTypePointer Workgroup %3
-%var = OpVariable %ptr Workgroup
-%1 = OpFunction %2 None %5
-%7 = OpLabel
-OpAtomicStore %var %workgroup %semantics %workgroup
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "AtomicStore: expected Memory Semantics to include a storage class"));
-}
-
-TEST_F(ValidateAtomics, MakeVisibleKHRRequiresStorageSemantics) {
-  const std::string text = R"(
-OpCapability Shader
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical VulkanKHR
-OpEntryPoint Fragment %1 "func"
-OpExecutionMode %1 OriginUpperLeft
-%2 = OpTypeVoid
-%3 = OpTypeInt 32 0
-%semantics = OpConstant %3 16386
-%5 = OpTypeFunction %2
-%workgroup = OpConstant %3 2
-%ptr = OpTypePointer Workgroup %3
-%var = OpVariable %ptr Workgroup
-%1 = OpFunction %2 None %5
-%7 = OpLabel
-%ld = OpAtomicLoad %3 %var %workgroup %semantics
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "AtomicLoad: expected Memory Semantics to include a storage class"));
-}
-
 TEST_F(ValidateAtomics, VulkanMemoryModelAllowsQueueFamilyKHR) {
   const std::string body = R"(
 %val = OpAtomicAnd %u32 %u32_var %queuefamily %relaxed %u32_1
@@ -2374,8 +1819,8 @@
   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicAnd: Memory Scope QueueFamilyKHR requires "
-                        "capability VulkanMemoryModelKHR\n  %42 = OpAtomicAnd "
-                        "%uint %29 %uint_5 %uint_0_1 %uint_1\n"));
+                        "capability VulkanMemoryModelKHR\n  %44 = OpAtomicAnd "
+                        "%uint %31 %uint_5 %uint_0_1 %uint_1\n"));
 }
 
 TEST_F(ValidateAtomics, SemanticsSpecConstantShader) {
@@ -2536,64 +1981,6 @@
           "AtomicCompareExchangeWeak requires SPIR-V version 1.3 or earlier"));
 }
 
-TEST_F(ValidateAtomics, CompareExchangeVolatileMatch) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpCapability VulkanMemoryModelKHR
-OpCapability Linkage
-OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical VulkanKHR
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_0 = OpConstant %int 0
-%int_1 = OpConstant %int 1
-%workgroup = OpConstant %int 2
-%volatile = OpConstant %int 32768
-%ptr_wg_int = OpTypePointer Workgroup %int
-%wg_var = OpVariable %ptr_wg_int Workgroup
-%void_fn = OpTypeFunction %void
-%func = OpFunction %void None %void_fn
-%entry = OpLabel
-%cmp_ex = OpAtomicCompareExchange %int %wg_var %workgroup %volatile %volatile %int_0 %int_1
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
-}
-
-TEST_F(ValidateAtomics, CompareExchangeVolatileMismatch) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpCapability VulkanMemoryModelKHR
-OpCapability Linkage
-OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical VulkanKHR
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_0 = OpConstant %int 0
-%int_1 = OpConstant %int 1
-%workgroup = OpConstant %int 2
-%volatile = OpConstant %int 32768
-%non_volatile = OpConstant %int 0
-%ptr_wg_int = OpTypePointer Workgroup %int
-%wg_var = OpVariable %ptr_wg_int Workgroup
-%void_fn = OpTypeFunction %void
-%func = OpFunction %void None %void_fn
-%entry = OpLabel
-%cmp_ex = OpAtomicCompareExchange %int %wg_var %workgroup %non_volatile %volatile %int_0 %int_1
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Volatile mask setting must match for Equal and "
-                        "Unequal memory semantics"));
-}
-
 TEST_F(ValidateAtomics, CompareExchangeVolatileMismatchCooperativeMatrix) {
   const std::string spirv = R"(
 OpCapability Shader
@@ -2621,8 +2008,8 @@
 )";
 
   // This is ok because we cannot evaluate the spec constant defaults.
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
 TEST_F(ValidateAtomics, VolatileRequiresVulkanMemoryModel) {
diff --git a/test/val/val_barriers_test.cpp b/test/val/val_barriers_test.cpp
index ba8ac7d..6f2cb45 100644
--- a/test/val/val_barriers_test.cpp
+++ b/test/val/val_barriers_test.cpp
@@ -469,7 +469,7 @@
 
 TEST_F(ValidateBarriers, OpControlBarrierVulkanInvocationFailure) {
   const std::string body = R"(
-OpControlBarrier %workgroup %invocation %acquire
+OpControlBarrier %workgroup %invocation %acquire_uniform_workgroup
 )";
 
   CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
@@ -478,8 +478,9 @@
               AnyVUID("VUID-StandaloneSpirv-None-04641"));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("ControlBarrier: Vulkan specification requires Memory "
-                "Semantics to be None if used with Invocation Memory Scope"));
+      HasSubstr(
+          "ControlBarrier: Vulkan specification requires Memory "
+          "Semantics to be Relaxed if used with Invocation Memory Scope"));
 }
 
 TEST_F(ValidateBarriers, OpControlBarrierAcquireAndRelease) {
@@ -490,25 +491,8 @@
   CompileSuccessfully(GenerateShaderCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("ControlBarrier: Memory Semantics can have at most one "
-                        "of the following bits set: Acquire, Release, "
-                        "AcquireRelease or SequentiallyConsistent"));
-}
-
-TEST_F(ValidateBarriers, OpControlBarrierVulkanSubgroupStorageClass) {
-  const std::string body = R"(
-OpControlBarrier %workgroup %device %acquire_release_subgroup
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-OpControlBarrier-04650"));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "ControlBarrier: expected Memory Semantics to include a "
-          "Vulkan-supported storage class if Memory Semantics is not None"));
+              HasSubstr("ControlBarrier: Memory Semantics must have at most "
+                        "one non-relaxed memory order bit set"));
 }
 
 TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionFragment1p1) {
@@ -687,9 +671,8 @@
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("OpControlBarrier requires one of the following "
-                        "Execution "
-                        "Models: TessellationControl, GLCompute, Kernel, "
-                        "MeshNV or TaskNV"));
+                        "Execution Models: TessellationControl, GLCompute, "
+                        "Kernel, MeshNV or TaskNV"));
 }
 
 TEST_F(ValidateBarriers, OpMemoryBarrierSuccess) {
@@ -800,53 +783,8 @@
   CompileSuccessfully(GenerateShaderCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("MemoryBarrier: Memory Semantics can have at most one "
-                        "of the following bits set: Acquire, Release, "
-                        "AcquireRelease or SequentiallyConsistent"));
-}
-
-TEST_F(ValidateBarriers, OpMemoryBarrierVulkanMemorySemanticsNone) {
-  const std::string body = R"(
-OpMemoryBarrier %device %none
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-OpMemoryBarrier-04732"));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("MemoryBarrier: Vulkan specification requires Memory Semantics "
-                "to have one of the following bits set: Acquire, Release, "
-                "AcquireRelease or SequentiallyConsistent"));
-}
-
-TEST_F(ValidateBarriers, OpMemoryBarrierVulkanMemorySemanticsAcquire) {
-  const std::string body = R"(
-OpMemoryBarrier %device %acquire
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-OpMemoryBarrier-04733"));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("MemoryBarrier: expected Memory Semantics to include a "
-                        "Vulkan-supported storage class"));
-}
-
-TEST_F(ValidateBarriers, OpMemoryBarrierVulkanSubgroupStorageClass) {
-  const std::string body = R"(
-OpMemoryBarrier %device %acquire_release_subgroup
-)";
-
-  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-OpMemoryBarrier-04733"));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("MemoryBarrier: expected Memory Semantics to include a "
-                        "Vulkan-supported storage class"));
+              HasSubstr("MemoryBarrier: Memory Semantics must have at most one "
+                        "non-relaxed memory order bit set"));
 }
 
 TEST_F(ValidateBarriers, OpNamedBarrierInitializeSuccess) {
@@ -959,9 +897,8 @@
   ASSERT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("MemoryNamedBarrier: Memory Semantics can have at most "
-                        "one of the following bits set: Acquire, Release, "
-                        "AcquireRelease or SequentiallyConsistent"));
+              HasSubstr("MemoryNamedBarrier: Memory Semantics must have "
+                        "at most one non-relaxed memory order bit set"));
 }
 
 TEST_F(ValidateBarriers, TypeAsMemoryScope) {
@@ -975,64 +912,6 @@
                                                "type"));
 }
 
-TEST_F(ValidateBarriers,
-       OpControlBarrierVulkanMemoryModelBanSequentiallyConsistent) {
-  const std::string text = R"(
-OpCapability Shader
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical VulkanKHR
-OpEntryPoint Fragment %1 "func"
-OpExecutionMode %1 OriginUpperLeft
-%2 = OpTypeVoid
-%3 = OpTypeInt 32 0
-%4 = OpConstant %3 16
-%5 = OpTypeFunction %2
-%6 = OpConstant %3 5
-%1 = OpFunction %2 None %5
-%7 = OpLabel
-OpControlBarrier %6 %6 %4
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("SequentiallyConsistent memory semantics cannot be "
-                        "used with the VulkanKHR memory model."));
-}
-
-TEST_F(ValidateBarriers,
-       OpMemoryBarrierVulkanMemoryModelBanSequentiallyConsistent) {
-  const std::string text = R"(
-OpCapability Shader
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical VulkanKHR
-OpEntryPoint Fragment %1 "func"
-OpExecutionMode %1 OriginUpperLeft
-%2 = OpTypeVoid
-%3 = OpTypeInt 32 0
-%4 = OpConstant %3 16
-%5 = OpTypeFunction %2
-%6 = OpConstant %3 5
-%1 = OpFunction %2 None %5
-%7 = OpLabel
-OpMemoryBarrier %6 %4
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("SequentiallyConsistent memory semantics cannot be "
-                        "used with the VulkanKHR memory model."));
-}
-
 TEST_F(ValidateBarriers, OutputMemoryKHRRequireVulkanMemoryModelKHR) {
   const std::string text = R"(
 OpCapability Shader
@@ -1108,120 +987,6 @@
                         "requires capability VulkanMemoryModelKHR"));
 }
 
-TEST_F(ValidateBarriers, MakeAvailableKHRRequiresReleaseSemantics) {
-  const std::string text = R"(
-OpCapability Shader
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical VulkanKHR
-OpEntryPoint Fragment %func "func"
-OpExecutionMode %func OriginUpperLeft
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%workgroup = OpConstant %int 2
-%semantics = OpConstant %int 8448
-%functy = OpTypeFunction %void
-%func = OpFunction %void None %functy
-%1 = OpLabel
-OpControlBarrier %workgroup %workgroup %semantics
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("ControlBarrier: MakeAvailableKHR Memory Semantics also "
-                "requires either Release or AcquireRelease Memory Semantics"));
-}
-
-TEST_F(ValidateBarriers, MakeVisibleKHRRequiresAcquireSemantics) {
-  const std::string text = R"(
-OpCapability Shader
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical VulkanKHR
-OpEntryPoint Fragment %func "func"
-OpExecutionMode %func OriginUpperLeft
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%workgroup = OpConstant %int 2
-%semantics = OpConstant %int 16640
-%functy = OpTypeFunction %void
-%func = OpFunction %void None %functy
-%1 = OpLabel
-OpControlBarrier %workgroup %workgroup %semantics
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("ControlBarrier: MakeVisibleKHR Memory Semantics also requires "
-                "either Acquire or AcquireRelease Memory Semantics"));
-}
-
-TEST_F(ValidateBarriers, MakeAvailableKHRRequiresStorageSemantics) {
-  const std::string text = R"(
-OpCapability Shader
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical VulkanKHR
-OpEntryPoint Fragment %func "func"
-OpExecutionMode %func OriginUpperLeft
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%workgroup = OpConstant %int 2
-%semantics = OpConstant %int 8196
-%functy = OpTypeFunction %void
-%func = OpFunction %void None %functy
-%1 = OpLabel
-OpMemoryBarrier %workgroup %semantics
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("MemoryBarrier: expected Memory Semantics to include a "
-                        "storage class"));
-}
-
-TEST_F(ValidateBarriers, MakeVisibleKHRRequiresStorageSemantics) {
-  const std::string text = R"(
-OpCapability Shader
-OpCapability VulkanMemoryModelKHR
-OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical VulkanKHR
-OpEntryPoint Fragment %func "func"
-OpExecutionMode %func OriginUpperLeft
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%workgroup = OpConstant %int 2
-%semantics = OpConstant %int 16386
-%functy = OpTypeFunction %void
-%func = OpFunction %void None %functy
-%1 = OpLabel
-OpMemoryBarrier %workgroup %semantics
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
-            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("MemoryBarrier: expected Memory Semantics to include a "
-                        "storage class"));
-}
-
 TEST_F(ValidateBarriers, SemanticsSpecConstantShader) {
   const std::string spirv = R"(
 OpCapability Shader
@@ -1333,7 +1098,7 @@
 %void = OpTypeVoid
 %int = OpTypeInt 32 0
 %device = OpConstant %int 1
-%semantics = OpConstant %int 0
+%semantics = OpConstant %int 72
 %functy = OpTypeFunction %void
 %func = OpFunction %void None %functy
 %1 = OpLabel
@@ -1363,7 +1128,7 @@
 %void = OpTypeVoid
 %int = OpTypeInt 32 0
 %device = OpConstant %int 1
-%semantics = OpConstant %int 0
+%semantics = OpConstant %int 72
 %functy = OpTypeFunction %void
 %func = OpFunction %void None %functy
 %1 = OpLabel
@@ -1376,60 +1141,6 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
-TEST_F(ValidateBarriers, VolatileMemoryBarrier) {
-  const std::string text = R"(
-OpCapability Shader
-OpCapability VulkanMemoryModelKHR
-OpCapability VulkanMemoryModelDeviceScopeKHR
-OpCapability Linkage
-OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical VulkanKHR
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%device = OpConstant %int 1
-%semantics = OpConstant %int 32768
-%functy = OpTypeFunction %void
-%func = OpFunction %void None %functy
-%1 = OpLabel
-OpMemoryBarrier %device %semantics
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(text);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Memory Semantics Volatile can only be used with "
-                        "atomic instructions"));
-}
-
-TEST_F(ValidateBarriers, VolatileControlBarrier) {
-  const std::string text = R"(
-OpCapability Shader
-OpCapability VulkanMemoryModelKHR
-OpCapability VulkanMemoryModelDeviceScopeKHR
-OpCapability Linkage
-OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical VulkanKHR
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%device = OpConstant %int 1
-%semantics = OpConstant %int 32768
-%functy = OpTypeFunction %void
-%func = OpFunction %void None %functy
-%1 = OpLabel
-OpControlBarrier %device %device %semantics
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(text);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Memory Semantics Volatile can only be used with "
-                        "atomic instructions"));
-}
-
 TEST_F(ValidateBarriers, CooperativeMatrixSpecConstantVolatile) {
   const std::string text = R"(
 OpCapability Shader
@@ -1452,8 +1163,8 @@
 OpFunctionEnd
 )";
 
-  CompileSuccessfully(text);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
 TEST_F(ValidateBarriers, CooperativeMatrixNonConstantSemantics) {
@@ -1478,8 +1189,9 @@
 OpFunctionEnd
 )";
 
-  CompileSuccessfully(text);
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Memory Semantics must be a constant instruction when "
                         "CooperativeMatrixNV capability is present"));
diff --git a/test/val/val_bitwise_test.cpp b/test/val/val_bitwise_test.cpp
index b849e7b..c906e47 100644
--- a/test/val/val_bitwise_test.cpp
+++ b/test/val/val_bitwise_test.cpp
@@ -420,13 +420,22 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-Base-04781"));
+  EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-RuntimeSpirv-None-10824"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("Expected 32-bit int type for Base operand: BitFieldInsert"));
 }
 
+TEST_F(ValidateBitwise, OpBitFieldInsertNot32Allow) {
+  const std::string body = R"(
+  %val1 = OpBitFieldInsert %u64 %u64_1 %u64_2 %s32_1 %s32_2
+  )";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
+  spvValidatorOptionsSetAllowVulkan32BitBitwise(getValidatorOptions(), true);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
 TEST_F(ValidateBitwise, OpBitFieldSExtractSuccess) {
   const std::string body = R"(
 %val1 = OpBitFieldSExtract %u64 %u64_1 %s32_1 %s32_2
@@ -504,8 +513,7 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-Base-04781"));
+  EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-RuntimeSpirv-None-10824"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("Expected 32-bit int type for Base operand: BitFieldSExtract"));
@@ -562,8 +570,7 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-Base-04781"));
+  EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-RuntimeSpirv-None-10824"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("Expected 32-bit int type for Base operand: BitReverse"));
@@ -607,10 +614,8 @@
 %val1 = OpBitCount %u32 %f64_1
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-Base-04781"));
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -637,8 +642,7 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0);
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-Base-04781"));
+  EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-RuntimeSpirv-None-10824"));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Expected 32-bit int type for Base operand: BitCount"));
 }
diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp
index 0104969..084a643 100644
--- a/test/val/val_builtins_test.cpp
+++ b/test/val/val_builtins_test.cpp
@@ -26,8 +26,6 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/spirv_target_env.h"
-#include "test/unit_spirv.h"
 #include "test/val/val_code_generator.h"
 #include "test/val/val_fixtures.h"
 
@@ -99,6 +97,10 @@
   generator.before_types_ += built_in;
   generator.before_types_ += "\n";
 
+  if (strncmp(built_in, "TessLevel", 9) == 0) {
+    generator.before_types_ += "OpMemberDecorate %built_in_type 0 Patch\n";
+  }
+
   std::ostringstream after_types;
 
   after_types << "%built_in_type = OpTypeStruct " << data_type << "\n";
@@ -139,7 +141,11 @@
     execution_modes << "OpExecutionMode %" << entry_point.name
                     << " OutputPoints\n";
   }
-  if (0 == std::strcmp(execution_model, "GLCompute")) {
+  if (0 == std::strcmp(execution_model, "GLCompute") ||
+      0 == std::strcmp(execution_model, "MeshEXT") ||
+      0 == std::strcmp(execution_model, "MeshNV") ||
+      0 == std::strcmp(execution_model, "MeshEXT") ||
+      0 == std::strcmp(execution_model, "TaskNV")) {
     execution_modes << "OpExecutionMode %" << entry_point.name
                     << " LocalSize 1 1 1\n";
   }
@@ -258,6 +264,10 @@
   generator.before_types_ += built_in;
   generator.before_types_ += "\n";
 
+  if (strncmp(built_in, "TessLevel", 9) == 0) {
+    generator.before_types_ += "OpMemberDecorate %built_in_type 0 Patch\n";
+  }
+
   std::ostringstream after_types;
   after_types << "%built_in_type = OpTypeStruct " << data_type << "\n";
   if (InitializerRequired(storage_class)) {
@@ -297,7 +307,11 @@
     execution_modes << "OpExecutionMode %" << entry_point.name
                     << " OutputPoints\n";
   }
-  if (0 == std::strcmp(execution_model, "GLCompute")) {
+  if (0 == std::strcmp(execution_model, "GLCompute") ||
+      0 == std::strcmp(execution_model, "MeshEXT") ||
+      0 == std::strcmp(execution_model, "MeshNV") ||
+      0 == std::strcmp(execution_model, "MeshEXT") ||
+      0 == std::strcmp(execution_model, "TaskNV")) {
     execution_modes << "OpExecutionMode %" << entry_point.name
                     << " LocalSize 1 1 1\n";
   }
@@ -395,6 +409,11 @@
   generator.before_types_ = "OpDecorate %built_in_var BuiltIn ";
   generator.before_types_ += built_in;
   generator.before_types_ += "\n";
+
+  if (strncmp(built_in, "TessLevel", 9) == 0) {
+    generator.before_types_ += "OpDecorate %built_in_var Patch\n";
+  }
+
   if ((0 == std::strcmp(storage_class, "Input")) &&
       (0 == std::strcmp(execution_model, "Fragment"))) {
     // ensure any needed input types that might require Flat
@@ -423,7 +442,7 @@
   }
   // Any kind of reference would do.
   entry_point.body = R"(
-%val = OpBitcast %u32 %built_in_var
+%val = OpCopyObject %built_in_ptr %built_in_var
 )";
 
   std::ostringstream execution_modes;
@@ -441,7 +460,11 @@
     execution_modes << "OpExecutionMode %" << entry_point.name
                     << " OutputPoints\n";
   }
-  if (0 == std::strcmp(execution_model, "GLCompute")) {
+  if (0 == std::strcmp(execution_model, "GLCompute") ||
+      0 == std::strcmp(execution_model, "MeshEXT") ||
+      0 == std::strcmp(execution_model, "MeshNV") ||
+      0 == std::strcmp(execution_model, "MeshEXT") ||
+      0 == std::strcmp(execution_model, "TaskNV")) {
     execution_modes << "OpExecutionMode %" << entry_point.name
                     << " LocalSize 1 1 1\n";
   }
@@ -847,6 +870,45 @@
                               "has components with bit width 64"))));
 
 INSTANTIATE_TEST_SUITE_P(
+    LocalInvocationIndexSuccess,
+    ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
+    Combine(Values("LocalInvocationIndex"), Values("GLCompute"),
+            Values("Input"), Values("%u32"), Values(nullptr),
+            Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+    LocalInvocationIndexNotGLCompute,
+    ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
+    Combine(Values("LocalInvocationIndex"),
+            Values("Vertex", "Fragment", "Geometry", "TessellationControl",
+                   "TessellationEvaluation"),
+            Values("Input"), Values("%u32"),
+            Values("VUID-LocalInvocationIndex-LocalInvocationIndex-04284"),
+            Values(TestResult(SPV_ERROR_INVALID_DATA,
+                              "to be used only with GLCompute, MeshNV, "
+                              "TaskNV, MeshEXT or TaskEXT execution model"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    LocalInvocationIndexNotInput,
+    ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
+    Combine(Values("LocalInvocationIndex"), Values("GLCompute"),
+            Values("Output"), Values("%u32"),
+            Values("VUID-LocalInvocationIndex-LocalInvocationIndex-04285"),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "to be only used for variables with Input storage class",
+                "uses storage class Output"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    LocalInvocationIndexNot32Int,
+    ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
+    Combine(Values("LocalInvocationIndex"), Values("GLCompute"),
+            Values("Input"), Values("%u32vec3", "%f32"),
+            Values("VUID-LocalInvocationIndex-LocalInvocationIndex-04286"),
+            Values(TestResult(SPV_ERROR_INVALID_DATA,
+                              "needs to be a 32-bit int scalar"))));
+
+INSTANTIATE_TEST_SUITE_P(
     InvocationIdSuccess,
     ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
     Combine(Values("InvocationId"), Values("Geometry", "TessellationControl"),
@@ -1423,16 +1485,6 @@
                           "to be used only with Fragment execution model"))));
 
 INSTANTIATE_TEST_SUITE_P(
-    SampleMaskWrongStorageClass,
-    ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
-    Combine(Values("SampleMask"), Values("Fragment"), Values("Workgroup"),
-            Values("%u32arr2"), Values("VUID-SampleMask-SampleMask-04358"),
-            Values(TestResult(
-                SPV_ERROR_INVALID_DATA,
-                "Vulkan spec allows BuiltIn SampleMask to be only used for "
-                "variables with Input or Output storage class"))));
-
-INSTANTIATE_TEST_SUITE_P(
     SampleMaskNotArray,
     ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
     Combine(Values("SampleMask"), Values("Fragment"), Values("Input"),
@@ -2645,7 +2697,7 @@
   entry_point.interfaces = "%built_in_var";
   // Any kind of reference would do.
   entry_point.body = R"(
-%val = OpBitcast %u32 %built_in_var
+%val = OpCopyObject %built_in_ptr %built_in_var
 )";
 
   std::ostringstream execution_modes;
@@ -2663,7 +2715,11 @@
     execution_modes << "OpExecutionMode %" << entry_point.name
                     << " OutputPoints\n";
   }
-  if (0 == std::strcmp(execution_model, "GLCompute")) {
+  if (0 == std::strcmp(execution_model, "GLCompute") ||
+      0 == std::strcmp(execution_model, "MeshEXT") ||
+      0 == std::strcmp(execution_model, "MeshNV") ||
+      0 == std::strcmp(execution_model, "MeshEXT") ||
+      0 == std::strcmp(execution_model, "TaskNV")) {
     execution_modes << "OpExecutionMode %" << entry_point.name
                     << " LocalSize 1 1 1\n";
   }
@@ -3575,6 +3631,7 @@
   EntryPoint entry_point;
   entry_point.name = "main_d_r";
   entry_point.execution_model = "MeshNV";
+  entry_point.execution_modes = "OpExecutionMode %main_d_r LocalSize 1 1 1";
   entry_point.interfaces = "%gl_PrimitiveID %gl_Layer %gl_ViewportIndex";
   generator.entry_points_.push_back(std::move(entry_point));
 
@@ -3613,7 +3670,9 @@
   EntryPoint entry_point;
   entry_point.name = "main_d_r";
   entry_point.execution_model = "MeshNV";
+  entry_point.execution_modes = "OpExecutionMode %main_d_r LocalSize 1 1 1";
   entry_point.interfaces = "%gl_PrimitiveID %gl_Layer %gl_ViewportIndex";
+  entry_point.body = "%ref_load = OpLoad %_arr_float_uint_81 %gl_PrimitiveID";
   generator.entry_points_.push_back(std::move(entry_point));
 
   CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_1);
@@ -3947,7 +4006,8 @@
         Values(TestResult(
             SPV_ERROR_INVALID_DATA,
             "Vulkan spec allows BuiltIn PrimitiveShadingRateKHR to be used "
-            "only with Vertex, Geometry, or MeshNV execution models."))));
+            "only with Vertex, Geometry, MeshNV or MeshEXT execution "
+            "models."))));
 
 INSTANTIATE_TEST_SUITE_P(
     PrimitiveShadingRateInvalidStorageClass,
@@ -4325,6 +4385,29 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
 }
 
+TEST_F(ValidateBuiltIns,
+       VulkanPrimitiveTriangleIndicesEXTInvalidExecutionMode) {
+  const std::string declarations = R"(
+%array = OpTypeArray %v3uint %uint_16
+%array_ptr = OpTypePointer Output %array
+%var = OpVariable %array_ptr Output
+%ptr = OpTypePointer Output %v3uint
+)";
+  const std::string body = R"(
+%access = OpAccessChain %ptr %var %int_0
+)";
+
+  CompileSuccessfully(
+      GenerateMeshShadingCode("PrimitiveTriangleIndicesEXT", "OutputPoints",
+                              body, declarations)
+          .c_str(),
+      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-PrimitiveTriangleIndicesEXT-"
+                      "PrimitiveTriangleIndicesEXT-07054"));
+}
+
 TEST_F(ValidateBuiltIns, VulkanPrimitiveTriangleIndicesEXTStorageClass) {
   const std::string declarations = R"(
 %array = OpTypeArray %v3uint %uint_16
@@ -4408,6 +4491,28 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
 }
 
+TEST_F(ValidateBuiltIns, VulkanPrimitiveLineIndicesEXTInvalidExecutionMode) {
+  const std::string declarations = R"(
+  %array = OpTypeArray %v2uint %uint_16
+  %array_ptr = OpTypePointer Output %array
+  %var = OpVariable %array_ptr Output
+  %ptr = OpTypePointer Output %v2uint
+  )";
+  const std::string body = R"(
+  %access = OpAccessChain %ptr %var %int_0
+  )";
+
+  CompileSuccessfully(
+      GenerateMeshShadingCode("PrimitiveLineIndicesEXT", "OutputPoints", body,
+                              declarations)
+          .c_str(),
+      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      AnyVUID("VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07048"));
+}
+
 TEST_F(ValidateBuiltIns, VulkanPrimitiveLineIndicesEXTStorageClass) {
   const std::string declarations = R"(
 %array = OpTypeArray %v2uint %uint_16
@@ -4471,6 +4576,28 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
 }
 
+TEST_F(ValidateBuiltIns, VulkanPrimitivePointIndicesEXTInvalidExecutionMode) {
+  const std::string declarations = R"(
+    %array = OpTypeArray %uint %uint_16
+    %array_ptr = OpTypePointer Output %array
+    %var = OpVariable %array_ptr Output
+    %ptr = OpTypePointer Output %uint
+    )";
+  const std::string body = R"(
+    %access = OpAccessChain %ptr %var %int_0
+    )";
+
+  CompileSuccessfully(
+      GenerateMeshShadingCode("PrimitivePointIndicesEXT", "OutputTrianglesNV",
+                              body, declarations)
+          .c_str(),
+      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      AnyVUID("VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07042"));
+}
+
 TEST_F(ValidateBuiltIns, VulkanPrimitivePointIndicesEXTStorageClass) {
   const std::string declarations = R"(
 %array = OpTypeArray %uint %uint_16
@@ -4515,6 +4642,2607 @@
       AnyVUID("VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07044"));
 }
 
+TEST_F(ValidateBuiltIns, VulkanBuiltinPrimtiveIDWithPerPrimitiveEXT) {
+  const std::string text = R"(
+               OpCapability MeshShadingEXT
+               OpCapability Shader
+               OpExtension "SPV_EXT_mesh_shader"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %MainMesh "MainMesh" %gl_PrimitiveID
+               OpExecutionMode %MainMesh OutputPrimitivesNV 1
+               OpExecutionMode %MainMesh OutputVertices 3
+               OpExecutionMode %MainMesh OutputTrianglesNV
+               OpExecutionMode %MainMesh LocalSize 1 1 1
+               OpSource Slang 1
+               OpName %MainMesh "MainMesh"
+               OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId
+               OpDecorate %gl_PrimitiveID PerPrimitiveNV
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_3 = OpConstant %uint 3
+     %uint_1 = OpConstant %uint 1
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+      %int_3 = OpConstant %int 3
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+     %uint_0 = OpConstant %uint 0
+    %v3float = OpTypeVector %float 3
+%_ptr_Output_v3float = OpTypePointer Output %v3float
+     %v3uint = OpTypeVector %uint 3
+%_ptr_Output_v3uint = OpTypePointer Output %v3uint
+%_ptr_Output_int = OpTypePointer Output %int
+%_arr_int_int_1 = OpTypeArray %int %int_1
+%_ptr_Output__arr_int_int_1 = OpTypePointer Output %_arr_int_int_1
+%gl_PrimitiveID = OpVariable %_ptr_Output__arr_int_int_1 Output
+   %MainMesh = OpFunction %void None %9
+         %25 = OpLabel
+               OpSetMeshOutputsEXT %uint_3 %uint_1
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinPrimtiveIDWithPerPrimitiveEXT) {
+  const std::string text = R"(
+       OpCapability MeshShadingEXT
+       OpCapability Shader
+       OpExtension "SPV_EXT_mesh_shader"
+       OpMemoryModel Logical GLSL450
+       OpEntryPoint MeshEXT %MainMesh "MainMesh" %gl_PrimitiveID
+       OpExecutionMode %MainMesh OutputPrimitivesNV 1
+       OpExecutionMode %MainMesh OutputVertices 3
+       OpExecutionMode %MainMesh OutputTrianglesNV
+       OpExecutionMode %MainMesh LocalSize 1 1 1
+       OpSource Slang 1
+       OpName %MainMesh "MainMesh"
+       OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId
+%void = OpTypeVoid
+  %9 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_3 = OpConstant %uint 3
+%uint_1 = OpConstant %uint 1
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%int_3 = OpConstant %int 3
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%uint_0 = OpConstant %uint 0
+%v3float = OpTypeVector %float 3
+%_ptr_Output_v3float = OpTypePointer Output %v3float
+%v3uint = OpTypeVector %uint 3
+%_ptr_Output_v3uint = OpTypePointer Output %v3uint
+%_ptr_Output_int = OpTypePointer Output %int
+%_arr_int_int_1 = OpTypeArray %int %int_1
+%_ptr_Output__arr_int_int_1 = OpTypePointer Output %_arr_int_int_1
+%gl_PrimitiveID = OpVariable %_ptr_Output__arr_int_int_1 Output
+%MainMesh = OpFunction %void None %9
+ %25 = OpLabel
+%ref_load = OpLoad %_arr_int_int_1 %gl_PrimitiveID
+       OpSetMeshOutputsEXT %uint_3 %uint_1
+       OpReturn
+       OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-PrimitiveId-PrimitiveId-07040"));
+}
+
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinViewportIndexWithPerPrimitiveEXT) {
+  const std::string text = R"(
+     OpCapability MeshShadingEXT
+     OpCapability Shader
+     OpExtension "SPV_EXT_mesh_shader"
+     OpMemoryModel Logical GLSL450
+     OpEntryPoint MeshEXT %MainMesh "MainMesh" %gl_ViewportIndex
+     OpExecutionMode %MainMesh OutputPrimitivesNV 1
+     OpExecutionMode %MainMesh OutputVertices 3
+     OpExecutionMode %MainMesh OutputTrianglesNV
+     OpExecutionMode %MainMesh LocalSize 1 1 1
+     OpSource Slang 1
+     OpName %MainMesh "MainMesh"
+     OpDecorate %gl_ViewportIndex BuiltIn ViewportIndex
+%void = OpTypeVoid
+%9 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_3 = OpConstant %uint 3
+%uint_1 = OpConstant %uint 1
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 1
+%int_1 = OpConstant %int 1
+%int_3 = OpConstant %int 3
+%uint_0 = OpConstant %uint 0
+%v3float = OpTypeVector %float 3
+%_ptr_Output_v3float = OpTypePointer Output %v3float
+%v3uint = OpTypeVector %uint 3
+%_ptr_Output_v3uint = OpTypePointer Output %v3uint
+%_ptr_Output_int = OpTypePointer Output %int
+%_arr_int_int_1 = OpTypeArray %int %int_1
+%_ptr_Output__arr_int_int_1 = OpTypePointer Output %_arr_int_int_1
+%gl_ViewportIndex = OpVariable %_ptr_Output__arr_int_int_1 Output
+%MainMesh = OpFunction %void None %9
+%25 = OpLabel
+%ref_load = OpLoad %_arr_int_int_1 %gl_ViewportIndex
+     OpSetMeshOutputsEXT %uint_3 %uint_1
+     OpReturn
+     OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-ViewportIndex-ViewportIndex-07060"));
+}
+
+TEST_F(ValidateBuiltIns, VulkanBuiltinPrimitivePointIndicesEXT) {
+  const std::string text = R"(
+       OpCapability MeshShadingEXT
+       OpExtension "SPV_EXT_mesh_shader"
+  %1 = OpExtInstImport "GLSL.std.450"
+       OpMemoryModel Logical GLSL450
+       OpEntryPoint MeshEXT %main "main" %gl_PrimitivePointIndicesEXT
+       OpExecutionMode %main LocalSize 32 1 1
+       OpExecutionMode %main OutputVertices 81
+       OpExecutionMode %main OutputPrimitivesEXT 32
+       OpExecutionMode %main OutputPoints
+       OpSource GLSL 460
+       OpSourceExtension "GL_EXT_mesh_shader"
+       OpName %main "main"
+       OpName %gl_PrimitivePointIndicesEXT "gl_PrimitivePointIndicesEXT"
+       OpDecorate %gl_PrimitivePointIndicesEXT BuiltIn PrimitivePointIndicesEXT
+       OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+%void = OpTypeVoid
+  %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%_arr_uint_uint_32 = OpTypeArray %uint %uint_32
+%_ptr_Output__arr_uint_uint_32 = OpTypePointer Output %_arr_uint_uint_32
+%gl_PrimitivePointIndicesEXT = OpVariable %_ptr_Output__arr_uint_uint_32 Output
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%uint_0 = OpConstant %uint 0
+%_ptr_Output_uint = OpTypePointer Output %uint
+%v3uint = OpTypeVector %uint 3
+%uint_1 = OpConstant %uint 1
+%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1
+%main = OpFunction %void None %3
+  %5 = OpLabel
+ %15 = OpAccessChain %_ptr_Output_uint %gl_PrimitivePointIndicesEXT %int_0
+       OpStore %15 %uint_0
+       OpReturn
+       OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateBuiltIns, VulkanBuiltinPrimitiveLineIndicesEXT) {
+  const std::string text = R"(
+          OpCapability MeshShadingEXT
+          OpExtension "SPV_EXT_mesh_shader"
+     %1 = OpExtInstImport "GLSL.std.450"
+          OpMemoryModel Logical GLSL450
+          OpEntryPoint MeshEXT %main "main" %gl_PrimitiveLineIndicesEXT
+          OpExecutionMode %main LocalSize 32 1 1
+          OpExecutionMode %main OutputVertices 81
+          OpExecutionMode %main OutputPrimitivesEXT 32
+          OpExecutionMode %main OutputLinesEXT
+          OpSource GLSL 460
+          OpSourceExtension "GL_EXT_mesh_shader"
+          OpName %main "main"
+          OpName %gl_PrimitiveLineIndicesEXT "gl_PrimitiveLineIndicesEXT"
+          OpDecorate %gl_PrimitiveLineIndicesEXT BuiltIn PrimitiveLineIndicesEXT
+          OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+  %void = OpTypeVoid
+     %3 = OpTypeFunction %void
+  %uint = OpTypeInt 32 0
+%v2uint = OpTypeVector %uint 2
+%uint_32 = OpConstant %uint 32
+%_arr_v2uint_uint_32 = OpTypeArray %v2uint %uint_32
+%_ptr_Output__arr_v2uint_uint_32 = OpTypePointer Output %_arr_v2uint_uint_32
+%gl_PrimitiveLineIndicesEXT = OpVariable %_ptr_Output__arr_v2uint_uint_32 Output
+   %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+%uint_0 = OpConstant %uint 0
+    %15 = OpConstantComposite %v2uint %uint_0 %uint_0
+%_ptr_Output_v2uint = OpTypePointer Output %v2uint
+%v3uint = OpTypeVector %uint 3
+%uint_1 = OpConstant %uint 1
+%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1
+  %main = OpFunction %void None %3
+     %5 = OpLabel
+    %17 = OpAccessChain %_ptr_Output_v2uint %gl_PrimitiveLineIndicesEXT %int_0
+          OpStore %17 %15
+          OpReturn
+          OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinPrimitiveLineIndicesEXT) {
+  const std::string text = R"(
+          OpCapability MeshShadingEXT
+          OpExtension "SPV_EXT_mesh_shader"
+     %1 = OpExtInstImport "GLSL.std.450"
+          OpMemoryModel Logical GLSL450
+          OpEntryPoint MeshEXT %main "main" %gl_PrimitiveLineIndicesEXT
+          OpExecutionMode %main LocalSize 32 1 1
+          OpExecutionMode %main OutputVertices 81
+          OpExecutionMode %main OutputPrimitivesEXT 32
+          OpExecutionMode %main OutputPoints
+          OpSource GLSL 460
+          OpSourceExtension "GL_EXT_mesh_shader"
+          OpName %main "main"
+          OpName %gl_PrimitiveLineIndicesEXT "gl_PrimitiveLineIndicesEXT"
+          OpDecorate %gl_PrimitiveLineIndicesEXT BuiltIn PrimitiveLineIndicesEXT
+          OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+  %void = OpTypeVoid
+     %3 = OpTypeFunction %void
+  %uint = OpTypeInt 32 0
+%v2uint = OpTypeVector %uint 2
+%uint_32 = OpConstant %uint 32
+%_arr_v2uint_uint_32 = OpTypeArray %v2uint %uint_32
+%_ptr_Output__arr_v2uint_uint_32 = OpTypePointer Output %_arr_v2uint_uint_32
+%gl_PrimitiveLineIndicesEXT = OpVariable %_ptr_Output__arr_v2uint_uint_32 Output
+   %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+%uint_0 = OpConstant %uint 0
+    %15 = OpConstantComposite %v2uint %uint_0 %uint_0
+%_ptr_Output_v2uint = OpTypePointer Output %v2uint
+%v3uint = OpTypeVector %uint 3
+%uint_1 = OpConstant %uint 1
+%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1
+  %main = OpFunction %void None %3
+     %5 = OpLabel
+    %17 = OpAccessChain %_ptr_Output_v2uint %gl_PrimitiveLineIndicesEXT %int_0
+          OpStore %17 %15
+          OpReturn
+          OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      AnyVUID("VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07048"));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinPrimitivePointIndicesEXT) {
+  const std::string text = R"(
+       OpCapability MeshShadingEXT
+       OpExtension "SPV_EXT_mesh_shader"
+  %1 = OpExtInstImport "GLSL.std.450"
+       OpMemoryModel Logical GLSL450
+       OpEntryPoint MeshEXT %main "main" %gl_PrimitivePointIndicesEXT
+       OpExecutionMode %main LocalSize 32 1 1
+       OpExecutionMode %main OutputVertices 81
+       OpExecutionMode %main OutputPrimitivesEXT 32
+       OpExecutionMode %main OutputTrianglesEXT
+       OpSource GLSL 460
+       OpSourceExtension "GL_EXT_mesh_shader"
+       OpName %main "main"
+       OpName %gl_PrimitivePointIndicesEXT "gl_PrimitivePointIndicesEXT"
+       OpDecorate %gl_PrimitivePointIndicesEXT BuiltIn PrimitivePointIndicesEXT
+       OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+%void = OpTypeVoid
+  %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%_arr_uint_uint_32 = OpTypeArray %uint %uint_32
+%_ptr_Output__arr_uint_uint_32 = OpTypePointer Output %_arr_uint_uint_32
+%gl_PrimitivePointIndicesEXT = OpVariable %_ptr_Output__arr_uint_uint_32 Output
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%uint_0 = OpConstant %uint 0
+%_ptr_Output_uint = OpTypePointer Output %uint
+%v3uint = OpTypeVector %uint 3
+%uint_1 = OpConstant %uint 1
+%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1
+%main = OpFunction %void None %3
+  %5 = OpLabel
+ %15 = OpAccessChain %_ptr_Output_uint %gl_PrimitivePointIndicesEXT %int_0
+       OpStore %15 %uint_0
+       OpReturn
+       OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      AnyVUID("VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07042"));
+}
+
+TEST_F(ValidateBuiltIns, VulkanBuiltinPrimitiveTriangleIndicesEXT) {
+  const std::string text = R"(
+    OpCapability MeshShadingEXT
+    OpExtension "SPV_EXT_mesh_shader"
+%1 = OpExtInstImport "GLSL.std.450"
+    OpMemoryModel Logical GLSL450
+    OpEntryPoint MeshEXT %main "main" %gl_PrimitiveTriangleIndicesEXT
+    OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+    OpExecutionMode %main OutputVertices 81
+    OpExecutionMode %main OutputPrimitivesEXT 32
+    OpExecutionMode %main OutputTrianglesEXT
+    OpSource GLSL 460
+    OpSourceExtension "GL_EXT_mesh_shader"
+    OpName %main "main"
+    OpName %gl_PrimitiveTriangleIndicesEXT "gl_PrimitiveTriangleIndicesEXT"
+    OpDecorate %gl_PrimitiveTriangleIndicesEXT BuiltIn PrimitiveTriangleIndicesEXT
+%void = OpTypeVoid
+%7 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%v3uint = OpTypeVector %uint 3
+%_arr_v3uint_uint_32 = OpTypeArray %v3uint %uint_32
+%_ptr_Output__arr_v3uint_uint_32 = OpTypePointer Output %_arr_v3uint_uint_32
+%gl_PrimitiveTriangleIndicesEXT = OpVariable %_ptr_Output__arr_v3uint_uint_32 Output
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%uint_0 = OpConstant %uint 0
+%15 = OpConstantComposite %v3uint %uint_0 %uint_0 %uint_0
+%_ptr_Output_v3uint = OpTypePointer Output %v3uint
+%17 = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1
+%main = OpFunction %void None %7
+%18 = OpLabel
+%19 = OpAccessChain %_ptr_Output_v3uint %gl_PrimitiveTriangleIndicesEXT %int_0
+    OpStore %19 %15
+    OpReturn
+    OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinPrimitiveTriangleIndicesEXT) {
+  const std::string text = R"(
+    OpCapability MeshShadingEXT
+    OpExtension "SPV_EXT_mesh_shader"
+%1 = OpExtInstImport "GLSL.std.450"
+    OpMemoryModel Logical GLSL450
+    OpEntryPoint MeshEXT %main "main" %gl_PrimitiveTriangleIndicesEXT
+    OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+    OpExecutionMode %main OutputVertices 81
+    OpExecutionMode %main OutputPrimitivesEXT 32
+    OpExecutionMode %main OutputPoints
+    OpSource GLSL 460
+    OpSourceExtension "GL_EXT_mesh_shader"
+    OpName %main "main"
+    OpName %gl_PrimitiveTriangleIndicesEXT "gl_PrimitiveTriangleIndicesEXT"
+    OpDecorate %gl_PrimitiveTriangleIndicesEXT BuiltIn PrimitiveTriangleIndicesEXT
+%void = OpTypeVoid
+%7 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%v3uint = OpTypeVector %uint 3
+%_arr_v3uint_uint_32 = OpTypeArray %v3uint %uint_32
+%_ptr_Output__arr_v3uint_uint_32 = OpTypePointer Output %_arr_v3uint_uint_32
+%gl_PrimitiveTriangleIndicesEXT = OpVariable %_ptr_Output__arr_v3uint_uint_32 Output
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%uint_0 = OpConstant %uint 0
+%15 = OpConstantComposite %v3uint %uint_0 %uint_0 %uint_0
+%_ptr_Output_v3uint = OpTypePointer Output %v3uint
+%17 = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1
+%main = OpFunction %void None %7
+%18 = OpLabel
+%19 = OpAccessChain %_ptr_Output_v3uint %gl_PrimitiveTriangleIndicesEXT %int_0
+    OpStore %19 %15
+    OpReturn
+    OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-PrimitiveTriangleIndicesEXT-"
+                      "PrimitiveTriangleIndicesEXT-07054"));
+}
+
+// https://github.com/KhronosGroup/SPIRV-Tools/issues/6307
+TEST_F(ValidateBuiltIns, VulkanBuiltinPrimitiveTriangleIndicesMultiEntrypoint) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpCapability MeshShadingEXT
+               OpCapability VulkanMemoryModel
+               OpExtension "SPV_EXT_mesh_shader"
+               OpMemoryModel Logical Vulkan
+               OpEntryPoint MeshEXT %1 "mesh" %positions %indices
+               OpEntryPoint Fragment %4 "frag" %color
+               OpExecutionMode %1 LocalSize 1 1 1
+               OpExecutionMode %1 OutputVertices 3
+               OpExecutionMode %1 OutputPrimitivesEXT 1
+               OpExecutionMode %1 OutputTrianglesEXT
+               OpExecutionMode %4 OriginUpperLeft
+               OpDecorate %_arr_v4float_uint_3 ArrayStride 16
+               OpDecorate %_arr_v3uint_uint_1 ArrayStride 16
+               OpDecorate %positions BuiltIn Position
+               OpDecorate %indices BuiltIn PrimitiveTriangleIndicesEXT
+               OpDecorate %color Location 0
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+       %uint = OpTypeInt 32 0
+     %uint_3 = OpConstant %uint 3
+%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3
+%_ptr_Output__arr_v4float_uint_3 = OpTypePointer Output %_arr_v4float_uint_3
+     %v3uint = OpTypeVector %uint 3
+     %uint_1 = OpConstant %uint 1
+%_arr_v3uint_uint_1 = OpTypeArray %v3uint %uint_1
+%_ptr_Output__arr_v3uint_uint_1 = OpTypePointer Output %_arr_v3uint_uint_1
+       %void = OpTypeVoid
+         %18 = OpTypeFunction %void
+     %uint_0 = OpConstant %uint 0
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+  %positions = OpVariable %_ptr_Output__arr_v4float_uint_3 Output
+    %indices = OpVariable %_ptr_Output__arr_v3uint_uint_1 Output
+      %color = OpVariable %_ptr_Output_v4float Output
+          %1 = OpFunction %void None %18
+         %21 = OpLabel
+               OpSetMeshOutputsEXT %uint_0 %uint_0
+               OpNoLine
+               OpReturn
+               OpFunctionEnd
+          %4 = OpFunction %void None %18
+         %22 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanPrimitivePointIndicesArraySizeMeshEXT) {
+  const std::string text = R"(
+   OpCapability MeshShadingEXT
+   OpExtension "SPV_EXT_mesh_shader"
+%1 = OpExtInstImport "GLSL.std.450"
+   OpMemoryModel Logical GLSL450
+   OpEntryPoint MeshEXT %main "main" %gl_PrimitivePointIndicesEXT
+   OpExecutionMode %main LocalSize 32 1 1
+   OpExecutionMode %main OutputVertices 81
+   OpExecutionMode %main OutputPrimitivesEXT 16
+   OpExecutionMode %main OutputPoints
+   OpSource GLSL 460
+   OpSourceExtension "GL_EXT_mesh_shader"
+   OpName %main "main"
+   OpName %gl_PrimitivePointIndicesEXT "gl_PrimitivePointIndicesEXT"
+   OpDecorate %gl_PrimitivePointIndicesEXT BuiltIn PrimitivePointIndicesEXT
+   OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%_arr_uint_uint_32 = OpTypeArray %uint %uint_32
+%_ptr_Output__arr_uint_uint_32 = OpTypePointer Output %_arr_uint_uint_32
+%gl_PrimitivePointIndicesEXT = OpVariable %_ptr_Output__arr_uint_uint_32 Output
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%uint_0 = OpConstant %uint 0
+%_ptr_Output_uint = OpTypePointer Output %uint
+%v3uint = OpTypeVector %uint 3
+%uint_1 = OpConstant %uint 1
+%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1
+%main = OpFunction %void None %3
+%5 = OpLabel
+%15 = OpAccessChain %_ptr_Output_uint %gl_PrimitivePointIndicesEXT %int_0
+   OpStore %15 %uint_0
+   OpReturn
+   OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      AnyVUID("VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07046"));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanPrimitiveLineIndicesArraySizeMeshEXT) {
+  const std::string text = R"(
+      OpCapability MeshShadingEXT
+      OpExtension "SPV_EXT_mesh_shader"
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint MeshEXT %main "main" %gl_PrimitiveLineIndicesEXT
+      OpExecutionMode %main LocalSize 32 1 1
+      OpExecutionMode %main OutputVertices 81
+      OpExecutionMode %main OutputPrimitivesEXT 16
+      OpExecutionMode %main OutputLinesEXT
+      OpSource GLSL 460
+      OpSourceExtension "GL_EXT_mesh_shader"
+      OpName %main "main"
+      OpName %gl_PrimitiveLineIndicesEXT "gl_PrimitiveLineIndicesEXT"
+      OpDecorate %gl_PrimitiveLineIndicesEXT BuiltIn PrimitiveLineIndicesEXT
+      OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+%void = OpTypeVoid
+ %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%v2uint = OpTypeVector %uint 2
+%uint_32 = OpConstant %uint 32
+%_arr_v2uint_uint_32 = OpTypeArray %v2uint %uint_32
+%_ptr_Output__arr_v2uint_uint_32 = OpTypePointer Output %_arr_v2uint_uint_32
+%gl_PrimitiveLineIndicesEXT = OpVariable %_ptr_Output__arr_v2uint_uint_32 Output
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%uint_0 = OpConstant %uint 0
+%15 = OpConstantComposite %v2uint %uint_0 %uint_0
+%_ptr_Output_v2uint = OpTypePointer Output %v2uint
+%v3uint = OpTypeVector %uint 3
+%uint_1 = OpConstant %uint 1
+%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1
+%main = OpFunction %void None %3
+ %5 = OpLabel
+%17 = OpAccessChain %_ptr_Output_v2uint %gl_PrimitiveLineIndicesEXT %int_0
+      OpStore %17 %15
+      OpReturn
+      OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      AnyVUID("VUID-PrimitiveLineIndicesEXT-PrimitiveLineIndicesEXT-07052"));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanPrimitiveTriangleIndicesArraySizeMeshEXT) {
+  const std::string text = R"(
+  OpCapability MeshShadingEXT
+  OpExtension "SPV_EXT_mesh_shader"
+%1 = OpExtInstImport "GLSL.std.450"
+  OpMemoryModel Logical GLSL450
+  OpEntryPoint MeshEXT %main "main" %gl_PrimitiveTriangleIndicesEXT
+  OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+  OpExecutionMode %main OutputVertices 81
+  OpExecutionMode %main OutputPrimitivesEXT 16
+  OpExecutionMode %main OutputTrianglesEXT
+  OpSource GLSL 460
+  OpSourceExtension "GL_EXT_mesh_shader"
+  OpName %main "main"
+  OpName %gl_PrimitiveTriangleIndicesEXT "gl_PrimitiveTriangleIndicesEXT"
+  OpDecorate %gl_PrimitiveTriangleIndicesEXT BuiltIn PrimitiveTriangleIndicesEXT
+%void = OpTypeVoid
+%7 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%v3uint = OpTypeVector %uint 3
+%_arr_v3uint_uint_32 = OpTypeArray %v3uint %uint_32
+%_ptr_Output__arr_v3uint_uint_32 = OpTypePointer Output %_arr_v3uint_uint_32
+%gl_PrimitiveTriangleIndicesEXT = OpVariable %_ptr_Output__arr_v3uint_uint_32 Output
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%uint_0 = OpConstant %uint 0
+%15 = OpConstantComposite %v3uint %uint_0 %uint_0 %uint_0
+%_ptr_Output_v3uint = OpTypePointer Output %v3uint
+%17 = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1
+%main = OpFunction %void None %7
+%18 = OpLabel
+%19 = OpAccessChain %_ptr_Output_v3uint %gl_PrimitiveTriangleIndicesEXT %int_0
+  OpStore %19 %15
+  OpReturn
+  OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-PrimitiveTriangleIndicesEXT-"
+                      "PrimitiveTriangleIndicesEXT-07058"));
+}
+
+// https://godbolt.org/z/xqsMqqnxd
+TEST_F(ValidateBuiltIns, VulkanMeshMultipleTopology) {
+  const std::string text = R"(
+               OpCapability MeshShadingEXT
+               OpCapability Shader
+               OpExtension "SPV_EXT_mesh_shader"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %main1 "main1" %26 %vertices_color
+               OpEntryPoint MeshEXT %main2 "main2" %43 %vertices_color_0
+               OpExecutionMode %main1 OutputVertices 3
+               OpExecutionMode %main1 OutputPrimitivesEXT 1
+               OpExecutionMode %main1 LocalSize 1 1 1
+               OpExecutionMode %main1 OutputTrianglesEXT
+               OpExecutionMode %main2 OutputVertices 3
+               OpExecutionMode %main2 OutputPrimitivesEXT 1
+               OpExecutionMode %main2 LocalSize 1 1 1
+               OpExecutionMode %main2 OutputLinesEXT
+               OpDecorate %vertices_color Location 0
+               OpDecorate %26 BuiltIn PrimitiveTriangleIndicesEXT
+               OpDecorate %vertices_color_0 Location 0
+               OpDecorate %43 BuiltIn PrimitiveLineIndicesEXT
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_3 = OpConstant %uint 3
+     %uint_1 = OpConstant %uint 1
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+        %int = OpTypeInt 32 1
+      %int_3 = OpConstant %int 3
+%_arr_v4float_int_3 = OpTypeArray %v4float %int_3
+%_ptr_Output__arr_v4float_int_3 = OpTypePointer Output %_arr_v4float_int_3
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+     %uint_0 = OpConstant %uint 0
+    %float_0 = OpConstant %float 0
+         %19 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+     %v3uint = OpTypeVector %uint 3
+      %int_1 = OpConstant %int 1
+%_arr_v3uint_int_1 = OpTypeArray %v3uint %int_1
+%_ptr_Output__arr_v3uint_int_1 = OpTypePointer Output %_arr_v3uint_int_1
+%_ptr_Output_v3uint = OpTypePointer Output %v3uint
+     %uint_2 = OpConstant %uint 2
+         %29 = OpConstantComposite %v3uint %uint_0 %uint_1 %uint_2
+         %38 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+     %v2uint = OpTypeVector %uint 2
+%_arr_v2uint_int_1 = OpTypeArray %v2uint %int_1
+%_ptr_Output__arr_v2uint_int_1 = OpTypePointer Output %_arr_v2uint_int_1
+%_ptr_Output_v2uint = OpTypePointer Output %v2uint
+         %46 = OpConstantComposite %v2uint %uint_0 %uint_1
+%vertices_color = OpVariable %_ptr_Output__arr_v4float_int_3 Output
+         %26 = OpVariable %_ptr_Output__arr_v3uint_int_1 Output
+%vertices_color_0 = OpVariable %_ptr_Output__arr_v4float_int_3 Output
+         %43 = OpVariable %_ptr_Output__arr_v2uint_int_1 Output
+      %main1 = OpFunction %void None %3
+          %4 = OpLabel
+               OpSetMeshOutputsEXT %uint_3 %uint_1
+         %17 = OpAccessChain %_ptr_Output_v4float %vertices_color %uint_0
+               OpStore %17 %19
+         %28 = OpAccessChain %_ptr_Output_v3uint %26 %uint_0
+               OpStore %28 %29
+               OpReturn
+               OpFunctionEnd
+      %main2 = OpFunction %void None %3
+         %34 = OpLabel
+               OpSetMeshOutputsEXT %uint_3 %uint_1
+         %37 = OpAccessChain %_ptr_Output_v4float %vertices_color_0 %uint_0
+               OpStore %37 %38
+         %45 = OpAccessChain %_ptr_Output_v2uint %43 %uint_0
+               OpStore %45 %46
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+// https://godbolt.org/z/MeTadeYEr
+TEST_F(ValidateBuiltIns, VulkanMeshMultipleArraySizes) {
+  const std::string text = R"(
+    OpCapability MeshShadingEXT
+               OpCapability Shader
+               OpExtension "SPV_EXT_mesh_shader"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %main1 "main1" %var1 %vertices_color
+               OpEntryPoint MeshEXT %main2 "main2" %var2 %vertices_color_0
+               OpExecutionMode %main1 OutputVertices 3
+               OpExecutionMode %main1 OutputPrimitivesEXT 2
+               OpExecutionMode %main1 LocalSize 1 1 1
+               OpExecutionMode %main1 OutputTrianglesEXT
+               OpExecutionMode %main2 OutputVertices 3
+               OpExecutionMode %main2 OutputPrimitivesEXT 4
+               OpExecutionMode %main2 LocalSize 1 1 1
+               OpExecutionMode %main2 OutputTrianglesEXT
+               OpSource Slang 1
+               OpName %vertices_color "vertices.color"
+               OpName %main1 "main1"
+               OpName %vertices_color_0 "vertices.color"
+               OpName %main2 "main2"
+               OpDecorate %vertices_color Location 0
+               OpDecorate %var1 BuiltIn PrimitiveTriangleIndicesEXT
+               OpDecorate %var2 BuiltIn PrimitiveTriangleIndicesEXT
+               OpDecorate %vertices_color_0 Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_3 = OpConstant %uint 3
+     %uint_1 = OpConstant %uint 1
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+        %int = OpTypeInt 32 1
+      %int_3 = OpConstant %int 3
+%_arr_v4float_int_3 = OpTypeArray %v4float %int_3
+%_ptr_Output__arr_v4float_int_3 = OpTypePointer Output %_arr_v4float_int_3
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+     %uint_0 = OpConstant %uint 0
+    %float_0 = OpConstant %float 0
+         %19 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+     %v3uint = OpTypeVector %uint 3
+      %int_2 = OpConstant %int 2
+      %int_4 = OpConstant %int 4
+%_arr_v3uint_int_2 = OpTypeArray %v3uint %int_2
+%_arr_v3uint_int_4 = OpTypeArray %v3uint %int_4
+%_ptr_Output__arr_v3uint_int_2 = OpTypePointer Output %_arr_v3uint_int_2
+%_ptr_Output__arr_v3uint_int_4 = OpTypePointer Output %_arr_v3uint_int_4
+%_ptr_Output_v3uint = OpTypePointer Output %v3uint
+     %uint_2 = OpConstant %uint 2
+         %29 = OpConstantComposite %v3uint %uint_0 %uint_1 %uint_2
+         %38 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+         %41 = OpConstantComposite %v3uint %uint_0 %uint_1 %uint_2
+%vertices_color = OpVariable %_ptr_Output__arr_v4float_int_3 Output
+         %var1 = OpVariable %_ptr_Output__arr_v3uint_int_2 Output
+         %var2 = OpVariable %_ptr_Output__arr_v3uint_int_4 Output
+%vertices_color_0 = OpVariable %_ptr_Output__arr_v4float_int_3 Output
+      %main1 = OpFunction %void None %3
+          %4 = OpLabel
+               OpSetMeshOutputsEXT %uint_3 %uint_1
+         %17 = OpAccessChain %_ptr_Output_v4float %vertices_color %uint_0
+               OpStore %17 %19
+         %28 = OpAccessChain %_ptr_Output_v3uint %var1 %uint_0
+               OpStore %28 %29
+               OpReturn
+               OpFunctionEnd
+      %main2 = OpFunction %void None %3
+         %34 = OpLabel
+               OpSetMeshOutputsEXT %uint_3 %uint_1
+         %37 = OpAccessChain %_ptr_Output_v4float %vertices_color_0 %uint_0
+               OpStore %37 %38
+         %40 = OpAccessChain %_ptr_Output_v3uint %var2 %uint_0
+               OpStore %40 %41
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanMeshMultipleArraySizes) {
+  const std::string text = R"(
+                 OpCapability MeshShadingEXT
+               OpCapability Shader
+               OpExtension "SPV_EXT_mesh_shader"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %main1 "main1" %26 %vertices_color
+               OpEntryPoint MeshEXT %main2 "main2" %26 %vertices_color_0
+               OpExecutionMode %main1 OutputVertices 3
+               OpExecutionMode %main1 OutputPrimitivesEXT 2
+               OpExecutionMode %main1 LocalSize 1 1 1
+               OpExecutionMode %main1 OutputTrianglesEXT
+               OpExecutionMode %main2 OutputVertices 3
+               OpExecutionMode %main2 OutputPrimitivesEXT 42
+               OpExecutionMode %main2 LocalSize 1 1 1
+               OpExecutionMode %main2 OutputTrianglesEXT
+               OpSource Slang 1
+               OpName %vertices_color "vertices.color"
+               OpName %main1 "main1"
+               OpName %vertices_color_0 "vertices.color"
+               OpName %main2 "main2"
+               OpDecorate %vertices_color Location 0
+               OpDecorate %26 BuiltIn PrimitiveTriangleIndicesEXT
+               OpDecorate %vertices_color_0 Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_3 = OpConstant %uint 3
+     %uint_1 = OpConstant %uint 1
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+        %int = OpTypeInt 32 1
+      %int_3 = OpConstant %int 3
+%_arr_v4float_int_3 = OpTypeArray %v4float %int_3
+%_ptr_Output__arr_v4float_int_3 = OpTypePointer Output %_arr_v4float_int_3
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+     %uint_0 = OpConstant %uint 0
+    %float_0 = OpConstant %float 0
+         %19 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+     %v3uint = OpTypeVector %uint 3
+      %int_2 = OpConstant %int 2
+%_arr_v3uint_int_2 = OpTypeArray %v3uint %int_2
+%_ptr_Output__arr_v3uint_int_2 = OpTypePointer Output %_arr_v3uint_int_2
+%_ptr_Output_v3uint = OpTypePointer Output %v3uint
+     %uint_2 = OpConstant %uint 2
+         %29 = OpConstantComposite %v3uint %uint_0 %uint_1 %uint_2
+         %38 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+         %41 = OpConstantComposite %v3uint %uint_0 %uint_1 %uint_2
+%vertices_color = OpVariable %_ptr_Output__arr_v4float_int_3 Output
+         %26 = OpVariable %_ptr_Output__arr_v3uint_int_2 Output
+%vertices_color_0 = OpVariable %_ptr_Output__arr_v4float_int_3 Output
+      %main1 = OpFunction %void None %3
+          %4 = OpLabel
+               OpSetMeshOutputsEXT %uint_3 %uint_1
+         %17 = OpAccessChain %_ptr_Output_v4float %vertices_color %uint_0
+               OpStore %17 %19
+         %28 = OpAccessChain %_ptr_Output_v3uint %26 %uint_0
+               OpStore %28 %29
+               OpReturn
+               OpFunctionEnd
+      %main2 = OpFunction %void None %3
+         %34 = OpLabel
+               OpSetMeshOutputsEXT %uint_3 %uint_1
+         %37 = OpAccessChain %_ptr_Output_v4float %vertices_color_0 %uint_0
+               OpStore %37 %38
+         %40 = OpAccessChain %_ptr_Output_v3uint %26 %uint_0
+               OpStore %40 %41
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-PrimitiveTriangleIndicesEXT-"
+                      "PrimitiveTriangleIndicesEXT-07058"));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanMeshMultipleArraySizesSharedVariable) {
+  const std::string text = R"(
+               OpCapability MeshShadingEXT
+               OpCapability Shader
+               OpExtension "SPV_EXT_mesh_shader"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %main1 "main1" %26 %vertices_color
+               OpEntryPoint MeshEXT %main2 "main2" %26 %vertices_color_0
+               OpExecutionMode %main1 OutputVertices 3
+               OpExecutionMode %main1 OutputPrimitivesEXT 2
+               OpExecutionMode %main1 LocalSize 1 1 1
+               OpExecutionMode %main1 OutputTrianglesEXT
+               OpExecutionMode %main2 OutputVertices 3
+               OpExecutionMode %main2 OutputPrimitivesEXT 4
+               OpExecutionMode %main2 LocalSize 1 1 1
+               OpExecutionMode %main2 OutputTrianglesEXT
+               OpSource Slang 1
+               OpName %vertices_color "vertices.color"
+               OpName %main1 "main1"
+               OpName %vertices_color_0 "vertices.color"
+               OpName %main2 "main2"
+               OpDecorate %vertices_color Location 0
+               OpDecorate %26 BuiltIn PrimitiveTriangleIndicesEXT
+               OpDecorate %vertices_color_0 Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_3 = OpConstant %uint 3
+     %uint_1 = OpConstant %uint 1
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+        %int = OpTypeInt 32 1
+      %int_3 = OpConstant %int 3
+%_arr_v4float_int_3 = OpTypeArray %v4float %int_3
+%_ptr_Output__arr_v4float_int_3 = OpTypePointer Output %_arr_v4float_int_3
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+     %uint_0 = OpConstant %uint 0
+    %float_0 = OpConstant %float 0
+         %19 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+     %v3uint = OpTypeVector %uint 3
+      %int_2 = OpConstant %int 2
+%_arr_v3uint_int_2 = OpTypeArray %v3uint %int_2
+%_ptr_Output__arr_v3uint_int_2 = OpTypePointer Output %_arr_v3uint_int_2
+%_ptr_Output_v3uint = OpTypePointer Output %v3uint
+     %uint_2 = OpConstant %uint 2
+         %29 = OpConstantComposite %v3uint %uint_0 %uint_1 %uint_2
+         %38 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+         %41 = OpConstantComposite %v3uint %uint_0 %uint_1 %uint_2
+%vertices_color = OpVariable %_ptr_Output__arr_v4float_int_3 Output
+         %26 = OpVariable %_ptr_Output__arr_v3uint_int_2 Output
+%vertices_color_0 = OpVariable %_ptr_Output__arr_v4float_int_3 Output
+      %main1 = OpFunction %void None %3
+          %4 = OpLabel
+               OpSetMeshOutputsEXT %uint_3 %uint_1
+         %17 = OpAccessChain %_ptr_Output_v4float %vertices_color %uint_0
+               OpStore %17 %19
+         %28 = OpAccessChain %_ptr_Output_v3uint %26 %uint_0
+               OpStore %28 %29
+               OpReturn
+               OpFunctionEnd
+      %main2 = OpFunction %void None %3
+         %34 = OpLabel
+               OpSetMeshOutputsEXT %uint_3 %uint_1
+         %37 = OpAccessChain %_ptr_Output_v4float %vertices_color_0 %uint_0
+               OpStore %37 %38
+         %40 = OpAccessChain %_ptr_Output_v3uint %26 %uint_0
+               OpStore %40 %41
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-PrimitiveTriangleIndicesEXT-"
+                      "PrimitiveTriangleIndicesEXT-07058"));
+}
+
+TEST_F(ValidateBuiltIns, BadExecModelVulkanPrimitivePointIndicesEXT) {
+  const std::string text = R"(
+  OpCapability MeshShadingNV
+  OpCapability MeshShadingEXT
+  OpExtension "SPV_NV_mesh_shader"
+  OpExtension "SPV_EXT_mesh_shader"
+%1 = OpExtInstImport "GLSL.std.450"
+  OpMemoryModel Logical GLSL450
+  OpEntryPoint MeshNV %main "main" %gl_PrimitivePointIndicesEXT
+  OpExecutionMode %main LocalSize 32 1 1
+  OpExecutionMode %main OutputVertices 81
+  OpExecutionMode %main OutputPrimitivesEXT 32
+  OpExecutionMode %main OutputPoints
+  OpSource GLSL 460
+  OpSourceExtension "GL_EXT_mesh_shader"
+  OpName %main "main"
+  OpName %gl_PrimitivePointIndicesEXT "gl_PrimitivePointIndicesEXT"
+  OpDecorate %gl_PrimitivePointIndicesEXT BuiltIn PrimitivePointIndicesEXT
+  OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%_arr_uint_uint_32 = OpTypeArray %uint %uint_32
+%_ptr_Output__arr_uint_uint_32 = OpTypePointer Output %_arr_uint_uint_32
+%gl_PrimitivePointIndicesEXT = OpVariable %_ptr_Output__arr_uint_uint_32 Output
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%uint_0 = OpConstant %uint 0
+%_ptr_Output_uint = OpTypePointer Output %uint
+%v3uint = OpTypeVector %uint 3
+%uint_1 = OpConstant %uint 1
+%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1
+%main = OpFunction %void None %3
+%5 = OpLabel
+%15 = OpAccessChain %_ptr_Output_uint %gl_PrimitivePointIndicesEXT %int_0
+  OpStore %15 %uint_0
+  OpReturn
+  OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      AnyVUID("VUID-PrimitivePointIndicesEXT-PrimitivePointIndicesEXT-07041"));
+}
+
+TEST_F(ValidateBuiltIns, VulkanBuiltinCullPrimitiveEXTInBlock) {
+  const std::string text = R"(
+      OpCapability MeshShadingEXT
+      OpExtension "SPV_EXT_mesh_shader"
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT
+      OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+      OpExecutionMode %main OutputVertices 81
+      OpExecutionMode %main OutputPrimitivesEXT 32
+      OpExecutionMode %main OutputTrianglesEXT
+      OpSource GLSL 450
+      OpSourceExtension "GL_EXT_mesh_shader"
+      OpName %main "main"
+      OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT"
+      OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_CullPrimitiveEXT"
+      OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT"
+      OpDecorate %gl_MeshPerPrimitiveEXT Block
+      OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn CullPrimitiveEXT
+      OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT
+%void = OpTypeVoid
+ %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%gl_MeshPerPrimitiveEXT = OpTypeStruct %bool
+%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32
+%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32
+%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output
+%main = OpFunction %void None %3
+ %5 = OpLabel
+      OpReturn
+      OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinCullPrimitiveEXTBlockArraySize) {
+  const std::string text = R"(
+        OpCapability MeshShadingEXT
+        OpExtension "SPV_EXT_mesh_shader"
+   %1 = OpExtInstImport "GLSL.std.450"
+        OpMemoryModel Logical GLSL450
+        OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT
+        OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+        OpExecutionMode %main OutputVertices 81
+        OpExecutionMode %main OutputPrimitivesEXT 32
+        OpExecutionMode %main OutputTrianglesEXT
+        OpSource GLSL 450
+        OpSourceExtension "GL_EXT_mesh_shader"
+        OpName %main "main"
+        OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT"
+        OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_CullPrimitiveEXT"
+        OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT"
+        OpDecorate %gl_MeshPerPrimitiveEXT Block
+        OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn CullPrimitiveEXT
+        OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT
+  %void = OpTypeVoid
+   %3 = OpTypeFunction %void
+  %uint = OpTypeInt 32 0
+  %uint_32 = OpConstant %uint 32
+  %uint_16 = OpConstant %uint 16
+  %uint_1 = OpConstant %uint 1
+  %int = OpTypeInt 32 1
+  %bool = OpTypeBool
+  %gl_MeshPerPrimitiveEXT = OpTypeStruct %bool
+  %_arr_gl_MeshPerPrimitiveEXT_uint_16 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_16
+  %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_16 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_16
+  %gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_16 Output
+  %main = OpFunction %void None %3
+   %5 = OpLabel
+ %ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_16 %gl_MeshPrimitivesEXT
+        OpReturn
+        OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-CullPrimitiveEXT-CullPrimitiveEXT-10590"));
+}
+
+TEST_F(ValidateBuiltIns, VulkanBuiltinCullPrimitiveEXTMissingBlock) {
+  const std::string text = R"(
+      OpCapability MeshShadingEXT
+      OpExtension "SPV_EXT_mesh_shader"
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT
+      OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+      OpExecutionMode %main OutputVertices 81
+      OpExecutionMode %main OutputPrimitivesEXT 32
+      OpExecutionMode %main OutputTrianglesEXT
+      OpSource GLSL 450
+      OpSourceExtension "GL_EXT_mesh_shader"
+      OpName %main "main"
+      OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT"
+      OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_CullPrimitiveEXT"
+      OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT"
+      OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn CullPrimitiveEXT
+      OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT
+%void = OpTypeVoid
+ %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%gl_MeshPerPrimitiveEXT = OpTypeStruct %bool
+%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32
+%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32
+%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output
+%main = OpFunction %void None %3
+ %5 = OpLabel
+%ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_32 %gl_MeshPrimitivesEXT
+      OpReturn
+      OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-CullPrimitiveEXT-CullPrimitiveEXT-07036"));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("Scalar boolean must be in a Block"));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinCullPrimitiveEXTType) {
+  const std::string text = R"(
+      OpCapability MeshShadingEXT
+      OpExtension "SPV_EXT_mesh_shader"
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT
+      OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+      OpExecutionMode %main OutputVertices 81
+      OpExecutionMode %main OutputPrimitivesEXT 32
+      OpExecutionMode %main OutputTrianglesEXT
+      OpSource GLSL 450
+      OpSourceExtension "GL_EXT_mesh_shader"
+      OpName %main "main"
+      OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT"
+      OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_CullPrimitiveEXT"
+      OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT"
+      OpDecorate %gl_MeshPerPrimitiveEXT Block
+      OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn CullPrimitiveEXT
+      OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT
+%void = OpTypeVoid
+ %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%gl_MeshPerPrimitiveEXT = OpTypeStruct %int
+%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32
+%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32
+%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output
+%main = OpFunction %void None %3
+ %5 = OpLabel
+%ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_32 %gl_MeshPrimitivesEXT
+      OpReturn
+      OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-CullPrimitiveEXT-CullPrimitiveEXT-07036"));
+}
+
+// from https://github.com/KhronosGroup/SPIRV-Tools/issues/5980
+TEST_F(ValidateBuiltIns, VulkanBuiltinCullPrimitiveEXTArrayOfBool) {
+  const std::string text = R"(
+               OpCapability MeshShadingEXT
+               OpExtension "SPV_EXT_mesh_shader"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %main "main" %gl_LocalInvocationIndex %gl_Position %4 %5
+               OpExecutionMode %main LocalSize 2 1 1
+               OpExecutionMode %main OutputTrianglesEXT
+               OpExecutionMode %main OutputVertices 2
+               OpExecutionMode %main OutputPrimitivesEXT 2
+               OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex
+               OpDecorate %gl_Position BuiltIn Position
+               OpDecorate %4 BuiltIn PrimitiveTriangleIndicesEXT
+               OpDecorate %5 BuiltIn CullPrimitiveEXT
+               OpDecorate %5 PerPrimitiveEXT
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+       %bool = OpTypeBool
+      %false = OpConstantFalse %bool
+%_ptr_Input_uint = OpTypePointer Input %uint
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2
+%_ptr_Output__arr_v4float_uint_2 = OpTypePointer Output %_arr_v4float_uint_2
+     %v3uint = OpTypeVector %uint 3
+%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2
+%_ptr_Output__arr_v3uint_uint_2 = OpTypePointer Output %_arr_v3uint_uint_2
+%_arr_bool_uint_2 = OpTypeArray %bool %uint_2
+%_ptr_Output__arr_bool_uint_2 = OpTypePointer Output %_arr_bool_uint_2
+       %void = OpTypeVoid
+         %21 = OpTypeFunction %void
+%_ptr_Output_bool = OpTypePointer Output %bool
+%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input
+%gl_Position = OpVariable %_ptr_Output__arr_v4float_uint_2 Output
+          %4 = OpVariable %_ptr_Output__arr_v3uint_uint_2 Output
+          %5 = OpVariable %_ptr_Output__arr_bool_uint_2 Output
+       %main = OpFunction %void None %21
+         %23 = OpLabel
+         %24 = OpLoad %uint %gl_LocalInvocationIndex
+               OpSetMeshOutputsEXT %uint_2 %uint_2
+         %25 = OpAccessChain %_ptr_Output_bool %5 %24
+               OpStore %25 %false
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinCullPrimitiveEXTArrayType) {
+  const std::string text = R"(
+               OpCapability MeshShadingEXT
+               OpExtension "SPV_EXT_mesh_shader"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %main "main" %gl_LocalInvocationIndex %gl_Position %4 %5
+               OpExecutionMode %main LocalSize 2 1 1
+               OpExecutionMode %main OutputTrianglesEXT
+               OpExecutionMode %main OutputVertices 2
+               OpExecutionMode %main OutputPrimitivesEXT 2
+               OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex
+               OpDecorate %gl_Position BuiltIn Position
+               OpDecorate %4 BuiltIn PrimitiveTriangleIndicesEXT
+               OpDecorate %5 BuiltIn CullPrimitiveEXT
+               OpDecorate %5 PerPrimitiveEXT
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+       %bool = OpTypeBool
+      %false = OpConstantFalse %bool
+%_ptr_Input_uint = OpTypePointer Input %uint
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2
+%_ptr_Output__arr_v4float_uint_2 = OpTypePointer Output %_arr_v4float_uint_2
+     %v3uint = OpTypeVector %uint 3
+%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2
+%_ptr_Output__arr_v3uint_uint_2 = OpTypePointer Output %_arr_v3uint_uint_2
+%_arr_uint_uint_2 = OpTypeArray %uint %uint_2
+%_ptr_Output__arr_uint_uint_2 = OpTypePointer Output %_arr_uint_uint_2
+       %void = OpTypeVoid
+         %21 = OpTypeFunction %void
+%_ptr_Output_uint = OpTypePointer Output %uint
+%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input
+%gl_Position = OpVariable %_ptr_Output__arr_v4float_uint_2 Output
+          %4 = OpVariable %_ptr_Output__arr_v3uint_uint_2 Output
+          %5 = OpVariable %_ptr_Output__arr_uint_uint_2 Output
+       %main = OpFunction %void None %21
+         %23 = OpLabel
+         %24 = OpLoad %uint %gl_LocalInvocationIndex
+               OpSetMeshOutputsEXT %uint_2 %uint_2
+         %25 = OpAccessChain %_ptr_Output_uint %5 %24
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-CullPrimitiveEXT-CullPrimitiveEXT-07036"));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinCullPrimitiveEXTArrayOfBoolSize) {
+  const std::string text = R"(
+          OpCapability MeshShadingEXT
+          OpExtension "SPV_EXT_mesh_shader"
+          OpMemoryModel Logical GLSL450
+          OpEntryPoint MeshEXT %main "main" %gl_LocalInvocationIndex %gl_Position %4 %5
+          OpExecutionMode %main LocalSize 2 1 1
+          OpExecutionMode %main OutputTrianglesEXT
+          OpExecutionMode %main OutputVertices 2
+          OpExecutionMode %main OutputPrimitivesEXT 2
+          OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex
+          OpDecorate %gl_Position BuiltIn Position
+          OpDecorate %4 BuiltIn PrimitiveTriangleIndicesEXT
+          OpDecorate %5 BuiltIn CullPrimitiveEXT
+          OpDecorate %5 PerPrimitiveEXT
+  %uint = OpTypeInt 32 0
+%uint_2 = OpConstant %uint 2
+%uint_4 = OpConstant %uint 4
+  %bool = OpTypeBool
+ %false = OpConstantFalse %bool
+%_ptr_Input_uint = OpTypePointer Input %uint
+ %float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2
+%_ptr_Output__arr_v4float_uint_2 = OpTypePointer Output %_arr_v4float_uint_2
+%v3uint = OpTypeVector %uint 3
+%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2
+%_ptr_Output__arr_v3uint_uint_2 = OpTypePointer Output %_arr_v3uint_uint_2
+%_arr_bool_uint_4 = OpTypeArray %bool %uint_4
+%_ptr_Output__arr_bool_uint_4 = OpTypePointer Output %_arr_bool_uint_4
+  %void = OpTypeVoid
+    %21 = OpTypeFunction %void
+%_ptr_Output_bool = OpTypePointer Output %bool
+%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input
+%gl_Position = OpVariable %_ptr_Output__arr_v4float_uint_2 Output
+     %4 = OpVariable %_ptr_Output__arr_v3uint_uint_2 Output
+     %5 = OpVariable %_ptr_Output__arr_bool_uint_4 Output
+  %main = OpFunction %void None %21
+    %23 = OpLabel
+    %24 = OpLoad %uint %gl_LocalInvocationIndex
+          OpSetMeshOutputsEXT %uint_2 %uint_2
+    %25 = OpAccessChain %_ptr_Output_bool %5 %24
+          OpStore %25 %false
+          OpReturn
+          OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-CullPrimitiveEXT-CullPrimitiveEXT-10589"));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinCullPrimitiveEXTInterfaceVariable) {
+  const std::string text = R"(
+         OpCapability MeshShadingEXT
+         OpExtension "SPV_EXT_mesh_shader"
+         OpMemoryModel Logical GLSL450
+         OpEntryPoint MeshEXT %main "main" %gl_LocalInvocationIndex %gl_Position %4 %5 %gl_MeshPrimitivesEXT
+         OpExecutionMode %main LocalSize 2 1 1
+         OpExecutionMode %main OutputTrianglesEXT
+         OpExecutionMode %main OutputVertices 2
+         OpExecutionMode %main OutputPrimitivesEXT 2
+         OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex
+         OpDecorate %gl_Position BuiltIn Position
+         OpDecorate %4 BuiltIn PrimitiveTriangleIndicesEXT
+         OpDecorate %5 BuiltIn CullPrimitiveEXT
+         OpDecorate %5 PerPrimitiveEXT
+         OpDecorate %gl_MeshPerPrimitiveEXT Block
+         OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn CullPrimitiveEXT
+         OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT
+ %uint = OpTypeInt 32 0
+%uint_2 = OpConstant %uint 2
+ %bool = OpTypeBool
+%false = OpConstantFalse %bool
+%_ptr_Input_uint = OpTypePointer Input %uint
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2
+%_ptr_Output__arr_v4float_uint_2 = OpTypePointer Output %_arr_v4float_uint_2
+%v3uint = OpTypeVector %uint 3
+%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2
+%_ptr_Output__arr_v3uint_uint_2 = OpTypePointer Output %_arr_v3uint_uint_2
+%_arr_bool_uint_2 = OpTypeArray %bool %uint_2
+%_ptr_Output__arr_bool_uint_2 = OpTypePointer Output %_arr_bool_uint_2
+ %void = OpTypeVoid
+   %21 = OpTypeFunction %void
+%_ptr_Output_bool = OpTypePointer Output %bool
+%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input
+%gl_Position = OpVariable %_ptr_Output__arr_v4float_uint_2 Output
+    %4 = OpVariable %_ptr_Output__arr_v3uint_uint_2 Output
+    %5 = OpVariable %_ptr_Output__arr_bool_uint_2 Output
+%gl_MeshPerPrimitiveEXT = OpTypeStruct %bool
+%_arr_gl_MeshPerPrimitiveEXT_uint_2 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_2
+%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_2 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_2
+%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_2 Output
+ %main = OpFunction %void None %21
+   %23 = OpLabel
+%ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_2 %gl_MeshPrimitivesEXT
+   %24 = OpLoad %uint %gl_LocalInvocationIndex
+         OpSetMeshOutputsEXT %uint_2 %uint_2
+   %25 = OpAccessChain %_ptr_Output_bool %5 %24
+         OpStore %25 %false
+         OpReturn
+         OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-CullPrimitiveEXT-CullPrimitiveEXT-10591"));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinCullPrimitiveEXTStorageClass) {
+  const std::string text = R"(
+      OpCapability MeshShadingEXT
+      OpExtension "SPV_EXT_mesh_shader"
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT
+      OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+      OpExecutionMode %main OutputVertices 81
+      OpExecutionMode %main OutputPrimitivesEXT 32
+      OpExecutionMode %main OutputTrianglesEXT
+      OpSource GLSL 450
+      OpSourceExtension "GL_EXT_mesh_shader"
+      OpName %main "main"
+      OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT"
+      OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_CullPrimitiveEXT"
+      OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT"
+      OpDecorate %gl_MeshPerPrimitiveEXT Block
+      OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn CullPrimitiveEXT
+      OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT
+%void = OpTypeVoid
+ %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%gl_MeshPerPrimitiveEXT = OpTypeStruct %bool
+%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32
+%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Input %_arr_gl_MeshPerPrimitiveEXT_uint_32
+%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Input
+%main = OpFunction %void None %3
+ %5 = OpLabel
+      OpReturn
+      OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-CullPrimitiveEXT-CullPrimitiveEXT-07035"));
+}
+
+TEST_F(ValidateBuiltIns, BadBuiltinCullPrimitiveEXTWithPerPrimitiveEXT) {
+  const std::string text = R"(
+      OpCapability MeshShadingEXT
+      OpExtension "SPV_EXT_mesh_shader"
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT
+      OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+      OpExecutionMode %main OutputVertices 81
+      OpExecutionMode %main OutputPrimitivesEXT 32
+      OpExecutionMode %main OutputTrianglesEXT
+      OpSource GLSL 450
+      OpSourceExtension "GL_EXT_mesh_shader"
+      OpName %main "main"
+      OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT"
+      OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_CullPrimitiveEXT"
+      OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT"
+      OpDecorate %gl_MeshPerPrimitiveEXT Block
+      OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn CullPrimitiveEXT
+%void = OpTypeVoid
+ %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%gl_MeshPerPrimitiveEXT = OpTypeStruct %bool
+%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32
+%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32
+%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output
+%main = OpFunction %void None %3
+ %5 = OpLabel
+ %ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_32 %gl_MeshPrimitivesEXT
+      OpReturn
+      OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-CullPrimitiveEXT-CullPrimitiveEXT-07038"));
+}
+
+TEST_F(ValidateBuiltIns, BadBuiltinPrimitiveShadingRateWithPerPrimitiveEXT) {
+  const std::string text = R"(
+      OpCapability FragmentShadingRateKHR
+      OpCapability MeshShadingEXT
+      OpExtension "SPV_EXT_mesh_shader"
+      OpExtension "SPV_KHR_fragment_shading_rate"
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT
+      OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+      OpExecutionMode %main OutputVertices 81
+      OpExecutionMode %main OutputPrimitivesEXT 32
+      OpExecutionMode %main OutputTrianglesEXT
+      OpSource GLSL 450
+      OpSourceExtension "GL_EXT_mesh_shader"
+      OpName %main "main"
+      OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT"
+      OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_PrimitiveShadingRateKHR"
+      OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT"
+      OpDecorate %gl_MeshPerPrimitiveEXT Block
+      OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn PrimitiveShadingRateKHR
+%void = OpTypeVoid
+ %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%gl_MeshPerPrimitiveEXT = OpTypeStruct %int
+%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32
+%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32
+%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output
+%main = OpFunction %void None %3
+ %5 = OpLabel
+%ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_32 %gl_MeshPrimitivesEXT
+      OpReturn
+      OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      AnyVUID("VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-07059"));
+}
+
+TEST_F(ValidateBuiltIns, BadExecModelVulkanCullPrimitiveEXT) {
+  const std::string text = R"(
+         OpCapability MeshShadingNV
+         OpCapability MeshShadingEXT
+         OpExtension "SPV_NV_mesh_shader"
+         OpExtension "SPV_EXT_mesh_shader"
+    %1 = OpExtInstImport "GLSL.std.450"
+         OpMemoryModel Logical GLSL450
+         OpEntryPoint MeshNV %main "main" %gl_MeshPrimitivesEXT
+         OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+         OpExecutionMode %main OutputVertices 81
+         OpExecutionMode %main OutputPrimitivesNV 32
+         OpExecutionMode %main OutputTrianglesNV
+         OpSource GLSL 450
+         OpSourceExtension "GL_EXT_mesh_shader"
+         OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT
+         OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn CullPrimitiveEXT
+         OpDecorate %gl_MeshPerPrimitiveEXT Block
+ %void = OpTypeVoid
+    %3 = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%v3uint = OpTypeVector %uint 3
+ %bool = OpTypeBool
+  %int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%gl_MeshPerPrimitiveEXT = OpTypeStruct %bool
+%_ptr_Output_bool = OpTypePointer Output %bool
+%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32
+%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32
+%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output
+ %main = OpFunction %void None %3
+    %5 = OpLabel
+   %18 = OpAccessChain %_ptr_Output_bool %gl_MeshPrimitivesEXT %int_0 %int_0
+         OpReturn
+         OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-CullPrimitiveEXT-CullPrimitiveEXT-07034"));
+}
+
+TEST_F(ValidateBuiltIns, VulkanBuiltinLayerInBlockMeshEXT) {
+  const std::string text = R"(
+      OpCapability MeshShadingEXT
+      OpExtension "SPV_EXT_mesh_shader"
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT
+      OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+      OpExecutionMode %main OutputVertices 81
+      OpExecutionMode %main OutputPrimitivesEXT 32
+      OpExecutionMode %main OutputTrianglesEXT
+      OpSource GLSL 450
+      OpSourceExtension "GL_EXT_mesh_shader"
+      OpName %main "main"
+      OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT"
+      OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_Layer"
+      OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT"
+      OpDecorate %gl_MeshPerPrimitiveEXT Block
+      OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn Layer
+      OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT
+%void = OpTypeVoid
+ %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%gl_MeshPerPrimitiveEXT = OpTypeStruct %int
+%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32
+%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32
+%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output
+%main = OpFunction %void None %3
+ %5 = OpLabel
+      OpReturn
+      OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateBuiltIns, VulkanBuiltinLayerAsArrayOfIntMeshEXT) {
+  const std::string text = R"(
+      OpCapability MeshShadingEXT
+      OpExtension "SPV_EXT_mesh_shader"
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint MeshEXT %main "main" %gl_Layer
+      OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+      OpExecutionMode %main OutputVertices 81
+      OpExecutionMode %main OutputPrimitivesEXT 32
+      OpExecutionMode %main OutputTrianglesEXT
+      OpSource GLSL 450
+      OpSourceExtension "GL_EXT_mesh_shader"
+      OpName %main "main"
+      OpDecorate %gl_Layer BuiltIn Layer
+      OpDecorate %gl_Layer PerPrimitiveEXT
+%void = OpTypeVoid
+ %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%_arr_gl_Layer_uint_32 = OpTypeArray %int %uint_32
+%_ptr_Output__arr_gl_Layer_uint_32 = OpTypePointer Output %_arr_gl_Layer_uint_32
+%gl_Layer = OpVariable %_ptr_Output__arr_gl_Layer_uint_32 Output
+%main = OpFunction %void None %3
+ %5 = OpLabel
+      OpReturn
+      OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinLayerArrayTypeMeshEXT) {
+  const std::string text = R"(
+      OpCapability MeshShadingEXT
+      OpExtension "SPV_EXT_mesh_shader"
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint MeshEXT %main "main" %gl_Layer
+      OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+      OpExecutionMode %main OutputVertices 81
+      OpExecutionMode %main OutputPrimitivesEXT 32
+      OpExecutionMode %main OutputTrianglesEXT
+      OpSource GLSL 450
+      OpSourceExtension "GL_EXT_mesh_shader"
+      OpName %main "main"
+      OpDecorate %gl_Layer BuiltIn Layer
+      OpDecorate %gl_Layer PerPrimitiveEXT
+%void = OpTypeVoid
+ %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%_arr_gl_Layer_uint_32 = OpTypeArray %bool %uint_32
+%_ptr_Output__arr_gl_Layer_uint_32 = OpTypePointer Output %_arr_gl_Layer_uint_32
+%gl_Layer = OpVariable %_ptr_Output__arr_gl_Layer_uint_32 Output
+%main = OpFunction %void None %3
+ %5 = OpLabel
+%ref_load = OpLoad %_arr_gl_Layer_uint_32 %gl_Layer
+      OpReturn
+      OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-Layer-Layer-10592"));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinLayerInBlockMeshEXTType) {
+  const std::string text = R"(
+      OpCapability MeshShadingEXT
+      OpExtension "SPV_EXT_mesh_shader"
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT
+      OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+      OpExecutionMode %main OutputVertices 81
+      OpExecutionMode %main OutputPrimitivesEXT 32
+      OpExecutionMode %main OutputTrianglesEXT
+      OpSource GLSL 450
+      OpSourceExtension "GL_EXT_mesh_shader"
+      OpName %main "main"
+      OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT"
+      OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_Layer"
+      OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT"
+      OpDecorate %gl_MeshPerPrimitiveEXT Block
+      OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn Layer
+      OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT
+%void = OpTypeVoid
+ %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%gl_MeshPerPrimitiveEXT = OpTypeStruct %bool
+%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32
+%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32
+%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output
+%main = OpFunction %void None %3
+ %5 = OpLabel
+ %ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_32 %gl_MeshPrimitivesEXT
+      OpReturn
+      OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-Layer-Layer-10592"));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinLayerArrayOfIntSizeMeshEXT) {
+  const std::string text = R"(
+      OpCapability MeshShadingEXT
+      OpExtension "SPV_EXT_mesh_shader"
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint MeshEXT %main "main" %gl_Layer
+      OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+      OpExecutionMode %main OutputVertices 81
+      OpExecutionMode %main OutputPrimitivesEXT 16
+      OpExecutionMode %main OutputTrianglesEXT
+      OpSource GLSL 450
+      OpSourceExtension "GL_EXT_mesh_shader"
+      OpName %main "main"
+      OpDecorate %gl_Layer BuiltIn Layer
+      OpDecorate %gl_Layer PerPrimitiveEXT
+%void = OpTypeVoid
+ %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%_arr_gl_Layer_uint_32 = OpTypeArray %int %uint_32
+%_ptr_Output__arr_gl_Layer_uint_32 = OpTypePointer Output %_arr_gl_Layer_uint_32
+%gl_Layer = OpVariable %_ptr_Output__arr_gl_Layer_uint_32 Output
+%main = OpFunction %void None %3
+ %5 = OpLabel
+%ref_load = OpLoad %_arr_gl_Layer_uint_32 %gl_Layer
+      OpReturn
+      OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-Layer-Layer-10593"));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinLayerInBlockArraySizeMeshEXT) {
+  const std::string text = R"(
+      OpCapability MeshShadingEXT
+      OpExtension "SPV_EXT_mesh_shader"
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT
+      OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+      OpExecutionMode %main OutputVertices 81
+      OpExecutionMode %main OutputPrimitivesEXT 16
+      OpExecutionMode %main OutputTrianglesEXT
+      OpSource GLSL 450
+      OpSourceExtension "GL_EXT_mesh_shader"
+      OpName %main "main"
+      OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT"
+      OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_Layer"
+      OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT"
+      OpDecorate %gl_MeshPerPrimitiveEXT Block
+      OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn Layer
+      OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT
+%void = OpTypeVoid
+ %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%gl_MeshPerPrimitiveEXT = OpTypeStruct %int
+%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32
+%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32
+%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output
+%main = OpFunction %void None %3
+ %5 = OpLabel
+%ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_32 %gl_MeshPrimitivesEXT
+      OpReturn
+      OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-Layer-Layer-10594"));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinLayerWithPerPrimitiveEXT) {
+  const std::string text = R"(
+          OpCapability MeshShadingEXT
+          OpCapability Shader
+          OpExtension "SPV_EXT_mesh_shader"
+          OpMemoryModel Logical GLSL450
+          OpEntryPoint MeshEXT %MainMesh "MainMesh" %gl_Layer
+          OpExecutionMode %MainMesh OutputPrimitivesNV 1
+          OpExecutionMode %MainMesh OutputVertices 3
+          OpExecutionMode %MainMesh OutputTrianglesNV
+          OpExecutionMode %MainMesh LocalSize 1 1 1
+          OpSource Slang 1
+          OpName %MainMesh "MainMesh"
+          OpDecorate %gl_Layer BuiltIn Layer
+  %void = OpTypeVoid
+     %9 = OpTypeFunction %void
+  %uint = OpTypeInt 32 0
+%uint_3 = OpConstant %uint 3
+%uint_1 = OpConstant %uint 1
+ %float = OpTypeFloat 32
+   %int = OpTypeInt 32 1
+ %int_1 = OpConstant %int 1
+ %int_3 = OpConstant %int 3
+%uint_0 = OpConstant %uint 0
+%v3float = OpTypeVector %float 3
+%_ptr_Output_v3float = OpTypePointer Output %v3float
+%v3uint = OpTypeVector %uint 3
+%_ptr_Output_v3uint = OpTypePointer Output %v3uint
+%_ptr_Output_int = OpTypePointer Output %int
+%_arr_int_int_1 = OpTypeArray %int %int_1
+%_ptr_Output__arr_int_int_1 = OpTypePointer Output %_arr_int_int_1
+%gl_Layer = OpVariable %_ptr_Output__arr_int_int_1 Output
+%MainMesh = OpFunction %void None %9
+    %25 = OpLabel
+%ref_load = OpLoad %_arr_int_int_1 %gl_Layer
+          OpSetMeshOutputsEXT %uint_3 %uint_1
+          OpReturn
+          OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-Layer-Layer-07039"));
+}
+
+TEST_F(ValidateBuiltIns, VulkanBuiltinPrimitiveShadingRateKHRInBlockMeshEXT) {
+  const std::string text = R"(
+    OpCapability FragmentShadingRateKHR
+    OpCapability MeshShadingEXT
+    OpExtension "SPV_EXT_mesh_shader"
+    OpExtension "SPV_KHR_fragment_shading_rate"
+%1 = OpExtInstImport "GLSL.std.450"
+    OpMemoryModel Logical GLSL450
+    OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT
+    OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+    OpExecutionMode %main OutputVertices 81
+    OpExecutionMode %main OutputPrimitivesEXT 32
+    OpExecutionMode %main OutputTrianglesEXT
+    OpSource GLSL 450
+    OpSourceExtension "GL_EXT_mesh_shader"
+    OpName %main "main"
+    OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT"
+    OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_PrimitiveShadingRateEXT"
+    OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT"
+    OpDecorate %gl_MeshPerPrimitiveEXT Block
+    OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn PrimitiveShadingRateKHR
+    OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%gl_MeshPerPrimitiveEXT = OpTypeStruct %int
+%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32
+%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32
+%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output
+%main = OpFunction %void None %3
+%5 = OpLabel
+    OpReturn
+    OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateBuiltIns,
+       VulkanBuiltinPrimitiveShadingRateKHRInArrayOfIntMeshEXT) {
+  const std::string text = R"(
+      OpCapability FragmentShadingRateKHR
+      OpCapability MeshShadingEXT
+      OpExtension "SPV_EXT_mesh_shader"
+      OpExtension "SPV_KHR_fragment_shading_rate"
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint MeshEXT %main "main" %gl_PrimitiveShadingRateEXT
+      OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+      OpExecutionMode %main OutputVertices 81
+      OpExecutionMode %main OutputPrimitivesEXT 32
+      OpExecutionMode %main OutputTrianglesEXT
+      OpSource GLSL 450
+      OpSourceExtension "GL_EXT_mesh_shader"
+      OpName %main "main"
+      OpName %gl_PrimitiveShadingRateEXT "gl_PrimitiveShadingRateEXT"
+      OpDecorate %gl_PrimitiveShadingRateEXT BuiltIn PrimitiveShadingRateKHR
+      OpDecorate %gl_PrimitiveShadingRateEXT PerPrimitiveEXT
+%void = OpTypeVoid
+ %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%int = OpTypeInt 32 1
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%uint_0 = OpConstant %uint 0
+%int_0 = OpConstant %int 0
+%bool = OpTypeBool
+%_arr_gl_PrimitiveShadingRateEXT_uint_32 = OpTypeArray %int %uint_32
+%_ptr_Output__arr_gl_PrimitiveShadingRateEXT_uint_32 = OpTypePointer Output %_arr_gl_PrimitiveShadingRateEXT_uint_32
+%gl_PrimitiveShadingRateEXT = OpVariable %_ptr_Output__arr_gl_PrimitiveShadingRateEXT_uint_32 Output
+%uint_ptr = OpTypePointer Output %int
+%main = OpFunction %void None %3
+ %5 = OpLabel
+      %21 = OpAccessChain %uint_ptr %gl_PrimitiveShadingRateEXT %uint_0
+      OpStore %21 %int_0
+      OpReturn
+      OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateBuiltIns,
+       BadVulkanBuiltinPrimitiveShadingRateKHRInArrayTypeMeshEXT) {
+  const std::string text = R"(
+      OpCapability FragmentShadingRateKHR
+      OpCapability MeshShadingEXT
+      OpExtension "SPV_EXT_mesh_shader"
+      OpExtension "SPV_KHR_fragment_shading_rate"
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint MeshEXT %main "main" %gl_PrimitiveShadingRateEXT
+      OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+      OpExecutionMode %main OutputVertices 81
+      OpExecutionMode %main OutputPrimitivesEXT 32
+      OpExecutionMode %main OutputTrianglesEXT
+      OpSource GLSL 450
+      OpSourceExtension "GL_EXT_mesh_shader"
+      OpName %main "main"
+      OpName %gl_PrimitiveShadingRateEXT "gl_PrimitiveShadingRateEXT"
+      OpDecorate %gl_PrimitiveShadingRateEXT BuiltIn PrimitiveShadingRateKHR
+      OpDecorate %gl_PrimitiveShadingRateEXT PerPrimitiveEXT
+%void = OpTypeVoid
+ %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%_arr_gl_PrimitiveShadingRateEXT_uint_32 = OpTypeArray %bool %uint_32
+%_ptr_Output__arr_gl_PrimitiveShadingRateEXT_uint_32 = OpTypePointer Output %_arr_gl_PrimitiveShadingRateEXT_uint_32
+%gl_PrimitiveShadingRateEXT = OpVariable %_ptr_Output__arr_gl_PrimitiveShadingRateEXT_uint_32 Output
+%main = OpFunction %void None %3
+ %5 = OpLabel
+ %ref_load = OpLoad %_arr_gl_PrimitiveShadingRateEXT_uint_32 %gl_PrimitiveShadingRateEXT
+      OpReturn
+      OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      AnyVUID("VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-10598"));
+}
+
+TEST_F(ValidateBuiltIns,
+       BadVulkanBuiltinPrimitiveShadingRateKHRInBlockTypeMeshEXT) {
+  const std::string text = R"(
+    OpCapability FragmentShadingRateKHR
+    OpCapability MeshShadingEXT
+    OpExtension "SPV_EXT_mesh_shader"
+    OpExtension "SPV_KHR_fragment_shading_rate"
+%1 = OpExtInstImport "GLSL.std.450"
+    OpMemoryModel Logical GLSL450
+    OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT
+    OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+    OpExecutionMode %main OutputVertices 81
+    OpExecutionMode %main OutputPrimitivesEXT 32
+    OpExecutionMode %main OutputTrianglesEXT
+    OpSource GLSL 450
+    OpSourceExtension "GL_EXT_mesh_shader"
+    OpName %main "main"
+    OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT"
+    OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_PrimitiveShadingRateEXT"
+    OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT"
+    OpDecorate %gl_MeshPerPrimitiveEXT Block
+    OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn PrimitiveShadingRateKHR
+    OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%gl_MeshPerPrimitiveEXT = OpTypeStruct %bool
+%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32
+%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32
+%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output
+%main = OpFunction %void None %3
+%5 = OpLabel
+%ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_32 %gl_MeshPrimitivesEXT
+    OpReturn
+    OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      AnyVUID("VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-10598"));
+}
+
+TEST_F(ValidateBuiltIns,
+       BadVulkanBuiltinPrimitiveShadingRateKHRInBlockSizeMeshEXT) {
+  const std::string text = R"(
+    OpCapability FragmentShadingRateKHR
+    OpCapability MeshShadingEXT
+    OpExtension "SPV_EXT_mesh_shader"
+    OpExtension "SPV_KHR_fragment_shading_rate"
+%1 = OpExtInstImport "GLSL.std.450"
+    OpMemoryModel Logical GLSL450
+    OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT
+    OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+    OpExecutionMode %main OutputVertices 81
+    OpExecutionMode %main OutputPrimitivesEXT 16
+    OpExecutionMode %main OutputTrianglesEXT
+    OpSource GLSL 450
+    OpSourceExtension "GL_EXT_mesh_shader"
+    OpName %main "main"
+    OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT"
+    OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_PrimitiveShadingRateEXT"
+    OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT"
+    OpDecorate %gl_MeshPerPrimitiveEXT Block
+    OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn PrimitiveShadingRateKHR
+    OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%gl_MeshPerPrimitiveEXT = OpTypeStruct %int
+%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32
+%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32
+%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output
+%main = OpFunction %void None %3
+%5 = OpLabel
+%ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_32 %gl_MeshPrimitivesEXT
+    OpReturn
+    OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      AnyVUID("VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-10600"));
+}
+
+TEST_F(ValidateBuiltIns,
+       BadVulkanBuiltinPrimitiveShadingRateKHRInArraySizeMeshEXT) {
+  const std::string text = R"(
+      OpCapability FragmentShadingRateKHR
+      OpCapability MeshShadingEXT
+      OpExtension "SPV_EXT_mesh_shader"
+      OpExtension "SPV_KHR_fragment_shading_rate"
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint MeshEXT %main "main" %gl_PrimitiveShadingRateEXT
+      OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+      OpExecutionMode %main OutputVertices 81
+      OpExecutionMode %main OutputPrimitivesEXT 16
+      OpExecutionMode %main OutputTrianglesEXT
+      OpSource GLSL 450
+      OpSourceExtension "GL_EXT_mesh_shader"
+      OpName %main "main"
+      OpName %gl_PrimitiveShadingRateEXT "gl_PrimitiveShadingRateEXT"
+      OpDecorate %gl_PrimitiveShadingRateEXT BuiltIn PrimitiveShadingRateKHR
+      OpDecorate %gl_PrimitiveShadingRateEXT PerPrimitiveEXT
+%void = OpTypeVoid
+ %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%_arr_gl_PrimitiveShadingRateEXT_uint_32 = OpTypeArray %int %uint_32
+%_ptr_Output__arr_gl_PrimitiveShadingRateEXT_uint_32 = OpTypePointer Output %_arr_gl_PrimitiveShadingRateEXT_uint_32
+%gl_PrimitiveShadingRateEXT = OpVariable %_ptr_Output__arr_gl_PrimitiveShadingRateEXT_uint_32 Output
+%main = OpFunction %void None %3
+ %5 = OpLabel
+%ref_load = OpLoad %_arr_gl_PrimitiveShadingRateEXT_uint_32 %gl_PrimitiveShadingRateEXT
+      OpReturn
+      OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      AnyVUID("VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-10599"));
+}
+
+TEST_F(ValidateBuiltIns, VulkanBuiltinViewportIndexInBlockMeshEXT) {
+  const std::string text = R"(
+    OpCapability MeshShadingEXT
+    OpExtension "SPV_EXT_mesh_shader"
+%1 = OpExtInstImport "GLSL.std.450"
+    OpMemoryModel Logical GLSL450
+    OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT
+    OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+    OpExecutionMode %main OutputVertices 81
+    OpExecutionMode %main OutputPrimitivesEXT 32
+    OpExecutionMode %main OutputTrianglesEXT
+    OpSource GLSL 450
+    OpSourceExtension "GL_EXT_mesh_shader"
+    OpName %main "main"
+    OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT"
+    OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_ViewportIndex"
+    OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT"
+    OpDecorate %gl_MeshPerPrimitiveEXT Block
+    OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn ViewportIndex
+    OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%gl_MeshPerPrimitiveEXT = OpTypeStruct %int
+%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32
+%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32
+%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output
+%main = OpFunction %void None %3
+%5 = OpLabel
+    OpReturn
+    OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateBuiltIns, VulkanBuiltinViewportIndexAsArrayOfIntMeshEXT) {
+  const std::string text = R"(
+      OpCapability MeshShadingEXT
+      OpExtension "SPV_EXT_mesh_shader"
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint MeshEXT %main "main" %gl_ViewportIndex
+      OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+      OpExecutionMode %main OutputVertices 81
+      OpExecutionMode %main OutputPrimitivesEXT 32
+      OpExecutionMode %main OutputTrianglesEXT
+      OpSource GLSL 450
+      OpSourceExtension "GL_EXT_mesh_shader"
+      OpName %main "main"
+      OpName %gl_ViewportIndex "gl_ViewportIndex"
+      OpDecorate %gl_ViewportIndex BuiltIn ViewportIndex
+      OpDecorate %gl_ViewportIndex PerPrimitiveEXT
+%void = OpTypeVoid
+ %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%_arr_gl_ViewportIndex_uint_32 = OpTypeArray %int %uint_32
+%_ptr_Output__arr_gl_ViewportIndex_uint_32 = OpTypePointer Output %_arr_gl_ViewportIndex_uint_32
+%gl_ViewportIndex = OpVariable %_ptr_Output__arr_gl_ViewportIndex_uint_32 Output
+%main = OpFunction %void None %3
+ %5 = OpLabel
+      OpReturn
+      OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinViewportIndexInBlockTypeMeshEXT) {
+  const std::string text = R"(
+    OpCapability MeshShadingEXT
+    OpExtension "SPV_EXT_mesh_shader"
+%1 = OpExtInstImport "GLSL.std.450"
+    OpMemoryModel Logical GLSL450
+    OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT
+    OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+    OpExecutionMode %main OutputVertices 81
+    OpExecutionMode %main OutputPrimitivesEXT 32
+    OpExecutionMode %main OutputTrianglesEXT
+    OpSource GLSL 450
+    OpSourceExtension "GL_EXT_mesh_shader"
+    OpName %main "main"
+    OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT"
+    OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_ViewportIndex"
+    OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT"
+    OpDecorate %gl_MeshPerPrimitiveEXT Block
+    OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn ViewportIndex
+    OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%gl_MeshPerPrimitiveEXT = OpTypeStruct %bool
+%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32
+%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32
+%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output
+%main = OpFunction %void None %3
+%5 = OpLabel
+%ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_32 %gl_MeshPrimitivesEXT
+    OpReturn
+    OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-ViewportIndex-ViewportIndex-10601"));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinViewportIndexAsArrayTypeMeshEXT) {
+  const std::string text = R"(
+      OpCapability MeshShadingEXT
+      OpExtension "SPV_EXT_mesh_shader"
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint MeshEXT %main "main" %gl_ViewportIndex
+      OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+      OpExecutionMode %main OutputVertices 81
+      OpExecutionMode %main OutputPrimitivesEXT 32
+      OpExecutionMode %main OutputTrianglesEXT
+      OpSource GLSL 450
+      OpSourceExtension "GL_EXT_mesh_shader"
+      OpName %main "main"
+      OpName %gl_ViewportIndex "gl_ViewportIndex"
+      OpDecorate %gl_ViewportIndex BuiltIn ViewportIndex
+      OpDecorate %gl_ViewportIndex PerPrimitiveEXT
+%void = OpTypeVoid
+ %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%_arr_gl_ViewportIndex_uint_32 = OpTypeArray %bool %uint_32
+%_ptr_Output__arr_gl_ViewportIndex_uint_32 = OpTypePointer Output %_arr_gl_ViewportIndex_uint_32
+%gl_ViewportIndex = OpVariable %_ptr_Output__arr_gl_ViewportIndex_uint_32 Output
+%main = OpFunction %void None %3
+ %5 = OpLabel
+%ref_load = OpLoad %_arr_gl_ViewportIndex_uint_32 %gl_ViewportIndex
+      OpReturn
+      OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-ViewportIndex-ViewportIndex-10601"));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinViewportIndexInBlockArraySizeMeshEXT) {
+  const std::string text = R"(
+    OpCapability MeshShadingEXT
+    OpExtension "SPV_EXT_mesh_shader"
+%1 = OpExtInstImport "GLSL.std.450"
+    OpMemoryModel Logical GLSL450
+    OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT
+    OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+    OpExecutionMode %main OutputVertices 81
+    OpExecutionMode %main OutputPrimitivesEXT 16
+    OpExecutionMode %main OutputTrianglesEXT
+    OpSource GLSL 450
+    OpSourceExtension "GL_EXT_mesh_shader"
+    OpName %main "main"
+    OpName %gl_MeshPerPrimitiveEXT "gl_MeshPerPrimitiveEXT"
+    OpMemberName %gl_MeshPerPrimitiveEXT 0 "gl_ViewportIndex"
+    OpName %gl_MeshPrimitivesEXT "gl_MeshPrimitivesEXT"
+    OpDecorate %gl_MeshPerPrimitiveEXT Block
+    OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn ViewportIndex
+    OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%gl_MeshPerPrimitiveEXT = OpTypeStruct %int
+%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32
+%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32
+%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output
+%main = OpFunction %void None %3
+%5 = OpLabel
+%ref_load = OpLoad %_arr_gl_MeshPerPrimitiveEXT_uint_32 %gl_MeshPrimitivesEXT
+    OpReturn
+    OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-ViewportIndex-ViewportIndex-10603"));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinViewportIndexAsArrayOfIntSizeMeshEXT) {
+  const std::string text = R"(
+      OpCapability MeshShadingEXT
+      OpExtension "SPV_EXT_mesh_shader"
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint MeshEXT %main "main" %gl_ViewportIndex
+      OpExecutionModeId %main LocalSizeId %uint_32 %uint_1 %uint_1
+      OpExecutionMode %main OutputVertices 81
+      OpExecutionMode %main OutputPrimitivesEXT 16
+      OpExecutionMode %main OutputTrianglesEXT
+      OpSource GLSL 450
+      OpSourceExtension "GL_EXT_mesh_shader"
+      OpName %main "main"
+      OpName %gl_ViewportIndex "gl_ViewportIndex"
+      OpDecorate %gl_ViewportIndex BuiltIn ViewportIndex
+      OpDecorate %gl_ViewportIndex PerPrimitiveEXT
+%void = OpTypeVoid
+ %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%uint_1 = OpConstant %uint 1
+%int = OpTypeInt 32 1
+%bool = OpTypeBool
+%_arr_gl_ViewportIndex_uint_32 = OpTypeArray %int %uint_32
+%_ptr_Output__arr_gl_ViewportIndex_uint_32 = OpTypePointer Output %_arr_gl_ViewportIndex_uint_32
+%gl_ViewportIndex = OpVariable %_ptr_Output__arr_gl_ViewportIndex_uint_32 Output
+%main = OpFunction %void None %3
+ %5 = OpLabel
+%ref_load = OpLoad %_arr_gl_ViewportIndex_uint_32 %gl_ViewportIndex
+      OpReturn
+      OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-ViewportIndex-ViewportIndex-10602"));
+}
+
+TEST_F(ValidateBuiltIns, BadVulkanBuiltinPrimitiveIdFragmentWithRayTracing) {
+  const std::string text = R"(
+          OpCapability Shader
+          OpCapability RayTracingKHR
+          OpExtension "SPV_KHR_ray_tracing"
+          OpMemoryModel Logical GLSL450
+          OpEntryPoint Fragment %main "main" %outVar %gl_PrimitiveID
+          OpExecutionMode %main OriginUpperLeft
+          OpDecorate %outVar Location 0
+          OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId
+          OpDecorate %gl_PrimitiveID Flat
+  %void = OpTypeVoid
+     %4 = OpTypeFunction %void
+   %int = OpTypeInt 32 1
+ %v4int = OpTypeVector %int 4
+%ptrOut = OpTypePointer Output %v4int
+%outVar = OpVariable %ptrOut Output
+ %ptrIn = OpTypePointer Input %int
+%gl_PrimitiveID = OpVariable %ptrIn Input
+  %main = OpFunction %void None %4
+     %6 = OpLabel
+    %13 = OpLoad %int %gl_PrimitiveID
+    %14 = OpCompositeConstruct %v4int %13 %13 %13 %13
+          OpStore %outVar %14
+          OpReturn
+          OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-PrimitiveId-Fragment-04333"));
+}
+
+TEST_F(ValidateBuiltIns, TessellationMissingPatch) {
+  const std::string spirv = R"(
+               OpCapability Tessellation
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint TessellationControl %main "main" %gl_TessLevelInner %gl_TessLevelOuter
+               OpExecutionMode %main OutputVertices 3
+               OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner
+               OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter
+               OpDecorate %gl_TessLevelOuter Patch
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+%_ptr_Output__arr_float_uint_2 = OpTypePointer Output %_arr_float_uint_2
+%gl_TessLevelInner = OpVariable %_ptr_Output__arr_float_uint_2 Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+    %float_1 = OpConstant %float 1
+%_ptr_Output_float = OpTypePointer Output %float
+     %uint_4 = OpConstant %uint 4
+%_arr_float_uint_4 = OpTypeArray %float %uint_4
+%_ptr_Output__arr_float_uint_4 = OpTypePointer Output %_arr_float_uint_4
+%gl_TessLevelOuter = OpVariable %_ptr_Output__arr_float_uint_4 Output
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+         %17 = OpAccessChain %_ptr_Output_float %gl_TessLevelInner %int_0
+               OpStore %17 %float_1
+         %22 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %int_0
+               OpStore %22 %float_1
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("BuiltIn TessLevelInner variable needs to also have a "
+                        "Patch decoration"));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-TessLevelInner-10880"));
+}
+
+// From dEQP-VK.mesh_shader.ext.builtin.primitive_id_spirv
+TEST_F(ValidateBuiltIns, PrimitiveIdInFragmentWithMeshCapability) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability MeshShadingEXT
+               OpExtension "SPV_EXT_mesh_shader"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %9 %gl_PrimitiveID
+               OpExecutionMode %4 OriginUpperLeft
+               OpDecorate %9 Location 0
+               OpDecorate %gl_PrimitiveID Flat
+               OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+          %9 = OpVariable %_ptr_Output_v4float Output
+        %int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%gl_PrimitiveID = OpVariable %_ptr_Input_int Input
+%int_1629198956 = OpConstant %int 1629198956
+       %bool = OpTypeBool
+    %float_0 = OpConstant %float 0
+    %float_1 = OpConstant %float 1
+         %19 = OpConstantComposite %v4float %float_0 %float_0 %float_1 %float_1
+         %20 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_1
+     %v4bool = OpTypeVector %bool 4
+          %4 = OpFunction %void None %3
+          %5 = OpLabel
+         %13 = OpLoad %int %gl_PrimitiveID
+         %16 = OpIEqual %bool %13 %int_1629198956
+         %22 = OpCompositeConstruct %v4bool %16 %16 %16 %16
+         %23 = OpSelect %v4float %22 %19 %20
+               OpStore %9 %23
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+// https://github.com/KhronosGroup/SPIRV-Tools/issues/6237
+TEST_F(ValidateBuiltIns, MeshBuiltinUnsignedInt) {
+  const std::string spirv = R"(
+               OpCapability FragmentShadingRateKHR
+               OpCapability MeshShadingEXT
+               OpExtension "SPV_EXT_mesh_shader"
+               OpExtension "SPV_KHR_fragment_shading_rate"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %main "main" %gl_MeshPrimitivesEXT
+               OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1
+               OpExecutionMode %main OutputVertices 81
+               OpExecutionMode %main OutputPrimitivesEXT 32
+               OpExecutionMode %main OutputTrianglesEXT
+               OpDecorate %gl_MeshPerPrimitiveEXT Block
+               OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn PrimitiveId
+               OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT
+               OpMemberDecorate %gl_MeshPerPrimitiveEXT 1 BuiltIn Layer
+               OpMemberDecorate %gl_MeshPerPrimitiveEXT 1 PerPrimitiveEXT
+               OpMemberDecorate %gl_MeshPerPrimitiveEXT 2 BuiltIn ViewportIndex
+               OpMemberDecorate %gl_MeshPerPrimitiveEXT 2 PerPrimitiveEXT
+               OpMemberDecorate %gl_MeshPerPrimitiveEXT 3 BuiltIn CullPrimitiveEXT
+               OpMemberDecorate %gl_MeshPerPrimitiveEXT 3 PerPrimitiveEXT
+               OpMemberDecorate %gl_MeshPerPrimitiveEXT 4 BuiltIn PrimitiveShadingRateKHR
+               OpMemberDecorate %gl_MeshPerPrimitiveEXT 4 PerPrimitiveEXT
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+        %int = OpTypeInt 32 1
+       %bool = OpTypeBool
+      %int_0 = OpConstant %int 0
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+     %uint_3 = OpConstant %uint 3
+     %uint_4 = OpConstant %uint 4
+    %uint_81 = OpConstant %uint 81
+    %uint_32 = OpConstant %uint 32
+%gl_MeshPerPrimitiveEXT = OpTypeStruct %uint %uint %uint %bool %uint
+%_arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_32
+%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_32
+%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_32 Output
+%_ptr_Output_uint = OpTypePointer Output %uint
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+               OpSetMeshOutputsEXT %uint_81 %uint_32
+         %20 = OpAccessChain %_ptr_Output_uint %gl_MeshPrimitivesEXT %int_0 %uint_0
+               OpStore %20 %uint_1
+         %22 = OpAccessChain %_ptr_Output_uint %gl_MeshPrimitivesEXT %int_0 %uint_1
+               OpStore %22 %uint_2
+         %24 = OpAccessChain %_ptr_Output_uint %gl_MeshPrimitivesEXT %int_0 %uint_2
+               OpStore %24 %uint_3
+         %26 = OpAccessChain %_ptr_Output_uint %gl_MeshPrimitivesEXT %int_0 %uint_4
+               OpStore %26 %uint_4
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateBuiltIns, HitTriangleVertexPositionExecutionModel) {
+  const std::string spirv = R"(
+                OpCapability RayTracingKHR
+               OpCapability RayTracingPositionFetchKHR
+               OpExtension "SPV_KHR_ray_tracing"
+               OpExtension "SPV_KHR_ray_tracing_position_fetch"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint IntersectionKHR %main "main" %gl_HitTriangleVertexPositionsEXT
+               OpDecorate %gl_HitTriangleVertexPositionsEXT BuiltIn HitTriangleVertexPositionsKHR
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+%_ptr_Function_v3float = OpTypePointer Function %v3float
+       %uint = OpTypeInt 32 0
+     %uint_3 = OpConstant %uint 3
+%_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3
+%_ptr_Input__arr_v3float_uint_3 = OpTypePointer Input %_arr_v3float_uint_3
+%gl_HitTriangleVertexPositionsEXT = OpVariable %_ptr_Input__arr_v3float_uint_3 Input
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_Input_v3float = OpTypePointer Input %v3float
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+        %v19 = OpVariable %_ptr_Function_v3float Function
+         %18 = OpAccessChain %_ptr_Input_v3float %gl_HitTriangleVertexPositionsEXT %int_0
+         %19 = OpLoad %v3float %18
+               OpStore %v19 %19
+               OpTerminateRayKHR
+               OpFunctionEnd
+)";
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Vulkan spec does not allow BuiltIn HitTriangleVertexPositionsKHR to "
+          "be used with the execution model IntersectionKHR"));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-HitTriangleVertexPositionsKHR-"
+                      "HitTriangleVertexPositionsKHR-08747"));
+}
+
+TEST_F(ValidateBuiltIns, HitTriangleVertexPositionType) {
+  const std::string spirv = R"(
+                OpCapability RayTracingKHR
+               OpCapability RayTracingPositionFetchKHR
+               OpExtension "SPV_KHR_ray_tracing"
+               OpExtension "SPV_KHR_ray_tracing_position_fetch"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint AnyHitKHR %main "main" %gl_HitTriangleVertexPositionsEXT
+               OpDecorate %gl_HitTriangleVertexPositionsEXT BuiltIn HitTriangleVertexPositionsKHR
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+%_ptr_Function_v3float = OpTypePointer Function %v3float
+       %uint = OpTypeInt 32 0
+     %uint_4 = OpConstant %uint 4
+%_arr_v3float_uint_4 = OpTypeArray %v3float %uint_4
+%_ptr_Input__arr_v3float_uint_4 = OpTypePointer Input %_arr_v3float_uint_4
+%gl_HitTriangleVertexPositionsEXT = OpVariable %_ptr_Input__arr_v3float_uint_4 Input
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_Input_v3float = OpTypePointer Input %v3float
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+        %v19 = OpVariable %_ptr_Function_v3float Function
+         %18 = OpAccessChain %_ptr_Input_v3float %gl_HitTriangleVertexPositionsEXT %int_0
+         %19 = OpLoad %v3float %18
+               OpStore %v19 %19
+               OpTerminateRayKHR
+               OpFunctionEnd
+)";
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("array length must be 3"));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-HitTriangleVertexPositionsKHR-"
+                      "HitTriangleVertexPositionsKHR-08749"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_capability_test.cpp b/test/val/val_capability_test.cpp
index 0d84caa..70fd98e 100644
--- a/test/val/val_capability_test.cpp
+++ b/test/val/val_capability_test.cpp
@@ -116,6 +116,10 @@
 using ValidateCapabilityVulkan11 = spvtest::ValidateBase<CapTestParameter>;
 // Always assembles using Vulkan 1.2.
 using ValidateCapabilityVulkan12 = spvtest::ValidateBase<CapTestParameter>;
+// Always assembles using Vulkan 1.3.
+using ValidateCapabilityVulkan13 = spvtest::ValidateBase<CapTestParameter>;
+// Always assembles using Vulkan 1.4.
+using ValidateCapabilityVulkan14 = spvtest::ValidateBase<CapTestParameter>;
 
 TEST_F(ValidateCapability, Default) {
   const char str[] = R"(
@@ -325,6 +329,119 @@
   return *r;
 }
 
+const std::vector<std::string>& AllSpirV16Capabilities() {
+  static const auto r = new std::vector<std::string>{
+    "",
+    "Matrix",
+    "Shader",
+    "Geometry",
+    "Tessellation",
+    "Addresses",
+    "Linkage",
+    "Kernel",
+    "Vector16",
+    "Float16Buffer",
+    "Float16",
+    "Float64",
+    "Int64",
+    "Int64Atomics",
+    "ImageBasic",
+    "ImageReadWrite",
+    "ImageMipmap",
+    "Pipes",
+    "Groups",
+    "DeviceEnqueue",
+    "LiteralSampler",
+    "AtomicStorage",
+    "Int16",
+    "TessellationPointSize",
+    "GeometryPointSize",
+    "ImageGatherExtended",
+    "StorageImageMultisample",
+    "UniformBufferArrayDynamicIndexing",
+    "SampledImageArrayDynamicIndexing",
+    "StorageBufferArrayDynamicIndexing",
+    "StorageImageArrayDynamicIndexing",
+    "ClipDistance",
+    "CullDistance",
+    "ImageCubeArray",
+    "SampleRateShading",
+    "ImageRect",
+    "SampledRect",
+    "GenericPointer",
+    "Int8",
+    "InputAttachment",
+    "SparseResidency",
+    "MinLod",
+    "Sampled1D",
+    "Image1D",
+    "SampledCubeArray",
+    "SampledBuffer",
+    "ImageBuffer",
+    "ImageMSArray",
+    "StorageImageExtendedFormats",
+    "ImageQuery",
+    "DerivativeControl",
+    "InterpolationFunction",
+    "TransformFeedback",
+    "GeometryStreams",
+    "StorageImageReadWithoutFormat",
+    "StorageImageWriteWithoutFormat",
+    "MultiViewport",
+    "SubgroupDispatch",
+    "NamedBarrier",
+    "PipeStorage",
+    "GroupNonUniform",
+    "GroupNonUniformVote",
+    "GroupNonUniformArithmetic",
+    "GroupNonUniformBallot",
+    "GroupNonUniformShuffle",
+    "GroupNonUniformShuffleRelative",
+    "GroupNonUniformClustered",
+    "GroupNonUniformQuad",
+    "DrawParameters",
+    "StorageBuffer16BitAccess",
+    "StorageUniformBufferBlock16",
+    "UniformAndStorageBuffer16BitAccess",
+    "StorageUniform16",
+    "StoragePushConstant16",
+    "StorageInputOutput16",
+    "DeviceGroup",
+    "MultiView",
+    "VariablePointersStorageBuffer",
+    "VariablePointers",
+    "DenormPreserve",
+    "DenormFlushToZero",
+    "SignedZeroInfNanPreserve",
+    "RoundingModeRTE",
+    "RoundingModeRTZ",
+    // Omitted due to extra validation requirements on memory model.
+    //"VulkanMemoryModel",
+    //"VulkanMemoryModelDeviceScope",
+    "StorageBuffer8BitAccess",
+    "UniformAndStorageBuffer8BitAccess",
+    "StoragePushConstant8",
+    "ShaderViewportIndex",
+    "ShaderLayer",
+    "PhysicalStorageBufferAddresses",
+    "RuntimeDescriptorArray",
+    "UniformTexelBufferArrayDynamicIndexing",
+    "StorageTexelBufferArrayDynamicIndexing",
+    "UniformBufferArrayNonUniformIndexing",
+    "SampledImageArrayNonUniformIndexing",
+    "StorageBufferArrayNonUniformIndexing",
+    "StorageImageArrayNonUniformIndexing",
+    "InputAttachmentArrayNonUniformIndexing",
+    "UniformTexelBufferArrayNonUniformIndexing",
+    "StorageTexelBufferArrayNonUniformIndexing",
+    "DotProduct",
+    "DotProductInputAll",
+    "DotProductInput4x8Bit",
+    "DotProductInput4x8BitPacked"
+  };
+  return *r;
+}
+
 const std::vector<std::string>& AllSpirV10Capabilities() {
   static const auto r = new std::vector<std::string>{
     "",
@@ -586,6 +703,194 @@
   return *r;
 }
 
+const std::vector<std::string>& AllVulkan13Capabilities() {
+  static const auto r = new std::vector<std::string>{
+    "",
+    "Matrix",
+    "Shader",
+    "InputAttachment",
+    "Sampled1D",
+    "Image1D",
+    "SampledBuffer",
+    "ImageBuffer",
+    "ImageQuery",
+    "DerivativeControl",
+    "Geometry",
+    "Tessellation",
+    "Float16",
+    "Float64",
+    "Int64",
+    "Int64Atomics",
+    "Int16",
+    "TessellationPointSize",
+    "GeometryPointSize",
+    "ImageGatherExtended",
+    "StorageImageMultisample",
+    "UniformBufferArrayDynamicIndexing",
+    "SampledImageArrayDynamicIndexing",
+    "StorageBufferArrayDynamicIndexing",
+    "StorageImageArrayDynamicIndexing",
+    "ClipDistance",
+    "CullDistance",
+    "ImageCubeArray",
+    "SampleRateShading",
+    "Int8",
+    "SparseResidency",
+    "MinLod",
+    "SampledCubeArray",
+    "ImageMSArray",
+    "StorageImageExtendedFormats",
+    "InterpolationFunction",
+    "StorageImageReadWithoutFormat",
+    "StorageImageWriteWithoutFormat",
+    "MultiViewport",
+    "GroupNonUniform",
+    "GroupNonUniformVote",
+    "GroupNonUniformArithmetic",
+    "GroupNonUniformBallot",
+    "GroupNonUniformShuffle",
+    "GroupNonUniformShuffleRelative",
+    "GroupNonUniformClustered",
+    "GroupNonUniformQuad",
+    "DrawParameters",
+    "StorageBuffer16BitAccess",
+    "StorageUniformBufferBlock16",
+    "UniformAndStorageBuffer16BitAccess",
+    "StorageUniform16",
+    "StoragePushConstant16",
+    "StorageInputOutput16",
+    "DeviceGroup",
+    "MultiView",
+    "VariablePointersStorageBuffer",
+    "VariablePointers",
+    "TransformFeedback",
+    "GeometryStreams",
+    "DenormPreserve",
+    "DenormFlushToZero",
+    "SignedZeroInfNanPreserve",
+    "RoundingModeRTE",
+    "RoundingModeRTZ",
+    "VulkanMemoryModel",
+    "VulkanMemoryModelDeviceScope",
+    "StorageBuffer8BitAccess",
+    "UniformAndStorageBuffer8BitAccess",
+    "StoragePushConstant8",
+    "ShaderViewportIndex",
+    "ShaderLayer",
+    "PhysicalStorageBufferAddresses",
+    "RuntimeDescriptorArray",
+    "UniformTexelBufferArrayDynamicIndexing",
+    "StorageTexelBufferArrayDynamicIndexing",
+    "UniformBufferArrayNonUniformIndexing",
+    "SampledImageArrayNonUniformIndexing",
+    "StorageBufferArrayNonUniformIndexing",
+    "StorageImageArrayNonUniformIndexing",
+    "InputAttachmentArrayNonUniformIndexing",
+    "UniformTexelBufferArrayNonUniformIndexing",
+    "StorageTexelBufferArrayNonUniformIndexing",
+    "DotProduct",
+    "DotProductInputAll",
+    "DotProductInput4x8Bit",
+    "DotProductInput4x8BitPacked",
+    "DemoteToHelperInvocation",
+  };
+  return *r;
+}
+
+const std::vector<std::string>& AllVulkan14Capabilities() {
+  static const auto r = new std::vector<std::string>{
+    "",
+    "Matrix",
+    "Shader",
+    "InputAttachment",
+    "Sampled1D",
+    "Image1D",
+    "SampledBuffer",
+    "ImageBuffer",
+    "ImageQuery",
+    "DerivativeControl",
+    "Geometry",
+    "Tessellation",
+    "Float16",
+    "Float64",
+    "Int64",
+    "Int64Atomics",
+    "Int16",
+    "TessellationPointSize",
+    "GeometryPointSize",
+    "ImageGatherExtended",
+    "StorageImageMultisample",
+    "UniformBufferArrayDynamicIndexing",
+    "SampledImageArrayDynamicIndexing",
+    "StorageBufferArrayDynamicIndexing",
+    "StorageImageArrayDynamicIndexing",
+    "ClipDistance",
+    "CullDistance",
+    "ImageCubeArray",
+    "SampleRateShading",
+    "Int8",
+    "SparseResidency",
+    "MinLod",
+    "SampledCubeArray",
+    "ImageMSArray",
+    "StorageImageExtendedFormats",
+    "InterpolationFunction",
+    "StorageImageReadWithoutFormat",
+    "StorageImageWriteWithoutFormat",
+    "MultiViewport",
+    "GroupNonUniform",
+    "GroupNonUniformVote",
+    "GroupNonUniformArithmetic",
+    "GroupNonUniformBallot",
+    "GroupNonUniformShuffle",
+    "GroupNonUniformShuffleRelative",
+    "GroupNonUniformClustered",
+    "GroupNonUniformQuad",
+    "DrawParameters",
+    "StorageBuffer16BitAccess",
+    "StorageUniformBufferBlock16",
+    "UniformAndStorageBuffer16BitAccess",
+    "StorageUniform16",
+    "StoragePushConstant16",
+    "StorageInputOutput16",
+    "DeviceGroup",
+    "MultiView",
+    "VariablePointersStorageBuffer",
+    "VariablePointers",
+    "TransformFeedback",
+    "GeometryStreams",
+    "DenormPreserve",
+    "DenormFlushToZero",
+    "SignedZeroInfNanPreserve",
+    "RoundingModeRTE",
+    "RoundingModeRTZ",
+    "VulkanMemoryModel",
+    "VulkanMemoryModelDeviceScope",
+    "StorageBuffer8BitAccess",
+    "UniformAndStorageBuffer8BitAccess",
+    "StoragePushConstant8",
+    "ShaderViewportIndex",
+    "ShaderLayer",
+    "PhysicalStorageBufferAddresses",
+    "RuntimeDescriptorArray",
+    "UniformTexelBufferArrayDynamicIndexing",
+    "StorageTexelBufferArrayDynamicIndexing",
+    "UniformBufferArrayNonUniformIndexing",
+    "SampledImageArrayNonUniformIndexing",
+    "StorageBufferArrayNonUniformIndexing",
+    "StorageImageArrayNonUniformIndexing",
+    "InputAttachmentArrayNonUniformIndexing",
+    "UniformTexelBufferArrayNonUniformIndexing",
+    "StorageTexelBufferArrayNonUniformIndexing",
+    "DotProduct",
+    "DotProductInputAll",
+    "DotProductInput4x8Bit",
+    "DotProductInput4x8BitPacked",
+    "DemoteToHelperInvocation",
+  };
+  return *r;
+}
+
 const std::vector<std::string>& MatrixDependencies() {
   static const auto r = new std::vector<std::string>{
   "Matrix",
@@ -1940,6 +2245,54 @@
           AllVulkan12Capabilities())
 )));
 
+INSTANTIATE_TEST_SUITE_P(Capabilities, ValidateCapabilityVulkan13,
+                        Combine(
+                            // All capabilities to try.
+                            ValuesIn(AllSpirV16Capabilities()),
+                            Values(
+std::make_pair(std::string(kGLSL450MemoryModel) +
+          "OpEntryPoint Vertex %func \"shader\" %var\n" +
+          "OpDecorate %var BuiltIn PointSize\n"
+          "%float = OpTypeFloat 32\n"
+          "%ptr_output_float = OpTypePointer Output %float\n"
+          "%var = OpVariable %ptr_output_float Output\n" + std::string(kVoidFVoid),
+          AllVulkan13Capabilities()),
+std::make_pair(std::string(kGLSL450MemoryModel) +
+          "OpEntryPoint Vertex %func \"shader\" %var\n" +
+          "OpDecorate %var BuiltIn CullDistance\n"
+          "%float = OpTypeFloat 32\n"
+          "%int = OpTypeInt 32 0\n"
+          "%int_1 = OpConstant %int 1\n"
+          "%array = OpTypeArray %float %int_1\n"
+          "%ptr = OpTypePointer Output %array\n"
+          "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid),
+          AllVulkan13Capabilities())
+)));
+
+INSTANTIATE_TEST_SUITE_P(Capabilities, ValidateCapabilityVulkan14,
+                        Combine(
+                            // All capabilities to try.
+                            ValuesIn(AllSpirV16Capabilities()),
+                            Values(
+std::make_pair(std::string(kGLSL450MemoryModel) +
+          "OpEntryPoint Vertex %func \"shader\" %var\n" +
+          "OpDecorate %var BuiltIn PointSize\n"
+          "%float = OpTypeFloat 32\n"
+          "%ptr_output_float = OpTypePointer Output %float\n"
+          "%var = OpVariable %ptr_output_float Output\n" + std::string(kVoidFVoid),
+          AllVulkan14Capabilities()),
+std::make_pair(std::string(kGLSL450MemoryModel) +
+          "OpEntryPoint Vertex %func \"shader\" %var\n" +
+          "OpDecorate %var BuiltIn CullDistance\n"
+          "%float = OpTypeFloat 32\n"
+          "%int = OpTypeInt 32 0\n"
+          "%int_1 = OpConstant %int 1\n"
+          "%array = OpTypeArray %float %int_1\n"
+          "%ptr = OpTypePointer Output %array\n"
+          "%var = OpVariable %ptr Output\n" + std::string(kVoidFVoid),
+          AllVulkan14Capabilities())
+)));
+
 // TODO(umar): Selection Control
 // TODO(umar): Loop Control
 // TODO(umar): Function Control
@@ -2017,24 +2370,16 @@
 // TODO(umar): Instruction capability checks
 
 spv_result_t spvCoreOperandTableNameLookup(spv_target_env env,
-                                           const spv_operand_table table,
                                            const spv_operand_type_t type,
                                            const char* name,
                                            const size_t nameLength) {
-  if (!table) return SPV_ERROR_INVALID_TABLE;
   if (!name) return SPV_ERROR_INVALID_POINTER;
 
-  for (uint64_t typeIndex = 0; typeIndex < table->count; ++typeIndex) {
-    const auto& group = table->types[typeIndex];
-    if (type != group.type) continue;
-    for (uint64_t index = 0; index < group.count; ++index) {
-      const auto& entry = group.entries[index];
-      // Check for min version only.
-      if (spvVersionForTargetEnv(env) >= entry.minVersion &&
-          nameLength == strlen(entry.name) &&
-          !strncmp(entry.name, name, nameLength)) {
-        return SPV_SUCCESS;
-      }
+  const spvtools::OperandDesc* entry = nullptr;
+  if (SPV_SUCCESS == spvtools::LookupOperand(type, name, nameLength, &entry)) {
+    // Check for min version only.
+    if (spvVersionForTargetEnv(env) >= entry->minVersion) {
+      return SPV_SUCCESS;
     }
   }
 
@@ -2045,8 +2390,7 @@
 bool Exists(const std::string& capability, spv_target_env env) {
   ScopedContext sc(env);
   return SPV_SUCCESS ==
-         spvCoreOperandTableNameLookup(env, sc.context->operand_table,
-                                       SPV_OPERAND_TYPE_CAPABILITY,
+         spvCoreOperandTableNameLookup(env, SPV_OPERAND_TYPE_CAPABILITY,
                                        capability.c_str(), capability.size());
 }
 
@@ -2114,6 +2458,28 @@
   }
 }
 
+TEST_P(ValidateCapabilityVulkan13, Capability) {
+  const std::string capability = Capability(GetParam());
+  if (Exists(capability, SPV_ENV_VULKAN_1_3)) {
+    const std::string test_code = MakeAssembly(GetParam());
+    CompileSuccessfully(test_code, SPV_ENV_VULKAN_1_3);
+    ASSERT_EQ(ExpectedResult(GetParam()),
+              ValidateInstructions(SPV_ENV_VULKAN_1_3))
+        << test_code;
+  }
+}
+
+TEST_P(ValidateCapabilityVulkan14, Capability) {
+  const std::string capability = Capability(GetParam());
+  if (Exists(capability, SPV_ENV_VULKAN_1_4)) {
+    const std::string test_code = MakeAssembly(GetParam());
+    CompileSuccessfully(test_code, SPV_ENV_VULKAN_1_4);
+    ASSERT_EQ(ExpectedResult(GetParam()),
+              ValidateInstructions(SPV_ENV_VULKAN_1_4))
+        << test_code;
+  }
+}
+
 TEST_P(ValidateCapabilityOpenGL40, Capability) {
   const std::string capability = Capability(GetParam());
   if (Exists(capability, SPV_ENV_OPENGL_4_0)) {
@@ -2253,6 +2619,26 @@
       HasSubstr("Capability DrawParameters is not allowed by Vulkan 1.0"));
 }
 
+using OpenCLFloat16CapabilityBase = spvtest::ValidateBase<spv_target_env>;
+
+TEST_P(OpenCLFloat16CapabilityBase, OpenCLFloat16Capability) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Float16
+OpMemoryModel Physical64 OpenCL
+%f16    = OpTypeFloat 16
+)";
+  CompileSuccessfully(spirv, GetParam());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(GetParam()));
+}
+
+INSTANTIATE_TEST_SUITE_P(OpenCLFloat16Capability, OpenCLFloat16CapabilityBase,
+                         ValuesIn(std::vector<spv_target_env>{
+                             SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_2_0,
+                             SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_2_2}));
+
 TEST_F(ValidateCapability, NonOpenCL12FullCapability) {
   const std::string spirv = R"(
 OpCapability Kernel
@@ -2828,7 +3214,8 @@
                               : "";
   return std::string("OpCapability ") + cap + extra_cap + R"(
 OpCapability Shader
-OpMemoryModel Logical )" + mem_model + R"(
+OpMemoryModel Logical )" +
+         mem_model + R"(
 OpEntryPoint Vertex %main "main"
 %void = OpTypeVoid
 %void_fn = OpTypeFunction %void
@@ -2977,6 +3364,169 @@
   EXPECT_THAT(getDiagnosticString(), Eq(""));
 }
 
+TEST_F(ValidateCapability,
+       CooperativeMatrixKHRShaderRequiresVulkanMemoryModel) {
+  const auto spirv = R"(
+OpCapability Shader
+OpCapability CooperativeMatrixKHR
+OpCapability Linkage
+OpExtension "SPV_KHR_cooperative_matrix"
+OpMemoryModel Logical GLSL450
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_6);
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "If the Shader and CooperativeMatrixKHR capabilities are declared, "
+          "the VulkanMemoryModel capability must also be declared"));
+}
+
+TEST_F(ValidateCapability, TileShadingQCOM) {
+  const auto spirv = R"(
+OpCapability Shader
+OpCapability TileShadingQCOM
+OpExtension "SPV_QCOM_tile_shading"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %func "main"
+)" + std::string(kVoidFVoid);
+
+  spv_target_env env = SPV_ENV_VULKAN_1_4;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions(env));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("The TileShadingQCOM capability must not be enabled "
+                        "in any stage other than compute or fragment"));
+}
+
+TEST_F(ValidateCapability, ColorAttachmentReadEXTRequireCapability) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability StorageImageReadWithoutFormat
+OpExtension "SPV_EXT_shader_tile_image"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+%void = OpTypeVoid
+%voidfn = OpTypeFunction %void
+%f32 = OpTypeFloat 32
+%img_type_f32 = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
+%ptr_image_f32 = OpTypePointer UniformConstant %img_type_f32
+%uniform_image_f32 = OpVariable %ptr_image_f32 UniformConstant
+%main = OpFunction %void None %voidfn
+%entry = OpLabel
+%img = OpLoad %img_type_f32 %uniform_image_f32
+%res1 = OpColorAttachmentReadEXT %f32 %img
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode ColorAttachmentReadEXT requires one of these "
+                        "capabilities: TileImageColorReadAccessEXT"));
+}
+
+TEST_F(ValidateCapability, DepthAttachmentReadEXTRequireCapability) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_EXT_shader_tile_image"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+%void = OpTypeVoid
+%voidfn = OpTypeFunction %void
+%f32 = OpTypeFloat 32
+%main = OpFunction %void None %voidfn
+%entry = OpLabel
+%res1 = OpDepthAttachmentReadEXT %f32
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode DepthAttachmentReadEXT requires one of these "
+                        "capabilities: TileImageDepthReadAccessEXT"));
+}
+
+TEST_F(ValidateCapability, StencilAttachmentReadEXTRequireCapability) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_EXT_shader_tile_image"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+%void = OpTypeVoid
+%voidfn = OpTypeFunction %void
+%u32 = OpTypeInt 32 0
+%main = OpFunction %void None %voidfn
+%entry = OpLabel
+%res1 = OpStencilAttachmentReadEXT %u32
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode StencilAttachmentReadEXT requires one of these "
+                        "capabilities: TileImageStencilReadAccessEXT"));
+}
+
+// https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/11860#issuecomment-4047435834
+TEST_F(ValidateCapability, InputAttachmentArrayDynamicIndexingVulkan12) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability InputAttachment
+               OpCapability InputAttachmentArrayDynamicIndexing
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %color %xs
+               OpExecutionMode %main OriginUpperLeft
+               OpDecorate %color Location 0
+               OpDecorate %xs Binding 0
+               OpDecorate %xs DescriptorSet 0
+               OpDecorate %xs InputAttachmentIndex 0
+               OpDecorate %index SpecId 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+      %color = OpVariable %_ptr_Output_v4float Output
+         %11 = OpTypeImage %float SubpassData 0 0 0 2 Unknown
+       %uint = OpTypeInt 32 0
+     %uint_4 = OpConstant %uint 4
+%_arr_11_uint_4 = OpTypeArray %11 %uint_4
+%_ptr_UniformConstant__arr_11_uint_4 = OpTypePointer UniformConstant %_arr_11_uint_4
+         %xs = OpVariable %_ptr_UniformConstant__arr_11_uint_4 UniformConstant
+        %int = OpTypeInt 32 1
+      %index = OpSpecConstant %int 3
+%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
+      %int_0 = OpConstant %int 0
+      %v2int = OpTypeVector %int 2
+         %24 = OpConstantComposite %v2int %int_0 %int_0
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+         %20 = OpAccessChain %_ptr_UniformConstant_11 %xs %index
+         %21 = OpLoad %11 %20
+         %25 = OpImageRead %v4float %21 %24
+               OpStore %color %25
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp
index ae2e45b..cf595ac 100644
--- a/test/val/val_cfg_test.cpp
+++ b/test/val/val_cfg_test.cpp
@@ -2600,20 +2600,20 @@
                OpExecutionMode %1 LocalSize 1 1 1
                OpSource HLSL 600
        %bool = OpTypeBool
-%_ptr_Function_bool = OpTypePointer Function %bool
+%_ptr_Workgroup_bool = OpTypePointer Workgroup %bool
+          %7 = OpVariable %_ptr_Workgroup_bool Workgroup
+          %8 = OpVariable %_ptr_Workgroup_bool Workgroup
        %void = OpTypeVoid
           %5 = OpTypeFunction %void
           %1 = OpFunction %void None %5
           %6 = OpLabel
-          %7 = OpVariable %_ptr_Function_bool Function
-          %8 = OpVariable %_ptr_Function_bool Function
           %9 = OpUndef %bool
                OpSelectionMerge %10 None
                OpBranchConditional %9 %11 %10
          %11 = OpLabel
                OpBranch %10
          %10 = OpLabel
-         %12 = OpPhi %_ptr_Function_bool %7 %6 %8 %11
+         %12 = OpPhi %_ptr_Workgroup_bool %7 %6 %8 %11
                OpReturn
                OpFunctionEnd
 )";
@@ -2625,7 +2625,7 @@
 TEST_F(ValidateCFG, VarPtrStgBufShaderWithPhiStgBufPtr) {
   const std::string text = R"(
                OpCapability Shader
-               OpCapability VariablePointersStorageBuffer
+               OpCapability VariablePointers
                OpExtension "SPV_KHR_variable_pointers"
                OpMemoryModel Logical GLSL450
                OpEntryPoint GLCompute %1 "main"
@@ -4316,7 +4316,7 @@
     OpMemoryModel Logical Simple
     OpEntryPoint Fragment %main "main"
     OpExecutionMode %main OriginUpperLeft
-    
+
     %void    = OpTypeVoid
     %void_fn = OpTypeFunction %void
 
@@ -5241,6 +5241,156 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateCFG, LifetimeGood) {
+  const std::string text = R"(
+           OpCapability Kernel
+           OpCapability Addresses
+           OpCapability Int64
+           OpCapability Int8
+           OpMemoryModel Physical64 OpenCL
+           OpEntryPoint Kernel %func "main"
+           OpExecutionMode %func ContractionOff
+           OpDecorate %24 Alignment 4
+   %uint = OpTypeInt 32 0
+   %void = OpTypeVoid
+      %5 = OpTypeFunction %void
+  %ulong = OpTypeInt 64 0
+ %uint_4 = OpConstant %uint 4
+%_arr_uint_4 = OpTypeArray %uint %uint_4
+%_ptr_arr_uint_4 = OpTypePointer Function %_arr_uint_4
+      %uchar = OpTypeInt 8 0
+%_ptr_uchar = OpTypePointer Function %uchar
+    %14 = OpTypeFunction %void %_ptr_uchar
+%_ptr_uint = OpTypePointer Function %uint
+  %bool = OpTypeBool
+%uint_n = OpConstantNull %uint
+%uint_1 = OpConstant %uint 1
+  %func = OpFunction %void None %5
+    %52 = OpLabel
+    %24 = OpVariable %_ptr_arr_uint_4 Function
+    %28 = OpSGreaterThan %bool %uint_1 %uint_n
+          OpBranchConditional %28 %53 %54
+    %53 = OpLabel
+    %29 = OpBitcast %_ptr_uchar %24
+
+          OpLifetimeStart %29 16
+    %30 = OpBitcast %_ptr_uint %24
+          OpStore %30 %uint_1 Aligned 4
+    %36 = OpBitcast %_ptr_uchar %24
+          OpLifetimeStop %36 16
+
+          OpBranch %54
+    %54 = OpLabel
+          OpReturn
+          OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, LifetimeStorageClass) {
+  const std::string text = R"(
+           OpCapability Kernel
+           OpCapability Addresses
+           OpCapability Int64
+           OpCapability Int8
+           OpMemoryModel Physical64 OpenCL
+           OpEntryPoint Kernel %func "main"
+           OpExecutionMode %func ContractionOff
+           OpDecorate %24 Alignment 4
+   %uint = OpTypeInt 32 0
+   %void = OpTypeVoid
+      %5 = OpTypeFunction %void
+  %ulong = OpTypeInt 64 0
+ %uint_4 = OpConstant %uint 4
+%_arr_uint_4 = OpTypeArray %uint %uint_4
+%_ptr_arr_uint_4 = OpTypePointer Function %_arr_uint_4
+      %uchar = OpTypeInt 8 0
+%_ptr_uchar = OpTypePointer CrossWorkgroup %uchar
+    %14 = OpTypeFunction %void %_ptr_uchar
+%_ptr_uint = OpTypePointer Function %uint
+  %bool = OpTypeBool
+%uint_n = OpConstantNull %uint
+%uint_1 = OpConstant %uint 1
+  %func = OpFunction %void None %5
+    %52 = OpLabel
+    %24 = OpVariable %_ptr_arr_uint_4 Function
+    %28 = OpSGreaterThan %bool %uint_1 %uint_n
+          OpBranchConditional %28 %53 %54
+    %53 = OpLabel
+    %29 = OpBitcast %_ptr_uchar %24
+
+          OpLifetimeStart %29 16
+    %30 = OpBitcast %_ptr_uint %24
+          OpStore %30 %uint_1 Aligned 4
+    %36 = OpBitcast %_ptr_uchar %24
+          OpLifetimeStop %36 16
+
+          OpBranch %54
+    %54 = OpLabel
+          OpReturn
+          OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpLifetimeStart pointer operand must be in the "
+                        "Function storage class"));
+}
+
+TEST_F(ValidateCFG, LifetimeNonPointer) {
+  const std::string text = R"(
+           OpCapability Kernel
+           OpCapability Addresses
+           OpCapability Int64
+           OpCapability Int8
+           OpMemoryModel Physical64 OpenCL
+           OpEntryPoint Kernel %func "main"
+           OpExecutionMode %func ContractionOff
+           OpDecorate %24 Alignment 4
+   %uint = OpTypeInt 32 0
+   %void = OpTypeVoid
+      %5 = OpTypeFunction %void
+  %ulong = OpTypeInt 64 0
+ %uint_4 = OpConstant %uint 4
+%_arr_uint_4 = OpTypeArray %uint %uint_4
+%_ptr_arr_uint_4 = OpTypePointer Function %_arr_uint_4
+      %uchar = OpTypeInt 8 0
+%_ptr_uchar = OpTypePointer Function %uchar
+    %14 = OpTypeFunction %void %_ptr_uchar
+%_ptr_uint = OpTypePointer Function %uint
+  %bool = OpTypeBool
+%uint_n = OpConstantNull %uint
+%uint_1 = OpConstant %uint 1
+  %func = OpFunction %void None %5
+    %52 = OpLabel
+    %24 = OpVariable %_ptr_arr_uint_4 Function
+    %28 = OpSGreaterThan %bool %uint_1 %uint_n
+          OpBranchConditional %28 %53 %54
+    %53 = OpLabel
+
+          OpLifetimeStart %28 16
+    %30 = OpBitcast %_ptr_uint %24
+          OpStore %30 %uint_1 Aligned 4
+    %36 = OpBitcast %_ptr_uchar %24
+          OpLifetimeStop %36 16
+
+          OpBranch %54
+    %54 = OpLabel
+          OpReturn
+          OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpLifetimeStart pointer operand type must be a OpTypePointer"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_composites_test.cpp b/test/val/val_composites_test.cpp
index 6e0d7c0..a6dcf83 100644
--- a/test/val/val_composites_test.cpp
+++ b/test/val/val_composites_test.cpp
@@ -33,7 +33,9 @@
 std::string GenerateShaderCode(
     const std::string& body,
     const std::string& capabilities_and_extensions = "",
-    const std::string& execution_model = "Fragment") {
+    const std::string& execution_model = "Fragment",
+    const std::string& extra_types = "",
+    const std::string& memory_model = "Logical GLSL450") {
   std::ostringstream ss;
   ss << R"(
 OpCapability Shader
@@ -41,7 +43,7 @@
 )";
 
   ss << capabilities_and_extensions;
-  ss << "OpMemoryModel Logical GLSL450\n";
+  ss << "OpMemoryModel " << memory_model << "\n";
   ss << "OpEntryPoint " << execution_model << " %main \"main\"\n";
   if (execution_model == "Fragment") {
     ss << "OpExecutionMode %main OriginUpperLeft\n";
@@ -93,7 +95,11 @@
 
 %ptr_big_struct = OpTypePointer Uniform %big_struct
 %var_big_struct = OpVariable %ptr_big_struct Uniform
+)";
 
+  ss << extra_types;
+
+  ss << R"(
 %main = OpFunction %void None %func
 %main_entry = OpLabel
 )";
@@ -315,6 +321,19 @@
               HasSubstr("Expected number of constituents to be at least 2"));
 }
 
+TEST_F(ValidateComposites, CompositeConstructLongVectorOnlyOneConstituent) {
+  const std::string body = R"(
+%val1 = OpCompositeConstruct %f32vec4 %f32vec4_0123
+)";
+  const std::string caps = R"(
+OpCapability LongVectorEXT
+OpExtension "SPV_EXT_long_vector"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, caps).c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
 TEST_F(ValidateComposites, CompositeConstructVectorWrongConsituent1) {
   const std::string body = R"(
 %val1 = OpCompositeConstruct %f32vec4 %f32 %f32vec2_12
@@ -523,6 +542,201 @@
                         "corresponding member type of Result Type struct"));
 }
 
+TEST_F(ValidateComposites, CompositeConstructReplicateVectorGood) {
+  const std::string body = R"(
+%val1 = OpCompositeConstructReplicateEXT %f32vec4 %f32_0
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body,
+                         "OpCapability ReplicatedCompositesEXT\nOpExtension "
+                         "\"SPV_EXT_replicated_composites\"\n")
+          .c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateComposites, CompositeConstructReplicateMatrixGood) {
+  const std::string body = R"(
+%val1 = OpCompositeConstructReplicateEXT %f32mat22 %f32vec2_01
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body,
+                         "OpCapability ReplicatedCompositesEXT\nOpExtension "
+                         "\"SPV_EXT_replicated_composites\"\n",
+                         "Fragment")
+          .c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateComposites, CompositeConstructReplicateArrayGood) {
+  const std::string body = R"(
+%val1 = OpCompositeConstructReplicateEXT %f32vec2arr3 %f32vec2_12
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body,
+                         "OpCapability ReplicatedCompositesEXT\nOpExtension "
+                         "\"SPV_EXT_replicated_composites\"\n")
+          .c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateComposites, CompositeConstructReplicateStructGood) {
+  const std::string copy_types = R"(
+%f32struct = OpTypeStruct %f32 %f32 %f32
+)";
+
+  const std::string body = R"(
+%val1 = OpCompositeConstructReplicateEXT %f32struct %f32_0
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body,
+                         "OpCapability ReplicatedCompositesEXT\nOpExtension "
+                         "\"SPV_EXT_replicated_composites\"\n",
+                         "Fragment", copy_types)
+          .c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateComposites, CompositeConstructReplicateCoopMatGood) {
+  const std::string extra_types = R"(
+%u32_8 = OpConstant %u32 8
+%u32_16 = OpConstant %u32 16
+%subgroup = OpConstant %u32 3
+%useA = OpConstant %u32 0
+%f32mat_nv = OpTypeCooperativeMatrixNV %f32 %subgroup %u32_8 %u32_8
+%f32mat_khr = OpTypeCooperativeMatrixKHR %f32 %subgroup %u32_16 %u32_16 %useA
+)";
+
+  const std::string body = R"(
+%val1 = OpCompositeConstructReplicateEXT %f32mat_nv %f32_0
+%val2 = OpCompositeConstructReplicateEXT %f32mat_khr %f32_0
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body,
+                         "OpCapability ReplicatedCompositesEXT\n"
+                         "OpCapability CooperativeMatrixNV\n"
+                         "OpCapability CooperativeMatrixKHR\n"
+                         "OpCapability VulkanMemoryModel\n"
+                         "OpCapability Float16\n"
+                         "OpExtension \"SPV_EXT_replicated_composites\"\n"
+                         "OpExtension \"SPV_NV_cooperative_matrix\"\n"
+                         "OpExtension \"SPV_KHR_cooperative_matrix\"\n"
+                         "OpExtension \"SPV_KHR_vulkan_memory_model\"\n",
+                         "Fragment", extra_types, "Logical Vulkan")
+          .c_str(),
+      SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+}
+
+TEST_F(ValidateComposites, CompositeConstructReplicateTensorGood) {
+  const std::string extra_types = R"(
+%arr = OpTypeArray %u32 %u32_1
+%c_arr = OpConstantNull %arr
+%tensor = OpTypeTensorARM %f32 %u32_1 %c_arr
+)";
+
+  const std::string body = R"(
+%val1 = OpCompositeConstructReplicateEXT %tensor %f32_0
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body,
+                         "OpCapability ReplicatedCompositesEXT\nOpCapability "
+                         "TensorsARM\nOpExtension "
+                         "\"SPV_EXT_replicated_composites\"\nOpExtension "
+                         "\"SPV_ARM_tensors\"\n",
+                         "Fragment", extra_types)
+          .c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateComposites, CompositeConstructReplicateCoopMatWrongOperand) {
+  const std::string extra_types = R"(
+%u32_8 = OpConstant %u32 8
+%subgroup = OpConstant %u32 3
+%f32mat_nv = OpTypeCooperativeMatrixNV %f32 %subgroup %u32_8 %u32_8
+)";
+
+  const std::string body = R"(
+%val1 = OpCompositeConstructReplicateEXT %f32mat_nv %u32_0
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body,
+                         "OpCapability ReplicatedCompositesEXT\n"
+                         "OpCapability CooperativeMatrixNV\n"
+                         "OpCapability Float16\n"
+                         "OpExtension \"SPV_EXT_replicated_composites\"\n"
+                         "OpExtension \"SPV_NV_cooperative_matrix\"\n",
+                         "Fragment", extra_types)
+          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected Value type to be equal to the result's element type"));
+}
+
+TEST_F(ValidateComposites, CompositeConstructReplicateTensorWrongOperand) {
+  const std::string extra_types = R"(
+%tensor = OpTypeTensorARM %f32
+)";
+
+  const std::string body = R"(
+%val1 = OpCompositeConstructReplicateEXT %tensor %u32_0
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body,
+                         "OpCapability ReplicatedCompositesEXT\nOpCapability "
+                         "TensorsARM\nOpExtension "
+                         "\"SPV_EXT_replicated_composites\"\nOpExtension "
+                         "\"SPV_ARM_tensors\"\n",
+                         "Fragment", extra_types)
+          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected Value type to be equal to the result's element type"));
+}
+
+TEST_F(ValidateComposites, CompositeConstructReplicateWrongOperandType) {
+  const std::string body = R"(
+%val1 = OpCompositeConstructReplicateEXT %f32vec4 %u32_0
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body,
+                         "OpCapability ReplicatedCompositesEXT\nOpExtension "
+                         "\"SPV_EXT_replicated_composites\"\n")
+          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected Value type to be equal to the result's element type"));
+}
+
+TEST_F(ValidateComposites, CompositeConstructReplicateNotComposite) {
+  const std::string body = R"(
+%val1 = OpCompositeConstructReplicateEXT %f32 %f32_0
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body,
+                         "OpCapability ReplicatedCompositesEXT\nOpExtension "
+                         "\"SPV_EXT_replicated_composites\"\n")
+          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Result Type to be a composite type"));
+}
+
 TEST_F(ValidateComposites, CopyObjectSuccess) {
   const std::string body = R"(
 %val1 = OpCopyObject %f32 %f32_0
@@ -1565,9 +1779,10 @@
 OpCapability Shader
 OpCapability Float16
 OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
 OpExtension "SPV_KHR_cooperative_matrix"
 OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical GLSL450
+OpMemoryModel Logical VulkanKHR
 OpEntryPoint GLCompute %main "main"
 %void = OpTypeVoid
 %func = OpTypeFunction %void
@@ -1592,8 +1807,8 @@
 OpReturn
 OpFunctionEnd)";
 
-  CompileSuccessfully(body.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -1606,9 +1821,10 @@
 OpCapability Shader
 OpCapability Float16
 OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
 OpExtension "SPV_KHR_cooperative_matrix"
 OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical GLSL450
+OpMemoryModel Logical VulkanKHR
 OpEntryPoint GLCompute %main "main"
 %void = OpTypeVoid
 %func = OpTypeFunction %void
@@ -1633,8 +1849,9 @@
 OpReturn
 OpFunctionEnd)";
 
-  CompileSuccessfully(body.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("Expected Constituent type to be equal to the component type"));
@@ -2102,6 +2319,1414 @@
               HasSubstr("OpCopyObject cannot have void result type"));
 }
 
+TEST_F(ValidateComposites, CoopVecConstantCompositePass) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability CooperativeVectorNV
+OpExtension "SPV_NV_cooperative_vector"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%bool = OpTypeBool
+%f16 = OpTypeFloat 16
+%f32 = OpTypeFloat 32
+%u32 = OpTypeInt 32 0
+
+%u32_16 = OpConstant %u32 16
+%useA = OpConstant %u32 0
+%subgroup = OpConstant %u32 3
+
+%f16vec = OpTypeCooperativeVectorNV %f16 %u32_16
+
+%f16_1 = OpConstant %f16 1
+
+%f16vec_1 = OpConstantComposite %f16vec %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateComposites, CoopVecConstantCompositeMismatchFail) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability CooperativeVectorNV
+OpExtension "SPV_NV_cooperative_vector"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%bool = OpTypeBool
+%f16 = OpTypeFloat 16
+%f32 = OpTypeFloat 32
+%u32 = OpTypeInt 32 0
+
+%u32_16 = OpConstant %u32 16
+%useA = OpConstant %u32 0
+%subgroup = OpConstant %u32 3
+
+%f16vec = OpTypeCooperativeVectorNV %f16 %u32_16
+
+%f32_1 = OpConstant %f32 1
+
+%f16vec_1 = OpConstantComposite %f16vec %f32_1
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpConstantComposite Constituent <id> count does not match "
+                "Result Type <id> '11[%11]'s vector component count"));
+}
+
+TEST_F(ValidateComposites, CoopVecCompositeConstructPass) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability CooperativeVectorNV
+OpExtension "SPV_NV_cooperative_vector"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%bool = OpTypeBool
+%f16 = OpTypeFloat 16
+%f32 = OpTypeFloat 32
+%u32 = OpTypeInt 32 0
+
+%u32_16 = OpConstant %u32 16
+%useA = OpConstant %u32 0
+%subgroup = OpConstant %u32 3
+
+%f16vec = OpTypeCooperativeVectorNV %f16 %u32_16
+
+%f16_1 = OpConstant %f16 1
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+
+%f16vec_1 = OpCompositeConstruct %f16vec %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1
+
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateComposites, CoopVecCompositeConstructMismatchFail) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability CooperativeVectorNV
+OpExtension "SPV_NV_cooperative_vector"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%bool = OpTypeBool
+%f16 = OpTypeFloat 16
+%f32 = OpTypeFloat 32
+%u32 = OpTypeInt 32 0
+
+%u32_16 = OpConstant %u32 16
+%useA = OpConstant %u32 0
+%subgroup = OpConstant %u32 3
+
+%f16vec = OpTypeCooperativeVectorNV %f16 %u32_16
+
+%f32_1 = OpConstant %f32 1
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+
+%f16vec_1 = OpCompositeConstruct %f16vec %f32_1
+
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Constituents to be scalars or vectors of the "
+                        "same type as Result Type components"));
+}
+
+TEST_F(ValidateComposites, CoopVecInsertExtractDynamicPass) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability CooperativeVectorNV
+OpExtension "SPV_NV_cooperative_vector"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%bool = OpTypeBool
+%f16 = OpTypeFloat 16
+%f32 = OpTypeFloat 32
+%u32 = OpTypeInt 32 0
+
+%u32_1 = OpConstant %u32 1
+%u32_16 = OpConstant %u32 16
+%useA = OpConstant %u32 0
+%subgroup = OpConstant %u32 3
+
+%f16vec = OpTypeCooperativeVectorNV %f16 %u32_16
+
+%f16_1 = OpConstant %f16 1
+%f16vec_1 = OpConstantComposite %f16vec %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1 %f16_1
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+
+%insert = OpVectorInsertDynamic %f16vec %f16vec_1 %f16_1 %u32_1
+%extract = OpVectorExtractDynamic %f16 %insert %u32_1
+
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvBitCastArrayQCOM1) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%u8int = OpTypeInt 8 0
+%uint = OpTypeInt 32 0
+%half = OpTypeFloat 16
+%uint_8 = OpConstant %uint 8
+%uint_16 = OpConstant %uint 16
+%_arr_u8int_uint_8 = OpTypeArray %u8int %uint_8
+%_arr_half_uint_16 = OpTypeArray %half %uint_16
+%_ptr_Function__arr_half_uint_16 = OpTypePointer Function %_arr_half_uint_16
+%main = OpFunction %void None %3
+%5 = OpLabel
+%hvec8A = OpVariable %_ptr_Function__arr_half_uint_16 Function
+%16 = OpLoad %_arr_half_uint_16 %hvec8A
+%18 = OpBitCastArrayQCOM %_arr_u8int_uint_8 %16
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode BitCastArrayQCOM requires the result element "
+                        "type be one of 32-bit OpTypeInt (signed/unsigned), "
+                        "32-bit OpTypeFloat and 16-bit OpTypeFloat"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvBitCastArrayQCOM2) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%u8int = OpTypeInt 8 0
+%uint = OpTypeInt 32 0
+;%half = OpTypeFloat 16
+%uint_8 = OpConstant %uint 8
+%uint_32 = OpConstant %uint 32
+%_arr_uint_uint_8 = OpTypeArray %uint %uint_8
+%_arr_u8int_uint_32 = OpTypeArray %u8int %uint_32
+%_ptr_Function__arr_u8int_uint_32 = OpTypePointer Function %_arr_u8int_uint_32
+%main = OpFunction %void None %3
+%5 = OpLabel
+%u8A = OpVariable %_ptr_Function__arr_u8int_uint_32 Function
+%16 = OpLoad %_arr_u8int_uint_32 %u8A
+%18 = OpBitCastArrayQCOM %_arr_uint_uint_8 %16
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode BitCastArrayQCOM requires the source element "
+                        "type be one of 32-bit OpTypeInt (signed/unsigned), "
+                        "32-bit OpTypeFloat and 16-bit OpTypeFloat"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvBitCastArrayQCOM3) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%half = OpTypeFloat 16
+%uint_16 = OpConstant %uint 16
+%_arr_uint_uint_16 = OpTypeArray %uint %uint_16
+%_arr_half_uint_16 = OpTypeArray %half %uint_16
+%_ptr_Function__arr_half_uint_16 = OpTypePointer Function %_arr_half_uint_16
+%main = OpFunction %void None %3
+%5 = OpLabel
+%hvec8A = OpVariable %_ptr_Function__arr_half_uint_16 Function
+%16 = OpLoad %_arr_half_uint_16 %hvec8A
+%18 = OpBitCastArrayQCOM %_arr_uint_uint_16 %16
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode BitCastArrayQCOM requires source and result "
+                        "types be compatible for conversion."));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvExtractSubArrayQCOM1) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%uint_0 = OpConstant %uint 0
+%uint_8 = OpConstant %uint 8
+%uint_64 = OpConstant %uint 64
+%_arr_uint_uint_64 = OpTypeArray %uint %uint_64
+%_arr_float_uint_8 = OpTypeArray %float %uint_8
+%_ptr_Function__arr_uint_uint_64 = OpTypePointer Function %_arr_uint_uint_64
+%main = OpFunction %void None %3
+%5 = OpLabel
+%uvec64Acc = OpVariable %_ptr_Function__arr_uint_uint_64 Function
+%83 = OpLoad %_arr_uint_uint_64 %uvec64Acc
+%86 = OpExtractSubArrayQCOM %_arr_float_uint_8 %83 %uint_0
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode ExtractSubArrayQCOM requires the input "
+                        "and result element types match."));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvConstructQCOMTy) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%u8int = OpTypeInt 8 0
+%float = OpTypeFloat 32
+%uint_0 = OpConstant %uint 0
+%uint_3 = OpConstant %uint 3
+%uint_8 = OpConstant %uint 8
+%uint_32 = OpConstant %uint 32
+%uint_64 = OpConstant %uint 64
+%_arr_u8int_uint_32 = OpTypeArray %u8int %uint_32
+%_ptr_Function__arr_u8int_uint_32 = OpTypePointer Function %_arr_u8int_uint_32
+%113 = OpTypeCooperativeMatrixKHR %float %uint_3 %uint_64 %uint_8 %uint_0
+%main = OpFunction %void None %3
+%5 = OpLabel
+%u8_32 = OpVariable %_ptr_Function__arr_u8int_uint_32 Function
+%116 = OpLoad %_arr_u8int_uint_32 %u8_32
+%118 = OpCompositeConstructCoopMatQCOM %113 %116
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode CompositeConstructCoopMatQCOM requires ether "
+                        "the input element type is equal to the result element "
+                        "type or it is the unsigned 32-bit integer."));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvConstructQCOMA1) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%u8int = OpTypeInt 8 0
+%float = OpTypeFloat 32
+%uint_0 = OpConstant %uint 0
+%uint_3 = OpConstant %uint 3
+%uint_8 = OpConstant %uint 8
+%uint_32 = OpConstant %uint 32
+%uint_64 = OpConstant %uint 64
+%_arr_uint_uint_8 = OpTypeArray %uint %uint_8
+%_ptr_Function__arr_uint_uint_8 = OpTypePointer Function %_arr_uint_uint_8
+%113 = OpTypeCooperativeMatrixKHR %uint %uint_3 %uint_64 %uint_8 %uint_0
+%main = OpFunction %void None %3
+%5 = OpLabel
+%u32_8 = OpVariable %_ptr_Function__arr_uint_uint_8 Function
+%116 = OpLoad %_arr_uint_uint_8 %u32_8
+%118 = OpCompositeConstructCoopMatQCOM %113 %116
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode CompositeConstructCoopMatQCOM requires "
+                        "the result element type is one of 8-bit OpTypeInt "
+                        "signed/unsigned, 16- or 32-bit OpTypeFloat when "
+                        "result coopmat's use is MatrixAKHR"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvConstructQCOMA2) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%u8int = OpTypeInt 8 0
+%float = OpTypeFloat 32
+%uint_0 = OpConstant %uint 0
+%uint_3 = OpConstant %uint 3
+%uint_8 = OpConstant %uint 8
+%uint_16 = OpConstant %uint 16
+%uint_32 = OpConstant %uint 32
+%uint_64 = OpConstant %uint 64
+%_arr_uint_uint_8 = OpTypeArray %uint %uint_8
+%_ptr_Function__arr_uint_uint_8 = OpTypePointer Function %_arr_uint_uint_8
+%113 = OpTypeCooperativeMatrixKHR %float %uint_3 %uint_64 %uint_16 %uint_0
+%main = OpFunction %void None %3
+%5 = OpLabel
+%u32_8 = OpVariable %_ptr_Function__arr_uint_uint_8 Function
+%116 = OpLoad %_arr_uint_uint_8 %u32_8
+%118 = OpCompositeConstructCoopMatQCOM %113 %116
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode CompositeConstructCoopMatQCOM requires "
+                        "the columns of the result coopmat have the bit "
+                        "length of 256 when result coopmat's use is "
+                        "MatrixAKHR"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvConstructQCOMA3) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%u8int = OpTypeInt 8 0
+%float = OpTypeFloat 32
+%uint_0 = OpConstant %uint 0
+%uint_3 = OpConstant %uint 3
+%uint_8 = OpConstant %uint 8
+%uint_16 = OpConstant %uint 16
+%uint_32 = OpConstant %uint 32
+%uint_64 = OpConstant %uint 64
+%_arr_u8int_uint_16 = OpTypeArray %u8int %uint_16
+%_ptr_Function__arr_u8int_uint_16 = OpTypePointer Function %_arr_u8int_uint_16
+%113 = OpTypeCooperativeMatrixKHR %u8int %uint_3 %uint_64 %uint_32 %uint_0
+%main = OpFunction %void None %3
+%5 = OpLabel
+%u8_16 = OpVariable %_ptr_Function__arr_u8int_uint_16 Function
+%116 = OpLoad %_arr_u8int_uint_16 %u8_16
+%118 = OpCompositeConstructCoopMatQCOM %113 %116
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode CompositeConstructCoopMatQCOM requires "
+                        "the source array length be 8 if its elt type is "
+                        "32-bit unsigned OpTypeInt and be the result's "
+                        "number of columns, otherwise when result coopmat's "
+                        "use is MatrixAKHR"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvConstructQCOMB1) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%u8int = OpTypeInt 8 0
+%float = OpTypeFloat 32
+%uint_1 = OpConstant %uint 1
+%uint_3 = OpConstant %uint 3
+%uint_8 = OpConstant %uint 8
+%uint_32 = OpConstant %uint 32
+%uint_64 = OpConstant %uint 64
+%_arr_uint_uint_8 = OpTypeArray %uint %uint_8
+%_ptr_Function__arr_uint_uint_8 = OpTypePointer Function %_arr_uint_uint_8
+%113 = OpTypeCooperativeMatrixKHR %uint %uint_3 %uint_8 %uint_64 %uint_1
+%main = OpFunction %void None %3
+%5 = OpLabel
+%u32_8 = OpVariable %_ptr_Function__arr_uint_uint_8 Function
+%116 = OpLoad %_arr_uint_uint_8 %u32_8
+%118 = OpCompositeConstructCoopMatQCOM %113 %116
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode CompositeConstructCoopMatQCOM requires "
+                        "the result element type is one of 8-bit OpTypeInt "
+                        "signed/unsigned, 16- or 32-bit OpTypeFloat when "
+                        "result coopmat's use is MatrixBKHR"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvConstructQCOMB2) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%u8int = OpTypeInt 8 0
+%float = OpTypeFloat 32
+%uint_1 = OpConstant %uint 1
+%uint_3 = OpConstant %uint 3
+%uint_8 = OpConstant %uint 8
+%uint_16 = OpConstant %uint 16
+%uint_32 = OpConstant %uint 32
+%uint_64 = OpConstant %uint 64
+%_arr_uint_uint_8 = OpTypeArray %uint %uint_8
+%_ptr_Function__arr_uint_uint_8 = OpTypePointer Function %_arr_uint_uint_8
+%113 = OpTypeCooperativeMatrixKHR %float %uint_3 %uint_16 %uint_64 %uint_1
+%main = OpFunction %void None %3
+%5 = OpLabel
+%u32_8 = OpVariable %_ptr_Function__arr_uint_uint_8 Function
+%116 = OpLoad %_arr_uint_uint_8 %u32_8
+%118 = OpCompositeConstructCoopMatQCOM %113 %116
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode CompositeConstructCoopMatQCOM requires "
+                        "the rows of the result operand have the bit "
+                        "length of 256 when result coopmat's use is "
+                        "MatrixBKHR"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvConstructQCOMB3) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%u8int = OpTypeInt 8 0
+%float = OpTypeFloat 32
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%uint_3 = OpConstant %uint 3
+%uint_8 = OpConstant %uint 8
+%uint_16 = OpConstant %uint 16
+%uint_32 = OpConstant %uint 32
+%uint_64 = OpConstant %uint 64
+%_arr_u8int_uint_16 = OpTypeArray %u8int %uint_16
+%_ptr_Function__arr_u8int_uint_16 = OpTypePointer Function %_arr_u8int_uint_16
+%113 = OpTypeCooperativeMatrixKHR %u8int %uint_3 %uint_32 %uint_64 %uint_1
+%main = OpFunction %void None %3
+%5 = OpLabel
+%u8_16 = OpVariable %_ptr_Function__arr_u8int_uint_16 Function
+%116 = OpLoad %_arr_u8int_uint_16 %u8_16
+%118 = OpCompositeConstructCoopMatQCOM %113 %116
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Opcode CompositeConstructCoopMatQCOM requires "
+                "the source array length be 8 if its elt type "
+                "is 32-bit unsigned OpTypeInt and be the result's "
+                "number of rows, otherwise when result coopmat's use is "
+                "MatrixBKHR"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvConstructQCOMAcc1) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%u8int = OpTypeInt 8 0
+%float = OpTypeFloat 32
+%uint_0 = OpConstant %uint 0
+%uint_2 = OpConstant %uint 2
+%uint_3 = OpConstant %uint 3
+%uint_8 = OpConstant %uint 8
+%uint_32 = OpConstant %uint 32
+%uint_64 = OpConstant %uint 64
+%_arr_u8int_uint_32 = OpTypeArray %u8int %uint_32
+%_ptr_Function__arr_u8int_uint_32 = OpTypePointer Function %_arr_u8int_uint_32
+%113 = OpTypeCooperativeMatrixKHR %u8int %uint_3 %uint_64 %uint_64 %uint_2
+%main = OpFunction %void None %3
+%5 = OpLabel
+%u8_32 = OpVariable %_ptr_Function__arr_u8int_uint_32 Function
+%116 = OpLoad %_arr_u8int_uint_32 %u8_32
+%118 = OpCompositeConstructCoopMatQCOM %113 %116
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode CompositeConstructCoopMatQCOM requires "
+                        "the result element type is one of 32-bit "
+                        "OpTypeInt signed/unsigned, 16- or 32-bit "
+                        "OpTypeFloat when result coopmat's use is "
+                        "MatrixAccumulatorKHR"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvConstructQCOMAcc2) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%half = OpTypeFloat 16
+%uint_2 = OpConstant %uint 2
+%uint_3 = OpConstant %uint 3
+%uint_16 = OpConstant %uint 16
+%uint_64 = OpConstant %uint 64
+%_arr_uint_uint_16 = OpTypeArray %uint %uint_16
+%_ptr_Function__arr_uint_uint_16 = OpTypePointer Function %_arr_uint_uint_16
+%113 = OpTypeCooperativeMatrixKHR %half %uint_3 %uint_64 %uint_64 %uint_2
+%main = OpFunction %void None %3
+%5 = OpLabel
+%u32_16 = OpVariable %_ptr_Function__arr_uint_uint_16 Function
+%116 = OpLoad %_arr_uint_uint_16 %u32_16
+%118 = OpCompositeConstructCoopMatQCOM %113 %116
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode CompositeConstructCoopMatQCOM requires "
+                        "the source array length be a half of the number "
+                        "of columns of the resulting cooerative matrix if "
+                        "the matrix's componet type is 16-bit OpTypeFloat "
+                        "and be equal to the number of columns, otherwise, "
+                        "when result coopmat's use is "
+                        "MatrixAccumulatorKHR"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMA1) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%uint_3 = OpConstant %uint 3
+%uint_8 = OpConstant %uint 8
+%uint_64 = OpConstant %uint 64
+%_arr_uint_uint_8 = OpTypeArray %uint %uint_8
+%_ptr_Function__arr_uint_uint_8 = OpTypePointer Function %_arr_uint_uint_8
+%113 = OpTypeCooperativeMatrixKHR %uint %uint_3 %uint_64 %uint_8 %uint_0
+%_ptr_Function_113 = OpTypePointer Function %113
+%main = OpFunction %void None %3
+%5 = OpLabel
+%matA_u32_64_8 = OpVariable %_ptr_Function_113 Function
+%249 = OpLoad %113 %matA_u32_64_8
+%251 = OpCompositeExtractCoopMatQCOM %_arr_uint_uint_8 %249
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode CompositeExtractCoopMatQCOM requires "
+                        "the source element type be one of 8-bit "
+                        "OpTypeInt signed/unsigned, 16- or 32-bit "
+                        "OpTypeFloat when source coopmat's use is "
+                        "MatrixAKHR"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMA2) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%uint_0 = OpConstant %uint 0
+%uint_3 = OpConstant %uint 3
+%uint_8 = OpConstant %uint 8
+%uint_16 = OpConstant %uint 16
+%uint_64 = OpConstant %uint 64
+%_arr_uint_uint_8 = OpTypeArray %uint %uint_8
+%_ptr_Function__arr_uint_uint_8 = OpTypePointer Function %_arr_uint_uint_8
+%113 = OpTypeCooperativeMatrixKHR %float %uint_3 %uint_64 %uint_16 %uint_0
+%_ptr_Function_113 = OpTypePointer Function %113
+%main = OpFunction %void None %3
+%5 = OpLabel
+%matA_u32_64_8 = OpVariable %_ptr_Function_113 Function
+%249 = OpLoad %113 %matA_u32_64_8
+%251 = OpCompositeExtractCoopMatQCOM %_arr_uint_uint_8 %249
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode CompositeExtractCoopMatQCOM "
+                        "requires the columns of the source "
+                        "coopmat have the bit length of 256 "
+                        "when source coopmat's use is "
+                        "MatrixAKHR"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMA3) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%u8int = OpTypeInt 8 0
+%uint_0 = OpConstant %uint 0
+%uint_3 = OpConstant %uint 3
+%uint_8 = OpConstant %uint 8
+%uint_32 = OpConstant %uint 32
+%uint_64 = OpConstant %uint 64
+%_arr_u8int_uint_64 = OpTypeArray %u8int %uint_64
+%_ptr_Function__arr_u8int_uint_64 = OpTypePointer Function %_arr_u8int_uint_64
+%113 = OpTypeCooperativeMatrixKHR %u8int %uint_3 %uint_64 %uint_32 %uint_0
+%_ptr_Function_113 = OpTypePointer Function %113
+%main = OpFunction %void None %3
+%5 = OpLabel
+%matA_u8_64_32 = OpVariable %_ptr_Function_113 Function
+%249 = OpLoad %113 %matA_u8_64_32
+%251 = OpCompositeExtractCoopMatQCOM %_arr_u8int_uint_64 %249
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode CompositeExtractCoopMatQCOM requires "
+                        "either the result element type be the same as "
+                        "the source cooperative matrix's component type "
+                        "and its length be the same as the number of "
+                        "columns of the matrix or the result element "
+                        "type be unsigned 32-bit OpTypeInt and the "
+                        "length be 8 when source coopmat's use is "
+                        "MatrixAKHR"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMA4) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%u8int = OpTypeInt 8 0
+%uint_0 = OpConstant %uint 0
+%uint_3 = OpConstant %uint 3
+%uint_8 = OpConstant %uint 8
+%uint_16 = OpConstant %uint 16
+%uint_32 = OpConstant %uint 32
+%uint_64 = OpConstant %uint 64
+%_arr_uint_uint_16 = OpTypeArray %uint %uint_16
+%_ptr_Function__arr_uint_uint_16 = OpTypePointer Function %_arr_uint_uint_16
+%113 = OpTypeCooperativeMatrixKHR %u8int %uint_3 %uint_64 %uint_32 %uint_0
+%_ptr_Function_113 = OpTypePointer Function %113
+%main = OpFunction %void None %3
+%5 = OpLabel
+%matA_u8_64_32 = OpVariable %_ptr_Function_113 Function
+%249 = OpLoad %113 %matA_u8_64_32
+%251 = OpCompositeExtractCoopMatQCOM %_arr_uint_uint_16 %249
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode CompositeExtractCoopMatQCOM requires "
+                        "either the result element type be the same as "
+                        "the source cooperative matrix's component type "
+                        "and its length be the same as the number of "
+                        "columns of the matrix or the result element "
+                        "type be unsigned 32-bit OpTypeInt and the "
+                        "length be 8 when source coopmat's use is "
+                        "MatrixAKHR"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMB1) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%uint_3 = OpConstant %uint 3
+%uint_8 = OpConstant %uint 8
+%uint_64 = OpConstant %uint 64
+%_arr_uint_uint_8 = OpTypeArray %uint %uint_8
+%_ptr_Function__arr_uint_uint_8 = OpTypePointer Function %_arr_uint_uint_8
+%113 = OpTypeCooperativeMatrixKHR %uint %uint_3 %uint_8 %uint_64 %uint_1
+%_ptr_Function_113 = OpTypePointer Function %113
+%main = OpFunction %void None %3
+%5 = OpLabel
+%matA_u32_64_8 = OpVariable %_ptr_Function_113 Function
+%249 = OpLoad %113 %matA_u32_64_8
+%251 = OpCompositeExtractCoopMatQCOM %_arr_uint_uint_8 %249
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode CompositeExtractCoopMatQCOM "
+                        "requires the source element type be "
+                        "one of 8-bit OpTypeInt signed/unsigned, "
+                        "16- or 32-bit OpTypeFloat when source "
+                        "coopmat's use is "
+                        "MatrixBKHR"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMB2) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%uint_3 = OpConstant %uint 3
+%uint_8 = OpConstant %uint 8
+%uint_16 = OpConstant %uint 16
+%uint_64 = OpConstant %uint 64
+%_arr_uint_uint_8 = OpTypeArray %uint %uint_8
+%_ptr_Function__arr_uint_uint_8 = OpTypePointer Function %_arr_uint_uint_8
+%113 = OpTypeCooperativeMatrixKHR %float %uint_3 %uint_16 %uint_64 %uint_1
+%_ptr_Function_113 = OpTypePointer Function %113
+%main = OpFunction %void None %3
+%5 = OpLabel
+%matA_u32_64_8 = OpVariable %_ptr_Function_113 Function
+%249 = OpLoad %113 %matA_u32_64_8
+%251 = OpCompositeExtractCoopMatQCOM %_arr_uint_uint_8 %249
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode CompositeExtractCoopMatQCOM requires "
+                        "the rows of the source coopmat have the bit "
+                        "length of 256 when source coopmat's use is "
+                        "MatrixBKHR"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMB3) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%u8int = OpTypeInt 8 0
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%uint_3 = OpConstant %uint 3
+%uint_8 = OpConstant %uint 8
+%uint_16 = OpConstant %uint 16
+%uint_32 = OpConstant %uint 32
+%uint_64 = OpConstant %uint 64
+%_arr_u8int_uint_16 = OpTypeArray %u8int %uint_16
+%_ptr_Function__arr_u8int_uint_16 = OpTypePointer Function %_arr_u8int_uint_16
+%113 = OpTypeCooperativeMatrixKHR %u8int %uint_3 %uint_32 %uint_64 %uint_1
+%_ptr_Function_113 = OpTypePointer Function %113
+%main = OpFunction %void None %3
+%5 = OpLabel
+%matA_u8_16_64 = OpVariable %_ptr_Function_113 Function
+%249 = OpLoad %113 %matA_u8_16_64
+%251 = OpCompositeExtractCoopMatQCOM %_arr_u8int_uint_16 %249
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode CompositeExtractCoopMatQCOM requires "
+                        "either the result element type be the same as "
+                        "the source cooperative matrix's component type "
+                        "and its length be the same as the number of "
+                        "rows of the matrix or the result element "
+                        "type be unsigned 32-bit OpTypeInt and the "
+                        "length be 8 when source coopmat's use is "
+                        "MatrixBKHR"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMB4) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%u8int = OpTypeInt 8 0
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%uint_3 = OpConstant %uint 3
+%uint_16 = OpConstant %uint 16
+%uint_32 = OpConstant %uint 32
+%uint_64 = OpConstant %uint 64
+%_arr_uint_uint_16 = OpTypeArray %uint %uint_16
+%_ptr_Function__arr_uint_uint_16 = OpTypePointer Function %_arr_uint_uint_16
+%113 = OpTypeCooperativeMatrixKHR %u8int %uint_3 %uint_32 %uint_64 %uint_1
+%_ptr_Function_113 = OpTypePointer Function %113
+%main = OpFunction %void None %3
+%5 = OpLabel
+%matA_u8_32_64 = OpVariable %_ptr_Function_113 Function
+%249 = OpLoad %113 %matA_u8_32_64
+%251 = OpCompositeExtractCoopMatQCOM %_arr_uint_uint_16 %249
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode CompositeExtractCoopMatQCOM requires "
+                        "either the result element type be the same as "
+                        "the source cooperative matrix's component type "
+                        "and its length be the same as the number of "
+                        "rows of the matrix or the result element "
+                        "type be unsigned 32-bit OpTypeInt and the "
+                        "length be 8 when source coopmat's use is "
+                        "MatrixBKHR"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMAcc1) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%u8int = OpTypeInt 8 0
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%uint_2 = OpConstant %uint 2
+%uint_3 = OpConstant %uint 3
+%uint_8 = OpConstant %uint 8
+%uint_64 = OpConstant %uint 64
+%_arr_uint_uint_8 = OpTypeArray %uint %uint_8
+%_ptr_Function__arr_uint_uint_8 = OpTypePointer Function %_arr_uint_uint_8
+%113 = OpTypeCooperativeMatrixKHR %u8int %uint_3 %uint_64 %uint_64 %uint_2
+%_ptr_Function_113 = OpTypePointer Function %113
+%main = OpFunction %void None %3
+%5 = OpLabel
+%matA_u32_64_64 = OpVariable %_ptr_Function_113 Function
+%249 = OpLoad %113 %matA_u32_64_64
+%251 = OpCompositeExtractCoopMatQCOM %_arr_uint_uint_8 %249
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode CompositeExtractCoopMatQCOM "
+                        "requires the source element type be "
+                        "one of 32-bit OpTypeInt signed/unsigned, "
+                        "16- or 32-bit OpTypeFloat when source "
+                        "coopmat's use is "
+                        "MatrixAccumulatorKHR"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMAcc2) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%uint_2 = OpConstant %uint 2
+%uint_3 = OpConstant %uint 3
+%uint_32 = OpConstant %uint 32
+%uint_64 = OpConstant %uint 64
+%_arr_uint_uint_32 = OpTypeArray %uint %uint_32
+%_ptr_Function__arr_uint_uint_32 = OpTypePointer Function %_arr_uint_uint_32
+%113 = OpTypeCooperativeMatrixKHR %uint %uint_3 %uint_64 %uint_64 %uint_2
+%_ptr_Function_113 = OpTypePointer Function %113
+%main = OpFunction %void None %3
+%5 = OpLabel
+%matA_u32_64_64 = OpVariable %_ptr_Function_113 Function
+%249 = OpLoad %113 %matA_u32_64_64
+%251 = OpCompositeExtractCoopMatQCOM %_arr_uint_uint_32 %249
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode CompositeExtractCoopMatQCOM requires "
+                        "either the result element type be the same as "
+                        "the source cooperative matrix's component type "
+                        "and its length be the same as the number of "
+                        "columns of the matrix or the result element "
+                        "type be unsigned 32-bit OpTypeInt and the length "
+                        "be the number of the columns of the matrix if "
+                        "its component type is 32-bit OpTypeFloat and be "
+                        "a half of the number of the columns of the matrix "
+                        "if its component type is 16-bit OpTypeFloat when "
+                        "source coopmat's use is "
+                        "MatrixAccumulatorKHR"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvExtractQCOMAcc3) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%half = OpTypeFloat 16
+%uint_0 = OpConstant %uint 0
+%uint_2 = OpConstant %uint 2
+%uint_3 = OpConstant %uint 3
+%uint_64 = OpConstant %uint 64
+%_arr_uint_uint_64 = OpTypeArray %uint %uint_64
+%_ptr_Function__arr_uint_uint_64 = OpTypePointer Function %_arr_uint_uint_64
+%113 = OpTypeCooperativeMatrixKHR %half %uint_3 %uint_64 %uint_64 %uint_2
+%_ptr_Function_113 = OpTypePointer Function %113
+%main = OpFunction %void None %3
+%5 = OpLabel
+%matA_f16_64_64 = OpVariable %_ptr_Function_113 Function
+%249 = OpLoad %113 %matA_f16_64_64
+%251 = OpCompositeExtractCoopMatQCOM %_arr_uint_uint_64 %249
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode CompositeExtractCoopMatQCOM requires "
+                        "either the result element type be the same as "
+                        "the source cooperative matrix's component type "
+                        "and its length be the same as the number of "
+                        "columns of the matrix or the result element "
+                        "type be unsigned 32-bit OpTypeInt and the length "
+                        "be the number of the columns of the matrix if "
+                        "its component type is 32-bit OpTypeFloat and be "
+                        "a half of the number of the columns of the matrix "
+                        "if its component type is 16-bit OpTypeFloat when "
+                        "source coopmat's use is "
+                        "MatrixAccumulatorKHR"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvExtractSubArrayQCOM2) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int8
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%u8int = OpTypeInt 8 0
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%uint_8 = OpConstant %uint 8
+%uint_64 = OpConstant %uint 64
+%_arr_u8int_uint_8 = OpTypeArray %u8int %uint_8
+%_arr_u8int_uint_64 = OpTypeArray %u8int %uint_64
+%_ptr_Function__arr_u8int_uint_64 = OpTypePointer Function %_arr_u8int_uint_64
+%main = OpFunction %void None %3
+%5 = OpLabel
+%uvec64Acc = OpVariable %_ptr_Function__arr_u8int_uint_64 Function
+%83 = OpLoad %_arr_u8int_uint_64 %uvec64Acc
+%86 = OpExtractSubArrayQCOM %_arr_u8int_uint_8 %83 %uint_0
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode ExtractSubArrayQCOM requires the element type "
+                        "be one of 32-bit OpTypeInt (signed/unsigned), 32-bit "
+                        "OpTypeFloat and 16-bit OpTypeFloat"));
+}
+
+TEST_F(ValidateComposites, CoopMatVecConvExtractSubArrayQCOM3) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int16
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%ui16 = OpTypeInt 16 0
+%uint = OpTypeInt 32 0
+%f16 = OpTypeFloat 16
+%u16int_0 = OpConstant %ui16 0
+%uint_8 = OpConstant %uint 8
+%uint_64 = OpConstant %uint 64
+%_arr_f16_uint_8 = OpTypeArray %f16 %uint_8
+%_arr_f16_uint_64 = OpTypeArray %f16 %uint_64
+%_ptr_Function__arr_f16_uint_64 = OpTypePointer Function %_arr_f16_uint_64
+%main = OpFunction %void None %3
+%5 = OpLabel
+%f16vec64Acc = OpVariable %_ptr_Function__arr_f16_uint_64 Function
+%83 = OpLoad %_arr_f16_uint_64 %f16vec64Acc
+%86 = OpExtractSubArrayQCOM %_arr_f16_uint_8 %83 %u16int_0
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Opcode ExtractSubArrayQCOM requires the type of the "
+                        "start index operand be 32-bit OpTypeInt"));
+}
+
+TEST_F(ValidateComposites, VectorShuffleNotVectorOp1) {
+  const std::string spirv = R"(
+OpCapability ClipDistance
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%67 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%v3float = OpTypeVector %float 3
+%_ptr_Function_float = OpTypePointer Function %float
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%3 = OpVariable %_ptr_Input_v4float Input
+%func = OpFunction %void None %67
+%label = OpLabel
+%43 = OpVariable %_ptr_Function_float Function
+%373 = OpLoad %v4float %3
+%shuffle = OpVectorShuffle %v2float %1 %373 538976288 538976288
+%422 = OpLoad %v3float %43
+OpReturnValue %422
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("The type of Vector 1 must be a vector type"));
+}
+
+TEST_F(ValidateComposites, VectorShuffleNotVectorOp2) {
+  const std::string spirv = R"(
+OpCapability ClipDistance
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%67 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%v3float = OpTypeVector %float 3
+%_ptr_Function_float = OpTypePointer Function %float
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%3 = OpVariable %_ptr_Input_v4float Input
+%func = OpFunction %void None %67
+%label = OpLabel
+%43 = OpVariable %_ptr_Function_float Function
+%373 = OpLoad %v4float %3
+%shuffle = OpVectorShuffle %v2float %373 %1 538976288 538976288
+%422 = OpLoad %v3float %43
+OpReturnValue %422
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("The type of Vector 2 must be a vector type"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_constants_test.cpp b/test/val/val_constants_test.cpp
index 4727877..d447b5c 100644
--- a/test/val/val_constants_test.cpp
+++ b/test/val/val_constants_test.cpp
@@ -37,28 +37,39 @@
 
 using ValidateConstant = spvtest::ValidateBase<bool>;
 
-#define kBasicTypes                             \
-  "%bool = OpTypeBool "                         \
-  "%uint = OpTypeInt 32 0 "                     \
-  "%uint2 = OpTypeVector %uint 2 "              \
-  "%float = OpTypeFloat 32 "                    \
-  "%_ptr_uint = OpTypePointer Workgroup %uint " \
-  "%uint_0 = OpConstantNull %uint "             \
-  "%uint2_0 = OpConstantNull %uint "            \
-  "%float_0 = OpConstantNull %float "           \
-  "%false = OpConstantFalse %bool "             \
-  "%true = OpConstantTrue %bool "               \
+#define kBasicTypes                                        \
+  "%bool = OpTypeBool "                                    \
+  "%uint = OpTypeInt 32 0 "                                \
+  "%uint64 = OpTypeInt 64 0 "                              \
+  "%uint2 = OpTypeVector %uint 2 "                         \
+  "%float = OpTypeFloat 32 "                               \
+  "%float64 = OpTypeFloat 64 "                             \
+  "%_ptr_uint = OpTypePointer Workgroup %uint "            \
+  "%uint_0 = OpConstantNull %uint "                        \
+  "%uint64_0 = OpConstantNull %uint64 "                    \
+  "%uint2_0 = OpConstantComposite %uint2 %uint_0 %uint_0 " \
+  "%float_0 = OpConstantNull %float "                      \
+  "%float64_0 = OpConstantNull %float64 "                  \
+  "%false = OpConstantFalse %bool "                        \
+  "%true = OpConstantTrue %bool "                          \
   "%null = OpConstantNull %_ptr_uint "
 
-#define kShaderPreamble    \
-  "OpCapability Shader\n"  \
-  "OpCapability Linkage\n" \
+#define kShaderPreamble                         \
+  "OpCapability Shader\n"                       \
+  "OpCapability Linkage\n"                      \
+  "OpCapability Int64\n"                        \
+  "OpCapability Float64\n"                      \
+  "OpCapability VariablePointers\n"             \
+  "OpExtension \"SPV_KHR_variable_pointers\"\n" \
   "OpMemoryModel Logical Simple\n"
 
-#define kKernelPreamble      \
-  "OpCapability Kernel\n"    \
-  "OpCapability Linkage\n"   \
-  "OpCapability Addresses\n" \
+#define kKernelPreamble           \
+  "OpCapability Kernel\n"         \
+  "OpCapability Linkage\n"        \
+  "OpCapability Int64\n"          \
+  "OpCapability Float64\n"        \
+  "OpCapability GenericPointer\n" \
+  "OpCapability Addresses\n"      \
   "OpMemoryModel Physical32 OpenCL\n"
 
 struct ConstantOpCase {
@@ -66,6 +77,7 @@
   std::string assembly;
   bool expect_success;
   std::string expect_err;
+  spv_result_t expected_result = SPV_ERROR_INVALID_ID;
 };
 
 using ValidateConstantOp = spvtest::ValidateBase<ConstantOpCase>;
@@ -78,7 +90,7 @@
     EXPECT_EQ(SPV_SUCCESS, result);
     EXPECT_THAT(getDiagnosticString(), Eq(""));
   } else {
-    EXPECT_EQ(SPV_ERROR_INVALID_ID, result);
+    EXPECT_EQ(GetParam().expected_result, result);
     EXPECT_THAT(getDiagnosticString(), HasSubstr(GetParam().expect_err));
   }
 }
@@ -90,9 +102,8 @@
 INSTANTIATE_TEST_SUITE_P(
     UniversalInShader, ValidateConstantOp,
     ValuesIn(std::vector<ConstantOpCase>{
-        // TODO(dneto): Conversions must change width.
-        GOOD_SHADER_10("%v = OpSpecConstantOp %uint SConvert %uint_0"),
-        GOOD_SHADER_10("%v = OpSpecConstantOp %float FConvert %float_0"),
+        GOOD_SHADER_10("%v = OpSpecConstantOp %uint SConvert %uint64_0"),
+        GOOD_SHADER_10("%v = OpSpecConstantOp %float FConvert %float64_0"),
         GOOD_SHADER_10("%v = OpSpecConstantOp %uint SNegate %uint_0"),
         GOOD_SHADER_10("%v = OpSpecConstantOp %uint Not %uint_0"),
         GOOD_SHADER_10("%v = OpSpecConstantOp %uint IAdd %uint_0 %uint_0"),
@@ -147,9 +158,8 @@
 INSTANTIATE_TEST_SUITE_P(
     UniversalInKernel, ValidateConstantOp,
     ValuesIn(std::vector<ConstantOpCase>{
-        // TODO(dneto): Conversions must change width.
-        GOOD_KERNEL_10("%v = OpSpecConstantOp %uint SConvert %uint_0"),
-        GOOD_KERNEL_10("%v = OpSpecConstantOp %float FConvert %float_0"),
+        GOOD_KERNEL_10("%v = OpSpecConstantOp %uint SConvert %uint64_0"),
+        GOOD_KERNEL_10("%v = OpSpecConstantOp %float FConvert %float64_0"),
         GOOD_KERNEL_10("%v = OpSpecConstantOp %uint SNegate %uint_0"),
         GOOD_KERNEL_10("%v = OpSpecConstantOp %uint Not %uint_0"),
         GOOD_KERNEL_10("%v = OpSpecConstantOp %uint IAdd %uint_0 %uint_0"),
@@ -204,70 +214,64 @@
 INSTANTIATE_TEST_SUITE_P(
     UConvert, ValidateConstantOp,
     ValuesIn(std::vector<ConstantOpCase>{
-        // TODO(dneto): Conversions must change width.
         {SPV_ENV_UNIVERSAL_1_0,
          kKernelPreamble kBasicTypes
-         "%v = OpSpecConstantOp %uint UConvert %uint_0",
+         "%v = OpSpecConstantOp %uint UConvert %uint64_0",
          true, ""},
         {SPV_ENV_UNIVERSAL_1_1,
          kKernelPreamble kBasicTypes
-         "%v = OpSpecConstantOp %uint UConvert %uint_0",
+         "%v = OpSpecConstantOp %uint UConvert %uint64_0",
          true, ""},
         {SPV_ENV_UNIVERSAL_1_3,
          kKernelPreamble kBasicTypes
-         "%v = OpSpecConstantOp %uint UConvert %uint_0",
+         "%v = OpSpecConstantOp %uint UConvert %uint64_0",
          true, ""},
         {SPV_ENV_UNIVERSAL_1_3,
          kKernelPreamble kBasicTypes
-         "%v = OpSpecConstantOp %uint UConvert %uint_0",
+         "%v = OpSpecConstantOp %uint UConvert %uint64_0",
          true, ""},
         {SPV_ENV_UNIVERSAL_1_4,
          kKernelPreamble kBasicTypes
-         "%v = OpSpecConstantOp %uint UConvert %uint_0",
+         "%v = OpSpecConstantOp %uint UConvert %uint64_0",
          true, ""},
         {SPV_ENV_UNIVERSAL_1_0,
          kShaderPreamble kBasicTypes
-         "%v = OpSpecConstantOp %uint UConvert %uint_0",
+         "%v = OpSpecConstantOp %uint UConvert %uint64_0",
          false,
          "Prior to SPIR-V 1.4, specialization constant operation "
          "UConvert requires Kernel capability"},
         {SPV_ENV_UNIVERSAL_1_1,
          kShaderPreamble kBasicTypes
-         "%v = OpSpecConstantOp %uint UConvert %uint_0",
+         "%v = OpSpecConstantOp %uint UConvert %uint64_0",
          false,
          "Prior to SPIR-V 1.4, specialization constant operation "
          "UConvert requires Kernel capability"},
         {SPV_ENV_UNIVERSAL_1_3,
          kShaderPreamble kBasicTypes
-         "%v = OpSpecConstantOp %uint UConvert %uint_0",
+         "%v = OpSpecConstantOp %uint UConvert %uint64_0",
          false,
          "Prior to SPIR-V 1.4, specialization constant operation "
          "UConvert requires Kernel capability"},
         {SPV_ENV_UNIVERSAL_1_3,
          kShaderPreamble kBasicTypes
-         "%v = OpSpecConstantOp %uint UConvert %uint_0",
+         "%v = OpSpecConstantOp %uint UConvert %uint64_0",
          false,
          "Prior to SPIR-V 1.4, specialization constant operation "
          "UConvert requires Kernel capability"},
         {SPV_ENV_UNIVERSAL_1_4,
          kShaderPreamble kBasicTypes
-         "%v = OpSpecConstantOp %uint UConvert %uint_0",
+         "%v = OpSpecConstantOp %uint UConvert %uint64_0",
          true, ""},
     }));
 
 INSTANTIATE_TEST_SUITE_P(
     KernelInKernel, ValidateConstantOp,
     ValuesIn(std::vector<ConstantOpCase>{
-        // TODO(dneto): Conversions must change width.
         GOOD_KERNEL_10("%v = OpSpecConstantOp %uint ConvertFToS %float_0"),
         GOOD_KERNEL_10("%v = OpSpecConstantOp %float ConvertSToF %uint_0"),
         GOOD_KERNEL_10("%v = OpSpecConstantOp %uint ConvertFToU %float_0"),
         GOOD_KERNEL_10("%v = OpSpecConstantOp %float ConvertUToF %uint_0"),
-        GOOD_KERNEL_10("%v = OpSpecConstantOp %uint UConvert %uint_0"),
-        GOOD_KERNEL_10(
-            "%v = OpSpecConstantOp %_ptr_uint GenericCastToPtr %null"),
-        GOOD_KERNEL_10(
-            "%v = OpSpecConstantOp %_ptr_uint PtrCastToGeneric %null"),
+        GOOD_KERNEL_10("%v = OpSpecConstantOp %uint UConvert %uint64_0"),
         GOOD_KERNEL_10("%v = OpSpecConstantOp %uint Bitcast %uint_0"),
         GOOD_KERNEL_10("%v = OpSpecConstantOp %float FNegate %float_0"),
         GOOD_KERNEL_10("%v = OpSpecConstantOp %float FAdd %float_0 %float_0"),
@@ -295,7 +299,8 @@
 INSTANTIATE_TEST_SUITE_P(
     KernelInShader, ValidateConstantOp,
     ValuesIn(std::vector<ConstantOpCase>{
-        // TODO(dneto): Conversions must change width.
+        // Don't need to test GenericCastToPtr or PtrCastToGeneric as a valid
+        // module can't have a Generic storage class with Shader capability
         BAD_SHADER_10("%v = OpSpecConstantOp %uint ConvertFToS %float_0",
                       "ConvertFToS"),
         BAD_SHADER_10("%v = OpSpecConstantOp %float ConvertSToF %uint_0",
@@ -304,10 +309,6 @@
                       "ConvertFToU"),
         BAD_SHADER_10("%v = OpSpecConstantOp %float ConvertUToF %uint_0",
                       "ConvertUToF"),
-        BAD_SHADER_10("%v = OpSpecConstantOp %_ptr_uint GenericCastToPtr %null",
-                      "GenericCastToPtr"),
-        BAD_SHADER_10("%v = OpSpecConstantOp %_ptr_uint PtrCastToGeneric %null",
-                      "PtrCastToGeneric"),
         BAD_SHADER_10("%v = OpSpecConstantOp %uint Bitcast %uint_0", "Bitcast"),
         BAD_SHADER_10("%v = OpSpecConstantOp %float FNegate %float_0",
                       "FNegate"),
@@ -343,11 +344,15 @@
         // SPV_AMD_gpu_shader_int16 should enable UConvert for OpSpecConstantOp
         // https://github.com/KhronosGroup/glslang/issues/848
         {SPV_ENV_UNIVERSAL_1_0,
-         "OpCapability Shader "
+         "OpCapability Shader\n"
+         "OpCapability Int64\n"
+         "OpCapability Float64\n"
+         "OpCapability VariablePointers\n"
          "OpCapability Linkage ; So we don't need to define a function\n"
-         "OpExtension \"SPV_AMD_gpu_shader_int16\" "
+         "OpExtension \"SPV_AMD_gpu_shader_int16\"\n"
+         "OpExtension \"SPV_KHR_variable_pointers\"\n"
          "OpMemoryModel Logical Simple " kBasicTypes
-         "%v = OpSpecConstantOp %uint UConvert %uint_0",
+         "%v = OpSpecConstantOp %uint UConvert %uint64_0",
          true, ""},
     }));
 
@@ -490,8 +495,666 @@
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
-          "OpConstantComposite Constituent <id> '13[%13]'s type "
-          "does not match Result Type <id> '3[%v2uint]'s vector element type"));
+          "OpConstantComposite Constituent <id> '17[%17]'s type "
+          "does not match Result Type <id> '4[%v2uint]'s vector element type"));
+}
+
+TEST_F(ValidateConstant, ConstantCompositeReplicateVectorGood) {
+  std::string spirv =
+      std::string(
+          "OpCapability Shader\nOpCapability Linkage\nOpCapability "
+          "Int64\nOpCapability Float64\nOpCapability "
+          "VariablePointers\nOpCapability ReplicatedCompositesEXT\nOpExtension "
+          "\"SPV_KHR_variable_pointers\"\nOpExtension "
+          "\"SPV_EXT_replicated_composites\"\nOpMemoryModel Logical Simple\n") +
+      kBasicTypes + R"(
+%int = OpTypeInt 32 1
+%v4int = OpTypeVector %int 4
+%int_0 = OpConstant %int 0
+%const_vector = OpConstantCompositeReplicateEXT %v4int %int_0
+)";
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateConstant, SpecConstantCompositeReplicateVectorGood) {
+  std::string spirv =
+      std::string(
+          "OpCapability Shader\nOpCapability Linkage\nOpCapability "
+          "Int64\nOpCapability Float64\nOpCapability "
+          "VariablePointers\nOpCapability ReplicatedCompositesEXT\nOpExtension "
+          "\"SPV_KHR_variable_pointers\"\nOpExtension "
+          "\"SPV_EXT_replicated_composites\"\nOpMemoryModel Logical Simple\n") +
+      kBasicTypes + R"(
+%int = OpTypeInt 32 1
+%v4int = OpTypeVector %int 4
+%int_0 = OpSpecConstant %int 0
+%const_vector = OpSpecConstantCompositeReplicateEXT %v4int %int_0
+)";
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateConstant, ConstantCompositeReplicateMatrixGood) {
+  std::string spirv =
+      std::string(
+          "OpCapability Shader\nOpCapability Linkage\nOpCapability "
+          "Int64\nOpCapability Float64\nOpCapability "
+          "VariablePointers\nOpCapability ReplicatedCompositesEXT\nOpExtension "
+          "\"SPV_KHR_variable_pointers\"\nOpExtension "
+          "\"SPV_EXT_replicated_composites\"\nOpMemoryModel Logical Simple\n") +
+      kBasicTypes + R"(
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%v_0 = OpConstantNull %v2float
+%const_matrix = OpConstantCompositeReplicateEXT %mat2x2 %v_0
+)";
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateConstant, ConstantCompositeReplicateArrayGood) {
+  std::string spirv =
+      std::string(
+          "OpCapability Shader\nOpCapability Linkage\nOpCapability "
+          "Int64\nOpCapability Float64\nOpCapability "
+          "VariablePointers\nOpCapability ReplicatedCompositesEXT\nOpExtension "
+          "\"SPV_KHR_variable_pointers\"\nOpExtension "
+          "\"SPV_EXT_replicated_composites\"\nOpMemoryModel Logical Simple\n") +
+      kBasicTypes + R"(
+%int = OpTypeInt 32 1
+%int_4 = OpConstant %int 4
+%arr = OpTypeArray %int %int_4
+%int_0 = OpConstantNull %int
+%const_arr = OpConstantCompositeReplicateEXT %arr %int_0
+)";
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateConstant, ConstantCompositeReplicateStructGood) {
+  std::string spirv =
+      std::string(
+          "OpCapability Shader\nOpCapability Linkage\nOpCapability "
+          "Int64\nOpCapability Float64\nOpCapability "
+          "VariablePointers\nOpCapability ReplicatedCompositesEXT\nOpExtension "
+          "\"SPV_KHR_variable_pointers\"\nOpExtension "
+          "\"SPV_EXT_replicated_composites\"\nOpMemoryModel Logical Simple\n") +
+      kBasicTypes + R"(
+%int = OpTypeInt 32 1
+%struct = OpTypeStruct %int %int %int
+%int_0 = OpConstantNull %int
+%const_struct = OpConstantCompositeReplicateEXT %struct %int_0
+)";
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateConstant, ConstantCompositeReplicateWrongOperandType) {
+  std::string spirv =
+      std::string(
+          "OpCapability Shader\nOpCapability Linkage\nOpCapability "
+          "Int64\nOpCapability Float64\nOpCapability "
+          "VariablePointers\nOpCapability ReplicatedCompositesEXT\nOpExtension "
+          "\"SPV_KHR_variable_pointers\"\nOpExtension "
+          "\"SPV_EXT_replicated_composites\"\nOpMemoryModel Logical Simple\n") +
+      kBasicTypes + R"(
+%int = OpTypeInt 32 1
+%v4int = OpTypeVector %int 4
+%const_vector = OpConstantCompositeReplicateEXT %v4int %float_0
+)";
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpConstantCompositeReplicateEXT Constituent <id> '11[%11]'s type "
+          "does not match Result Type <id> '17[%v4int]'s element type"));
+}
+
+TEST_F(ValidateConstant, ConstantCompositeReplicateSpecOperand) {
+  std::string spirv =
+      std::string(
+          "OpCapability Shader\nOpCapability Linkage\nOpCapability "
+          "Int64\nOpCapability Float64\nOpCapability "
+          "VariablePointers\nOpCapability ReplicatedCompositesEXT\nOpExtension "
+          "\"SPV_KHR_variable_pointers\"\nOpExtension "
+          "\"SPV_EXT_replicated_composites\"\nOpMemoryModel Logical Simple\n") +
+      kBasicTypes + R"(
+%int = OpTypeInt 32 1
+%int_4 = OpConstant %int 4
+%arr = OpTypeArray %int %int_4
+%int_0 = OpSpecConstant %int 0
+%const_arr = OpConstantCompositeReplicateEXT %arr %int_0
+)";
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpConstantCompositeReplicateEXT must not have spec "
+                        "constant operands: <id>"));
+}
+
+TEST_F(ValidateConstant, ConstantCompositeReplicateNotConstant) {
+  std::string spirv =
+      std::string(
+          "OpCapability Kernel\nOpCapability Linkage\nOpCapability "
+          "Int64\nOpCapability Float64\nOpCapability "
+          "VariablePointers\nOpCapability Addresses\nOpCapability "
+          "ReplicatedCompositesEXT\nOpExtension "
+          "\"SPV_KHR_variable_pointers\"\nOpExtension "
+          "\"SPV_EXT_replicated_composites\"\nOpMemoryModel Physical64 "
+          "OpenCL\n") +
+      kBasicTypes + R"(
+%uint_4 = OpConstant %uint 4
+%ptr = OpTypePointer Private %uint
+%var = OpVariable %ptr Private
+%arr = OpTypeArray %ptr %uint_4
+%const_arr = OpConstantCompositeReplicateEXT %arr %var
+)";
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpConstantCompositeReplicateEXT must only have "
+                        "constant or undef operands: <id>"));
+}
+
+TEST_F(ValidateConstant, ConstantCompositeSpecOperand) {
+  std::string spirv =
+      std::string(
+          "OpCapability Shader\nOpCapability Linkage\nOpCapability "
+          "Int64\nOpCapability Float64\nOpCapability "
+          "VariablePointers\nOpCapability ReplicatedCompositesEXT\nOpExtension "
+          "\"SPV_KHR_variable_pointers\"\nOpExtension "
+          "\"SPV_EXT_replicated_composites\"\nOpMemoryModel Logical Simple\n") +
+      kBasicTypes + R"(
+%int = OpTypeInt 32 1
+%int_4 = OpConstant %int 4
+%arr = OpTypeArray %int %int_4
+%int_0 = OpSpecConstant %int 0
+%const_arr = OpConstantComposite %arr %int_0 %int_0 %int_0 %int_0
+)";
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpConstantComposite must not have spec constant operands: <id>"));
+}
+
+TEST_F(ValidateConstant, ConstantCompositeNotConstant) {
+  std::string spirv =
+      std::string(
+          "OpCapability Kernel\nOpCapability Linkage\nOpCapability "
+          "Int64\nOpCapability Float64\nOpCapability "
+          "VariablePointers\nOpCapability Addresses\nOpExtension "
+          "\"SPV_KHR_variable_pointers\"\nOpMemoryModel Physical64 OpenCL\n") +
+      kBasicTypes + R"(
+%uint_4 = OpConstant %uint 4
+%ptr = OpTypePointer Private %uint
+%var = OpVariable %ptr Private
+%arr = OpTypeArray %ptr %uint_4
+%const_arr = OpConstantComposite %arr %var %var %var %var
+)";
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpConstantComposite must only have constant or undef "
+                        "operands: <id>"));
+}
+
+TEST_F(ValidateConstant, ConstantCompositeReplicateNotComposite) {
+  std::string spirv =
+      std::string(
+          "OpCapability Shader\nOpCapability Linkage\nOpCapability "
+          "Int64\nOpCapability Float64\nOpCapability "
+          "VariablePointers\nOpCapability ReplicatedCompositesEXT\nOpExtension "
+          "\"SPV_KHR_variable_pointers\"\nOpExtension "
+          "\"SPV_EXT_replicated_composites\"\nOpMemoryModel Logical Simple\n") +
+      kBasicTypes + R"(
+%int = OpTypeInt 32 1
+%int_0 = OpConstantNull %int
+%const_vector = OpConstantCompositeReplicateEXT %float %int_0
+)";
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpConstantCompositeReplicateEXT Result Type <id> '5[%float]' "
+                "is not a composite type"));
+}
+
+TEST_F(ValidateConstant, BadShaderOperandsQuantizeToF16) {
+  std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%uint = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%uint_1 = OpConstant %uint 1
+%float_1 = OpConstant %float 1
+%good = OpSpecConstantOp %float QuantizeToF16 %float_1
+%bad1 = OpSpecConstantOp %uint QuantizeToF16 %float_1
+%bad2 = OpSpecConstantOp %float QuantizeToF16 %uint_1
+)";
+
+  CompileSuccessfully(spirv);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected 32-bit float scalar or vector type as Result Type"));
+}
+
+TEST_F(ValidateConstant, BadCooperativeMatrixLength) {
+  std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpMemoryModel Logical VulkanKHR
+%uint = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%uint_1 = OpConstant %uint 1
+%uint_8 = OpConstant %uint 8
+%float_1 = OpConstant %float 1
+%subgroup = OpConstant %uint 3
+%use_A = OpConstant %uint 0
+%f16mat = OpTypeCooperativeMatrixKHR %float %subgroup %uint_8 %uint_8 %use_A
+
+%good = OpSpecConstantOp %uint CooperativeMatrixLengthKHR %f16mat
+%bad1 = OpSpecConstantOp %uint CooperativeMatrixLengthKHR %float_1
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("must be OpTypeCooperativeMatrixKHR"));
+}
+
+// Some check use SPV_ERROR_INVALID_DATA vs SPV_ERROR_INVALID_ID
+#define BAD_KERNEL_OPERANDS(STR, ERR)                                   \
+  {                                                                     \
+    SPV_ENV_UNIVERSAL_1_0, kKernelPreamble kBasicTypes STR, false, ERR, \
+        SPV_ERROR_INVALID_DATA                                          \
+  }
+
+#define BAD_KERNEL_OPERANDS_ID(STR, ERR) \
+  { SPV_ENV_UNIVERSAL_1_0, kKernelPreamble kBasicTypes STR, false, ERR, }
+
+// 2 of each, first has bad return type, second has bad operand
+INSTANTIATE_TEST_SUITE_P(
+    BadOperandsKernel, ValidateConstantOp,
+    ValuesIn(std::vector<ConstantOpCase>{
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float SConvert %uint_0",
+            "Expected int scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint SConvert %uint_0",
+            "Expected input to have different bit width from Result Type"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint FConvert %float_0",
+            "Expected float scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %float FConvert %float_0",
+                            "Expected component type of Value to be different "
+                            "from component type of Result Type"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float UConvert %uint_0",
+            "Expected unsigned int scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint UConvert %uint_0",
+            "Expected input to have different bit width from Result Type"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float ConvertFToS %float_0",
+            "Expected int scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint ConvertFToS %uint2_0",
+                            "Expected input to be float scalar or vector"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint ConvertSToF %uint_0",
+            "Expected float scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %float ConvertSToF %float_0",
+                            "Expected input to be int scalar or vector"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float ConvertFToU %float_0",
+            "Expected unsigned int scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint ConvertFToU %uint2_0",
+                            "Expected input to be float scalar or vector"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint ConvertUToF %uint_0",
+            "Expected float scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %float ConvertUToF %float_0",
+                            "Expected input to be int scalar or vector"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint ConvertUToF %uint_0",
+            "Expected float scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %float ConvertUToF %float_0",
+                            "Expected input to be int scalar or vector"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float UDiv %uint_0 %uint_0",
+            "Expected unsigned int scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint UDiv %uint_0 %float_0",
+            "Expected arithmetic operands to be of Result Type"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float UMod %uint_0 %uint_0",
+            "Expected unsigned int scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint UMod %uint_0 %float_0",
+            "Expected arithmetic operands to be of Result Type"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float ISub %uint_0 %uint_0",
+            "Expected int scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint ISub %uint_0 %float_0",
+                            "Expected int scalar or vector type as operand"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float IAdd %uint_0 %uint_0",
+            "Expected int scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint IAdd %uint_0 %float_0",
+                            "Expected int scalar or vector type as operand"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float IMul %uint_0 %uint_0",
+            "Expected int scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint IMul %uint_0 %float_0",
+                            "Expected int scalar or vector type as operand"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float SDiv %uint_0 %uint_0",
+            "Expected int scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint SDiv %uint_0 %float_0",
+                            "Expected int scalar or vector type as operand"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float SRem %uint_0 %uint_0",
+            "Expected int scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint SRem %uint_0 %float_0",
+                            "Expected int scalar or vector type as operand"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float SMod %uint_0 %uint_0",
+            "Expected int scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint SMod %uint_0 %float_0",
+                            "Expected int scalar or vector type as operand"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float SNegate %uint_0",
+            "Expected int scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint SNegate %float_0",
+                            "Expected int scalar or vector type as operand"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint FAdd %float_0 %float_0",
+            "Expected floating scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float FAdd %float_0 %uint_0",
+            "Expected arithmetic operands to be of Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint FSub %float_0 %float_0",
+            "Expected floating scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float FSub %float_0 %uint_0",
+            "Expected arithmetic operands to be of Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint FMul %float_0 %float_0",
+            "Expected floating scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float FMul %float_0 %uint_0",
+            "Expected arithmetic operands to be of Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint FDiv %float_0 %float_0",
+            "Expected floating scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float FDiv %float_0 %uint_0",
+            "Expected arithmetic operands to be of Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint FRem %float_0 %float_0",
+            "Expected floating scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float FRem %float_0 %uint_0",
+            "Expected arithmetic operands to be of Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint FMod %float_0 %float_0",
+            "Expected floating scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float FMod %float_0 %uint_0",
+            "Expected arithmetic operands to be of Result Type"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float ShiftRightLogical %uint_0 %uint_0",
+            "Expected int scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint ShiftRightLogical %uint_0 %float_0",
+            "Expected Shift to be int scalar or vector"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float ShiftRightArithmetic %uint_0 %uint_0",
+            "Expected int scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint ShiftRightArithmetic %uint_0 %float_0",
+            "Expected Shift to be int scalar or vector"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float ShiftLeftLogical %uint_0 %uint_0",
+            "Expected int scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint ShiftLeftLogical %uint_0 %float_0",
+            "Expected Shift to be int scalar or vector"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float BitwiseOr %uint_0 %uint_0",
+            "Expected int scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint BitwiseOr %uint_0 %float_0",
+            "Expected int scalar or vector as operand"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float BitwiseXor %uint_0 %uint_0",
+            "Expected int scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint BitwiseXor %uint_0 %float_0",
+            "Expected int scalar or vector as operand"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float BitwiseAnd %uint_0 %uint_0",
+            "Expected int scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint BitwiseAnd %uint_0 %float_0",
+            "Expected int scalar or vector as operand"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float Not %uint_0",
+            "Expected int scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint Not %float_0",
+                            "Expected int scalar or vector as operand"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float LogicalOr %true %false",
+            "Expected bool scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %bool LogicalOr %true %uint_0",
+            "Expected both operands to be of Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float LogicalAnd %true %false",
+            "Expected bool scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %bool LogicalAnd %true %uint_0",
+            "Expected both operands to be of Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float LogicalEqual %true %false",
+            "Expected bool scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %bool LogicalEqual %true %uint_0",
+            "Expected both operands to be of Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float LogicalNotEqual %true %false",
+            "Expected bool scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %bool LogicalNotEqual %uint_0 %false",
+            "Expected both operands to be of Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float LogicalNot %true",
+            "Expected bool scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %bool LogicalNot %uint_0",
+                            "Expected operand to be of Result Type"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float Select %true %uint_0 %uint_0",
+            "Expected both objects to be of Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint Select %uint_0 %uint_0 %uint_0",
+            "Expected bool scalar or vector type as condition"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float IEqual %uint_0 %uint_0",
+            "Expected bool scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %bool IEqual %uint_0 %float_0",
+            "Expected operands to be scalar or vector int"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float INotEqual %uint_0 %uint_0",
+            "Expected bool scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %bool INotEqual %uint_0 %float_0",
+            "Expected operands to be scalar or vector int"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float ULessThan %uint_0 %uint_0",
+            "Expected bool scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %bool ULessThan %uint_0 %float_0",
+            "Expected operands to be scalar or vector int"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float SLessThan %uint_0 %uint_0",
+            "Expected bool scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %bool SLessThan %uint_0 %float_0",
+            "Expected operands to be scalar or vector int"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float ULessThanEqual %uint_0 %uint_0",
+            "Expected bool scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %bool ULessThanEqual %uint_0 %float_0",
+            "Expected operands to be scalar or vector int"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float SLessThanEqual %uint_0 %uint_0",
+            "Expected bool scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %bool SLessThanEqual %uint_0 %float_0",
+            "Expected operands to be scalar or vector int"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float UGreaterThan %uint_0 %uint_0",
+            "Expected bool scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %bool UGreaterThan %uint_0 %float_0",
+            "Expected operands to be scalar or vector int"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float UGreaterThanEqual %uint_0 %uint_0",
+            "Expected bool scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %bool UGreaterThanEqual %uint_0 %float_0",
+            "Expected operands to be scalar or vector int"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float SGreaterThan %uint_0 %uint_0",
+            "Expected bool scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %bool SGreaterThan %uint_0 %float_0",
+            "Expected operands to be scalar or vector int"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float SGreaterThanEqual %uint_0 %uint_0",
+            "Expected bool scalar or vector type as Result Type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %bool SGreaterThanEqual %uint_0 %float_0",
+            "Expected operands to be scalar or vector int"),
+
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float GenericCastToPtr %null",
+            "Expected Result Type to be a pointer"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float PtrCastToGeneric %null",
+            "Expected Result Type to be a pointer"),
+
+        BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %bool Bitcast %uint_0",
+                            "Expected Result Type to be a pointer or int or "
+                            "float vector or scalar type"),
+        BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint Bitcast %true",
+                            "Expected input to be a pointer or int or float "
+                            "vector or scalar"),
+
+        BAD_KERNEL_OPERANDS_ID(
+            "%v = OpSpecConstantOp %float VectorShuffle %uint2_0 %uint2_0 1 3",
+            "The Result Type of OpVectorShuffle must be a vector type"),
+        BAD_KERNEL_OPERANDS_ID(
+            "%v = OpSpecConstantOp %uint2 VectorShuffle %uint2_0 %uint_0 1 3",
+            "The type of Vector 2 must be a vector type"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float CompositeExtract %uint2_0 1",
+            "Result type (OpTypeFloat) does not match the type that results "
+            "from indexing into the composite (OpTypeInt)"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint CompositeExtract %uint_0 1",
+            "Reached non-composite type while indexes still remain to be "
+            "traversed"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %float CompositeInsert %uint_0 %uint2_0 1",
+            "The Result Type must be the same as Composite type in "
+            "OpSpecConstantOp yielding Result Id 5"),
+        BAD_KERNEL_OPERANDS(
+            "%v = OpSpecConstantOp %uint2 CompositeInsert %uint_0 %uint_0 1",
+            "The Result Type must be the same as Composite type in "
+            "OpSpecConstantOp yielding Result Id 4"),
+
+        // TODO - Still need to add access chains
+        //
+        // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint AccessChain %null",
+        //                     "AccessChain"),
+        // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %_ptr_uint AccessChain
+        // %null %float_0",
+        //     "AccessChain"),
+        // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint InBoundsAccessChain
+        // %null",
+        //     "InBoundsAccessChain"),
+        // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %_ptr_uint
+        // InBoundsAccessChain %null %float_0",
+        //                     "InBoundsAccessChain"),
+        // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint PtrAccessChain %null
+        // %uint_0",
+        //     "PtrAccessChain"),
+        // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %_ptr_uint PtrAccessChain
+        // %float_0 %float_0",
+        //     "PtrAccessChain"),
+        // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint
+        // InBoundsPtrAccessChain %null %uint_0",
+        //     "InBoundsPtrAccessChain"),
+        // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %_ptr_uint
+        // InBoundsPtrAccessChain %float_0 %float_0",
+        //                     "InBoundsPtrAccessChain"),
+    }));
+
+TEST_F(ValidateConstant, ForwardConstantFunctionPointerINTEL) {
+  const std::string spirv = R"(
+OpCapability Linkage
+OpCapability Shader
+OpCapability FunctionPointersINTEL
+OpExtension "SPV_INTEL_function_pointers"
+OpMemoryModel Logical Simple
+%void = OpTypeVoid
+%functype = OpTypeFunction %void
+; UniformConstant avoids logical pointer validation conflicts in Function sc
+%ptr_fun = OpTypePointer UniformConstant %functype
+%const_ptr = OpConstantFunctionPointerINTEL %ptr_fun %target_func
+%target_func = OpFunction %void None %functype
+%lbl = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
 }  // namespace
diff --git a/test/val/val_conversion_test.cpp b/test/val/val_conversion_test.cpp
index 3869626..61593b2 100644
--- a/test/val/val_conversion_test.cpp
+++ b/test/val/val_conversion_test.cpp
@@ -39,6 +39,7 @@
   const std::string capabilities =
       R"(
 OpCapability Shader
+OpCapability Float16
 OpCapability Int64
 OpCapability Float64)";
 
@@ -54,6 +55,7 @@
 %func = OpTypeFunction %void
 %bool = OpTypeBool
 %f32 = OpTypeFloat 32
+%f16 = OpTypeFloat 16
 %u32 = OpTypeInt 32 0
 %s32 = OpTypeInt 32 1
 %f64 = OpTypeFloat 64
@@ -84,6 +86,8 @@
 %f32_3 = OpConstant %f32 3
 %f32_4 = OpConstant %f32 4
 
+%f16_1 = OpConstant %f16 1
+
 %s32_0 = OpConstant %s32 0
 %s32_1 = OpConstant %s32 1
 %s32_2 = OpConstant %s32 2
@@ -581,7 +585,7 @@
                         "Type: FConvert"));
 }
 
-TEST_F(ValidateConversion, FConvertSameBitWidth) {
+TEST_F(ValidateConversion, FConvertSameBitWidthNoEncoding) {
   const std::string body = R"(
 %val = OpFConvert %f32 %f32_1
 )";
@@ -589,8 +593,46 @@
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected input to have different bit width from "
-                        "Result Type: FConvert"));
+              HasSubstr("Expected component type of Value to be different from "
+                        "component type of Result Type: FConvert"));
+}
+
+TEST_F(ValidateConversion, ValidFConvertSameBitWidthDifferentEncoding) {
+  const std::string extensions = R"(
+OpCapability Float8EXT
+OpExtension "SPV_EXT_float8"
+)";
+  const std::string types = R"(
+%fp8e4m3 = OpTypeFloat 8 Float8E4M3EXT
+%fp8e5m2 = OpTypeFloat 8 Float8E5M2EXT
+%fp8e4m3_1 = OpConstant %fp8e4m3 1
+%fp8e5m2_1 = OpConstant %fp8e5m2 1
+)";
+  const std::string body = R"(
+%val1 = OpFConvert %fp8e4m3 %fp8e5m2_1
+%val2 = OpFConvert %fp8e5m2 %fp8e4m3_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateConversion, FConvertFloat16ToBFloat16) {
+  const std::string extensions = R"(
+OpCapability BFloat16TypeKHR
+OpExtension "SPV_KHR_bfloat16"
+)";
+
+  const std::string types = R"(
+%bf16 = OpTypeFloat 16 BFloat16KHR
+)";
+
+  const std::string body = R"(
+%val = OpFConvert %bf16 %f16_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extensions, "", types).c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
 TEST_F(ValidateConversion, QuantizeToF16Success) {
@@ -1196,9 +1238,10 @@
 OpCapability Float16
 OpCapability Int16
 OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
 OpExtension "SPV_KHR_cooperative_matrix"
 OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical GLSL450
+OpMemoryModel Logical VulkanKHR
 OpEntryPoint GLCompute %main "main"
 %void = OpTypeVoid
 %func = OpTypeFunction %void
@@ -1263,8 +1306,8 @@
 OpReturn
 OpFunctionEnd)";
 
-  CompileSuccessfully(body.c_str());
-  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
 TEST_F(ValidateConversion, CoopMatKHRConversionUseMismatchFail) {
@@ -1273,9 +1316,10 @@
 OpCapability Float16
 OpCapability Int16
 OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
 OpExtension "SPV_KHR_cooperative_matrix"
 OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical GLSL450
+OpMemoryModel Logical VulkanKHR
 OpEntryPoint GLCompute %main "main"
 %void = OpTypeVoid
 %func = OpTypeFunction %void
@@ -1308,8 +1352,9 @@
 OpReturn
 OpFunctionEnd)";
 
-  CompileSuccessfully(body.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("Expected Use of Matrix type and Result Type to be identical"));
@@ -1321,10 +1366,12 @@
 OpCapability Float16
 OpCapability Int16
 OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
 OpExtension "SPV_KHR_cooperative_matrix"
 OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical GLSL450
+OpMemoryModel Logical VulkanKHR
 OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 32 1 1
 %void = OpTypeVoid
 %func = OpTypeFunction %void
 %bool = OpTypeBool
@@ -1338,11 +1385,11 @@
 %u32_8 = OpConstant %u32 8
 %u32_4 = OpConstant %u32 4
 %subgroup = OpConstant %u32 3
-%device = OpConstant %u32 1
+%workgroup = OpConstant %u32 2
 %use_A = OpConstant %u32 0
 
 %f16mat = OpTypeCooperativeMatrixKHR %f16 %subgroup %u32_8 %u32_8 %use_A
-%f32mat = OpTypeCooperativeMatrixKHR %f32 %device %u32_8 %u32_8 %use_A
+%f32mat = OpTypeCooperativeMatrixKHR %f32 %workgroup %u32_8 %u32_8 %use_A
 
 %f16_1 = OpConstant %f16 1
 
@@ -1356,8 +1403,9 @@
 OpReturn
 OpFunctionEnd)";
 
-  CompileSuccessfully(body.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("Expected scopes of Matrix and Result Type to be identical"));
@@ -1468,9 +1516,12 @@
 
   CompileSuccessfully(GenerateKernelCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected input to be a pointer or int scalar if "
-                        "Result Type is pointer: Bitcast"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("In SPIR-V 1.4 or earlier (and without "
+                "SPV_KHR_physical_storage_buffer), expected input to be a "
+                "pointer or int scalar if "
+                "Result Type is pointer: Bitcast"));
 }
 
 TEST_F(ValidateConversion, BitcastPtrWrongInputTypeSPV1p5) {
@@ -1481,9 +1532,12 @@
   CompileSuccessfully(GenerateKernelCode(body).c_str(), SPV_ENV_UNIVERSAL_1_5);
   ASSERT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected input to be a pointer, int scalar or 32-bit "
-                        "int vector if Result Type is pointer: Bitcast"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("In SPIR-V 1.5 or later (or with "
+                "SPV_KHR_physical_storage_buffer), expected input to be a "
+                "pointer, int scalar or 32-bit "
+                "int vector if Result Type is pointer: Bitcast"));
 }
 
 TEST_F(ValidateConversion, BitcastPtrWrongInputTypePhysicalStorageBufferKHR) {
@@ -1496,9 +1550,12 @@
                          "\nOpExtension \"SPV_KHR_physical_storage_buffer\"")
           .c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected input to be a pointer, int scalar or 32-bit "
-                        "int vector if Result Type is pointer: Bitcast"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("In SPIR-V 1.5 or later (or with "
+                "SPV_KHR_physical_storage_buffer), expected input to be a "
+                "pointer, int scalar or 32-bit "
+                "int vector if Result Type is pointer: Bitcast"));
 }
 
 TEST_F(ValidateConversion, BitcastPtrWrongInputTypeIntVectorSPV1p5) {
@@ -1509,9 +1566,12 @@
   CompileSuccessfully(GenerateKernelCode(body).c_str(), SPV_ENV_UNIVERSAL_1_5);
   ASSERT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected input to be a pointer, int scalar or 32-bit "
-                        "int vector if Result Type is pointer: Bitcast"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("In SPIR-V 1.5 or later (or with "
+                "SPV_KHR_physical_storage_buffer), expected input to be a "
+                "pointer, int scalar or 32-bit "
+                "int vector if Result Type is pointer: Bitcast"));
 }
 
 TEST_F(ValidateConversion,
@@ -1525,9 +1585,12 @@
                          "\nOpExtension \"SPV_KHR_physical_storage_buffer\"")
           .c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected input to be a pointer, int scalar or 32-bit "
-                        "int vector if Result Type is pointer: Bitcast"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("In SPIR-V 1.5 or later (or with "
+                "SPV_KHR_physical_storage_buffer), expected input to be a "
+                "pointer, int scalar or 32-bit "
+                "int vector if Result Type is pointer: Bitcast"));
 }
 
 TEST_F(ValidateConversion, BitcastPtrWrongResultType) {
@@ -1538,7 +1601,9 @@
   CompileSuccessfully(GenerateKernelCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Pointer can only be converted to another pointer or "
+              HasSubstr("In SPIR-V 1.4 or earlier (and without "
+                        "SPV_KHR_physical_storage_buffer), pointer can only be "
+                        "converted to another pointer or "
                         "int scalar: Bitcast"));
 }
 
@@ -1550,9 +1615,13 @@
   CompileSuccessfully(GenerateKernelCode(body).c_str(), SPV_ENV_UNIVERSAL_1_5);
   ASSERT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Pointer can only be converted to another pointer, int "
-                        "scalar or 32-bit int vector: Bitcast"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "In SPIR-V 1.5 or later (or with SPV_KHR_physical_storage_buffer), "
+          "pointer can only be converted "
+          "to another pointer, int "
+          "scalar or 32-bit int vector: Bitcast"));
 }
 
 TEST_F(ValidateConversion, BitcastPtrWrongResultTypePhysicalStorageBufferKHR) {
@@ -1565,9 +1634,13 @@
                          "\nOpExtension \"SPV_KHR_physical_storage_buffer\"")
           .c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Pointer can only be converted to another pointer, int "
-                        "scalar or 32-bit int vector: Bitcast"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "In SPIR-V 1.5 or later (or with SPV_KHR_physical_storage_buffer), "
+          "pointer can only be converted "
+          "to another pointer, int "
+          "scalar or 32-bit int vector: Bitcast"));
 }
 
 TEST_F(ValidateConversion, BitcastPtrWrongResultTypeIntVectorSPV1p5) {
@@ -1578,9 +1651,13 @@
   CompileSuccessfully(GenerateKernelCode(body).c_str(), SPV_ENV_UNIVERSAL_1_5);
   ASSERT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Pointer can only be converted to another pointer, int "
-                        "scalar or 32-bit int vector: Bitcast"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "In SPIR-V 1.5 or later (or with SPV_KHR_physical_storage_buffer), "
+          "pointer can only be converted "
+          "to another pointer, int "
+          "scalar or 32-bit int vector: Bitcast"));
 }
 
 TEST_F(ValidateConversion,
@@ -1594,9 +1671,13 @@
                          "\nOpExtension \"SPV_KHR_physical_storage_buffer\"")
           .c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Pointer can only be converted to another pointer, int "
-                        "scalar or 32-bit int vector: Bitcast"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "In SPIR-V 1.5 or later (or with SPV_KHR_physical_storage_buffer), "
+          "pointer can only be converted "
+          "to another pointer, int "
+          "scalar or 32-bit int vector: Bitcast"));
 }
 
 TEST_F(ValidateConversion, BitcastDifferentTotalBitWidth) {
@@ -1943,25 +2024,30 @@
 TEST_F(ValidateConversion, BitcastUntypedPointerInput) {
   const std::string spirv = R"(
 OpCapability Shader
+OpCapability Int64
 OpCapability VariablePointers
 OpCapability UntypedPointersKHR
 OpCapability WorkgroupMemoryExplicitLayoutKHR
+OpCapability PhysicalStorageBufferAddresses
 OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
 OpExtension "SPV_KHR_variable_pointers"
 OpExtension "SPV_KHR_untyped_pointers"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main" %var
+OpExtension "SPV_KHR_physical_storage_buffer"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint GLCompute %main "main"
 OpDecorate %struct Block
 OpMemberDecorate %struct 0 Offset 0
 %void = OpTypeVoid
 %int = OpTypeInt 32 0
+%long = OpTypeInt 64 0
+%long_0 = OpConstant %long 0
 %struct = OpTypeStruct %int
-%ptr = OpTypeUntypedPointerKHR Workgroup
-%var = OpUntypedVariableKHR %ptr Workgroup %struct
+%ptr = OpTypeUntypedPointerKHR PhysicalStorageBuffer
 %void_fn = OpTypeFunction %void
 %main = OpFunction %void None %void_fn
 %entry = OpLabel
-%cast = OpBitcast %int %var
+%ptr_cast = OpBitcast %ptr %long_0
+%cast = OpBitcast %long %ptr_cast
 OpReturn
 OpFunctionEnd
 )";
@@ -1973,23 +2059,27 @@
 TEST_F(ValidateConversion, BitcastUntypedPointerOutput) {
   const std::string spirv = R"(
 OpCapability Shader
+OpCapability Int64
 OpCapability VariablePointers
 OpCapability UntypedPointersKHR
 OpCapability WorkgroupMemoryExplicitLayoutKHR
+OpCapability PhysicalStorageBufferAddresses
 OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
 OpExtension "SPV_KHR_variable_pointers"
 OpExtension "SPV_KHR_untyped_pointers"
-OpMemoryModel Logical GLSL450
+OpExtension "SPV_KHR_physical_storage_buffer"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
 OpEntryPoint GLCompute %main "main"
 %void = OpTypeVoid
 %int = OpTypeInt 32 0
 %int_0 = OpConstant %int 0
-%ptr = OpTypeUntypedPointerKHR Workgroup
-%var = OpUntypedVariableKHR %ptr Workgroup %int
+%long = OpTypeInt 64 0
+%long_0 = OpConstant %long 0
+%ptr = OpTypeUntypedPointerKHR PhysicalStorageBuffer
 %void_fn = OpTypeFunction %void
 %main = OpFunction %void None %void_fn
 %entry = OpLabel
-%cast = OpBitcast %ptr %int_0
+%cast = OpBitcast %ptr %long_0
 OpReturn
 OpFunctionEnd
 )";
@@ -2147,10 +2237,11 @@
 OpCapability Int16
 OpCapability CooperativeMatrixConversionsNV
 OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
 OpExtension "SPV_KHR_cooperative_matrix"
 OpExtension "SPV_NV_cooperative_matrix2"
 OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical GLSL450
+OpMemoryModel Logical VulkanKHR
 OpEntryPoint GLCompute %main "main"
 %void = OpTypeVoid
 %func = OpTypeFunction %void
@@ -2276,8 +2367,8 @@
 OpReturn
 OpFunctionEnd)";
 
-  CompileSuccessfully(body.c_str());
-  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
 TEST_F(ValidateConversion, CoopMat2TransposeShapeFail) {
@@ -2287,10 +2378,11 @@
 OpCapability Int16
 OpCapability CooperativeMatrixConversionsNV
 OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
 OpExtension "SPV_KHR_cooperative_matrix"
 OpExtension "SPV_NV_cooperative_matrix2"
 OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical GLSL450
+OpMemoryModel Logical VulkanKHR
 OpEntryPoint GLCompute %main "main"
 %void = OpTypeVoid
 %func = OpTypeFunction %void
@@ -2319,13 +2411,471 @@
 OpReturn
 OpFunctionEnd)";
 
-  CompileSuccessfully(body.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Expected rows of Matrix type and Result Type to be "
                         "swapped with columns"));
 }
 
+TEST_F(ValidateConversion, CoopVecConversionSuccess) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int16
+OpCapability CooperativeVectorNV
+OpCapability ReplicatedCompositesEXT
+OpExtension "SPV_NV_cooperative_vector"
+OpExtension "SPV_EXT_replicated_composites"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%bool = OpTypeBool
+%f16 = OpTypeFloat 16
+%f32 = OpTypeFloat 32
+%u16 = OpTypeInt 16 0
+%u32 = OpTypeInt 32 0
+%s16 = OpTypeInt 16 1
+%s32 = OpTypeInt 32 1
+
+%u32_8 = OpConstant %u32 8
+%use_A = OpConstant %u32 0
+%subgroup = OpConstant %u32 3
+
+%f16vec = OpTypeCooperativeVectorNV %f16 %u32_8
+%f32vec = OpTypeCooperativeVectorNV %f32 %u32_8
+%u16vec = OpTypeCooperativeVectorNV %u16 %u32_8
+%u32vec = OpTypeCooperativeVectorNV %u32 %u32_8
+%s16vec = OpTypeCooperativeVectorNV %s16 %u32_8
+%s32vec = OpTypeCooperativeVectorNV %s32 %u32_8
+
+%f16_1 = OpConstant %f16 1
+%f32_1 = OpConstant %f32 1
+%u16_1 = OpConstant %u16 1
+%u32_1 = OpConstant %u32 1
+%s16_1 = OpConstant %s16 1
+%s32_1 = OpConstant %s32 1
+
+%f16vec_1 = OpConstantCompositeReplicateEXT %f16vec %f16_1
+%f32vec_1 = OpConstantCompositeReplicateEXT %f32vec %f32_1
+%u16vec_1 = OpConstantCompositeReplicateEXT %u16vec %u16_1
+%u32vec_1 = OpConstantCompositeReplicateEXT %u32vec %u32_1
+%s16vec_1 = OpConstantCompositeReplicateEXT %s16vec %s16_1
+%s32vec_1 = OpConstantCompositeReplicateEXT %s32vec %s32_1
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+
+%val11 = OpConvertFToU %u16vec %f16vec_1
+%val12 = OpConvertFToU %u32vec %f16vec_1
+%val13 = OpConvertFToS %s16vec %f16vec_1
+%val14 = OpConvertFToS %s32vec %f16vec_1
+%val15 = OpFConvert %f32vec %f16vec_1
+
+%val21 = OpConvertFToU %u16vec %f32vec_1
+%val22 = OpConvertFToU %u32vec %f32vec_1
+%val23 = OpConvertFToS %s16vec %f32vec_1
+%val24 = OpConvertFToS %s32vec %f32vec_1
+%val25 = OpFConvert %f16vec %f32vec_1
+
+%val31 = OpConvertUToF %f16vec %u16vec_1
+%val32 = OpConvertUToF %f32vec %u16vec_1
+%val33 = OpUConvert %u32vec %u16vec_1
+%val34 = OpSConvert %s32vec %u16vec_1
+
+%val41 = OpConvertSToF %f16vec %s16vec_1
+%val42 = OpConvertSToF %f32vec %s16vec_1
+%val43 = OpUConvert %u32vec %s16vec_1
+%val44 = OpSConvert %s32vec %s16vec_1
+
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateConversion, CoopVecConversionDimMismatchFail) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int16
+OpCapability CooperativeVectorNV
+OpCapability ReplicatedCompositesEXT
+OpExtension "SPV_NV_cooperative_vector"
+OpExtension "SPV_EXT_replicated_composites"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%bool = OpTypeBool
+%f16 = OpTypeFloat 16
+%f32 = OpTypeFloat 32
+%u16 = OpTypeInt 16 0
+%u32 = OpTypeInt 32 0
+%s16 = OpTypeInt 16 1
+%s32 = OpTypeInt 32 1
+
+%u32_8 = OpConstant %u32 8
+%u32_4 = OpConstant %u32 4
+%subgroup = OpConstant %u32 3
+%use_A = OpConstant %u32 0
+%use_B = OpConstant %u32 1
+
+%f16vec = OpTypeCooperativeVectorNV %f16 %u32_8
+%f32vec = OpTypeCooperativeVectorNV %f32 %u32_4
+
+%f16_1 = OpConstant %f16 1
+
+%f16vec_1 = OpConstantCompositeReplicateEXT %f16vec %f16_1
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+
+%val1 = OpFConvert %f32vec %f16vec_1
+
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected number of components to be identical"));
+}
+
+TEST_F(ValidateConversion, ConvertPtrToUVectorWithMaskedGatherScatterSuccess) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Int64
+OpCapability MaskedGatherScatterINTEL
+OpExtension "SPV_INTEL_masked_gather_scatter"
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %main "main"
+%void = OpTypeVoid
+%u64 = OpTypeInt 64 0
+%u32 = OpTypeInt 32 0
+%ptr = OpTypePointer CrossWorkgroup %u32
+%v2ptr = OpTypeVector %ptr 2
+%v2u64 = OpTypeVector %u64 2
+%fn = OpTypeFunction %void %v2ptr
+%main = OpFunction %void None %fn
+%p = OpFunctionParameter %v2ptr
+%entry = OpLabel
+%result = OpConvertPtrToU %v2u64 %p
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv.c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateConversion, ConvertPtrToUVectorWithoutCapabilityFails) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Int64
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %main "main"
+%void = OpTypeVoid
+%u64 = OpTypeInt 64 0
+%u32 = OpTypeInt 32 0
+%ptr = OpTypePointer CrossWorkgroup %u32
+%v2ptr = OpTypeVector %ptr 2
+%v2u64 = OpTypeVector %u64 2
+%fn = OpTypeFunction %void %v2ptr
+%main = OpFunction %void None %fn
+%p = OpFunctionParameter %v2ptr
+%entry = OpLabel
+%result = OpConvertPtrToU %v2u64 %p
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("OpTypeVector Component Type"));
+}
+
+TEST_F(ValidateConversion, ConvertPtrToUVectorResultScalarInputFails) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Int64
+OpCapability MaskedGatherScatterINTEL
+OpExtension "SPV_INTEL_masked_gather_scatter"
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %main "main"
+%void = OpTypeVoid
+%u64 = OpTypeInt 64 0
+%u32 = OpTypeInt 32 0
+%ptr = OpTypePointer CrossWorkgroup %u32
+%v2u64 = OpTypeVector %u64 2
+%fn = OpTypeFunction %void %ptr
+%main = OpFunction %void None %fn
+%p = OpFunctionParameter %ptr
+%entry = OpLabel
+%result = OpConvertPtrToU %v2u64 %p
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected input to be a vector when Result Type is a "
+                        "vector: ConvertPtrToU"));
+}
+
+TEST_F(ValidateConversion, ConvertPtrToUVectorDimensionMismatchFails) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Int64
+OpCapability MaskedGatherScatterINTEL
+OpExtension "SPV_INTEL_masked_gather_scatter"
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %main "main"
+%void = OpTypeVoid
+%u64 = OpTypeInt 64 0
+%u32 = OpTypeInt 32 0
+%ptr = OpTypePointer CrossWorkgroup %u32
+%v2ptr = OpTypeVector %ptr 2
+%v4u64 = OpTypeVector %u64 4
+%fn = OpTypeFunction %void %v2ptr
+%main = OpFunction %void None %fn
+%p = OpFunctionParameter %v2ptr
+%entry = OpLabel
+%result = OpConvertPtrToU %v4u64 %p
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected input to have the same dimension as Result Type: "
+                "ConvertPtrToU"));
+}
+
+TEST_F(ValidateConversion, ConvertUToPtrVectorWithMaskedGatherScatterSuccess) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Int64
+OpCapability MaskedGatherScatterINTEL
+OpExtension "SPV_INTEL_masked_gather_scatter"
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %main "main"
+%void = OpTypeVoid
+%u64 = OpTypeInt 64 0
+%u32 = OpTypeInt 32 0
+%ptr = OpTypePointer CrossWorkgroup %u32
+%v2ptr = OpTypeVector %ptr 2
+%v2u64 = OpTypeVector %u64 2
+%fn = OpTypeFunction %void %v2u64
+%main = OpFunction %void None %fn
+%addrs = OpFunctionParameter %v2u64
+%entry = OpLabel
+%result = OpConvertUToPtr %v2ptr %addrs
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv.c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateConversion, ConvertUToPtrVectorWithoutCapabilityFails) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Int64
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %main "main"
+%void = OpTypeVoid
+%u64 = OpTypeInt 64 0
+%u32 = OpTypeInt 32 0
+%ptr = OpTypePointer CrossWorkgroup %u32
+%v2ptr = OpTypeVector %ptr 2
+%v2u64 = OpTypeVector %u64 2
+%fn = OpTypeFunction %void %v2u64
+%main = OpFunction %void None %fn
+%addrs = OpFunctionParameter %v2u64
+%entry = OpLabel
+%result = OpConvertUToPtr %v2ptr %addrs
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("OpTypeVector Component Type"));
+}
+
+TEST_F(ValidateConversion, ConvertUToPtrVectorResultScalarInputFails) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Int64
+OpCapability MaskedGatherScatterINTEL
+OpExtension "SPV_INTEL_masked_gather_scatter"
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %main "main"
+%void = OpTypeVoid
+%u64 = OpTypeInt 64 0
+%u32 = OpTypeInt 32 0
+%ptr = OpTypePointer CrossWorkgroup %u32
+%v2ptr = OpTypeVector %ptr 2
+%fn = OpTypeFunction %void %u64
+%main = OpFunction %void None %fn
+%addr = OpFunctionParameter %u64
+%entry = OpLabel
+%result = OpConvertUToPtr %v2ptr %addr
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected input to be a vector when Result Type is a "
+                        "vector: ConvertUToPtr"));
+}
+
+TEST_F(ValidateConversion, ConvertUToPtrVectorDimensionMismatchFails) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Int64
+OpCapability MaskedGatherScatterINTEL
+OpExtension "SPV_INTEL_masked_gather_scatter"
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %main "main"
+%void = OpTypeVoid
+%u64 = OpTypeInt 64 0
+%u32 = OpTypeInt 32 0
+%ptr = OpTypePointer CrossWorkgroup %u32
+%v4ptr = OpTypeVector %ptr 4
+%v2u64 = OpTypeVector %u64 2
+%fn = OpTypeFunction %void %v2u64
+%main = OpFunction %void None %fn
+%addrs = OpFunctionParameter %v2u64
+%entry = OpLabel
+%result = OpConvertUToPtr %v4ptr %addrs
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected input to have the same dimension as Result Type: "
+                "ConvertUToPtr"));
+}
+
+TEST_F(ValidateConversion, ConvertPtrToUVec4WithMaskedGatherScatterSuccess) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Int64
+OpCapability MaskedGatherScatterINTEL
+OpExtension "SPV_INTEL_masked_gather_scatter"
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %main "main"
+%void = OpTypeVoid
+%u64 = OpTypeInt 64 0
+%u32 = OpTypeInt 32 0
+%ptr = OpTypePointer CrossWorkgroup %u32
+%v4ptr = OpTypeVector %ptr 4
+%v4u64 = OpTypeVector %u64 4
+%fn = OpTypeFunction %void %v4ptr
+%main = OpFunction %void None %fn
+%p = OpFunctionParameter %v4ptr
+%entry = OpLabel
+%result = OpConvertPtrToU %v4u64 %p
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv.c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateConversion, ConvertUToPtrVec4WithMaskedGatherScatterSuccess) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Int64
+OpCapability MaskedGatherScatterINTEL
+OpExtension "SPV_INTEL_masked_gather_scatter"
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %main "main"
+%void = OpTypeVoid
+%u64 = OpTypeInt 64 0
+%u32 = OpTypeInt 32 0
+%ptr = OpTypePointer CrossWorkgroup %u32
+%v4ptr = OpTypeVector %ptr 4
+%v4u64 = OpTypeVector %u64 4
+%fn = OpTypeFunction %void %v4u64
+%main = OpFunction %void None %fn
+%addrs = OpFunctionParameter %v4u64
+%entry = OpLabel
+%result = OpConvertUToPtr %v4ptr %addrs
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv.c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+// Test that scalar conversion still works with capability present
+TEST_F(ValidateConversion, ConvertPtrToUScalarWithMaskedGatherScatterSuccess) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Int64
+OpCapability MaskedGatherScatterINTEL
+OpExtension "SPV_INTEL_masked_gather_scatter"
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %main "main"
+%void = OpTypeVoid
+%u64 = OpTypeInt 64 0
+%u32 = OpTypeInt 32 0
+%ptr = OpTypePointer CrossWorkgroup %u32
+%fn = OpTypeFunction %void %ptr
+%main = OpFunction %void None %fn
+%p = OpFunctionParameter %ptr
+%entry = OpLabel
+%result = OpConvertPtrToU %u64 %p
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv.c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateConversion, ConvertUToPtrScalarWithMaskedGatherScatterSuccess) {
+  const std::string spirv = R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Int64
+OpCapability MaskedGatherScatterINTEL
+OpExtension "SPV_INTEL_masked_gather_scatter"
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %main "main"
+%void = OpTypeVoid
+%u64 = OpTypeInt 64 0
+%u32 = OpTypeInt 32 0
+%ptr = OpTypePointer CrossWorkgroup %u32
+%fn = OpTypeFunction %void %u64
+%main = OpFunction %void None %fn
+%addr = OpFunctionParameter %u64
+%entry = OpLabel
+%result = OpConvertUToPtr %ptr %addr
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv.c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_data_test.cpp b/test/val/val_data_test.cpp
index 349e5e9..210e1d1 100644
--- a/test/val/val_data_test.cpp
+++ b/test/val/val_data_test.cpp
@@ -14,10 +14,12 @@
 
 // Validation tests for Data Rules.
 
+#include <sstream>
 #include <string>
 #include <utility>
 
 #include "gmock/gmock.h"
+#include "spirv/unified1/spirv.hpp11"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
 
@@ -71,6 +73,44 @@
      OpCapability Int64
      OpMemoryModel Logical GLSL450
 )";
+std::string header_with_bfloat16 = R"(
+     OpCapability Shader
+     OpCapability Linkage
+     OpCapability BFloat16TypeKHR
+     OpCapability BFloat16DotProductKHR
+     OpCapability BFloat16CooperativeMatrixKHR
+     OpExtension "SPV_KHR_bfloat16"
+     OpMemoryModel Logical GLSL450
+)";
+std::string header_with_float8 = R"(
+     OpCapability Shader
+     OpCapability Linkage
+     OpCapability Float8EXT
+     OpCapability Float8CooperativeMatrixEXT
+     OpExtension "SPV_EXT_float8"
+     OpMemoryModel Logical GLSL450
+)";
+std::string header_with_float8_and_bfloat16 = R"(
+     OpCapability Shader
+     OpCapability Linkage
+     OpCapability Float8EXT
+     OpCapability Float8CooperativeMatrixEXT
+     OpCapability BFloat16TypeKHR
+     OpExtension "SPV_EXT_float8"
+     OpExtension "SPV_KHR_bfloat16"
+     OpMemoryModel Logical GLSL450
+)";
+std::string header_with_float8_no_coop_matrix = R"(
+     OpCapability Shader
+     OpCapability Linkage
+     OpCapability Float8EXT
+     OpCapability CooperativeMatrixKHR
+     OpCapability VulkanMemoryModel
+     OpExtension "SPV_EXT_float8"
+     OpExtension "SPV_KHR_cooperative_matrix"
+     OpExtension "SPV_KHR_vulkan_memory_model"
+     OpMemoryModel Logical VulkanKHR
+)";
 std::string header_with_float16 = R"(
      OpCapability Shader
      OpCapability Linkage
@@ -90,9 +130,22 @@
      OpMemoryModel Logical GLSL450
 )";
 
+std::string header_with_float64_bfloat16 = R"(
+     OpCapability Shader
+     OpCapability Linkage
+     OpCapability Float64
+     OpCapability BFloat16TypeKHR
+     OpExtension "SPV_KHR_bfloat16"
+     OpMemoryModel Logical GLSL450
+)";
+
 std::string invalid_comp_error = "Illegal number of components";
-std::string missing_cap_error = "requires the Vector16 capability";
+std::string missing_cap_error =
+    "requires the Vector16 or LongVectorEXT capability";
 std::string missing_int8_cap_error = "requires the Int8 capability";
+std::string missing_int4_cap_error =
+    "Using a 4-bit integer type requires the Int4TypeINTEL "
+    "or ArbitraryPrecisionIntegersINTEL capability.";
 std::string missing_int16_cap_error =
     "requires the Int16 capability,"
     " or an extension that explicitly enables 16-bit integers.";
@@ -248,6 +301,33 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
 }
 
+TEST_F(ValidateData, int4_bad) {
+  std::string str = header + "%2 = OpTypeInt 4 0";
+  CompileSuccessfully(str.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_int4_cap_error));
+}
+
+TEST_F(ValidateData, int4_with_arbitrary_precision_good) {
+  std::string str =
+      HeaderWith(
+          "ArbitraryPrecisionIntegersINTEL "
+          "OpExtension \"SPV_INTEL_arbitrary_precision_integers\"") +
+      " %2 = OpTypeInt 4 0";
+  CompileSuccessfully(str.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
+}
+
+TEST_F(ValidateData, int4_signed_with_arbitrary_precision_good) {
+  std::string str =
+      HeaderWith(
+          "ArbitraryPrecisionIntegersINTEL "
+          "OpExtension \"SPV_INTEL_arbitrary_precision_integers\"") +
+      " %2 = OpTypeInt 4 1";
+  CompileSuccessfully(str.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
+}
+
 TEST_F(ValidateData, int16_good) {
   std::string str = header_with_int16 + "%2 = OpTypeInt 16 1";
   CompileSuccessfully(str.c_str());
@@ -334,12 +414,96 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateData, float8_good) {
+  std::string str = header_with_float8 +
+                    R"(%2 = OpTypeFloat 8 Float8E4M3EXT
+%3 = OpTypeFloat 8 Float8E5M2EXT
+)";
+  CompileSuccessfully(str.c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
+}
+
+TEST_F(ValidateData, bfloat16_good) {
+  std::string str = header_with_bfloat16 + "%2 = OpTypeFloat 16 BFloat16KHR";
+  CompileSuccessfully(str.c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateData, cooperative_matrix_bfloat16_good) {
+  std::string str = header_with_bfloat16 + R"(
+%u32 = OpTypeInt 32 0
+%u32_16 = OpConstant %u32 16
+%useA = OpConstant %u32 0
+%subgroup = OpConstant %u32 3
+%bf16 = OpTypeFloat 16 BFloat16KHR
+%bf16matA = OpTypeCooperativeMatrixKHR %bf16 %subgroup %u32_16 %u32_16 %useA
+)";
+  CompileSuccessfully(str.c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateData, cooperative_matrix_float8_good) {
+  std::string str = header_with_float8 + R"(
+%u32 = OpTypeInt 32 0
+%u32_16 = OpConstant %u32 16
+%useA = OpConstant %u32 0
+%subgroup = OpConstant %u32 3
+%fp8e4m3 = OpTypeFloat 8 Float8E4M3EXT
+%fp8e5m2 = OpTypeFloat 8 Float8E5M2EXT
+%fp8e4m3_matA = OpTypeCooperativeMatrixKHR %fp8e4m3 %subgroup %u32_16 %u32_16 %useA
+%fp8e5m2_matA = OpTypeCooperativeMatrixKHR %fp8e5m2 %subgroup %u32_16 %u32_16 %useA
+)";
+  CompileSuccessfully(str.c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateData, cooperative_matrix_float8_no_capability_bad) {
+  std::string str = header_with_float8_no_coop_matrix + R"(
+%u32 = OpTypeInt 32 0
+%u32_16 = OpConstant %u32 16
+%useA = OpConstant %u32 0
+%subgroup = OpConstant %u32 3
+%fp8e4m3 = OpTypeFloat 8 Float8E4M3EXT
+%fp8e5m2 = OpTypeFloat 8 Float8E5M2EXT
+%fp8e4m3_matA = OpTypeCooperativeMatrixKHR %fp8e4m3 %subgroup %u32_16 %u32_16 %useA
+%fp8e5m2_matA = OpTypeCooperativeMatrixKHR %fp8e5m2 %subgroup %u32_16 %u32_16 %useA
+)";
+  CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("require Float8CooperativeMatrixEXT be declared"));
+}
+
 TEST_F(ValidateData, float16_buffer_good) {
   std::string str = header_with_float16_buffer + "%2 = OpTypeFloat 16";
   CompileSuccessfully(str.c_str());
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateData, float32_with_encoding_number_bad) {
+  std::string str = header_with_bfloat16 + "%2 = OpTypeFloat 32 !9999";
+  const auto& err = CompileFailure(str.c_str());
+  EXPECT_THAT(err, HasSubstr("Invalid OpTypeFloat encoding"));
+}
+
+TEST_F(ValidateData, float32_with_encoding_enum_bad) {
+  std::string str = header_with_bfloat16 + "%2 = OpTypeFloat 32 BFloat16";
+  const auto& err = CompileFailure(str.c_str());
+  EXPECT_THAT(err, HasSubstr("Invalid FP encoding 'BFloat16'"));
+}
+
+TEST_F(ValidateData, float64_with_encoding_number_bad) {
+  std::string str = header_with_float64 + "%2 = OpTypeFloat 64 !9999";
+  const auto& err = CompileFailure(str.c_str());
+  EXPECT_THAT(err, HasSubstr("Invalid OpTypeFloat encoding"));
+}
+
+TEST_F(ValidateData, float64_with_encoding_enum_bad) {
+  std::string str = header_with_float64 + "%2 = OpTypeFloat 64 BFloat16";
+  const auto& err = CompileFailure(str.c_str());
+  EXPECT_THAT(err, HasSubstr("Invalid FP encoding 'BFloat16'"));
+}
+
 TEST_F(ValidateData, float16_bad) {
   std::string str = header + "%2 = OpTypeFloat 16";
   CompileSuccessfully(str.c_str());
@@ -347,19 +511,251 @@
   EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_float16_cap_error));
 }
 
-TEST_F(ValidateData, float64_good) {
-  std::string str = header_with_float64 + "%2 = OpTypeFloat 64";
+TEST_F(ValidateData, bfloat16_missing_cap_bad) {
+  std::string str = header + "%2 = OpTypeFloat 16 BFloat16KHR";
+  CompileSuccessfully(str.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("requires one of these capabilities: BFloat16TypeKHR"));
+}
+
+TEST_F(ValidateData, bfloat16_wrong_width_15_bad) {
+  std::stringstream ss;
+  ss << header_with_bfloat16 << "!"
+     << (4u << 16 | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 15 "
+     << static_cast<uint32_t>(spv::FPEncoding::BFloat16KHR);
+  CompileSuccessfully(ss.str().c_str());
+  OverwriteIdBound(100);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid number of bits (15) used for OpTypeFloat"))
+      << getDiagnosticString();
+}
+
+TEST_F(ValidateData, bfloat16_wrong_width_8_bad) {
+  std::stringstream ss;
+  ss << header_with_float8_and_bfloat16 << "!"
+     << (4u << 16 | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 8 "
+     << static_cast<uint32_t>(spv::FPEncoding::BFloat16KHR);
+  CompileSuccessfully(ss.str().c_str());
+  OverwriteIdBound(100);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Unsupported 8-bit floating point encoding"))
+      << getDiagnosticString();
+}
+
+TEST_F(ValidateData, bfloat16_too_many_operands_bad) {
+  std::stringstream ss;
+  ss << header_with_float8_and_bfloat16 << "!"
+     << (5u << 16 | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 16 "
+     << static_cast<uint32_t>(spv::FPEncoding::BFloat16KHR) << " 0";
+  CompileSuccessfully(ss.str().c_str());
+  OverwriteIdBound(100);
+  ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected no more operands after 4 words, but stated "
+                        "word count is 5"))
+      << getDiagnosticString();
+}
+
+TEST_F(ValidateData, float8_E4M3_missing_cap_bad) {
+  std::string str = header +
+                    R"(%2 = OpTypeFloat 8 Float8E4M3EXT
+)";
+  CompileSuccessfully(str.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("requires one of these capabilities: Float8EXT"));
+}
+
+TEST_F(ValidateData, float8_E5M2_missing_cap_bad) {
+  std::string str = header +
+                    R"(%2 = OpTypeFloat 8 Float8E5M2EXT
+)";
+  CompileSuccessfully(str.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("requires one of these capabilities: Float8EXT"));
+}
+
+TEST_F(ValidateData, float8_no_encoding_bad) {
+  std::string str = header_with_float8 + "%2 = OpTypeFloat 8";
+  CompileSuccessfully(str.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("8-bit floating point type requires an encoding"));
+}
+
+TEST_F(ValidateData, float8_bad_encoding) {
+  std::string str =
+      header_with_float8_and_bfloat16 + "%2 = OpTypeFloat 8 BFloat16KHR";
+  const auto& err = CompileFailure(str.c_str());
+  EXPECT_THAT(err, HasSubstr("Invalid bit width 8 for floating point encoding "
+                             "BFloat16KHR; expected 16"));
+}
+
+TEST_F(ValidateData, float8_E4M3_wrong_width_7_bad) {
+  std::stringstream ss;
+  ss << header_with_float8 << "!"
+     << ((4u << 16) | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 7 "
+     << static_cast<uint32_t>(spv::FPEncoding::Float8E4M3EXT);
+  CompileSuccessfully(ss.str().c_str());
+  OverwriteIdBound(100);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid number of bits (7) used for OpTypeFloat"))
+      << getDiagnosticString();
+}
+
+TEST_F(ValidateData, float8_E4M3_wrong_width_16_bad) {
+  std::stringstream ss;
+  ss << header_with_float8 << "!"
+     << ((4u << 16) | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 16 "
+     << static_cast<uint32_t>(spv::FPEncoding::Float8E4M3EXT);
+  CompileSuccessfully(ss.str().c_str());
+  OverwriteIdBound(100);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Unsupported 16-bit floating point encoding (4214)"))
+      << getDiagnosticString();
+}
+
+TEST_F(ValidateData, float8_E5M2_wrong_width_7_bad) {
+  std::stringstream ss;
+  ss << header_with_float8 << "!"
+     << ((4u << 16) | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 7 "
+     << static_cast<uint32_t>(spv::FPEncoding::Float8E5M2EXT);
+  CompileSuccessfully(ss.str().c_str());
+  OverwriteIdBound(100);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid number of bits (7) used for OpTypeFloat"))
+      << getDiagnosticString();
+}
+
+TEST_F(ValidateData, float8_E5M2_wrong_width_16_bad) {
+  std::stringstream ss;
+  ss << header_with_float8 << "!"
+     << ((4u << 16) | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 16 "
+     << static_cast<uint32_t>(spv::FPEncoding::Float8E5M2EXT);
+  CompileSuccessfully(ss.str().c_str());
+  OverwriteIdBound(100);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Unsupported 16-bit floating point encoding (4215)"))
+      << getDiagnosticString();
+}
+
+TEST_F(ValidateData, float8_e4m3_too_many_operands_bad) {
+  std::stringstream ss;
+  ss << header_with_float8 << "!"
+     << (5u << 16 | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 8 "
+     << static_cast<uint32_t>(spv::FPEncoding::Float8E4M3EXT) << " 0";
+  CompileSuccessfully(ss.str().c_str());
+  OverwriteIdBound(100);
+  ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected no more operands after 4 words, but stated "
+                        "word count is 5"))
+      << getDiagnosticString();
+}
+
+TEST_F(ValidateData, float8_e5m2_too_many_operands_bad) {
+  std::stringstream ss;
+  ss << header_with_float8 << "!"
+     << (5u << 16 | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 8 "
+     << static_cast<uint32_t>(spv::FPEncoding::Float8E5M2EXT) << " 0";
+  CompileSuccessfully(ss.str().c_str());
+  OverwriteIdBound(100);
+  ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected no more operands after 4 words, but stated "
+                        "word count is 5"))
+      << getDiagnosticString();
+}
+
+TEST_F(ValidateData, dot_bfloat16_bad) {
+  std::string str = R"(
+               OpCapability Shader
+               OpCapability BFloat16TypeKHR
+               OpExtension "SPV_KHR_bfloat16"
+          %1 = OpExtInstImport "GLSL.std.450"
+                OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 450
+               OpName %main "main"
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %bfloat16 = OpTypeFloat 16 BFloat16KHR
+%_ptr_Function_bfloat16 = OpTypePointer Function %bfloat16
+    %v2bfloat16 = OpTypeVector %bfloat16 2
+%_ptr_Function_v2bfloat16 = OpTypePointer Function %v2bfloat16
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %v1 = OpVariable %_ptr_Function_v2bfloat16 Function
+         %v2 = OpVariable %_ptr_Function_v2bfloat16 Function
+         %12 = OpLoad %v2bfloat16 %v1
+         %14 = OpLoad %v2bfloat16 %v2
+         %15 = OpDot %bfloat16 %12 %14
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("requires BFloat16DotProductKHR be declared."));
+}
+
+TEST_F(ValidateData, bfloat16_without_float16_capability_good) {
+  std::string str = header_with_bfloat16 + "%2 = OpTypeFloat 16 BFloat16KHR";
   CompileSuccessfully(str.c_str());
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(ValidateData, float64_bad) {
+TEST_F(ValidateData, float64_good) {
+  std::string str = header_with_float64 + "%2 = OpTypeFloat 64";
+
+  CompileSuccessfully(str.c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateData, float64_missing_cap_bad) {
   std::string str = header + "%2 = OpTypeFloat 64";
   CompileSuccessfully(str.c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_float64_cap_error));
 }
 
+TEST_F(ValidateData, float32_encoding_param_bad) {
+  std::stringstream ss;
+  ss << header_with_bfloat16 << "!"
+     << (4u << 16 | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 32 "
+     << static_cast<uint32_t>(spv::FPEncoding::BFloat16KHR);
+  CompileSuccessfully(ss.str().c_str());
+  OverwriteIdBound(100);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("32-bit floating point type must not have encoding parameter"))
+      << getDiagnosticString();
+}
+
+TEST_F(ValidateData, float64_encoding_param_bad) {
+  std::stringstream ss;
+  ss << header_with_float64_bfloat16 << "!"
+     << (4u << 16 | static_cast<uint32_t>(spv::Op::OpTypeFloat)) << " 99 64 "
+     << static_cast<uint32_t>(spv::FPEncoding::BFloat16KHR);
+  CompileSuccessfully(ss.str().c_str());
+  OverwriteIdBound(100);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("64-bit floating point type must not have encoding parameter"))
+      << getDiagnosticString();
+}
+
 // Number of bits in a float may be only one of: {16,32,64}
 TEST_F(ValidateData, float_invalid_num_bits) {
   std::string str = header + "%2 = OpTypeFloat 48";
@@ -627,7 +1023,8 @@
         OpExtension "SPV_KHR_variable_pointers"
         OpExtension "SPV_KHR_16bit_storage"
         OpMemoryModel Logical GLSL450
-        OpDecorate %_ FPRoundingMode )" + mode + R"(
+        OpDecorate %_ FPRoundingMode )" +
+                        mode + R"(
         %half = OpTypeFloat 16
         %float = OpTypeFloat 32
         %float_1_25 = OpConstant %float 1.25
@@ -723,6 +1120,8 @@
               OpMemberDecorate %struct_t 0 Offset 0
               OpMemberDecorate %struct_t 1 Offset 4
               OpDecorate %struct_t Block
+              OpDecorate %2 DescriptorSet 0
+              OpDecorate %2 Binding 0
      %uint_t = OpTypeInt 32 0
    %array_t = OpTypeRuntimeArray %uint_t
   %struct_t = OpTypeStruct %uint_t %array_t
diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp
index b5738d7..a87d0af 100644
--- a/test/val/val_decoration_test.cpp
+++ b/test/val/val_decoration_test.cpp
@@ -1,4 +1,6 @@
 // Copyright (c) 2017 Google Inc.
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -19,6 +21,7 @@
 
 #include "gmock/gmock.h"
 #include "source/val/decoration.h"
+#include "spirv-tools/libspirv.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_code_generator.h"
 #include "test/val/val_fixtures.h"
@@ -1517,9 +1520,9 @@
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("must be explicitly laid out with ArrayStride decorations"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("must be explicitly laid out with ArrayStride or "
+                        "ArrayStrideIdEXT decorations"));
 }
 
 TEST_F(ValidateDecorations, BufferBlockMissingArrayStrideBad) {
@@ -1549,9 +1552,9 @@
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("must be explicitly laid out with ArrayStride decorations"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("must be explicitly laid out with ArrayStride or "
+                        "ArrayStrideIdEXT decorations"));
 }
 
 TEST_F(ValidateDecorations, BlockNestedStructMissingArrayStrideBad) {
@@ -1586,9 +1589,9 @@
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("must be explicitly laid out with ArrayStride decorations"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("must be explicitly laid out with ArrayStride or "
+                        "ArrayStrideIdEXT decorations"));
 }
 
 TEST_F(ValidateDecorations, BufferBlockNestedStructMissingArrayStrideBad) {
@@ -1623,9 +1626,9 @@
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("must be explicitly laid out with ArrayStride decorations"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("must be explicitly laid out with ArrayStride or "
+                        "ArrayStrideIdEXT decorations"));
 }
 
 TEST_F(ValidateDecorations, BlockMissingMatrixStrideBad) {
@@ -1861,6 +1864,8 @@
                OpMemberDecorate %Output 6 MatrixStride 16
                OpMemberDecorate %Output 7 Offset 128
                OpDecorate %Output Block
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
        %void = OpTypeVoid
           %3 = OpTypeFunction %void
       %float = OpTypeFloat 32
@@ -2074,6 +2079,68 @@
                         "another Block or BufferBlock."));
 }
 
+TEST_F(ValidateDecorations, BlockCannotAppearWithinBlockArray) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main"
+OpMemberDecorate %outer 0 Offset 0
+OpMemberDecorate %outer 1 Offset 4
+OpMemberDecorate %outer 2 Offset 20
+OpDecorate %outer Block
+OpMemberDecorate %inner 0 Offset 0
+OpDecorate %inner Block
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%inner = OpTypeStruct %int
+%array = OpTypeArray %inner %int_4
+%outer = OpTypeStruct %int %array %int
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("rules: A Block or BufferBlock cannot be nested within "
+                        "another Block or BufferBlock."));
+}
+
+TEST_F(ValidateDecorations, BlockCannotAppearWithinBlockMultiArray) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main"
+OpMemberDecorate %outer 0 Offset 0
+OpMemberDecorate %outer 1 Offset 4
+OpDecorate %outer Block
+OpMemberDecorate %inner 0 Offset 0
+OpDecorate %inner Block
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%inner = OpTypeStruct %int
+%array1 = OpTypeArray %inner %int_4
+%array2 = OpTypeArray %array1 %int_4
+%outer = OpTypeStruct %int %array2
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("rules: A Block or BufferBlock cannot be nested within "
+                        "another Block or BufferBlock."));
+}
+
 TEST_F(ValidateDecorations, BlockLayoutForbidsTightScalarVec3PackingBad) {
   // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
   std::string spirv = R"(
@@ -2485,6 +2552,10 @@
              OpMemberDecorate %SSBO16 0 Offset 0
              OpDecorate %SSBO32 BufferBlock
              OpDecorate %SSBO16 BufferBlock
+             OpDecorate %varSSBO32 DescriptorSet 0
+             OpDecorate %varSSBO32 Binding 0
+             OpDecorate %varSSBO16 DescriptorSet 0
+             OpDecorate %varSSBO16 Binding 1
      %void = OpTypeVoid
     %voidf = OpTypeFunction %void
       %u32 = OpTypeInt 32 0
@@ -3412,6 +3483,7 @@
             OpExecutionMode %1 OriginUpperLeft
 
             OpDecorate %struct Block
+            OpMemberDecorate %struct 0 Offset 0
             OpDecorate %var Binding 0
 
     %void = OpTypeVoid
@@ -3454,6 +3526,7 @@
             OpExecutionMode %1 OriginUpperLeft
 
             OpDecorate %struct Block
+            OpMemberDecorate %struct 0 Offset 0
             OpDecorate %var DescriptorSet 0
 
     %void = OpTypeVoid
@@ -3496,6 +3569,7 @@
             OpExecutionMode %1 OriginUpperLeft
 
             OpDecorate %struct Block
+            OpMemberDecorate %struct 0 Offset 0
             OpDecorate %var Binding 0
 
     %void = OpTypeVoid
@@ -3534,35 +3608,6 @@
                 "decorations specified"));
 }
 
-TEST_F(ValidateDecorations,
-       VulkanStorageBufferMissingDescriptorAndBindingUnusedGood) {
-  std::string spirv = R"(
-            OpCapability Shader
-            OpExtension "SPV_KHR_storage_buffer_storage_class"
-            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
-  %struct = OpTypeStruct %float
-     %ptr = OpTypePointer StorageBuffer %struct
-     %var = OpVariable %ptr StorageBuffer
-
-       %1 = OpFunction %void None %voidfn
-   %label = OpLabel
-            OpReturn
-            OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
-  EXPECT_EQ(SPV_SUCCESS,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
-}
-
 TEST_F(ValidateDecorations, UniformMissingDescriptorSetGood) {
   std::string spirv = R"(
             OpCapability Shader
@@ -3839,6 +3884,8 @@
                OpMemberDecorate %Output 6 MatrixStride 16
                OpMemberDecorate %Output 7 Offset 96
                OpDecorate %Output BufferBlock
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
        %void = OpTypeVoid
           %3 = OpTypeFunction %void
       %float = OpTypeFloat 32
@@ -3972,6 +4019,8 @@
                OpMemberDecorate %Output 6 MatrixStride 16
                OpMemberDecorate %Output 7 Offset 128
                OpDecorate %Output Block
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
        %void = OpTypeVoid
           %3 = OpTypeFunction %void
       %float = OpTypeFloat 32
@@ -4040,6 +4089,8 @@
                OpMemberDecorate %Output 6 MatrixStride 16
                OpMemberDecorate %Output 7 Offset 128
                OpDecorate %Output Block
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
        %void = OpTypeVoid
           %3 = OpTypeFunction %void
       %float = OpTypeFloat 32
@@ -5253,6 +5304,71 @@
       HasSubstr("In Vulkan, the FPRoundingMode mode must only by RTE or RTZ."));
 }
 
+TEST_F(ValidateDecorations, KernelFPRoundingModeGood) {
+  std::string spirv = R"(
+               OpCapability Addresses
+               OpCapability Kernel
+               OpCapability Float64
+               OpMemoryModel Physical64 OpenCL
+               OpEntryPoint Kernel %kernel "test"
+               OpDecorate %out_float0 FPRoundingMode RTE
+               OpDecorate %out_float1 FPRoundingMode RTZ
+               OpDecorate %out_sint FPRoundingMode RTP
+               OpDecorate %out_uint FPRoundingMode RTN
+               OpDecorate %out_double FPRoundingMode RTE
+               OpDecorate %out_float2 FPRoundingMode RTZ
+       %uint = OpTypeInt 32 0
+       %void = OpTypeVoid
+      %float = OpTypeFloat 32
+     %double = OpTypeFloat 64
+   %functype = OpTypeFunction %void %uint %float %double
+     %kernel = OpFunction %void None %functype
+     %in_int = OpFunctionParameter %uint
+   %in_float = OpFunctionParameter %float
+  %in_double = OpFunctionParameter %double
+      %entry = OpLabel
+ %out_float0 = OpConvertSToF %float %in_int
+ %out_float1 = OpConvertUToF %float %in_int
+   %out_sint = OpConvertFToS %uint %in_float
+   %out_uint = OpConvertFToU %uint %in_float
+ %out_double = OpFConvert %double %in_float
+ %out_float2 = OpFConvert %float %in_double
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_0);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_0));
+}
+
+TEST_F(ValidateDecorations, KernelFPRoundingModeBadMode) {
+  std::string spirv = R"(
+               OpCapability Addresses
+               OpCapability Kernel
+               OpMemoryModel Physical64 OpenCL
+               OpEntryPoint Kernel %kernel "test"
+               OpDecorate %out_float FPRoundingMode RTE
+       %void = OpTypeVoid
+      %float = OpTypeFloat 32
+   %functype = OpTypeFunction %void %float
+     %kernel = OpFunction %void None %functype
+   %in_float = OpFunctionParameter %float
+      %entry = OpLabel
+  %out_float = OpFAdd %float %in_float %in_float
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("FPRoundingMode decoration can be applied only to a conversion "
+                "instruction to or from a floating-point type."));
+}
+
 TEST_F(ValidateDecorations, GroupDecorateTargetsDecorationGroup) {
   std::string spirv = R"(
 OpCapability Shader
@@ -5381,6 +5497,97 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+// OpDecorateId
+
+TEST_F(ValidateDecorations, DecorateIdGood) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical Simple
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpName %subgroupscope "subgroupscope"
+OpName %int0 "int0"
+OpName %fn "fn"
+OpDecorateId %int0 UniformId %subgroupscope
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 1
+%subgroupscope = OpConstant %int 3
+%int0 = OpConstantNull %int
+%fn = OpTypeFunction %void
+%main = OpFunction %void None %fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(ValidateDecorations, DecorateIdGroupBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical Simple
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpName %subgroupscope "subgroupscope"
+OpName %int0 "int0"
+OpName %fn "fn"
+OpName %group "group"
+OpDecorateId %group UniformId %subgroupscope
+%group = OpDecorationGroup
+OpGroupDecorate %group %int0
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 1
+%subgroupscope = OpConstant %int 3
+%int0 = OpConstantNull %int
+%fn = OpTypeFunction %void
+%main = OpFunction %void None %fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("must not be an OpDecorationGroup instruction.\n"
+                        "  OpDecorateId %group UniformId %subgroupscope"));
+}
+
+TEST_F(ValidateDecorations, DecorateIdOutOfOrderBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical Simple
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpName %subgroupscope "subgroupscope"
+OpName %int0 "int0"
+OpName %fn "fn"
+OpDecorateId %int0 UniformId %subgroupscope
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 1
+%int0 = OpConstantNull %int
+%subgroupscope = OpConstant %int 3
+%fn = OpTypeFunction %void
+%main = OpFunction %void None %fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("[%subgroupscope]' must appear earlier in the"
+                        " binary than the target\n"
+                        "  OpDecorateId %int0 UniformId %subgroupscope"));
+}
+
 // Uniform and UniformId decorations
 
 TEST_F(ValidateDecorations, UniformDecorationGood) {
@@ -5422,6 +5629,7 @@
 OpName %call "call"
 OpName %myfunc "myfunc"
 OpName %int0 "int0"
+OpName %int1 "int1"
 OpName %float0 "float0"
 OpName %fn "fn"
 )") + inst +
@@ -5429,10 +5637,11 @@
 %void = OpTypeVoid
 %float = OpTypeFloat 32
 %int = OpTypeInt 32 1
-%int0 = OpConstantNull %int
+%int1 = OpConstant %int 1
 %int_99 = OpConstant %int 99
 %subgroupscope = OpConstant %int 3
 %float0 = OpConstantNull %float
+%int0 = OpConstantNull %int
 %fn = OpTypeFunction %void
 %myfunc = OpFunction %void None %fn
 %myfuncentry = OpLabel
@@ -5549,7 +5758,7 @@
 
 TEST_F(ValidateDecorations, UniformDecorationWithScopeIdV14VulkanEnv) {
   const std::string spirv =
-      ShaderWithUniformLikeDecoration("OpDecorateId %int0 UniformId %int0");
+      ShaderWithUniformLikeDecoration("OpDecorateId %int0 UniformId %int1");
 
   CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1_SPIRV_1_4);
   EXPECT_EQ(SPV_ERROR_INVALID_DATA,
@@ -6179,34 +6388,6 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(ValidateDecorations, PSBAliasedRestrictPointerMissing) {
-  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
-%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
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(body.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("expected AliasedPointer or RestrictPointer for "
-                        "PhysicalStorageBuffer pointer"));
-}
-
 TEST_F(ValidateDecorations, PSBAliasedRestrictPointerBoth) {
   const std::string body = R"(
 OpCapability PhysicalStorageBufferAddresses
@@ -6233,8 +6414,8 @@
   CompileSuccessfully(body.c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("can't specify both AliasedPointer and RestrictPointer "
-                        "for PhysicalStorageBuffer pointer"));
+              HasSubstr("decorated with both AliasedPointer and "
+                        "RestrictPointer is not allowed"));
 }
 
 TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamSuccess) {
@@ -6267,38 +6448,6 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamMissing) {
-  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
-%uint64 = OpTypeInt 64 0
-%ptr = OpTypePointer PhysicalStorageBuffer %uint64
-%void = OpTypeVoid
-%voidfn = OpTypeFunction %void
-%fnptr = OpTypeFunction %void %ptr
-%main = OpFunction %void None %voidfn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-%fn = OpFunction %void None %fnptr
-%fparam = OpFunctionParameter %ptr
-%lab = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(body.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("expected Aliased or Restrict for "
-                        "PhysicalStorageBuffer pointer"));
-}
-
 TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamBoth) {
   const std::string body = R"(
 OpCapability PhysicalStorageBufferAddresses
@@ -6328,9 +6477,9 @@
 
   CompileSuccessfully(body.c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("can't specify both Aliased and Restrict for "
-                        "PhysicalStorageBuffer pointer"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("decorated with both Aliased and Restrict is not allowed"));
 }
 
 TEST_F(ValidateDecorations, PSBFPRoundingModeSuccess) {
@@ -6591,12 +6740,14 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
 }
 
-// NonWritable
+// NonReadable/NonWritable
 
 // Returns a SPIR-V shader module with variables in various storage classes,
-// parameterizable by which ID should be decorated as NonWritable.
-std::string ShaderWithNonWritableTarget(const std::string& target,
-                                        bool member_decorate = false) {
+// parameterizable by which ID should be decorated as NonReadable or
+// NonWritable.
+std::string ShaderWithNonReadableNonWritableTarget(
+    const std::string& target, bool member_decorate = false,
+    const std::string& decoration = "NonWritable") {
   const std::string decoration_inst =
       std::string(member_decorate ? "OpMemberDecorate " : "OpDecorate ") +
       target + (member_decorate ? " 0" : "");
@@ -6604,8 +6755,10 @@
   return std::string(R"(
             OpCapability Shader
             OpCapability RuntimeDescriptorArrayEXT
+            OpCapability TensorsARM
             OpExtension "SPV_EXT_descriptor_indexing"
             OpExtension "SPV_KHR_storage_buffer_storage_class"
+            OpExtension "SPV_ARM_tensors"
             OpMemoryModel Logical GLSL450
             OpEntryPoint Vertex %main "main"
             OpName %label "label"
@@ -6625,9 +6778,9 @@
             OpMemberDecorate %struct_b_rtarr 0 Offset 0
             OpDecorate %rtarr ArrayStride 4
 )") + decoration_inst +
+         " " + decoration +
 
-         R"( NonWritable
-
+         R"(
       %void = OpTypeVoid
    %void_fn = OpTypeFunction %void
      %float = OpTypeFloat 32
@@ -6642,6 +6795,8 @@
  %imstor = OpTypeImage %float 2D 0 0 0 2 R32f
  ; sampled image
  %imsam = OpTypeImage %float 2D 0 0 0 1 R32f
+ ; tensor
+ %tensor = OpTypeTensorARM %float %int_2
 %array_imstor = OpTypeArray %imstor %int_2
 %rta_imstor = OpTypeRuntimeArray %imstor
 
@@ -6655,6 +6810,7 @@
 %_ptr_imsam              = OpTypePointer UniformConstant %imsam
 %_ptr_array_imstor       = OpTypePointer UniformConstant %array_imstor
 %_ptr_rta_imstor         = OpTypePointer UniformConstant %rta_imstor
+%_ptr_tensor_UniformConstant = OpTypePointer UniformConstant %tensor
 
 %extra_fn = OpTypeFunction %void %float %_ptr_Private %_ptr_imstor
 
@@ -6667,6 +6823,7 @@
 %var_imsam = OpVariable %_ptr_imsam UniformConstant
 %var_array_imstor = OpVariable %_ptr_array_imstor UniformConstant
 %var_rta_imstor = OpVariable %_ptr_rta_imstor UniformConstant
+%var_tensor = OpVariable %_ptr_tensor_UniformConstant UniformConstant
 
   %helper = OpFunction %void None %extra_fn
  %param_f = OpFunctionParameter %float
@@ -6686,7 +6843,7 @@
 }
 
 TEST_F(ValidateDecorations, NonWritableLabelTargetBad) {
-  std::string spirv = ShaderWithNonWritableTarget("%label");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%label");
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
@@ -6695,7 +6852,7 @@
 }
 
 TEST_F(ValidateDecorations, NonWritableTypeTargetBad) {
-  std::string spirv = ShaderWithNonWritableTarget("%void");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%void");
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
@@ -6704,7 +6861,7 @@
 }
 
 TEST_F(ValidateDecorations, NonWritableValueTargetBad) {
-  std::string spirv = ShaderWithNonWritableTarget("%float_0");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%float_0");
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
@@ -6713,7 +6870,7 @@
 }
 
 TEST_F(ValidateDecorations, NonWritableValueParamBad) {
-  std::string spirv = ShaderWithNonWritableTarget("%param_f");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%param_f");
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
@@ -6721,7 +6878,7 @@
 }
 
 TEST_F(ValidateDecorations, NonWritablePointerParamButWrongTypeBad) {
-  std::string spirv = ShaderWithNonWritableTarget("%param_p");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%param_p");
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
@@ -6729,12 +6886,13 @@
       getDiagnosticString(),
       HasSubstr(
           "Target of NonWritable decoration is invalid: must "
-          "point to a storage image, uniform block, or storage "
+          "point to a storage image, tensor variable in UniformConstant "
+          "storage class, uniform block, or storage "
           "buffer\n  %param_p = OpFunctionParameter %_ptr_Private_float"));
 }
 
 TEST_F(ValidateDecorations, NonWritablePointerParamStorageImageGood) {
-  std::string spirv = ShaderWithNonWritableTarget("%param_pimstor");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%param_pimstor");
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
@@ -6742,7 +6900,7 @@
 }
 
 TEST_F(ValidateDecorations, NonWritableVarStorageImageGood) {
-  std::string spirv = ShaderWithNonWritableTarget("%var_imstor");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_imstor");
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
@@ -6750,18 +6908,20 @@
 }
 
 TEST_F(ValidateDecorations, NonWritableVarSampledImageBad) {
-  std::string spirv = ShaderWithNonWritableTarget("%var_imsam");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_imsam");
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Target of NonWritable decoration is invalid: must "
-                        "point to a storage image, uniform block, or storage "
-                        "buffer\n  %var_imsam"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Target of NonWritable decoration is invalid: must "
+                "point to a storage image, tensor variable in UniformConstant "
+                "storage class, uniform block, or storage "
+                "buffer\n  %var_imsam"));
 }
 
 TEST_F(ValidateDecorations, NonWritableVarUboGood) {
-  std::string spirv = ShaderWithNonWritableTarget("%var_ubo");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_ubo");
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
@@ -6794,7 +6954,7 @@
 }
 
 TEST_F(ValidateDecorations, NonWritableVarSsboInStorageBufferGood) {
-  std::string spirv = ShaderWithNonWritableTarget("%var_ssbo_sb");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_ssbo_sb");
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
@@ -6802,7 +6962,8 @@
 }
 
 TEST_F(ValidateDecorations, NonWritableMemberOfSsboInStorageBufferGood) {
-  std::string spirv = ShaderWithNonWritableTarget("%struct_b_rtarr", true);
+  std::string spirv =
+      ShaderWithNonReadableNonWritableTarget("%struct_b_rtarr", true);
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
@@ -6810,59 +6971,99 @@
 }
 
 TEST_F(ValidateDecorations, NonWritableMemberOfStructGood) {
-  std::string spirv = ShaderWithNonWritableTarget("%simple_struct", true);
+  std::string spirv =
+      ShaderWithNonReadableNonWritableTarget("%simple_struct", true);
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateDecorations, NonWritableTensorVarUniformConstantGood) {
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_tensor");
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(ValidateDecorations, NonReadableTensorVarUniformConstantGood) {
+  std::string spirv = ShaderWithNonReadableNonWritableTarget(
+      "%var_tensor", false, "NonReadable");
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
 TEST_F(ValidateDecorations, NonWritableVarWorkgroupBad) {
-  std::string spirv = ShaderWithNonWritableTarget("%var_wg");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_wg");
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Target of NonWritable decoration is invalid: must "
-                        "point to a storage image, uniform block, or storage "
-                        "buffer\n  %var_wg"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Target of NonWritable decoration is invalid: must "
+                "point to a storage image, tensor variable in UniformConstant "
+                "storage class, uniform block, or storage "
+                "buffer\n  %var_wg"));
+}
+
+TEST_F(ValidateDecorations, NonReadableVarWorkgroupBad) {
+  std::string spirv =
+      ShaderWithNonReadableNonWritableTarget("%var_wg", false, "NonReadable");
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Target of NonReadable decoration is invalid: must "
+                "point to a storage image, tensor variable in UniformConstant "
+                "storage class, uniform block, or storage "
+                "buffer\n  %var_wg"));
 }
 
 TEST_F(ValidateDecorations, NonWritableVarWorkgroupV14Bad) {
-  std::string spirv = ShaderWithNonWritableTarget("%var_wg");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_wg");
 
   CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Target of NonWritable decoration is invalid: must "
-                        "point to a storage image, uniform block, storage "
-                        "buffer, or variable in Private or Function storage "
-                        "class\n  %var_wg"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Target of NonWritable decoration is invalid: must "
+                "point to a storage image, tensor variable in UniformConstant "
+                "storage class, uniform block, storage "
+                "buffer, or variable in Private or Function storage class\n  "
+                "%var_wg"));
 }
 
 TEST_F(ValidateDecorations, NonWritableVarPrivateBad) {
-  std::string spirv = ShaderWithNonWritableTarget("%var_priv");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_priv");
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Target of NonWritable decoration is invalid: must "
-                        "point to a storage image, uniform block, or storage "
-                        "buffer\n  %var_priv"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Target of NonWritable decoration is invalid: must "
+                "point to a storage image, tensor variable in UniformConstant "
+                "storage class, uniform block, or storage "
+                "buffer\n  %var_priv"));
 }
 
 TEST_F(ValidateDecorations, NonWritableVarPrivateV13Bad) {
-  std::string spirv = ShaderWithNonWritableTarget("%var_priv");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_priv");
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Target of NonWritable decoration is invalid: must "
-                        "point to a storage image, uniform block, or storage "
-                        "buffer\n  %var_priv"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Target of NonWritable decoration is invalid: must "
+                "point to a storage image, tensor variable in UniformConstant "
+                "storage class, uniform block, or storage "
+                "buffer\n  %var_priv"));
 }
 
 TEST_F(ValidateDecorations, NonWritableVarPrivateV14Good) {
-  std::string spirv = ShaderWithNonWritableTarget("%var_priv");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_priv");
 
   CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
@@ -6870,36 +7071,41 @@
 }
 
 TEST_F(ValidateDecorations, NonWritableVarPrivateV13TargetV14Bad) {
-  std::string spirv = ShaderWithNonWritableTarget("%var_priv");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_priv");
 
   CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Target of NonWritable decoration is invalid: must "
-                        "point to a storage image, uniform block, or storage "
-                        "buffer\n  %var_priv"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Target of NonWritable decoration is invalid: must "
+                "point to a storage image, tensor variable in UniformConstant "
+                "storage class, uniform block, or storage "
+                "buffer\n  %var_priv"));
 }
 
 TEST_F(ValidateDecorations, NonWritableVarFunctionBad) {
-  std::string spirv = ShaderWithNonWritableTarget("%var_func");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_func");
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Target of NonWritable decoration is invalid: must "
-                        "point to a storage image, uniform block, or storage "
-                        "buffer\n  %var_func"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Target of NonWritable decoration is invalid: must "
+                "point to a storage image, tensor variable in UniformConstant "
+                "storage class, uniform block, or storage "
+                "buffer\n  %var_func"));
 }
 
 TEST_F(ValidateDecorations, NonWritableArrayGood) {
-  std::string spirv = ShaderWithNonWritableTarget("%var_array_imstor");
+  std::string spirv =
+      ShaderWithNonReadableNonWritableTarget("%var_array_imstor");
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
 TEST_F(ValidateDecorations, NonWritableRuntimeArrayGood) {
-  std::string spirv = ShaderWithNonWritableTarget("%var_rta_imstor");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_rta_imstor");
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
@@ -6940,18 +7146,20 @@
                 "is not valid for the Vulkan execution environment."))));
 
 TEST_F(ValidateDecorations, NonWritableVarFunctionV13Bad) {
-  std::string spirv = ShaderWithNonWritableTarget("%var_func");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_func");
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Target of NonWritable decoration is invalid: must "
-                        "point to a storage image, uniform block, or storage "
-                        "buffer\n  %var_func"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Target of NonWritable decoration is invalid: must "
+                "point to a storage image, tensor variable in UniformConstant "
+                "storage class, uniform block, or storage "
+                "buffer\n  %var_func"));
 }
 
 TEST_F(ValidateDecorations, NonWritableVarFunctionV14Good) {
-  std::string spirv = ShaderWithNonWritableTarget("%var_func");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_func");
 
   CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
@@ -6959,14 +7167,16 @@
 }
 
 TEST_F(ValidateDecorations, NonWritableVarFunctionV13TargetV14Bad) {
-  std::string spirv = ShaderWithNonWritableTarget("%var_func");
+  std::string spirv = ShaderWithNonReadableNonWritableTarget("%var_func");
 
   CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Target of NonWritable decoration is invalid: must "
-                        "point to a storage image, uniform block, or storage "
-                        "buffer\n  %var_func"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Target of NonWritable decoration is invalid: must "
+                "point to a storage image, tensor variable in UniformConstant "
+                "storage class, uniform block, or storage "
+                "buffer\n  %var_func"));
 }
 
 TEST_F(ValidateDecorations, BufferBlockV13ValV14Good) {
@@ -7072,7 +7282,7 @@
   CompileSuccessfully(spirv, env);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
   EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-Component-04924"));
+              AnyVUID("VUID-StandaloneSpirv-Component-10583"));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Component decoration specified for type"));
   EXPECT_THAT(getDiagnosticString(), HasSubstr("is not a scalar or vector"));
@@ -7298,9 +7508,9 @@
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
   EXPECT_THAT(getDiagnosticString(),
               AnyVUID("VUID-StandaloneSpirv-Component-04922"));
-  HasSubstr(
-      "Sequence of components starting with 2 "
-      "and ending with 6 gets larger than 3");
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Sequence of components starting with 2 "
+                        "and ending with 5 gets larger than 3"));
 }
 
 TEST_F(ValidateDecorations, ComponentDecoration64VecWideBadVulkan) {
@@ -7437,6 +7647,8 @@
 OpExecutionMode %main LocalSize 1 1 1
 OpDecorate %struct Block
 OpMemberDecorate %struct 0 Offset 0
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
 %void = OpTypeVoid
 %uint = OpTypeInt 32 0
 %struct = OpTypeStruct %uint
@@ -7550,6 +7762,8 @@
 OpExecutionMode %main LocalSize 1 1 1
 OpDecorate %struct Block
 OpMemberDecorate %struct 0 Offset 0
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
 %void = OpTypeVoid
 %uint = OpTypeInt 32 0
 %struct = OpTypeStruct %uint
@@ -7574,6 +7788,8 @@
 OpExecutionMode %main LocalSize 1 1 1
 OpDecorate %struct BufferBlock
 OpMemberDecorate %struct 0 Offset 0
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
 %void = OpTypeVoid
 %uint = OpTypeInt 32 0
 %struct = OpTypeStruct %uint
@@ -9388,9 +9604,9 @@
 
   CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Non-unique OpEntryPoint interface '2[%var]' is disallowed"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("In SPIR-V 1.4 or later, non-unique OpEntryPoint "
+                        "interface '2[%var]' is disallowed"));
 }
 
 TEST_F(ValidateDecorations, PhysicalStorageBufferMissingOffset) {
@@ -10239,6 +10455,7 @@
 OpMemberDecorate %struct 1 Offset 4
 )" + set + R"(OpMemberDecorate %test_type 0 Offset 0
 OpMemberDecorate %test_type 1 Offset 1
+OpDecorate %ptr ArrayStride 16
 %void = OpTypeVoid
 %int = OpTypeInt 32 0
 %int_0 = OpConstant %int 0
@@ -10247,7 +10464,8 @@
 %test_val = OpConstantNull %test_type
 %ptr = OpTypeUntypedPointerKHR )" +
                             sc + R"(
-%var = OpUntypedVariableKHR %ptr )" + sc + R"( %struct
+%var = OpUntypedVariableKHR %ptr )" +
+                            sc + R"( %struct
 %void_fn = OpTypeFunction %void
 %main = OpFunction %void None %void_fn
 %entry = OpLabel
@@ -10290,6 +10508,7 @@
 OpMemberDecorate %struct 0 Offset 0
 OpMemberDecorate %struct 1 Offset 4
 )" + set + R"(OpDecorate %test_type ArrayStride 4
+OpDecorate %ptr ArrayStride 16
 %void = OpTypeVoid
 %int = OpTypeInt 32 0
 %int_0 = OpConstant %int 0
@@ -10300,7 +10519,8 @@
 %struct = OpTypeStruct %int %int
 %ptr = OpTypeUntypedPointerKHR )" +
                             sc + R"(
-%var = OpUntypedVariableKHR %ptr )" + sc + R"( %struct
+%var = OpUntypedVariableKHR %ptr )" +
+                            sc + R"( %struct
 %void_fn = OpTypeFunction %void
 %main = OpFunction %void None %void_fn
 %entry = OpLabel
@@ -10437,6 +10657,808 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
 }
 
+const std::string kNodeShaderPrelude = R"(
+OpCapability Shader
+OpCapability ShaderEnqueueAMDX
+OpExtension "SPV_AMDX_shader_enqueue"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpEntryPoint GLCompute %other "other"
+)";
+
+const std::string kNodeShaderPostlude = R"(
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%node0 = OpConstantStringAMDX "node0"
+%node1 = OpConstantStringAMDX "node1"
+%node2 = OpConstantStringAMDX "node2"
+%S = OpTypeStruct
+%_payloadarr_S_0 = OpTypeNodePayloadArrayAMDX %S
+%_payloadarr_S = OpTypeNodePayloadArrayAMDX %S
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+%other = OpFunction %void None %void_fn
+%entry0 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+TEST_F(ValidateDecorations, NodeShader) {
+  const std::string spirv = kNodeShaderPrelude + R"(
+OpExecutionModeId %main ShaderIndexAMDX %uint_0
+OpExecutionModeId %main IsApiEntryAMDX %true
+OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1
+OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0
+OpExecutionModeId %other ShaderIndexAMDX %uint_0
+OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1
+OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2
+OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0
+OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1
+OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0
+)" + kNodeShaderPostlude;
+
+  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env));
+}
+
+TEST_F(ValidateDecorations, NodeShaderDecoratePayloadNodeName) {
+  const std::string spirv = kNodeShaderPrelude + R"(
+OpExecutionModeId %main ShaderIndexAMDX %uint_0
+OpExecutionModeId %main IsApiEntryAMDX %true
+OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1
+OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0
+OpExecutionModeId %other ShaderIndexAMDX %uint_0
+OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpDecorate %_payloadarr_S PayloadNodeNameAMDX %node1
+OpDecorate %_payloadarr_S_0 PayloadNodeNameAMDX %node2
+OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0
+OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1
+OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0
+)" + kNodeShaderPostlude;
+
+  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Decorations taking ID parameters may not be used with OpDecorate"));
+}
+
+TEST_F(ValidateDecorations, NodeShaderDecoratePayloadNodeBaseIndex) {
+  const std::string spirv = kNodeShaderPrelude + R"(
+OpExecutionModeId %main ShaderIndexAMDX %uint_0
+OpExecutionModeId %main IsApiEntryAMDX %true
+OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1
+OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0
+OpExecutionModeId %other ShaderIndexAMDX %uint_0
+OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1
+OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2
+OpDecorate %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0
+OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1
+OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0
+)" + kNodeShaderPostlude;
+
+  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Decorations taking ID parameters may not be used with OpDecorate"));
+}
+
+TEST_F(ValidateDecorations, NodeShaderDecoratePayloadNodeArraySize) {
+  const std::string spirv = kNodeShaderPrelude + R"(
+OpExecutionModeId %main ShaderIndexAMDX %uint_0
+OpExecutionModeId %main IsApiEntryAMDX %true
+OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1
+OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0
+OpExecutionModeId %other ShaderIndexAMDX %uint_0
+OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1
+OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2
+OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0
+OpDecorate %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1
+OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0
+)" + kNodeShaderPostlude;
+
+  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Decorations taking ID parameters may not be used with OpDecorate"));
+}
+
+TEST_F(ValidateDecorations, NodeShaderDecorateNodeSharesPayloadLimitsWith) {
+  const std::string spirv = kNodeShaderPrelude + R"(
+OpExecutionModeId %main ShaderIndexAMDX %uint_0
+OpExecutionModeId %main IsApiEntryAMDX %true
+OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1
+OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0
+OpExecutionModeId %other ShaderIndexAMDX %uint_0
+OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1
+OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2
+OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0
+OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1
+OpDecorate %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0
+)" + kNodeShaderPostlude;
+
+  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Decorations taking ID parameters may not be used with OpDecorate"));
+}
+
+TEST_F(ValidateDecorations, BlockArrayWithStride) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpDecorate %array ArrayStride 4
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%struct = OpTypeStruct %int
+%array = OpTypeArray %struct %int_4
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Array containing a Block or BufferBlock must not be "
+                        "decorated with ArrayStride"));
+}
+
+TEST_F(ValidateDecorations, BufferBlockRuntimeArrayWithStride) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %struct BufferBlock
+OpMemberDecorate %struct 0 Offset 0
+OpDecorate %array ArrayStride 4
+%int = OpTypeInt 32 0
+%struct = OpTypeStruct %int
+%array = OpTypeRuntimeArray %struct
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Array containing a Block or BufferBlock must not be "
+                        "decorated with ArrayStride"));
+}
+
+TEST_F(ValidateDecorations, BlockArrayWithoutStride) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%struct = OpTypeStruct %int
+%array = OpTypeArray %struct %int_4
+%ptr = OpTypePointer StorageBuffer %array
+%var = OpVariable %ptr StorageBuffer
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateDecorations, BlockArrayWithoutStrideUntypedAccessChain) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%struct = OpTypeStruct %int
+%array = OpTypeArray %struct %int_4
+%void = OpTypeVoid
+%ptr = OpTypeUntypedPointerKHR StorageBuffer
+%var = OpUntypedVariableKHR %ptr StorageBuffer %array
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpUntypedAccessChainKHR %ptr %array %var
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateDecorations, InvalidLayoutBlockFunctionPre1p4) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%block = OpTypeStruct %int
+%ptr_function_block = OpTypePointer Function %block
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpVariable %ptr_function_block Function
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateDecorations, InvalidLayoutBlockFunctionPost1p4) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%block = OpTypeStruct %int
+%ptr_function_block = OpTypePointer Function %block
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpVariable %ptr_function_block Function
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-10684"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Invalid explicit layout decorations on type for operand"));
+}
+
+TEST_F(ValidateDecorations, InvalidLayoutOffsetPrivatePre1p4) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpMemberDecorate %block 0 Offset 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%block = OpTypeStruct %int
+%ptr_private_block = OpTypePointer Private %block
+%void_fn = OpTypeFunction %void
+%var = OpVariable %ptr_private_block Private
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateDecorations, InvalidLayoutOffsetPrivatePost1p4) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpMemberDecorate %block 0 Offset 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%block = OpTypeStruct %int
+%ptr_private_block = OpTypePointer Private %block
+%void_fn = OpTypeFunction %void
+%var = OpVariable %ptr_private_block Private
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-10684"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Invalid explicit layout decorations on type for operand"));
+}
+
+TEST_F(ValidateDecorations, InvalidLayoutArrayStrideWorkgroupExplicitLayout) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability WorkgroupMemoryExplicitLayoutKHR
+OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %array ArrayStride 4
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%array = OpTypeArray %int %int_4
+%ptr_wg_block = OpTypePointer Workgroup %array
+%void_fn = OpTypeFunction %void
+%var = OpVariable %ptr_wg_block Workgroup
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateDecorations, InvalidLayoutArrayStrideWorkgroup) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %array ArrayStride 4
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%array = OpTypeArray %int %int_4
+%ptr_wg_block = OpTypePointer Workgroup %array
+%void_fn = OpTypeFunction %void
+%var = OpVariable %ptr_wg_block Workgroup
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-10684"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Invalid explicit layout decorations on type for operand"));
+}
+
+TEST_F(ValidateDecorations, InvalidLayoutArrayStrideUniformConstant) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %array ArrayStride 4
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%sampler = OpTypeSampler
+%array = OpTypeArray %sampler %int_4
+%ptr_uc_block = OpTypePointer UniformConstant %array
+%void_fn = OpTypeFunction %void
+%var = OpVariable %ptr_uc_block UniformConstant
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-10684"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Invalid explicit layout decorations on type for operand"));
+}
+
+TEST_F(ValidateDecorations, InvalidLayoutMatrixStrideFunctionPost1p4) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpMemberDecorate %block 0 MatrixStride 16
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%mat4x4 = OpTypeMatrix %v4float 4
+%block = OpTypeStruct %mat4x4
+%ptr_function_block = OpTypePointer Function %block
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpVariable %ptr_function_block Function
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-10684"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Invalid explicit layout decorations on type for operand"));
+}
+
+TEST_F(ValidateDecorations, InvalidLayoutNestedMatrixStrideFunctionPost1p4) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpMemberDecorate %block 0 MatrixStride 16
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%mat4x4 = OpTypeMatrix %v4float 4
+%block = OpTypeStruct %mat4x4
+%block2 = OpTypeStruct %block
+%int = OpTypeInt 32 0
+%int_2 = OpConstant %int 2
+%array = OpTypeArray %block2 %int_2
+%ptr_function_array = OpTypePointer Function %array
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpVariable %ptr_function_array Function
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-10684"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Invalid explicit layout decorations on type for operand"));
+}
+
+TEST_F(ValidateDecorations, InvalidLayoutBufferBlockWorkgroup) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block BufferBlock
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%block = OpTypeStruct %int
+%ptr_wg_block = OpTypePointer Workgroup %block
+%void_fn = OpTypeFunction %void
+%var = OpVariable %ptr_wg_block Workgroup
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-10684"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Invalid explicit layout decorations on type for operand"));
+}
+
+TEST_F(ValidateDecorations, InvalidLayoutUntypedStore) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%block = OpTypeStruct %int
+%block_null = OpConstantNull %block
+%ptr = OpTypeUntypedPointerKHR StorageBuffer
+%var = OpUntypedVariableKHR %ptr StorageBuffer %block
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpStore %var %block_null
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateDecorations, ExplicitLayoutOnPtrPhysicalStorageBuffer) {
+  const std::string spirv = R"(
+OpCapability PhysicalStorageBufferAddresses
+OpCapability Int64
+OpCapability Shader
+OpExtension "SPV_KHR_physical_storage_buffer"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %_ptr_PhysicalStorageBuffer_int ArrayStride 4
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int  ; ArrayStride 4
+%Foo = OpTypeStruct %_ptr_PhysicalStorageBuffer_int
+%_ptr_Function_Foo = OpTypePointer Function %Foo
+%int_0 = OpConstant %int 0
+%_ptr_Function__ptr_PhysicalStorageBuffer_int = OpTypePointer Function %_ptr_PhysicalStorageBuffer_int
+%ulong = OpTypeInt 64 0
+%ulong_0 = OpConstant %ulong 0
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%obj = OpVariable %_ptr_Function_Foo Function
+%obj_member = OpAccessChain %_ptr_Function__ptr_PhysicalStorageBuffer_int %obj %int_0
+%nullptr = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_int %ulong_0
+OpStore %obj_member %nullptr
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateDecorations, RuntimeArrayNotLargestOffsetInBlock) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 16
+OpMemberDecorate %block 1 Offset 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%array = OpTypeRuntimeArray %int
+%block = OpTypeStruct %int %array
+%ptr = OpTypePointer StorageBuffer %block
+%var = OpVariable %ptr StorageBuffer
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("has a runtime array at offset 0, but other members at "
+                        "larger offsets"));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
+}
+
+TEST_F(ValidateDecorations, RuntimeArrayNotLargestOffsetInBufferBlock) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %block BufferBlock
+OpMemberDecorate %block 0 Offset 16
+OpMemberDecorate %block 1 Offset 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%array = OpTypeRuntimeArray %int
+%block = OpTypeStruct %int %array
+%ptr = OpTypePointer Uniform %block
+%var = OpVariable %ptr Uniform
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("has a runtime array at offset 0, but other members at "
+                        "larger offsets"));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
+}
+
+TEST_F(ValidateDecorations, LongVectorUniformPass) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Int8
+OpCapability UniformAndStorageBuffer8BitAccess
+OpCapability LongVectorEXT
+
+OpExtension "SPV_KHR_8bit_storage"
+OpExtension "SPV_EXT_long_vector"
+
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %BP_main "main"
+
+OpDecorate %input0 DescriptorSet 0
+OpDecorate %input0 Binding 0
+OpDecorate %a10testtype ArrayStride 12
+OpDecorate %buf BufferBlock
+OpMemberDecorate %buf 0 Offset 0
+
+%void = OpTypeVoid
+%bool = OpTypeBool
+%u32 = OpTypeInt 32 0
+%voidf = OpTypeFunction %void
+%c_u32_10 = OpConstant %u32 10
+%vectorSizeConst = OpConstant %u32 12
+
+%scalartype = OpTypeInt 8 1
+%testtype = OpTypeVectorIdEXT %scalartype %vectorSizeConst
+
+%a10testtype = OpTypeArray %testtype %c_u32_10
+%buf = OpTypeStruct %a10testtype
+%bufptr = OpTypePointer Uniform %buf
+
+%input0 = OpVariable %bufptr Uniform
+
+
+%BP_main = OpFunction %void None %voidf
+%BP_label = OpLabel
+OpReturn
+OpFunctionEnd
+
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateDecorations, LongVectorUniformSpecConstantFail) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability LongVectorEXT
+
+OpExtension "SPV_EXT_long_vector"
+
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %BP_main "main"
+
+OpDecorate %input0 DescriptorSet 0
+OpDecorate %input0 Binding 0
+OpDecorate %buf BufferBlock
+OpMemberDecorate %buf 0 Offset 0
+OpDecorate %spec_const SpecId 1
+
+%void = OpTypeVoid
+%bool = OpTypeBool
+%u32 = OpTypeInt 32 0
+%voidf = OpTypeFunction %void
+%spec_const = OpSpecConstant %u32 12
+
+%scalartype = OpTypeInt 32 1
+%testtype = OpTypeVectorIdEXT %scalartype %spec_const
+
+%buf = OpTypeStruct %testtype
+%bufptr = OpTypePointer Uniform %buf
+
+%input0 = OpVariable %bufptr Uniform
+
+
+%BP_main = OpFunction %void None %voidf
+%BP_label = OpLabel
+OpReturn
+OpFunctionEnd
+
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Function-12294"));
+}
+
+TEST_F(ValidateDecorations, LongVectorWorkgroupSpecConstantFail) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability LongVectorEXT
+OpCapability WorkgroupMemoryExplicitLayoutKHR
+OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
+
+OpExtension "SPV_EXT_long_vector"
+
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %BP_main "main"
+
+OpDecorate %spec_const SpecId 1
+OpMemberDecorate %str 0 Offset 0
+OpDecorate %str Block
+
+%void = OpTypeVoid
+%bool = OpTypeBool
+%u32 = OpTypeInt 32 0
+%voidf = OpTypeFunction %void
+%spec_const = OpSpecConstant %u32 12
+
+%scalartype = OpTypeInt 32 1
+%testtype = OpTypeVectorIdEXT %scalartype %spec_const
+
+%str = OpTypeStruct %testtype
+%ptr_str_Workgroup = OpTypePointer Workgroup %str
+%var = OpVariable %ptr_str_Workgroup Workgroup
+
+%BP_main = OpFunction %void None %voidf
+%BP_label = OpLabel
+OpReturn
+OpFunctionEnd
+
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Function-12294"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_dot_product_test.cpp b/test/val/val_dot_product_test.cpp
new file mode 100644
index 0000000..c4d5c89
--- /dev/null
+++ b/test/val/val_dot_product_test.cpp
@@ -0,0 +1,418 @@
+// Copyright (c) 2026 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 <ostream>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "spirv-tools/libspirv.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+
+using ValidateDotProductMixAcc = spvtest::ValidateBase<bool>;
+
+std::string GenerateShaderCode(const std::string& body) {
+  std::stringstream ss;
+  ss << R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Float64
+OpCapability Float8EXT
+OpCapability BFloat16TypeKHR
+OpCapability DotProductFloat16AccFloat32VALVE
+OpCapability DotProductFloat16AccFloat16VALVE
+OpCapability DotProductBFloat16AccVALVE
+OpCapability DotProductFloat8AccFloat32VALVE
+OpExtension "SPV_VALVE_mixed_float_dot_product"
+OpExtension "SPV_EXT_float8"
+OpExtension "SPV_KHR_bfloat16"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+
+%f32 = OpTypeFloat 32
+%f16 = OpTypeFloat 16
+%bf16 = OpTypeFloat 16 BFloat16KHR
+%f64 = OpTypeFloat 64
+%f8_e4m3 = OpTypeFloat 8 Float8E4M3EXT
+%f8_e5m2 = OpTypeFloat 8 Float8E5M2EXT
+
+%v2f32 = OpTypeVector %f32 2
+%v2f16 = OpTypeVector %f16 2
+%v2bf16 = OpTypeVector %bf16 2
+%v3f16 = OpTypeVector %f16 3
+%v2f64 = OpTypeVector %f64 2
+%v4f8_e4m3 = OpTypeVector %f8_e4m3 4
+%v4f8_e5m2 = OpTypeVector %f8_e5m2 4
+%v4f16 = OpTypeVector %f16 4
+
+%f32_0 = OpConstant %f32 0
+%f32_1 = OpConstant %f32 1
+%f16_0 = OpConstant %f16 0
+%f16_1 = OpConstant %f16 1
+%bf16_1 = OpConstant %bf16 1
+%f8_e4m3_1 = OpConstant %f8_e4m3 1
+%f8_e5m2_1 = OpConstant %f8_e5m2 1
+
+%v2f16_0 = OpConstantComposite %v2f16 %f16_0 %f16_0
+%v2f16_1 = OpConstantComposite %v2f16 %f16_1 %f16_1
+%v2bf16_1 = OpConstantComposite %v2bf16 %bf16_1 %bf16_1
+%v3f16_1 = OpConstantComposite %v3f16 %f16_1 %f16_1 %f16_1
+%v2f32_1 = OpConstantComposite %v2f32 %f32_1 %f32_1
+%v4f8_e4m3_1 = OpConstantComposite %v4f8_e4m3 %f8_e4m3_1 %f8_e4m3_1 %f8_e4m3_1 %f8_e4m3_1
+%v4f8_e5m2_1 = OpConstantComposite %v4f8_e5m2 %f8_e5m2_1 %f8_e5m2_1 %f8_e5m2_1 %f8_e5m2_1
+%v4f16_1 = OpConstantComposite %v4f16 %f16_1 %f16_1 %f16_1 %f16_1
+
+%main = OpFunction %void None %func
+%label = OpLabel
+)";
+  ss << body;
+  ss << R"(
+OpReturn
+OpFunctionEnd
+)";
+  return ss.str();
+}
+
+TEST_F(ValidateDotProductMixAcc, FDot2MixAcc32Float16Good) {
+  const std::string body = R"(
+     %res = OpFDot2MixAcc32VALVE %f32 %v2f16_1 %v2f16_1 %f32_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(body));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateDotProductMixAcc, FDot2MixAcc32BFloat16Good) {
+  const std::string body = R"(
+     %res = OpFDot2MixAcc32VALVE %f32 %v2bf16_1 %v2bf16_1 %f32_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(body));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateDotProductMixAcc, FDot2MixAcc16Float16Good) {
+  const std::string body = R"(
+     %res = OpFDot2MixAcc16VALVE %f16 %v2f16_1 %v2f16_1 %f16_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(body));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateDotProductMixAcc, FDot2MixAcc16BFloat16Good) {
+  const std::string body = R"(
+     %res = OpFDot2MixAcc16VALVE %bf16 %v2bf16_1 %v2bf16_1 %bf16_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(body));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateDotProductMixAcc, FDot4MixAcc32Float8E4M3Good) {
+  const std::string body = R"(
+     %res = OpFDot4MixAcc32VALVE %f32 %v4f8_e4m3_1 %v4f8_e4m3_1 %f32_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(body));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateDotProductMixAcc, FDot4MixAcc32Float8E5M2Good) {
+  const std::string body = R"(
+     %res = OpFDot4MixAcc32VALVE %f32 %v4f8_e5m2_1 %v4f8_e5m2_1 %f32_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(body));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateDotProductMixAcc, FDot4MixAcc32Float8MixedEncodingGood) {
+  const std::string body = R"(
+     %res = OpFDot4MixAcc32VALVE %f32 %v4f8_e4m3_1 %v4f8_e5m2_1 %f32_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(body));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+TEST_F(ValidateDotProductMixAcc, CommonNotVector) {
+  const std::string body = R"(
+     %res = OpFDot2MixAcc32VALVE %f32 %f16_1 %v2f16_1 %f32_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(body));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected 'Vector 1' to be an vector."));
+}
+
+TEST_F(ValidateDotProductMixAcc, CommonBadLength) {
+  const std::string body = R"(
+     %res = OpFDot2MixAcc32VALVE %f32 %v3f16_1 %v3f16_1 %f32_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(body));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("components, but both need to be 2-components"));
+}
+
+TEST_F(ValidateDotProductMixAcc, Acc32BadResultType) {
+  const std::string body = R"(
+     %res = OpFDot2MixAcc32VALVE %f16 %v2f16_1 %v2f16_1 %f32_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(body));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result must be a 32-bit IEEE 754 float scalar type."));
+}
+
+TEST_F(ValidateDotProductMixAcc, Acc32BadComponentType) {
+  const std::string body = R"(
+     %res = OpFDot2MixAcc32VALVE %f32 %v2f32_1 %v2f32_1 %f32_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(body));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected 'Vector 1' to be a vector of 16-bit floats."));
+}
+
+TEST_F(ValidateDotProductMixAcc, Acc32BadAccType) {
+  const std::string body = R"(
+     %res = OpFDot2MixAcc32VALVE %f32 %v2f16_1 %v2f16_1 %f16_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(body));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Accumulator Type must be the same as the Result Type."));
+}
+
+TEST_F(ValidateDotProductMixAcc, Acc32MixedEncodingFloat16) {
+  const std::string body = R"(
+     %res = OpFDot2MixAcc32VALVE %f32 %v2f16_1 %v2bf16_1 %f32_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(body));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("'Vector 1' and 'Vector 2' must be the same float encoding."));
+}
+
+TEST_F(ValidateDotProductMixAcc, Acc16BadResultType) {
+  const std::string body = R"(
+     %res = OpFDot2MixAcc16VALVE %f32 %v2f16_1 %v2f16_1 %f16_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(body));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result must be a 16-bit float scalar type."));
+}
+
+TEST_F(ValidateDotProductMixAcc, Acc16BadAccType) {
+  const std::string body = R"(
+     %res = OpFDot2MixAcc16VALVE %f16 %v2f16_1 %v2f16_1 %f32_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(body));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Accumulator Type must be the same as the Result Type."));
+}
+
+TEST_F(ValidateDotProductMixAcc, Acc16MixedEncodingFloat16) {
+  const std::string body = R"(
+     %res = OpFDot2MixAcc16VALVE %f16 %v2f16_1 %v2bf16_1 %f16_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(body));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("'Vector 1' and 'Vector 2' must be the same float encoding."));
+}
+
+TEST_F(ValidateDotProductMixAcc, Acc16ResultWrongEncoding) {
+  const std::string body = R"(
+     %res = OpFDot2MixAcc16VALVE %f16 %v2bf16_1 %v2bf16_1 %f16_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(body));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result must have the same float encoding as 'Vector "
+                        "1' and 'Vector 2'."));
+}
+
+TEST_F(ValidateDotProductMixAcc, Acc16AccumulatorWrongEncoding) {
+  const std::string body = R"(
+     %res = OpFDot2MixAcc16VALVE %bf16 %v2bf16_1 %v2bf16_1 %f16_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(body));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Accumulator Type must be the same as the Result Type."));
+}
+
+TEST_F(ValidateDotProductMixAcc, Acc32Mix4BadVectorType) {
+  const std::string body = R"(
+     %res = OpFDot4MixAcc32VALVE %f32 %v2f16_1 %v2f16_1 %f32_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(body));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("components, but both need to be 4-components"));
+}
+
+TEST_F(ValidateDotProductMixAcc, Acc32Mix4BadComponentType) {
+  const std::string body = R"(
+     %res = OpFDot4MixAcc32VALVE %f32 %v4f16_1 %v4f16_1 %f32_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(body));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected 'Vector 1' to be a vector of 8-bit floats."));
+}
+
+TEST_F(ValidateDotProductMixAcc, Acc32MissingCapFloat16) {
+  const std::string ss = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability DotProductBFloat16AccVALVE
+OpExtension "SPV_VALVE_mixed_float_dot_product"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%f32 = OpTypeFloat 32
+%f16 = OpTypeFloat 16
+%v2f16 = OpTypeVector %f16 2
+
+%f32_1 = OpConstant %f32 1
+%f16_1 = OpConstant %f16 1
+%v2f16_1 = OpConstantComposite %v2f16 %f16_1 %f16_1
+
+%main = OpFunction %void None %func
+%label = OpLabel
+%res = OpFDot2MixAcc32VALVE %f32 %v2f16_1 %v2f16_1 %f32_1
+OpReturn
+OpFunctionEnd
+  )";
+  CompileSuccessfully(ss);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("DotProductFloat16AccFloat32VALVE capability is required"));
+}
+
+TEST_F(ValidateDotProductMixAcc, Acc32MissingCapBFloat16) {
+  const std::string ss = R"(
+OpCapability Shader
+OpCapability BFloat16TypeKHR
+OpCapability DotProductFloat16AccFloat32VALVE
+OpExtension "SPV_VALVE_mixed_float_dot_product"
+OpExtension "SPV_KHR_bfloat16"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%f32 = OpTypeFloat 32
+%bf16 = OpTypeFloat 16 BFloat16KHR
+%v2bf16 = OpTypeVector %bf16 2
+
+%f32_1 = OpConstant %f32 1
+%bf16_1 = OpConstant %bf16 1
+%v2bf16_1 = OpConstantComposite %v2bf16 %bf16_1 %bf16_1
+
+%main = OpFunction %void None %func
+%label = OpLabel
+%res = OpFDot2MixAcc32VALVE %f32 %v2bf16_1 %v2bf16_1 %f32_1
+OpReturn
+OpFunctionEnd
+  )";
+  CompileSuccessfully(ss);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DotProductBFloat16AccVALVE capability is required"));
+}
+
+TEST_F(ValidateDotProductMixAcc, Acc16MissingCapFloat16) {
+  const std::string ss = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability DotProductBFloat16AccVALVE
+OpExtension "SPV_VALVE_mixed_float_dot_product"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%f16 = OpTypeFloat 16
+%v2f16 = OpTypeVector %f16 2
+
+%f16_1 = OpConstant %f16 1
+%v2f16_1 = OpConstantComposite %v2f16 %f16_1 %f16_1
+
+%main = OpFunction %void None %func
+%label = OpLabel
+%res = OpFDot2MixAcc16VALVE %f16 %v2f16_1 %v2f16_1 %f16_1
+OpReturn
+OpFunctionEnd
+  )";
+  CompileSuccessfully(ss);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("DotProductFloat16AccFloat16VALVE capability is required"));
+}
+
+TEST_F(ValidateDotProductMixAcc, Acc16MissingCapBFloat16) {
+  const std::string ss = R"(
+OpCapability Shader
+OpCapability BFloat16TypeKHR
+OpCapability DotProductFloat16AccFloat16VALVE
+OpExtension "SPV_VALVE_mixed_float_dot_product"
+OpExtension "SPV_KHR_bfloat16"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%bf16 = OpTypeFloat 16 BFloat16KHR
+%v2bf16 = OpTypeVector %bf16 2
+
+%bf16_1 = OpConstant %bf16 1
+%v2bf16_1 = OpConstantComposite %v2bf16 %bf16_1 %bf16_1
+
+%main = OpFunction %void None %func
+%label = OpLabel
+%res = OpFDot2MixAcc16VALVE %bf16 %v2bf16_1 %v2bf16_1 %bf16_1
+OpReturn
+OpFunctionEnd
+  )";
+  CompileSuccessfully(ss);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DotProductBFloat16AccVALVE capability is required"));
+}
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/val_entry_point_test.cpp b/test/val/val_entry_point_test.cpp
index f28cf5d..80b90a5 100644
--- a/test/val/val_entry_point_test.cpp
+++ b/test/val/val_entry_point_test.cpp
@@ -50,6 +50,33 @@
               HasSubstr("Entry points cannot share the same name"));
 }
 
+TEST_F(ValidateEntryPoints, DuplicateConditionalEntryPointsAllowed) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability SpecConditionalINTEL
+OpExtension "SPV_INTEL_function_variants"
+OpMemoryModel Logical GLSL450
+OpConditionalEntryPointINTEL %10 GLCompute %3 "foo"
+OpConditionalEntryPointINTEL %11 GLCompute %4 "foo"
+%bool = OpTypeBool
+%1 = OpTypeVoid
+%2 = OpTypeFunction %1
+%10 = OpSpecConstantTrue %bool
+%11 = OpSpecConstantFalse %bool
+%3 = OpFunction %1 None %2
+%20 = OpLabel
+OpReturn
+OpFunctionEnd
+%4 = OpFunction %1 None %2
+%21 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(body);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
 TEST_F(ValidateEntryPoints, UniqueEntryPoints) {
   const std::string body = R"(
 OpCapability Shader
diff --git a/test/val/val_ext_inst_debug_test.cpp b/test/val/val_ext_inst_debug_test.cpp
index bd4ab3c..e3dedaf 100644
--- a/test/val/val_ext_inst_debug_test.cpp
+++ b/test/val/val_ext_inst_debug_test.cpp
@@ -22,9 +22,151 @@
 
 #include "gmock/gmock.h"
 #include "spirv-tools/libspirv.h"
+#include "spirv/unified1/NonSemanticShaderDebugInfo.h"
+#include "spirv/unified1/NonSemanticShaderDebugInfo100.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
 
+// Verify that the frozen NonSemanticShaderDebugInfo100.h opcode values match
+// the version-agnostic NonSemanticShaderDebugInfo.h values. The two headers
+// use different enum type names but must have identical opcode numbers.
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugInfoNone) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugInfoNone));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugCompilationUnit) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugCompilationUnit));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeBasic) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeBasic));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypePointer) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypePointer));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeQualifier) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeQualifier));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeArray) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeArray));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeVector) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeVector));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypedef) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypedef));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeFunction) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeFunction));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeEnum) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeEnum));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeComposite) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeComposite));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeMember) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeMember));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeInheritance) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeInheritance));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypePtrToMember) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypePtrToMember));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeTemplate) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeTemplate));
+static_assert(static_cast<uint32_t>(
+                  NonSemanticShaderDebugInfo100DebugTypeTemplateParameter) ==
+              static_cast<uint32_t>(
+                  NonSemanticShaderDebugInfoDebugTypeTemplateParameter));
+static_assert(
+    static_cast<uint32_t>(
+        NonSemanticShaderDebugInfo100DebugTypeTemplateTemplateParameter) ==
+    static_cast<uint32_t>(
+        NonSemanticShaderDebugInfoDebugTypeTemplateTemplateParameter));
+static_assert(
+    static_cast<uint32_t>(
+        NonSemanticShaderDebugInfo100DebugTypeTemplateParameterPack) ==
+    static_cast<uint32_t>(
+        NonSemanticShaderDebugInfoDebugTypeTemplateParameterPack));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugGlobalVariable) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugGlobalVariable));
+static_assert(
+    static_cast<uint32_t>(
+        NonSemanticShaderDebugInfo100DebugFunctionDeclaration) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugFunctionDeclaration));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugFunction) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugFunction));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugLexicalBlock) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugLexicalBlock));
+static_assert(
+    static_cast<uint32_t>(
+        NonSemanticShaderDebugInfo100DebugLexicalBlockDiscriminator) ==
+    static_cast<uint32_t>(
+        NonSemanticShaderDebugInfoDebugLexicalBlockDiscriminator));
+static_assert(static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugScope) ==
+              static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugScope));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugNoScope) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugNoScope));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugInlinedAt) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugInlinedAt));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugLocalVariable) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugLocalVariable));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugInlinedVariable) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugInlinedVariable));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugDeclare) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugDeclare));
+static_assert(static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugValue) ==
+              static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugValue));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugOperation) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugOperation));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugExpression) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugExpression));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugMacroDef) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugMacroDef));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugMacroUndef) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugMacroUndef));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugImportedEntity) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugImportedEntity));
+static_assert(static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugSource) ==
+              static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugSource));
+static_assert(
+    static_cast<uint32_t>(
+        NonSemanticShaderDebugInfo100DebugFunctionDefinition) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugFunctionDefinition));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugSourceContinued) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugSourceContinued));
+static_assert(static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugLine) ==
+              static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugLine));
+static_assert(static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugNoLine) ==
+              static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugNoLine));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugBuildIdentifier) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugBuildIdentifier));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugStoragePath) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugStoragePath));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugEntryPoint) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugEntryPoint));
+static_assert(
+    static_cast<uint32_t>(NonSemanticShaderDebugInfo100DebugTypeMatrix) ==
+    static_cast<uint32_t>(NonSemanticShaderDebugInfoDebugTypeMatrix));
+
 namespace spvtools {
 namespace val {
 namespace {
@@ -84,12 +226,24 @@
 using ValidateVulkan100DebugInfoDebugValue =
     spvtest::ValidateBase<std::pair<std::string, std::string>>;
 using ValidateVulkan100DebugInfo = spvtest::ValidateBase<std::string>;
+using ValidateVulkan101DebugInfo = spvtest::ValidateBase<std::string>;
 
-const static std::string shader_extension = R"(
+const static std::string shader_extension_100 = R"(
 OpExtension "SPV_KHR_non_semantic_info"
 %DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
 )";
 
+const static std::string shader_extension_101 = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.101"
+)";
+
+// Extension string for a future NSDI version; exercises forward-compatibility.
+const static std::string shader_extension_9999 = R"(
+OpExtension "SPV_KHR_non_semantic_info"
+%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.9999"
+)";
+
 const static std::string opencl_extension = R"(
 %DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
 )";
@@ -113,16 +267,9 @@
   ss << "%extinst = OpExtInstImport \"GLSL.std.450\"\n";
   ss << "OpMemoryModel Logical GLSL450\n";
   ss << "OpEntryPoint " << execution_model << " %main \"main\""
-     << " %f32_output"
-     << " %f32vec2_output"
-     << " %u32_output"
-     << " %u32vec2_output"
-     << " %u64_output"
-     << " %f32_input"
-     << " %f32vec2_input"
-     << " %u32_input"
-     << " %u32vec2_input"
-     << " %u64_input"
+     << " %f32_output" << " %f32vec2_output" << " %u32_output"
+     << " %u32vec2_output" << " %u64_output" << " %f32_input"
+     << " %f32vec2_input" << " %u32_input" << " %u32vec2_input" << " %u64_input"
      << "\n";
   if (execution_model == "Fragment") {
     ss << "OpExecutionMode %main OriginUpperLeft\n";
@@ -359,7 +506,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", "", dbg_inst, shader_extension, "Vertex"));
+      src, "", "", dbg_inst, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -427,8 +574,9 @@
 %foo_val = OpLoad %u32 %foo
 )";
 
-  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header + GetParam(), body, shader_extension, "Vertex"));
+  CompileSuccessfully(
+      GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header + GetParam(),
+                                     body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("DebugScope, DebugNoScope, DebugDeclare, DebugValue "
@@ -511,7 +659,7 @@
 %float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
 %v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
 %main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info
-%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_name FlagIsPublic 13 %main
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 13 %main
 )";
 
   const std::string body = R"(
@@ -742,7 +890,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst, "", shader_extension, "Vertex"));
+      src, "", dbg_inst, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("expected operand Source must be a result id of "
@@ -796,7 +944,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("expected operand Name must be a result id of "
@@ -850,7 +998,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("expected operand Size must be a result id of "
@@ -875,7 +1023,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, dbg_inst_header, "", shader_extension, "Vertex"));
+      src, constants, dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("expected operand Flags must be a result id of 32-bit "
@@ -1014,7 +1162,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -1037,7 +1185,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -1238,6 +1386,36 @@
                         "integer scalar type"));
 }
 
+TEST_F(ValidateOpenCL100DebugInfo,
+       DebugTypeArrayOpSpecConstantComponentCountFail) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+%spec_u32 = OpSpecConstant %u32 4
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %spec_u32
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", opencl_extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Component Count must be OpConstant with a 32- or "
+                        "64-bits integer scalar type or DebugGlobalVariable or "
+                        "DebugLocalVariable with a 32- or 64-bits unsigned "
+                        "integer scalar type"));
+}
+
 TEST_F(ValidateVulkan100DebugInfo, DebugTypeArray) {
   const std::string src = R"(
 %src = OpString "simple.hlsl"
@@ -1253,7 +1431,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -1282,7 +1460,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, dbg_inst_header, "", shader_extension, "Vertex"));
+      src, constants, dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -1301,7 +1479,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("expected operand Base Type is not a valid debug "
@@ -1323,13 +1501,13 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Component Count must be OpConstant with a 32- or "
-                        "64-bits integer scalar type or DebugGlobalVariable or "
-                        "DebugLocalVariable with a 32- or 64-bits unsigned "
-                        "integer scalar type"));
+              HasSubstr("Component Count must be a constant instruction with a "
+                        "32- or 64-bits integer scalar type or "
+                        "DebugGlobalVariable or DebugLocalVariable with a 32- "
+                        "or 64-bits unsigned integer scalar type"));
 }
 
 TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCountFloat) {
@@ -1347,13 +1525,13 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Component Count must be OpConstant with a 32- or "
-                        "64-bits integer scalar type or DebugGlobalVariable or "
-                        "DebugLocalVariable with a 32- or 64-bits unsigned "
-                        "integer scalar type"));
+              HasSubstr("Component Count must be a constant instruction with a "
+                        "32- or 64-bits integer scalar type or "
+                        "DebugGlobalVariable or DebugLocalVariable with a 32- "
+                        "or 64-bits unsigned integer scalar type"));
 }
 
 TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayComponentCountZero) {
@@ -1371,7 +1549,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -1398,13 +1576,62 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, dbg_inst_header, "", shader_extension, "Vertex"));
+      src, constants, dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Component Count must be OpConstant with a 32- or "
-                        "64-bits integer scalar type or DebugGlobalVariable or "
-                        "DebugLocalVariable with a 32- or 64-bits unsigned "
-                        "integer scalar type"));
+              HasSubstr("Component Count must be a constant instruction with a "
+                        "32- or 64-bits integer scalar type or "
+                        "DebugGlobalVariable or DebugLocalVariable with a 32- "
+                        "or 64-bits unsigned integer scalar type"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayOpSpecConstantComponentCount) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string constants = R"(
+%spec_u32 = OpSpecConstant %u32 4
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %spec_u32
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, constants, dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan100DebugInfo,
+       DebugTypeArrayOpSpecConstantOpComponentCount) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string constants = R"(
+%spec_a = OpSpecConstant %u32 2
+%spec_b = OpSpecConstant %u32 2
+%spec_op = OpSpecConstantOp %u32 IMul %spec_a %spec_b
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %spec_op
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, constants, dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
 TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVector) {
@@ -1523,7 +1750,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -1542,7 +1769,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("expected operand Base Type must be a result id of "
@@ -1564,7 +1791,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Component Count must be positive "
@@ -1586,7 +1813,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Component Count must be positive "
@@ -1613,7 +1840,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, dbg_inst_header, "", shader_extension, "Vertex"));
+      src, constants, dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -1637,7 +1864,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, dbg_inst_header, "", shader_extension, "Vertex"));
+      src, constants, dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("expected operand Vector Type must be a result id of "
@@ -1664,7 +1891,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, dbg_inst_header, "", shader_extension, "Vertex"));
+      src, constants, dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("expected operand Vector Count must be a result id of "
@@ -1691,7 +1918,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, dbg_inst_header, "", shader_extension, "Vertex"));
+      src, constants, dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Vector Count must be positive "
@@ -1718,7 +1945,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, dbg_inst_header, "", shader_extension, "Vertex"));
+      src, constants, dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Vector Count must be positive "
@@ -1808,7 +2035,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -1831,7 +2058,7 @@
   ss << param.first;
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", ss.str(), "", shader_extension, "Vertex"));
+      src, "", ss.str(), "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("expected operand " + param.second +
@@ -1951,7 +2178,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -1970,7 +2197,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -1992,7 +2219,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -2103,7 +2330,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -2126,7 +2353,7 @@
   ss << param.first;
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", ss.str(), "", shader_extension, "Vertex"));
+      src, "", ss.str(), "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("expected operand " + param.second));
@@ -2191,7 +2418,7 @@
 %v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
 %VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic
 %main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info
-%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_name FlagIsPublic 13 %main
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 6 1 %comp_unit %main_name FlagIsPublic 13 %main
 %foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic
 %child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic
 )";
@@ -2236,7 +2463,7 @@
 %v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
 %VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic
 %main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info
-%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_name FlagIsPublic 13 %main
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 13 %main
 %foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic
 %child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic
 )";
@@ -2418,7 +2645,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, dbg_inst_header, "", shader_extension, "Vertex"));
+      src, constants, dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -2456,7 +2683,7 @@
   ss << param.first;
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, ss.str(), "", shader_extension, "Vertex"));
+      src, constants, ss.str(), "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("expected operand " + param.second + " must be "));
@@ -2517,7 +2744,7 @@
   ss << param.first;
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, ss.str(), "", shader_extension, "Vertex"));
+      src, constants, ss.str(), "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   if (!param.second.empty()) {
     EXPECT_THAT(getDiagnosticString(),
@@ -2563,8 +2790,8 @@
 %dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
 %comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
 %main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
-%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_name FlagIsPublic
-%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_name FlagIsPublic 13 %main)";
+%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 4 1 %comp_unit %main_name FlagIsPublic
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 4 1 %comp_unit %main_name FlagIsPublic 13 %main)";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
       src, "", dbg_inst_header, "", opencl_extension, "Vertex"));
@@ -2588,7 +2815,7 @@
 %dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
 %comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
 %main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
-%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_name FlagIsPublic
+%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 4 1 %comp_unit %main_name FlagIsPublic
 %main_info = OpExtInst %void %DbgExt DebugFunction )"
      << param.first;
 
@@ -2603,25 +2830,25 @@
     AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugFunction,
     ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
         std::make_pair(
-            R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_name FlagIsPublic 13 %main)",
+            R"(%u32_0 %main_type_info %dbg_src 4 1 %comp_unit %main_name FlagIsPublic 13 %main)",
             "Name"),
         std::make_pair(
-            R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_name FlagIsPublic 13 %main)",
+            R"(%main_name %dbg_src %dbg_src 4 1 %comp_unit %main_name FlagIsPublic 13 %main)",
             "Type"),
         std::make_pair(
-            R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_name FlagIsPublic 13 %main)",
+            R"(%main_name %main_type_info %comp_unit 4 1 %comp_unit %main_name FlagIsPublic 13 %main)",
             "Source"),
         std::make_pair(
-            R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_name FlagIsPublic 13 %main)",
+            R"(%main_name %main_type_info %dbg_src 4 1 %dbg_src %main_name FlagIsPublic 13 %main)",
             "Parent"),
         std::make_pair(
-            R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic 13 %main)",
+            R"(%main_name %main_type_info %dbg_src 4 1 %comp_unit %void FlagIsPublic 13 %main)",
             "Linkage Name"),
         std::make_pair(
-            R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_name FlagIsPublic 13 %void)",
+            R"(%main_name %main_type_info %dbg_src 4 1 %comp_unit %main_name FlagIsPublic 13 %void)",
             "Function"),
         std::make_pair(
-            R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_name FlagIsPublic 13 %main %dbg_src)",
+            R"(%main_name %main_type_info %dbg_src 4 1 %comp_unit %main_name FlagIsPublic 13 %main %dbg_src)",
             "Declaration"),
     }));
 
@@ -2657,19 +2884,19 @@
     ValidateOpenCL100DebugInfoDebugFunctionDeclaration,
     ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
         std::make_pair(
-            R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_name FlagIsPublic)",
+            R"(%u32_0 %main_type_info %dbg_src 4 1 %comp_unit %main_name FlagIsPublic)",
             "Name"),
         std::make_pair(
-            R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_name FlagIsPublic)",
+            R"(%main_name %dbg_src %dbg_src 4 1 %comp_unit %main_name FlagIsPublic)",
             "Type"),
         std::make_pair(
-            R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_name FlagIsPublic)",
+            R"(%main_name %main_type_info %comp_unit 4 1 %comp_unit %main_name FlagIsPublic)",
             "Source"),
         std::make_pair(
-            R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_name FlagIsPublic)",
+            R"(%main_name %main_type_info %dbg_src 4 1 %dbg_src %main_name FlagIsPublic)",
             "Parent"),
         std::make_pair(
-            R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic)",
+            R"(%main_name %main_type_info %dbg_src 4 1 %comp_unit %void FlagIsPublic)",
             "Linkage Name"),
     }));
 
@@ -2683,21 +2910,16 @@
 "
 )";
 
-  const std::string constants = R"(
-%u32_12 = OpConstant %u32 12
-%u32_13 = OpConstant %u32 13
-)";
-
   const std::string dbg_inst_header = R"(
 %dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
 %comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
 %main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void
-%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_name %u32_3
-%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_name %u32_3 %u32_13
+%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src %u32_4 %u32_1 %comp_unit %main_name %u32_3
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_4 %u32_1 %comp_unit %main_name %u32_3 %u32_4
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -2711,11 +2933,6 @@
 "
 )";
 
-  const std::string constants = R"(
-%u32_12 = OpConstant %u32 12
-%u32_13 = OpConstant %u32 13
-)";
-
   const auto& param = GetParam();
 
   std::ostringstream ss;
@@ -2723,12 +2940,12 @@
 %dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
 %comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
 %main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void
-%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_name %u32_3
+%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src %u32_4 %u32_1 %comp_unit %main_name %u32_3
 %main_info = OpExtInst %void %DbgExt DebugFunction )"
      << param.first;
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, ss.str(), "", shader_extension, "Vertex"));
+      src, "", ss.str(), "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("expected operand " + param.second));
@@ -2738,22 +2955,22 @@
     AllVulkan100DebugInfoFail, ValidateVulkan100DebugInfoDebugFunction,
     ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
         std::make_pair(
-            R"(%u32_0 %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_name %u32_3 %u32_13)",
+            R"(%u32_0 %main_type_info %dbg_src %u32_4 %u32_1 %comp_unit %main_name %u32_3 %u32_4)",
             "Name"),
         std::make_pair(
-            R"(%main_name %dbg_src %dbg_src %u32_12 %u32_1 %comp_unit %main_name %u32_3 %u32_13)",
+            R"(%main_name %dbg_src %dbg_src %u32_4 %u32_1 %comp_unit %main_name %u32_3 %u32_4)",
             "Type"),
         std::make_pair(
-            R"(%main_name %main_type_info %comp_unit %u32_12 %u32_1 %comp_unit %main_name %u32_3 %u32_13)",
+            R"(%main_name %main_type_info %comp_unit %u32_4 %u32_1 %comp_unit %main_name %u32_3 %u32_4)",
             "Source"),
         std::make_pair(
-            R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %dbg_src %main_name %u32_3 %u32_13)",
+            R"(%main_name %main_type_info %dbg_src %u32_4 %u32_1 %dbg_src %main_name %u32_3 %u32_4)",
             "Parent"),
         std::make_pair(
-            R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %void %u32_3 %u32_13)",
+            R"(%main_name %main_type_info %dbg_src %u32_4 %u32_1 %comp_unit %void %u32_3 %u32_4)",
             "Linkage Name"),
         std::make_pair(
-            R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_name %u32_3 %u32_13 %dbg_src)",
+            R"(%main_name %main_type_info %dbg_src %u32_4 %u32_1 %comp_unit %main_name %u32_3 %u32_4 %dbg_src)",
             "Declaration"),
     }));
 
@@ -2767,11 +2984,6 @@
 "
 )";
 
-  const std::string constants = R"(
-%u32_12 = OpConstant %u32 12
-%u32_13 = OpConstant %u32 13
-)";
-
   const auto& param = GetParam();
 
   std::ostringstream ss;
@@ -2783,7 +2995,7 @@
      << param.first;
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, ss.str(), "", shader_extension, "Vertex"));
+      src, "", ss.str(), "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("expected operand " + param.second));
@@ -2794,19 +3006,19 @@
     ValidateVulkan100DebugInfoDebugFunctionDeclaration,
     ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
         std::make_pair(
-            R"(%u32_0 %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %main_name %u32_3)",
+            R"(%u32_0 %main_type_info %dbg_src %u32_4 %u32_1 %comp_unit %main_name %u32_3)",
             "Name"),
         std::make_pair(
-            R"(%main_name %dbg_src %dbg_src %u32_12 %u32_1 %comp_unit %main_name %u32_3)",
+            R"(%main_name %dbg_src %dbg_src %u32_4 %u32_1 %comp_unit %main_name %u32_3)",
             "Type"),
         std::make_pair(
-            R"(%main_name %main_type_info %comp_unit %u32_12 %u32_1 %comp_unit %main_name %u32_3)",
+            R"(%main_name %main_type_info %comp_unit %u32_4 %u32_1 %comp_unit %main_name %u32_3)",
             "Source"),
         std::make_pair(
-            R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %dbg_src %main_name %u32_3)",
+            R"(%main_name %main_type_info %dbg_src %u32_4 %u32_1 %dbg_src %main_name %u32_3)",
             "Parent"),
         std::make_pair(
-            R"(%main_name %main_type_info %dbg_src %u32_12 %u32_1 %comp_unit %void %u32_3)",
+            R"(%main_name %main_type_info %dbg_src %u32_4 %u32_1 %comp_unit %void %u32_3)",
             "Linkage Name"),
     }));
 
@@ -2831,7 +3043,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, dbg_inst_header, "", shader_extension, "Vertex"));
+      src, constants, dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("DebugFunction: expected operand Type must be a result "
@@ -2939,7 +3151,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -2959,7 +3171,7 @@
      << param.first;
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", ss.str(), "", shader_extension, "Vertex"));
+      src, "", ss.str(), "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("expected operand " + param.second));
@@ -2991,7 +3203,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, body, shader_extension, "Vertex"));
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope"));
 }
@@ -3012,7 +3224,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, body, shader_extension, "Vertex"));
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined At"));
 }
@@ -3107,7 +3319,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, dbg_inst_header, "", shader_extension, "Vertex"));
+      src, constants, dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -3134,7 +3346,7 @@
      << param.first;
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, ss.str(), "", shader_extension, "Vertex"));
+      src, constants, ss.str(), "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("expected operand " + param.second));
@@ -3227,7 +3439,7 @@
          %16 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %void %15
          %18 = OpExtInst %void %1 DebugFunction %17 %16 %12 1 1 %13 %17 FlagIsProtected|FlagIsPrivate 1 %src_main
          %21 = OpExtInst %void %1 DebugLocalVariable %20 %15 %12 1 17 %18 FlagIsLocal 0
-         %22 = OpExtInst %void %1 DebugLexicalBlock %12 1 28 %18
+         %22 = OpExtInst %void %1 DebugLexicalBlock %12 1 20 %18
                OpLine %4 1 1
        %main = OpFunction %void None %23
          %24 = OpLabel
@@ -3319,7 +3531,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, dbg_inst_header, body, shader_extension, "Vertex"));
+      src, constants, dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -3356,7 +3568,6 @@
       %u32_5 = OpConstant %uint 5
      %u32_10 = OpConstant %uint 10
      %u32_17 = OpConstant %uint 17
-     %u32_28 = OpConstant %uint 28
      %u32_32 = OpConstant %uint 32
     %uint_32 = OpConstant %uint 32
       %float = OpTypeFloat 32
@@ -3374,7 +3585,7 @@
          %16 = OpExtInst %void %1 DebugTypeFunction %u32_3 %void %15
          %18 = OpExtInst %void %1 DebugFunction %17 %16 %12 %u32_1 %u32_1 %13 %17 %u32_3 %u32_1
          %21 = OpExtInst %void %1 DebugLocalVariable %20 %15 %12 %u32_1 %u32_17 %18 %u32_4 %u32_0
-         %22 = OpExtInst %void %1 DebugLexicalBlock %12 %u32_1 %u32_28 %18
+         %22 = OpExtInst %void %1 DebugLexicalBlock %12 %u32_1 %u32_4 %18
        %main = OpFunction %void None %23
          %24 = OpLabel
 %param_var_foo = OpVariable %_ptr_Function_float Function
@@ -3420,8 +3631,9 @@
 %decl = OpExtInst %void %DbgExt DebugDeclare )"
      << param.first;
 
-  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, dbg_inst_header, ss.str(), shader_extension, "Vertex"));
+  CompileSuccessfully(
+      GenerateShaderCodeForDebugInfo(src, constants, dbg_inst_header, ss.str(),
+                                     shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("expected operand " + param.second));
@@ -3470,7 +3682,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      "", "", dbg_inst_header, "", shader_extension, "Vertex"));
+      "", "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -3481,7 +3693,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      "", "", dbg_inst_header, "", shader_extension, "Vertex"));
+      "", "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -3677,7 +3889,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -3705,7 +3917,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -3732,7 +3944,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -3757,7 +3969,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("expected operand Target must be DebugTypeComposite or "
@@ -3786,7 +3998,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -3962,7 +4174,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -3984,7 +4196,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -4005,7 +4217,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -4025,7 +4237,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -4048,7 +4260,7 @@
      << param.first;
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", ss.str(), "", shader_extension, "Vertex"));
+      src, "", ss.str(), "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("expected operand " + param.second));
@@ -4175,7 +4387,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, body, shader_extension, "Vertex"));
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -4200,7 +4412,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, body, shader_extension, "Vertex"));
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope"));
 }
@@ -4226,7 +4438,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, body, shader_extension, "Vertex"));
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined"));
 }
@@ -4365,7 +4577,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, dbg_inst_header, body, shader_extension, "Vertex"));
+      src, constants, dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -4399,7 +4611,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, dbg_inst_header, body, shader_extension, "Vertex"));
+      src, constants, dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -4431,8 +4643,9 @@
 %decl = OpExtInst %void %DbgExt DebugValue )"
      << param.first;
 
-  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, dbg_inst_header, ss.str(), shader_extension, "Vertex"));
+  CompileSuccessfully(
+      GenerateShaderCodeForDebugInfo(src, constants, dbg_inst_header, ss.str(),
+                                     shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("expected operand " + param.second));
@@ -4625,7 +4838,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, body, shader_extension, "Vertex"));
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -4647,7 +4860,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, body, shader_extension, "Vertex"));
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("DebugFunctionDefinition: expected operand Function "
@@ -4672,7 +4885,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, body, shader_extension, "Vertex"));
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("DebugFunctionDefinition: expected operand Definition "
@@ -4700,7 +4913,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, body, shader_extension, "Vertex"));
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("DebugFunctionDefinition: Was used multiple times in "
@@ -4733,7 +4946,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, body, shader_extension, "Vertex"));
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("DebugFunctionDefinition: Was referenced a "
@@ -4765,7 +4978,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, body, shader_extension, "Vertex"));
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("DebugFunctionDefinition: operand Definition must "
@@ -4799,7 +5012,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, constants, dbg_inst_header, body, shader_extension, "Vertex"));
+      src, constants, dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("DebugFunctionDefinition: must be in the entry basic "
@@ -4831,14 +5044,15 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, body, shader_extension, "Vertex"));
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
 TEST_F(ValidateVulkan100DebugInfo, DebugLine) {
   const std::string src = R"(
 %src = OpString "simple.hlsl"
-%code = OpString "int main() { }"
+%code = OpString "int main() {
+}"
 )";
 
   const std::string dbg_inst_header = R"(
@@ -4853,7 +5067,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, body, shader_extension, "Vertex"));
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -4869,7 +5083,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("debug info extension must appear in a function body"));
@@ -4887,7 +5101,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, "", shader_extension, "Vertex"));
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("debug info extension must appear in a function body"));
@@ -4910,7 +5124,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, body, shader_extension, "Vertex"));
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("DebugLine: expected operand Source must be a result "
@@ -4932,7 +5146,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, body, shader_extension, "Vertex"));
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("DebugLine: expected operand Line Start must be a "
@@ -4954,7 +5168,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, body, shader_extension, "Vertex"));
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("DebugLine: expected operand Line Start must be a "
@@ -4977,13 +5191,35 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, body, shader_extension, "Vertex"));
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("DebugLine: expected operand Line Start must be a "
                         "result id of 32-bit unsigned OpConstant"));
 }
 
+TEST_F(ValidateVulkan100DebugInfo, DebugLineLineStartZero) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "int main() { }"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+)";
+
+  const std::string body = R"(
+%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_0 %u32_1 %u32_0 %u32_0
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DebugLine: operand Line Start (0) is not allowed, "
+                        "source lines start at Line 1"));
+}
+
 TEST_F(ValidateVulkan100DebugInfo, DebugLineLineEndSmaller) {
   const std::string src = R"(
 %src = OpString "simple.hlsl"
@@ -4999,7 +5235,7 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, body, shader_extension, "Vertex"));
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -5021,12 +5257,1137 @@
 )";
 
   CompileSuccessfully(GenerateShaderCodeForDebugInfo(
-      src, "", dbg_inst_header, body, shader_extension, "Vertex"));
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DebugLine: operand Column End (0) is less than Column "
+                        "Start (1) when Line Start equals Line End"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugLineColumnEndSmallerMultiline) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "int main() {
+}"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+)";
+
+  const std::string body = R"(
+%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_1 %u32_2 %u32_1 %u32_0
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugLineOutOfBounds) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "// line 1
+int main() {
+}"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+)";
+
+  const std::string body = R"(
+%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_4 %u32_4 %u32_0 %u32_0
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DebugLine: operand Line End (4) is larger then the 3 "
+                        "lines found in the DebugSource text"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugLineColumnOutOfBounds) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "// line 1
+123"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+)";
+
+  const std::string body = R"(
+%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_2 %u32_2 %u32_1 %u32_5
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DebugLine: operand Column End (5) is larger then Line "
+                        "2 column length of 4 found in the DebugSource text"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugLineColumnOutOfBoundsEmptyLine) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "next line is empty
+
+line 3"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+)";
+
+  const std::string body = R"(
+%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_2 %u32_2 %u32_2 %u32_2
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DebugLine: operand Column End (2) is larger then Line "
+                        "2 column length of 1 found in the DebugSource text"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugLineColumnEmptyLine) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "next line is empty
+
+line 3"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+)";
+
+  const std::string body = R"(
+%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_2 %u32_2 %u32_1 %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugLineColumnStartOutOfBounds) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "1
+123
+line 3"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+)";
+
+  const std::string body = R"(
+%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_1 %u32_2 %u32_3 %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("DebugLine: operand Column Start (3) is larger then Line 1 "
+                "column length of 2 found in the DebugSource text"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugLineColumnEndOutOfBounds) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "1
+123
+line 3"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+)";
+
+  const std::string body = R"(
+%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_1 %u32_2 %u32_1 %u32_5
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DebugLine: operand Column End (5) is larger then Line "
+                        "2 column length of 4 found in the DebugSource text"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugLineDebugSourceContinuedSuccess) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "line 1
+line 2
+line 3
+"
+%code2 = OpString "line 4
+line 5"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%dbg_src2 = OpExtInst %void %DbgExt DebugSourceContinued %code2
+)";
+
+  const std::string body = R"(
+%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_4 %u32_5 %u32_1 %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugLineDebugSourceContinued) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "line 1
+line 2
+"
+%code2 = OpString "line 3
+line 4"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%dbg_src2 = OpExtInst %void %DbgExt DebugSourceContinued %code2
+)";
+
+  const std::string body = R"(
+%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_4 %u32_5 %u32_1 %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DebugLine: operand Line End (5) is larger then the 4 "
+                        "lines found in the DebugSource text"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo,
+       DebugLineMultipleDebugSourceContinuedSuccess) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "line 1
+"
+%code2 = OpString "line 2
+line 3
+"
+%code3 = OpString "line 4
+5"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%dbg_src2 = OpExtInst %void %DbgExt DebugSourceContinued %code2
+%dbg_src3 = OpExtInst %void %DbgExt DebugSourceContinued %code3
+)";
+
+  const std::string body = R"(
+%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_5 %u32_5 %u32_1 %u32_2
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugLineMultipleDebugSourceContinued) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "line 1
+"
+%code2 = OpString "line 2
+line 3
+"
+%code3 = OpString "line 4
+5"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%dbg_src2 = OpExtInst %void %DbgExt DebugSourceContinued %code2
+%dbg_src3 = OpExtInst %void %DbgExt DebugSourceContinued %code3
+)";
+
+  const std::string body = R"(
+%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_5 %u32_5 %u32_1 %u32_3
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DebugLine: operand Column End (3) is larger then Line "
+                        "5 column length of 2 found in the DebugSource text"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugSourceLineNoText) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src
+)";
+
+  const std::string body = R"(
+%line1 = OpExtInst %void %DbgExt DebugLine %dbg_src %u32_5 %u32_5 %u32_1 %u32_3
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugTypeMemberLine) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "line 1
+line 2
+line 3"
+%uint_name = OpString "uint"
+%member_name = OpString "x"
+)";
+
+  const std::string constants = R"(
+%u32_128 = OpConstant %u32 128
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%uint_info = OpExtInst %void %DbgExt DebugTypeBasic %uint_name %u32_32 %u32_4 %u32_0
+%x = OpExtInst %void %DbgExt DebugTypeMember %member_name %uint_info %dbg_src %u32_4 %u32_1 %u32_0 %u32_128 %u32_3
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, constants, dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DebugTypeMember: operand Line (4) is larger then the "
+                        "3 lines found in the DebugSource text"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugTypeMemberColumn) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "line 1
+line 2
+3"
+%uint_name = OpString "uint"
+%member_name = OpString "x"
+)";
+
+  const std::string constants = R"(
+%u32_128 = OpConstant %u32 128
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%uint_info = OpExtInst %void %DbgExt DebugTypeBasic %uint_name %u32_32 %u32_4 %u32_0
+%x = OpExtInst %void %DbgExt DebugTypeMember %member_name %uint_info %dbg_src %u32_3 %u32_3 %u32_0 %u32_128 %u32_3
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, constants, dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("DebugTypeMember: operand Column End (3) is larger then Line 3 "
+                "column length of 2 found in the DebugSource text"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugFunctionLine) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "int main() { }"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_2 %u32_1 %comp_unit %main_name %u32_3 %u32_1
+)";
+
+  const std::string body = R"(
+%main_def = OpExtInst %void %DbgExt DebugFunctionDefinition %main_info %main
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DebugFunction: operand Line (2) is larger then the "
+                        "1 lines found in the DebugSource text"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugFunctionDeclarationLine) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "int main() { }"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction %u32_3 %void
+%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src %u32_2 %u32_1 %comp_unit %main_name %u32_3
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src %u32_1 %u32_1 %comp_unit %main_name %u32_3 %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("DebugFunctionDeclaration: operand Line (2) is larger then the "
+                "1 lines found in the DebugSource text"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugLexicalBlockLine) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%main_block = OpExtInst %void %DbgExt DebugLexicalBlock %dbg_src %u32_2 %u32_1 %comp_unit %main_name
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("DebugLexicalBlock: operand Line (2) is larger then the "
+                "1 lines found in the DebugSource text"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugTypedefLine) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%foo_info = OpExtInst %void %DbgExt DebugTypedef %foo_name %float_info %dbg_src %u32_2 %u32_1 %comp_unit
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DebugTypedef: operand Line (2) is larger then the "
+                        "1 lines found in the DebugSource text"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugEnumLine) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%none = OpExtInst %void %DbgExt DebugInfoNone
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%foo_info1 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %float_info %dbg_src %u32_2 %u32_1 %comp_unit %u32_32 %u32_3 %u32_0 %foo_name %u32_1 %foo_name
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DebugTypeEnum: operand Line (2) is larger then the "
+                        "1 lines found in the DebugSource text"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugTypeCompositeLine) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src %u32_0 %u32_0 %u32_0 %u32_32 %u32_3
+%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name %u32_1 %dbg_src %u32_2 %u32_0 %comp_unit %foo_name %u32_32 %u32_3 %a
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("DebugTypeComposite: operand Line (2) is larger then the "
+                "1 lines found in the DebugSource text"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugGlobalVariableLine) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src %u32_0 %u32_0 %u32_0 %u32_32 %u32_3
+%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name %u32_1 %dbg_src %u32_1 %u32_0 %comp_unit %foo_name %u32_32 %u32_3 %a
+%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %t %dbg_src %u32_2 %u32_0 %comp_unit %foo_name %f32_input %u32_3
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("DebugGlobalVariable: operand Line (2) is larger then the "
+                "1 lines found in the DebugSource text"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugLocalVariableLine) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%null_expr = OpExtInst %void %DbgExt DebugExpression
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src %u32_2 %u32_1 %comp_unit %u32_4
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("DebugLocalVariable: operand Line (2) is larger then the "
+                "1 lines found in the DebugSource text"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugSourceDebugTypeTemplateParameterLine) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%ty_name = OpString "Texture"
+%t_name = OpString "T"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_none = OpExtInst %void %DbgExt DebugInfoNone
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name %u32_1 %dbg_src %u32_1 %u32_1 %comp_unit %ty_name %dbg_none %u32_3
+%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src %u32_2 %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
-          "DebugLine: operand Column End (0) is less than Column Start (1)"));
+          "DebugTypeTemplateParameter: operand Line (2) is larger then the "
+          "1 lines found in the DebugSource text"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeBasicExtraOperand) {
+  // A module declaring NSDI version 9999 may add an optional trailing operand
+  // to DebugTypeBasic. The parser absorbs it via the VARIABLE_ID sentinel;
+  // the validator accepts it because nsdi_version > kNSDIKnownVersion.
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "int main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_9999, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan100DebugInfo, UnknownInstructionAccepted) {
+  // Opcode 20000 is not defined in NSDI 100 (highest known opcode is 108).
+  // Both the text assembler and binary decoder handle it via the VARIABLE_ID
+  // fallback for unknown non-semantic opcodes; the validator's switch falls
+  // through to SPV_SUCCESS.
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "int main() {}"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%unknown_inst = OpExtInst %void %DbgExt 20000 %u32_0 %u32_1 %u32_2 %u32_3
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeBasicTwoExtraOperands) {
+  // Two extra trailing operands on an NSDI 9999 module; confirms the
+  // VARIABLE_ID sentinel re-expands as many times as needed, not just once.
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "int main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 %u32_1 %u32_2
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_9999, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugSourceExtraOperand) {
+  // DebugSource already has one optional operand (Text) in NSDI 100.  An
+  // additional trailing operand tests the VARIABLE_ID sentinel when a
+  // grammar-defined OPTIONAL_ID is also present.
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "int main() {}"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code %u32_0
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan100DebugInfo, UnknownInstructionNoOperands) {
+  // Opcode 20000 with zero operands; tests the zero-operand edge case.
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "int main() {}"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%unknown_inst = OpExtInst %void %DbgExt 20000
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan100DebugInfo, UnknownInstructionManyOperands) {
+  // Opcode 20000 with eight operands; tests that no loop limit is exceeded.
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "int main() {}"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%unknown_inst = OpExtInst %void %DbgExt 20000 %u32_0 %u32_1 %u32_2 %u32_3 %u32_4 %u32_5 %u32_0 %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugNoScopeExtraOperandInBody) {
+  // DebugNoScope is allowed in function bodies and has zero required operands.
+  // An extra trailing operand exercises the VARIABLE_ID sentinel in the body
+  // context, where isStartOfNewInst() uses the startsWithOp() fast path
+  // rather than the '%result = OpXxx' lookahead used at global scope.
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "int main() {}"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+)";
+
+  const std::string body = R"(
+%no_scope = OpExtInst %void %DbgExt DebugNoScope %u32_0
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, body, shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+// From
+// https://github.com/KhronosGroup/SPIRV-Tools/pull/5986#issuecomment-4156843064
+TEST_F(ValidateVulkan100DebugInfo,
+       DebugLineDebugSourceContinuedEmptyNewLineGood) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "line 1
+line 2
+"
+%code2 = OpString "line 3
+line 4 is really long and hold 32char here"
+
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%dbg_src2 = OpExtInst %void %DbgExt DebugSourceContinued %code2
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src %u32_0 %u32_0 %u32_0 %u32_32 %u32_3
+%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name %u32_1 %dbg_src %u32_4 %u32_32 %comp_unit %foo_name %u32_32 %u32_3 %a
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+// From
+// https://github.com/KhronosGroup/SPIRV-Tools/pull/6623#issuecomment-4206751217
+TEST_F(ValidateVulkan100DebugInfo, DebugLineDebugSourceContinuedSameLine) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code0 = OpString "line 1 here
+"
+%code1 = OpString "line 2 here"
+%code2 = OpString " still on line 2"
+%code3 = OpString " still on line 2 and its long
+line 3
+"
+%code4 = OpString "line 4"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src0 = OpExtInst %void %DbgExt DebugSource %src %code0
+%dbg_src1 = OpExtInst %void %DbgExt DebugSourceContinued %code1
+%dbg_src2 = OpExtInst %void %DbgExt DebugSourceContinued %code2
+%dbg_src3 = OpExtInst %void %DbgExt DebugSourceContinued %code3
+%dbg_src4 = OpExtInst %void %DbgExt DebugSourceContinued %code4
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src0 %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src0 %u32_0 %u32_0 %u32_0 %u32_32 %u32_3
+%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name %u32_1 %dbg_src0 %u32_2 %u32_32 %comp_unit %foo_name %u32_32 %u32_3 %a
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+// From
+// https://github.com/KhronosGroup/SPIRV-Tools/pull/5986#issuecomment-4156843064
+TEST_F(ValidateVulkan100DebugInfo,
+       DebugLineDebugSourceContinuedEmptyNewLineBad) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "line 1
+line 2
+" ; ignored
+%code2 = OpString "line 3
+line 4 and there is no line 5"
+
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%dbg_src2 = OpExtInst %void %DbgExt DebugSourceContinued %code2
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src %u32_0 %u32_0 %u32_0 %u32_32 %u32_3
+%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name %u32_1 %dbg_src %u32_5 %u32_1 %comp_unit %foo_name %u32_32 %u32_3 %a
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DebugTypeComposite: operand Line (5) is larger then "
+                        "the 4 lines found in the DebugSource text"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo,
+       DebugLineDebugSourceContinuedCombinedLineColumnCount) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "line 1" ; 6 column length
+%code2 = OpString "still line 1" ; 12 char
+%code3 = OpString "still line 1" ; 12 char + end-line
+
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%dbg_src2 = OpExtInst %void %DbgExt DebugSourceContinued %code2
+%dbg_src3 = OpExtInst %void %DbgExt DebugSourceContinued %code3
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src %u32_0 %u32_0 %u32_0 %u32_32 %u32_3
+%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name %u32_1 %dbg_src %u32_1 %u32_32 %comp_unit %foo_name %u32_32 %u32_3 %a
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("DebugTypeComposite: operand Column End (32) is larger then "
+                "Line 1 column length of 31 found in the DebugSource text"));
+}
+
+// Tests for NonSemantic.Shader.DebugInfo.101 instructions
+
+TEST_F(ValidateVulkan101DebugInfo, DebugTypeVectorIdEXT) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%vecid_info = OpExtInst %void %DbgExt DebugTypeVectorIdEXT %float_info %u32_4
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_101, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan101DebugInfo, DebugTypeVectorIdEXTWithSpecConst) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%spec_count = OpSpecConstant %u32 4
+%vecid_info = OpExtInst %void %DbgExt DebugTypeVectorIdEXT %float_info %spec_count
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_101, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan101DebugInfo, DebugTypeCooperativeMatrixKHR) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string constants = R"(
+%u32_8 = OpConstant %u32 8
+%u32_16 = OpConstant %u32 16
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%coopmtx_info = OpExtInst %void %DbgExt DebugTypeCooperativeMatrixKHR %float_info %u32_3 %u32_8 %u32_16 %u32_0
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, constants, dbg_inst_header, "", shader_extension_101, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan101DebugInfo, DebugTypeCooperativeMatrixKHRWithSpecConst) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%spec_scope = OpSpecConstant %u32 3
+%spec_rows = OpSpecConstant %u32 8
+%spec_cols = OpSpecConstant %u32 16
+%spec_use = OpSpecConstant %u32 0
+%coopmtx_info = OpExtInst %void %DbgExt DebugTypeCooperativeMatrixKHR %float_info %spec_scope %spec_rows %spec_cols %spec_use
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_101, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan101DebugInfo, DebugTypeBasicWithFPEncoding) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string constants = R"(
+%u32_8 = OpConstant %u32 8
+%u32_4214 = OpConstant %u32 4214
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%fp8_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_8 %u32_3 %u32_0 %u32_4214
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, constants, dbg_inst_header, "", shader_extension_101, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeVectorIdEXTFailVersion) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%vecid_info = OpExtInst %void %DbgExt DebugTypeVectorIdEXT %float_info %u32_4
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DebugTypeVectorIdEXT: requires "
+                        "NonSemantic.Shader.DebugInfo version"));
+}
+
+TEST_F(ValidateVulkan101DebugInfo, DebugTypeVectorIdEXTFailComponentType) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%float_vec = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4
+%vecid_info = OpExtInst %void %DbgExt DebugTypeVectorIdEXT %float_vec %u32_4
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_101, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand Component Type must be a result id "
+                        "of DebugTypeBasic"));
+}
+
+TEST_F(ValidateVulkan101DebugInfo, DebugTypeVectorIdEXTFailComponentCount) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%vecid_info = OpExtInst %void %DbgExt DebugTypeVectorIdEXT %float_info %float_info
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, "", shader_extension_101, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Component Count must be a result id of a 32-bit "
+                        "unsigned integer constant or specialization "
+                        "constant"));
+}
+
+TEST_F(ValidateVulkan100DebugInfo, DebugTypeCooperativeMatrixKHRFailVersion) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string constants = R"(
+%u32_8 = OpConstant %u32 8
+%u32_16 = OpConstant %u32 16
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%coopmtx_info = OpExtInst %void %DbgExt DebugTypeCooperativeMatrixKHR %float_info %u32_3 %u32_8 %u32_16 %u32_0
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, constants, dbg_inst_header, "", shader_extension_100, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DebugTypeCooperativeMatrixKHR: requires "
+                        "NonSemantic.Shader.DebugInfo version"));
+}
+
+TEST_F(ValidateVulkan101DebugInfo,
+       DebugTypeCooperativeMatrixKHRFailComponentType) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string constants = R"(
+%u32_8 = OpConstant %u32 8
+%u32_16 = OpConstant %u32 16
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%float_vec = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4
+%coopmtx_info = OpExtInst %void %DbgExt DebugTypeCooperativeMatrixKHR %float_vec %u32_3 %u32_8 %u32_16 %u32_0
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, constants, dbg_inst_header, "", shader_extension_101, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("expected operand Component Type must be a result id "
+                        "of DebugTypeBasic"));
+}
+
+TEST_F(ValidateVulkan101DebugInfo,
+       DebugTypeCooperativeMatrixKHRFailScopeNotConst) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string constants = R"(
+%u32_8 = OpConstant %u32 8
+%u32_16 = OpConstant %u32 16
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0
+%coopmtx_info = OpExtInst %void %DbgExt DebugTypeCooperativeMatrixKHR %float_info %dbg_src %u32_8 %u32_16 %u32_0
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, constants, dbg_inst_header, "", shader_extension_101, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Scope must be a result id of a 32-bit unsigned "
+                        "integer constant or specialization constant"));
+}
+
+TEST_F(ValidateVulkan101DebugInfo, DebugTypeBasicFPEncodingFailNotConst) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+  const std::string constants = R"(
+%u32_8 = OpConstant %u32 8
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5
+%fp8_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_8 %u32_3 %u32_0 %dbg_src
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, constants, dbg_inst_header, "", shader_extension_101, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("FPEncoding must be a result id of 32-bit unsigned "
+                        "OpConstant"));
 }
 
 }  // namespace
diff --git a/test/val/val_ext_inst_test.cpp b/test/val/val_ext_inst_test.cpp
index 23fd3aa..0c900ef 100644
--- a/test/val/val_ext_inst_test.cpp
+++ b/test/val/val_ext_inst_test.cpp
@@ -7569,6 +7569,92 @@
                 "DebugTypeFunction %uint_0 %12\n"));
 }
 
+using ValidateNSDI = spvtest::ValidateBase<bool>;
+
+TEST_F(ValidateNSDI, ValidVersion100) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+)";
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateNSDI, MissingVersion) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo."
+OpMemoryModel Logical GLSL450
+)";
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("NonSemantic.Shader.DebugInfo import does not encode "
+                        "the version correctly"));
+}
+
+TEST_F(ValidateNSDI, BadVersionNotANumber) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.1a"
+OpMemoryModel Logical GLSL450
+)";
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("NonSemantic.Shader.DebugInfo import does not encode "
+                        "the version correctly"));
+}
+
+TEST_F(ValidateNSDI, BadVersionTooLow) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.99"
+OpMemoryModel Logical GLSL450
+)";
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("NonSemantic.Shader.DebugInfo import version 99 is "
+                        "below the minimum supported version 100"));
+}
+
+TEST_F(ValidateNSDI, AcceptedFutureVersion) {
+  // Any version >= 100 is accepted; later versions are supersets of earlier
+  // ones.
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.999"
+OpMemoryModel Logical GLSL450
+)";
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateNSDI, FutureVersion101) {
+  // Version 101 is >= kNSDIMinVersion; the validator imposes no upper bound.
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.101"
+OpMemoryModel Logical GLSL450
+)";
+  CompileSuccessfully(text);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_extension_spv_ext_descriptor_heap.cpp b/test/val/val_extension_spv_ext_descriptor_heap.cpp
new file mode 100644
index 0000000..e387546
--- /dev/null
+++ b/test/val/val_extension_spv_ext_descriptor_heap.cpp
@@ -0,0 +1,2422 @@
+// Copyright 2025 The Khronos Group 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.
+
+// Tests for SPV_EXT_descriptor_heap
+
+#include <string>
+
+#include "gmock/gmock.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+
+using ValidateSpvEXTDescriptorHeap = spvtest::ValidateBase<bool>;
+
+TEST_F(ValidateSpvEXTDescriptorHeap, Valid) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpCapability ShaderNonUniform
+               OpCapability UniformBufferArrayNonUniformIndexing
+               OpCapability StorageBufferArrayNonUniformIndexing
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %resource_heap %sampler_heap %fragColor %uvs %index
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 460
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpSourceExtension "GL_EXT_nonuniform_qualifier"
+               OpName %main "main"
+               OpName %fragColor "fragColor"
+               OpName %resource_heap "resource_heap"
+               OpName %sampler_heap "sampler_heap"
+               OpName %uvs "uvs"
+               OpName %index "index"
+               OpName %UniformBuffer "UniformBuffer"
+               OpMemberName %UniformBuffer 0 "colorOffset"
+               OpName %StorageBufferA "StorageBufferA"
+               OpMemberName %StorageBufferA 0 "a"
+               OpName %StorageBufferB "StorageBufferB"
+               OpMemberName %StorageBufferB 0 "b"
+               OpDecorate %fragColor Location 0
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorateId %_runtimearr_14 ArrayStrideIdEXT %17
+               OpDecorate %sampler_heap BuiltIn SamplerHeapEXT
+               OpDecorateId %_runtimearr_22 ArrayStrideIdEXT %uint_2
+               OpDecorate %uvs Location 0
+               OpDecorate %index Flat
+               OpDecorate %index Location 1
+               OpDecorate %38 NonUniform
+               OpDecorate %UniformBuffer Block
+               OpMemberDecorate %UniformBuffer 0 Offset 0
+               OpDecorateId %_runtimearr_41 ArrayStrideIdEXT %42
+               OpDecorate %44 NonUniform
+               OpDecorate %46 NonUniform
+               OpDecorate %48 NonUniform
+               OpDecorate %StorageBufferA Block
+               OpMemberDecorate %StorageBufferA 0 Offset 0
+               OpDecorateId %_runtimearr_58 ArrayStrideIdEXT %59
+               OpDecorate %64 NonUniform
+               OpDecorate %StorageBufferB Block
+               OpMemberDecorate %StorageBufferB 0 Offset 0
+               OpDecorateId %_runtimearr_58_0 ArrayStrideIdEXT %69
+               OpDecorate %71 NonUniform
+               OpDecorate %73 NonUniform
+               OpDecorate %77 NonUniform
+               OpDecorate %44 NonReadable
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+  %fragColor = OpVariable %_ptr_Output_v4float Output
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+        %int = OpTypeInt 32 1
+     %int_27 = OpConstant %int 27
+         %14 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform
+         %17 = OpConstantSizeOfEXT %int %14
+%_runtimearr_14 = OpTypeRuntimeArray %14
+%sampler_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+      %int_0 = OpConstant %int 0
+         %22 = OpTypeSampler
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+%_runtimearr_22 = OpTypeRuntimeArray %22
+         %28 = OpTypeSampledImage %14
+    %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+        %uvs = OpVariable %_ptr_Input_v2float Input
+%_ptr_Input_uint = OpTypePointer Input %uint
+      %index = OpVariable %_ptr_Input_uint Input
+%UniformBuffer = OpTypeStruct %v4float
+         %41 = OpTypeBufferEXT Uniform
+         %42 = OpConstantSizeOfEXT %int %41
+%_runtimearr_41 = OpTypeRuntimeArray %41
+      %int_1 = OpConstant %int 1
+%StorageBufferA = OpTypeStruct %v4float
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+    %float_4 = OpConstant %float 4
+         %55 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4
+%_ptr_StorageBuffer = OpTypeUntypedPointerKHR StorageBuffer
+         %58 = OpTypeBufferEXT StorageBuffer
+         %59 = OpConstantSizeOfEXT %int %58
+%_runtimearr_58 = OpTypeRuntimeArray %58
+    %v3float = OpTypeVector %float 3
+%StorageBufferB = OpTypeStruct %v3float
+     %uint_0 = OpConstant %uint 0
+         %69 = OpConstantSizeOfEXT %int %58
+%_runtimearr_58_0 = OpTypeRuntimeArray %58
+%_ptr_Output_float = OpTypePointer Output %float
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %16 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_14 %resource_heap %int_27
+         %19 = OpLoad %14 %16
+         %23 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_22 %sampler_heap %int_0
+         %27 = OpLoad %22 %23
+         %29 = OpSampledImage %28 %19 %27
+         %33 = OpLoad %v2float %uvs
+         %34 = OpImageSampleImplicitLod %v4float %29 %33
+               OpStore %fragColor %34
+         %37 = OpLoad %uint %index
+         %38 = OpCopyObject %uint %37
+         %40 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_41 %resource_heap %38
+         %44 = OpBufferPointerEXT %_ptr_Uniform %40
+         %45 = OpUntypedAccessChainKHR %_ptr_Uniform %UniformBuffer %44 %int_0
+         %46 = OpLoad %v4float %45
+         %47 = OpLoad %v4float %fragColor
+         %48 = OpFAdd %v4float %47 %46
+               OpStore %fragColor %48
+         %57 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_58 %resource_heap %int_1
+         %61 = OpBufferPointerEXT %_ptr_StorageBuffer %57
+         %62 = OpUntypedAccessChainKHR %_ptr_StorageBuffer %StorageBufferA %61 %int_0
+               OpStore %62 %55
+         %63 = OpLoad %uint %index
+         %64 = OpCopyObject %uint %63
+         %68 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_58_0 %resource_heap %64
+         %71 = OpBufferPointerEXT %_ptr_StorageBuffer %68
+         %72 = OpUntypedAccessChainKHR %_ptr_StorageBuffer %StorageBufferB %71 %int_0 %uint_0
+         %73 = OpLoad %float %72
+         %75 = OpAccessChain %_ptr_Output_float %fragColor %uint_0
+         %76 = OpLoad %float %75
+         %77 = OpFAdd %float %76 %73
+         %78 = OpAccessChain %_ptr_Output_float %fragColor %uint_0
+               OpStore %78 %77
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, OffsetId64BitIndexingGood) {
+  const std::string str = R"(
+        OpCapability Shader
+        OpCapability Shader64BitIndexingEXT
+        OpCapability DescriptorHeapEXT
+        OpCapability Int64
+        OpExtension "SPV_EXT_descriptor_heap"
+        OpExtension "SPV_EXT_shader_64bit_indexing"
+  %1 = OpExtInstImport "GLSL.std.450"
+        OpMemoryModel Logical GLSL450
+        OpEntryPoint GLCompute %2 "main" %3
+        OpExecutionMode %2 LocalSize 1 1 1
+        OpSource GLSL 450
+        OpMemberDecorate %4 0 Offset 0
+        OpMemberDecorate %4 1 Offset 4
+        OpMemberDecorate %4 2 Offset 8
+        OpMemberDecorate %4 3 Offset 12
+        OpDecorate %4 Block
+        OpDecorate %3 Binding 0
+        OpDecorate %3 DescriptorSet 0
+  %5 = OpTypeVoid
+  %6 = OpTypeFunction %5
+  %7 = OpTypeInt 32 0
+  %8 = OpTypeInt 64 0
+  %4 = OpTypeStruct %7 %7 %7 %7
+  %9 = OpTypePointer StorageBuffer %4
+  %3 = OpVariable %9 StorageBuffer
+  %10 = OpTypeInt 32 1
+  %11 = OpConstant %10 0
+  %12 = OpConstant %10 1
+  %13 = OpConstant %10 2
+  %14 = OpConstant %10 3
+  %15 = OpTypePointer StorageBuffer %7
+  %16 = OpTypeBufferEXT StorageBuffer
+  %17 = OpTypeBufferEXT Uniform
+  %18 = OpTypeImage %7 2D 0 0 0 1 Unknown
+  %19 = OpTypeSampler
+  %20 = OpConstantSizeOfEXT %8 %16
+  %21 = OpConstantSizeOfEXT %8 %17
+  %22 = OpConstantSizeOfEXT %8 %18
+  %23 = OpConstantSizeOfEXT %8 %19
+  %2 = OpFunction %5 None %6
+  %24 = OpLabel
+  %25 = OpAccessChain %15 %3 %11
+  %26 = OpAccessChain %15 %3 %12
+  %27 = OpAccessChain %15 %3 %13
+  %28 = OpAccessChain %15 %3 %14
+  %29 = OpUConvert %7 %20
+  %30 = OpUConvert %7 %21
+  %31 = OpUConvert %7 %22
+  %32 = OpUConvert %7 %23
+        OpStore %25 %29
+        OpStore %26 %30
+        OpStore %27 %31
+        OpStore %28 %32
+        OpReturn
+        OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, AtomicImageFuncs) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability Image1D
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %resource_heap
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 460
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpName %main "main"
+               OpName %resource_heap "resource_heap"
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorateId %_runtimearr_10 ArrayStrideIdEXT %13
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+         %10 = OpTypeImage %int 1D 0 0 0 2 R32i
+         %13 = OpConstantSizeOfEXT %int %10
+%_runtimearr_10 = OpTypeRuntimeArray %10
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+%_ptr_Image_int = OpTypePointer Image %int
+ %_ptr_Image = OpTypeUntypedPointerKHR Image
+     %uint_1 = OpConstant %uint 1
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %12 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_10 %resource_heap %int_1
+         %19 = OpUntypedImageTexelPointerEXT %_ptr_Image %10 %12 %int_1 %uint_0
+         %21 = OpAtomicIAdd %int %19 %uint_1 %uint_0 %int_1
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, ValidMemberDecorateIdExt) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpCapability BindlessTextureNV
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpExtension "SPV_NV_bindless_texture"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpSamplerImageAddressingModeNV 64
+               OpEntryPoint Fragment %main "main" %resource_heap %fragColor
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 460
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpSourceExtension "GL_EXT_nonuniform_qualifier"
+               OpName %main "main"
+               OpName %fragColor "fragColor"
+               OpName %resource_heap "resource_heap"
+               OpName %U "U"
+               OpMemberName %U 0 "uv"
+               OpMemberName %U 1 "k"
+               OpMemberName %U 2 "g"
+               OpDecorate %fragColor Location 0
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorate %U Block
+               OpMemberDecorateIdEXT %U 0 OffsetIdEXT %specA
+               OpMemberDecorateIdEXT %U 1 OffsetIdEXT %specB
+               OpMemberDecorateIdEXT %U 2 OffsetIdEXT %specC
+               OpDecorateId %_runtimearr_20 ArrayStrideIdEXT %uint_2
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+%_ptr_Output_v2float = OpTypePointer Output %v2float
+  %fragColor = OpVariable %_ptr_Output_v2float Output
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+        %int = OpTypeInt 32 1
+      %int_3 = OpConstant %int 3
+       %uint = OpTypeInt 32 0
+     %v3uint = OpTypeVector %uint 3
+  %samplerTy = OpTypeSampler
+%_runtimearr_sampler = OpTypeRuntimeArray %samplerTy
+      %specA = OpSpecConstant %int 0
+      %specB = OpSpecConstant %int 4
+      %specC = OpSpecConstant %int 8
+          %U = OpTypeStruct %samplerTy %samplerTy %_runtimearr_sampler
+      %int_0 = OpConstant %int 0
+%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform
+         %20 = OpTypeBufferEXT Uniform
+     %uint_2 = OpConstant %uint 2
+%_runtimearr_20 = OpTypeRuntimeArray %20
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %19 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_20 %resource_heap %int_3
+         %23 = OpBufferPointerEXT %_ptr_Uniform %19
+         %24 = OpUntypedAccessChainKHR %_ptr_Uniform %U %23 %int_0
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, ValidMemberDecorateIdExt2) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpCapability BindlessTextureNV
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpExtension "SPV_NV_bindless_texture"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpSamplerImageAddressingModeNV 64
+               OpEntryPoint Fragment %main "main" %resource_heap %fragColor
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 460
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpSourceExtension "GL_EXT_nonuniform_qualifier"
+               OpName %main "main"
+               OpName %fragColor "fragColor"
+               OpName %resource_heap "resource_heap"
+               OpName %U "U"
+               OpMemberName %U 0 "uv"
+               OpMemberName %U 1 "k"
+               OpMemberName %U 2 "g"
+               OpDecorate %fragColor Location 0
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorate %U Block
+               OpMemberDecorateIdEXT %U 0 OffsetIdEXT %specA
+               OpMemberDecorateIdEXT %U 1 OffsetIdEXT %specB
+               OpMemberDecorateIdEXT %U 2 OffsetIdEXT %specC
+               OpDecorateId %_runtimearr_20 ArrayStrideIdEXT %uint_2
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+%_ptr_Output_v2float = OpTypePointer Output %v2float
+  %fragColor = OpVariable %_ptr_Output_v2float Output
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+        %int = OpTypeInt 32 1
+      %int_3 = OpConstant %int 3
+       %uint = OpTypeInt 32 0
+     %v3uint = OpTypeVector %uint 3
+  %samplerTy = OpTypeSampler
+%_runtimearr_sampler = OpTypeRuntimeArray %samplerTy
+      %specA = OpSpecConstant %int 0
+      %specB = OpSpecConstant %int 4
+      %specC = OpSpecConstant %int 8
+          %U = OpTypeStruct %v2float %int %_runtimearr_sampler
+      %int_0 = OpConstant %int 0
+%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform
+         %20 = OpTypeBufferEXT Uniform
+     %uint_2 = OpConstant %uint 2
+%_runtimearr_20 = OpTypeRuntimeArray %20
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %19 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_20 %resource_heap %int_3
+         %23 = OpBufferPointerEXT %_ptr_Uniform %19
+         %24 = OpUntypedAccessChainKHR %_ptr_Uniform %U %23 %int_0
+         %25 = OpLoad %v2float %24
+               OpStore %fragColor %25
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+}
+
+// From VVL CTS
+TEST_F(ValidateSpvEXTDescriptorHeap, Alignment) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability SampledBuffer
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %2 "main" %3 %4 %5
+               OpExecutionMode %2 LocalSize 1 1 1
+               OpSource GLSL 450
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpDecorate %3 BuiltIn ResourceHeapEXT
+               OpDecorateId %_runtimearr_12 ArrayStrideIdEXT %7
+               OpDecorate %4 Binding 0
+               OpDecorate %4 DescriptorSet 0
+               OpDecorate %_struct_8 Block
+               OpMemberDecorate %_struct_8 0 Offset 0
+               OpDecorate %5 Binding 1
+               OpDecorate %5 DescriptorSet 0
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+         %12 = OpTypeImage %uint Buffer 0 0 0 1 Unknown
+%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+         %15 = OpTypeFunction %uint %_ptr_UniformConstant
+     %uint_0 = OpConstant %uint 0
+     %v4uint = OpTypeVector %uint 4
+     %v2uint = OpTypeVector %uint 2
+          %3 = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+          %7 = OpConstantSizeOfEXT %uint %12
+%_runtimearr_12 = OpTypeRuntimeArray %12
+%_ptr_Function_uint = OpTypePointer Function %uint
+          %4 = OpVariable %_ptr_UniformConstant_12 UniformConstant
+     %uint_1 = OpConstant %uint 1
+  %_struct_8 = OpTypeStruct %v2uint
+%_ptr_StorageBuffer__struct_8 = OpTypePointer StorageBuffer %_struct_8
+          %5 = OpVariable %_ptr_StorageBuffer__struct_8 StorageBuffer
+%_ptr_StorageBuffer_v2uint = OpTypePointer StorageBuffer %v2uint
+     %v3uint = OpTypeVector %uint 3
+         %24 = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
+         %25 = OpFunction %uint None %15
+         %26 = OpFunctionParameter %_ptr_UniformConstant
+         %27 = OpLabel
+         %28 = OpLoad %12 %26
+         %29 = OpImageFetch %v4uint %28 %uint_0 ZeroExtend
+         %30 = OpCompositeExtract %uint %29 0
+               OpReturnValue %30
+               OpFunctionEnd
+          %2 = OpFunction %void None %10
+         %31 = OpLabel
+         %32 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_12 %3 %uint_0
+         %33 = OpUntypedAccessChainKHR %_ptr_UniformConstant %12 %4
+         %34 = OpFunctionCall %uint %25 %32
+         %35 = OpFunctionCall %uint %25 %33
+         %36 = OpCompositeConstruct %v2uint %34 %35
+         %37 = OpAccessChain %_ptr_StorageBuffer_v2uint %5 %uint_0
+               OpStore %37 %36
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, HeapBaseVarStorageClassResource) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %resource_heap %o
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpName %main "main"
+               OpName %o "o"
+               OpName %resource_heap "resource_heap"
+               OpName %U "U"
+               OpMemberName %U 0 "inputData"
+               OpDecorate %o Location 0
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorate %U Block
+               OpMemberDecorate %U 0 Offset 0
+               OpDecorateId %_runtimearr_17 ArrayStrideIdEXT %18
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+%_ptr_Output_uint = OpTypePointer Output %uint
+          %o = OpVariable %_ptr_Output_uint Output
+%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform
+%resource_heap = OpUntypedVariableKHR %_ptr_Uniform Uniform
+        %int = OpTypeInt 32 1
+      %int_9 = OpConstant %int 9
+          %U = OpTypeStruct %uint
+      %int_0 = OpConstant %int 0
+         %17 = OpTypeBufferEXT Uniform
+         %18 = OpConstantSizeOfEXT %int %17
+%_runtimearr_17 = OpTypeRuntimeArray %17
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %16 = OpUntypedAccessChainKHR %_ptr_Uniform %_runtimearr_17 %resource_heap %int_9
+         %20 = OpBufferPointerEXT %_ptr_Uniform %16
+         %21 = OpUntypedAccessChainKHR %_ptr_Uniform %U %20 %int_0
+         %22 = OpLoad %uint %21
+               OpStore %o %22
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(diag, AnyVUID("VUID-StandaloneSpirv-OpUntypedVariableKHR-11167"));
+  EXPECT_THAT(
+      diag,
+      HasSubstr("Storage class is Uniform, but Vulkan requires that Data Type "
+                "be specified when not using UniformConstant storage class"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, HeapBaseVarStorageClassSampler) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %resource_heap %sampler_heap %fragColor %uvs
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 460
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpSourceExtension "GL_EXT_nonuniform_qualifier"
+               OpName %main "main"
+               OpName %fragColor "fragColor"
+               OpName %resource_heap "resource_heap"
+               OpName %sampler_heap "sampler_heap"
+               OpName %uvs "uvs"
+               OpDecorate %fragColor Location 0
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorateId %_runtimearr_14 ArrayStrideIdEXT %17
+               OpDecorate %sampler_heap BuiltIn SamplerHeapEXT
+               OpDecorateId %_runtimearr_22 ArrayStrideIdEXT %uint_2
+               OpDecorate %uvs Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+  %fragColor = OpVariable %_ptr_Output_v4float Output
+%_ptr_StorageBuffer = OpTypeUntypedPointerKHR StorageBuffer
+        %int = OpTypeInt 32 1
+     %int_27 = OpConstant %int 27
+         %14 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+         %17 = OpConstantSizeOfEXT %int %14
+%_runtimearr_14 = OpTypeRuntimeArray %14
+%sampler_heap = OpUntypedVariableKHR %_ptr_StorageBuffer StorageBuffer
+%resource_heap = OpUntypedVariableKHR %_ptr_StorageBuffer StorageBuffer
+      %int_0 = OpConstant %int 0
+         %22 = OpTypeSampler
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+%_runtimearr_22 = OpTypeRuntimeArray %22
+         %28 = OpTypeSampledImage %14
+    %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+        %uvs = OpVariable %_ptr_Input_v2float Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %16 = OpUntypedAccessChainKHR %_ptr_StorageBuffer %_runtimearr_14 %resource_heap %int_27
+         %19 = OpLoad %14 %16
+         %23 = OpUntypedAccessChainKHR %_ptr_StorageBuffer %_runtimearr_22 %sampler_heap %int_0
+         %27 = OpLoad %22 %23
+         %29 = OpSampledImage %28 %19 %27
+         %33 = OpLoad %v2float %uvs
+         %34 = OpImageSampleImplicitLod %v4float %29 %33
+               OpStore %fragColor %34
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(diag, AnyVUID("VUID-StandaloneSpirv-OpUntypedVariableKHR-11167"));
+  EXPECT_THAT(
+      diag,
+      HasSubstr(
+          "Storage class is StorageBuffer, but Vulkan requires that Data Type "
+          "be specified when not using UniformConstant storage class"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, ResourceHeapWorkgroup) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpCapability WorkgroupMemoryExplicitLayoutKHR
+               OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+         %15 = OpTypeBufferEXT StorageBuffer
+%_ptr_workgroup = OpTypeUntypedPointerKHR Workgroup
+%resource_heap = OpUntypedVariableKHR %_ptr_workgroup Workgroup %15
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(diag, AnyVUID("VUID-ResourceHeapEXT-ResourceHeapEXT-11241"));
+  EXPECT_THAT(diag,
+              HasSubstr("The variable decorated with ResourceHeapEXT must be "
+                        "declared using the UniformConstant storage class"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, SamplerHeapWorkgroup) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpCapability WorkgroupMemoryExplicitLayoutKHR
+               OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %resource_heap BuiltIn SamplerHeapEXT
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+         %15 = OpTypeBufferEXT StorageBuffer
+%_ptr_workgroup = OpTypeUntypedPointerKHR Workgroup
+%resource_heap = OpUntypedVariableKHR %_ptr_workgroup Workgroup %15
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(diag, AnyVUID("VUID-SamplerHeapEXT-SamplerHeapEXT-11239"));
+  EXPECT_THAT(diag,
+              HasSubstr("The variable decorated with SamplerHeapEXT must be "
+                        "declared using the UniformConstant storage class"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, LoadDescHeapDerivedSampler) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %resource_heap %sampler_heap %fragColor %uvs
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 460
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpSourceExtension "GL_EXT_nonuniform_qualifier"
+               OpName %main "main"
+               OpName %fragColor "fragColor"
+               OpName %resource_heap "resource_heap"
+               OpName %sampler_heap "sampler_heap"
+               OpName %uvs "uvs"
+               OpDecorate %fragColor Location 0
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorateId %_runtimearr_14 ArrayStrideIdEXT %17
+               OpDecorate %sampler_heap BuiltIn ResourceHeapEXT
+               OpDecorateId %_runtimearr_22 ArrayStrideIdEXT %uint_2
+               OpDecorate %uvs Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+  %fragColor = OpVariable %_ptr_Output_v4float Output
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+        %int = OpTypeInt 32 1
+     %int_27 = OpConstant %int 27
+         %14 = OpTypeImage %float 2D 0 0 0 1 Unknown
+         %17 = OpConstantSizeOfEXT %int %14
+%_runtimearr_14 = OpTypeRuntimeArray %14
+%sampler_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+      %int_0 = OpConstant %int 0
+         %22 = OpTypeSampler
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+%_runtimearr_22 = OpTypeRuntimeArray %22
+         %28 = OpTypeSampledImage %14
+    %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+        %uvs = OpVariable %_ptr_Input_v2float Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %16 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_14 %resource_heap %int_27
+         %19 = OpLoad %14 %16
+         %23 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_22 %sampler_heap %int_0
+         %27 = OpLoad %22 %23
+         %29 = OpSampledImage %28 %19 %27
+         %33 = OpLoad %v2float %uvs
+         %34 = OpImageSampleImplicitLod %v4float %29 %33
+               OpStore %fragColor %34
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(diag, AnyVUID("VUID-StandaloneSpirv-Result-11336"));
+  EXPECT_THAT(
+      diag,
+      HasSubstr(
+          "pointer instruction has no descriptor set or binding "
+          "and is not derived from a variable decorated with SamplerHeapEXT"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, LoadDescHeapDerivedImage) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %resource_heap %sampler_heap %fragColor %uvs
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 460
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpSourceExtension "GL_EXT_nonuniform_qualifier"
+               OpName %main "main"
+               OpName %fragColor "fragColor"
+               OpName %resource_heap "resource_heap"
+               OpName %sampler_heap "sampler_heap"
+               OpName %uvs "uvs"
+               OpDecorate %fragColor Location 0
+               OpDecorate %resource_heap BuiltIn SamplerHeapEXT
+               OpDecorateId %_runtimearr_14 ArrayStrideIdEXT %17
+               OpDecorate %sampler_heap BuiltIn SamplerHeapEXT
+               OpDecorateId %_runtimearr_22 ArrayStrideIdEXT %uint_2
+               OpDecorate %uvs Location 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+  %fragColor = OpVariable %_ptr_Output_v4float Output
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+        %int = OpTypeInt 32 1
+     %int_27 = OpConstant %int 27
+         %14 = OpTypeImage %float 2D 0 0 0 1 Unknown
+         %17 = OpConstantSizeOfEXT %int %14
+%_runtimearr_14 = OpTypeRuntimeArray %14
+%sampler_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+      %int_0 = OpConstant %int 0
+         %22 = OpTypeSampler
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+%_runtimearr_22 = OpTypeRuntimeArray %22
+         %28 = OpTypeSampledImage %14
+    %v2float = OpTypeVector %float 2
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+        %uvs = OpVariable %_ptr_Input_v2float Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %16 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_14 %resource_heap %int_27
+         %19 = OpLoad %14 %16
+         %23 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_22 %sampler_heap %int_0
+         %27 = OpLoad %22 %23
+         %29 = OpSampledImage %28 %19 %27
+         %33 = OpLoad %v2float %uvs
+         %34 = OpImageSampleImplicitLod %v4float %29 %33
+               OpStore %fragColor %34
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(diag, AnyVUID("VUID-StandaloneSpirv-Result-11337"));
+  EXPECT_THAT(
+      diag,
+      HasSubstr(
+          "pointer instruction has no descriptor set or binding "
+          "and is not derived from a variable decorated with ResourceHeapEXT"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, LoadDescHeapDerivedAccStruct) {
+  const std::string str = R"(
+                OpCapability Shader
+                OpCapability RayQueryKHR
+                OpCapability UntypedPointersKHR
+                OpCapability DescriptorHeapEXT
+                OpExtension "SPV_EXT_descriptor_heap"
+                OpExtension "SPV_KHR_untyped_pointers"
+                OpExtension "SPV_KHR_ray_query"
+                OpMemoryModel Logical GLSL450
+                OpEntryPoint GLCompute %main "main" %resource_heap
+                OpExecutionMode %main LocalSize 1 1 1
+                OpName %resource_heap "resource_heap"
+                OpDecorate %resource_heap BuiltIn SamplerHeapEXT
+                OpDecorateId %_runtimearr_14 ArrayStrideIdEXT %17
+                %_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+                %resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+                %int = OpTypeInt 32 1
+                %int_27 = OpConstant %int 27
+                %void = OpTypeVoid
+                %as = OpTypeAccelerationStructureKHR
+                %ptr_as = OpTypePointer Function %as
+                %void_fn = OpTypeFunction %void
+                %17 = OpConstantSizeOfEXT %int %as
+                %_runtimearr_14 = OpTypeRuntimeArray %as
+                %main = OpFunction %void None %void_fn
+                %entry = OpLabel
+                %var = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_14 %resource_heap %int_27
+                %value = OpLoad %as %var
+                OpReturn
+                OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(diag, AnyVUID("VUID-StandaloneSpirv-Result-11339"));
+  EXPECT_THAT(
+      diag,
+      HasSubstr(
+          "pointer instruction has no descriptor set or binding "
+          "and is not derived from a variable decorated with ResourceHeapEXT"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, ConstantSizeOfScalarIntType) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %resource_heap %o
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpName %main "main"
+               OpName %o "o"
+               OpName %resource_heap "resource_heap"
+               OpName %U "U"
+               OpMemberName %U 0 "inputData"
+               OpDecorate %o Location 0
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorate %U Block
+               OpMemberDecorate %U 0 Offset 0
+               OpDecorateId %_runtimearr_17 ArrayStrideIdEXT %18
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+%_ptr_Output_uint = OpTypePointer Output %uint
+          %o = OpVariable %_ptr_Output_uint Output
+%_ptr_Uniform = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_Uniform UniformConstant
+        %int = OpTypeInt 32 1
+      %v2int = OpTypeVector %int 2
+      %int_9 = OpConstant %int 9
+          %U = OpTypeStruct %uint
+      %int_0 = OpConstant %int 0
+         %17 = OpTypeBufferEXT Uniform
+         %18 = OpConstantSizeOfEXT %v2int %17
+%_runtimearr_17 = OpTypeRuntimeArray %17
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %16 = OpUntypedAccessChainKHR %_ptr_Uniform %_runtimearr_17 %resource_heap %int_9
+         %20 = OpBufferPointerEXT %_ptr_Uniform %16
+         %21 = OpUntypedAccessChainKHR %_ptr_Uniform %U %20 %int_0
+         %22 = OpLoad %uint %21
+               OpStore %o %22
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(
+      diag,
+      HasSubstr(
+          "ArrayStrideIdEXT extra operand must be a 32-bit int scalar type"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, ConstantSizeOfDescriptorTypeTarget) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %resource_heap %o
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpName %main "main"
+               OpName %o "o"
+               OpName %resource_heap "resource_heap"
+               OpName %U "U"
+               OpMemberName %U 0 "inputData"
+               OpDecorate %o Location 0
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorate %U Block
+               OpMemberDecorate %U 0 Offset 0
+               OpDecorateId %_runtimearr_17 ArrayStrideIdEXT %18
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+%_ptr_Output_uint = OpTypePointer Output %uint
+          %o = OpVariable %_ptr_Output_uint Output
+%_ptr_Uniform = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_Uniform UniformConstant
+        %int = OpTypeInt 32 1
+      %int_9 = OpConstant %int 9
+          %U = OpTypeStruct %uint
+      %int_0 = OpConstant %int 0
+         %17 = OpTypeBufferEXT Uniform
+         %18 = OpConstantSizeOfEXT %int %U
+%_runtimearr_17 = OpTypeRuntimeArray %17
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %16 = OpUntypedAccessChainKHR %_ptr_Uniform %_runtimearr_17 %resource_heap %int_9
+         %20 = OpBufferPointerEXT %_ptr_Uniform %16
+         %21 = OpUntypedAccessChainKHR %_ptr_Uniform %U %20 %int_0
+         %22 = OpLoad %uint %21
+               OpStore %o %22
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(diag,
+              HasSubstr("For OpConstantSizeOfEXT instruction, its Type operand "
+                        "<Id> '5[%U]' must be a Descriptor type"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, OffsetId64BitIndexingBad) {
+  const std::string str = R"(
+        OpCapability Shader
+        OpCapability DescriptorHeapEXT
+        OpExtension "SPV_EXT_descriptor_heap"
+  %1 = OpExtInstImport "GLSL.std.450"
+        OpMemoryModel Logical GLSL450
+        OpEntryPoint GLCompute %2 "main" %3
+        OpExecutionMode %2 LocalSize 1 1 1
+        OpSource GLSL 450
+        OpMemberDecorate %4 0 Offset 0
+        OpMemberDecorate %4 1 Offset 4
+        OpMemberDecorate %4 2 Offset 8
+        OpMemberDecorate %4 3 Offset 12
+        OpDecorate %4 Block
+        OpDecorate %3 Binding 0
+        OpDecorate %3 DescriptorSet 0
+  %5 = OpTypeVoid
+  %6 = OpTypeFunction %5
+  %7 = OpTypeInt 32 0
+  %8 = OpTypeInt 64 0
+  %4 = OpTypeStruct %7 %7 %7 %7
+  %9 = OpTypePointer StorageBuffer %4
+  %3 = OpVariable %9 StorageBuffer
+  %10 = OpTypeInt 32 1
+  %11 = OpConstant %10 0
+  %12 = OpConstant %10 1
+  %13 = OpConstant %10 2
+  %14 = OpConstant %10 3
+  %15 = OpTypePointer StorageBuffer %7
+  %16 = OpTypeBufferEXT StorageBuffer
+  %17 = OpTypeBufferEXT Uniform
+  %18 = OpTypeImage %7 2D 0 0 0 1 Unknown
+  %19 = OpTypeSampler
+  %20 = OpConstantSizeOfEXT %8 %16
+  %21 = OpConstantSizeOfEXT %8 %17
+  %22 = OpConstantSizeOfEXT %8 %18
+  %23 = OpConstantSizeOfEXT %8 %19
+  %2 = OpFunction %5 None %6
+  %24 = OpLabel
+  %25 = OpAccessChain %15 %3 %11
+  %26 = OpAccessChain %15 %3 %12
+  %27 = OpAccessChain %15 %3 %13
+  %28 = OpAccessChain %15 %3 %14
+  %29 = OpUConvert %7 %20
+  %30 = OpUConvert %7 %21
+  %31 = OpUConvert %7 %22
+  %32 = OpUConvert %7 %23
+        OpStore %25 %29
+        OpStore %26 %30
+        OpStore %27 %31
+        OpStore %28 %32
+        OpReturn
+        OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(
+      diag,
+      HasSubstr("Using a 64-bit integer type requires the Int64 capability."));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, TypeBufferEXTStorageClass) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %resource_heap %o
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpName %main "main"
+               OpName %o "o"
+               OpName %resource_heap "resource_heap"
+               OpName %U "U"
+               OpMemberName %U 0 "inputData"
+               OpDecorate %o Location 0
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorate %U Block
+               OpMemberDecorate %U 0 Offset 0
+               OpDecorateId %_runtimearr_17 ArrayStrideIdEXT %18
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+%_ptr_Output_uint = OpTypePointer Output %uint
+          %o = OpVariable %_ptr_Output_uint Output
+%_ptr_Uniform = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_Uniform UniformConstant
+        %int = OpTypeInt 32 1
+      %int_9 = OpConstant %int 9
+          %U = OpTypeStruct %uint
+      %int_0 = OpConstant %int 0
+         %17 = OpTypeBufferEXT UniformConstant
+         %18 = OpConstantSizeOfEXT %int %17
+%_runtimearr_17 = OpTypeRuntimeArray %17
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %16 = OpUntypedAccessChainKHR %_ptr_Uniform %_runtimearr_17 %resource_heap %int_9
+         %20 = OpBufferPointerEXT %_ptr_Uniform %16
+         %21 = OpUntypedAccessChainKHR %_ptr_Uniform %U %20 %int_0
+         %22 = OpLoad %uint %21
+               OpStore %o %22
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(diag, HasSubstr("TypeBufferEXT StorageClass could only be "
+                              "StorageBuffer or Uniform."));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, BufferPointerEXTStorageClass) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %resource_heap %o
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpName %main "main"
+               OpName %o "o"
+               OpName %resource_heap "resource_heap"
+               OpName %U "U"
+               OpMemberName %U 0 "inputData"
+               OpDecorate %o Location 0
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorate %U Block
+               OpMemberDecorate %U 0 Offset 0
+               OpDecorateId %_runtimearr_17 ArrayStrideIdEXT %18
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+%_ptr_Output_uint = OpTypePointer Output %uint
+          %o = OpVariable %_ptr_Output_uint Output
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+        %int = OpTypeInt 32 1
+      %int_9 = OpConstant %int 9
+          %U = OpTypeStruct %uint
+      %int_0 = OpConstant %int 0
+         %17 = OpTypeBufferEXT Uniform
+         %18 = OpConstantSizeOfEXT %int %17
+%_runtimearr_17 = OpTypeRuntimeArray %17
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %16 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_17 %resource_heap %int_9
+         %20 = OpBufferPointerEXT %_ptr_UniformConstant %16
+         %21 = OpUntypedAccessChainKHR %_ptr_UniformConstant %U %20 %int_0
+         %22 = OpLoad %uint %21
+               OpStore %o %22
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(
+      diag,
+      HasSubstr("OpBufferPointerEXT's Result Type must be a pointer "
+                "type with a Storage Class of Uniform or StorageBuffer."));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, BufferPointerEXTLayout) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %resource_heap %o
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpName %main "main"
+               OpName %o "o"
+               OpName %resource_heap "resource_heap"
+               OpName %U "U"
+               OpMemberName %U 0 "inputData"
+               OpDecorate %o Location 0
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorate %U Block
+               OpMemberDecorate %U 0 Offset 0
+               OpDecorateId %_runtimearr_17 ArrayStrideIdEXT %18
+               OpDecorate %_ptr_UniformConstant Offset 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+%_ptr_Output_uint = OpTypePointer Output %uint
+          %o = OpVariable %_ptr_Output_uint Output
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+        %int = OpTypeInt 32 1
+      %int_9 = OpConstant %int 9
+          %U = OpTypeStruct %uint
+      %int_0 = OpConstant %int 0
+         %17 = OpTypeBufferEXT Uniform
+         %18 = OpConstantSizeOfEXT %int %17
+%_runtimearr_17 = OpTypeRuntimeArray %17
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %16 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_17 %resource_heap %int_9
+         %20 = OpBufferPointerEXT %_ptr_UniformConstant %16
+         %21 = OpUntypedAccessChainKHR %_ptr_UniformConstant %U %20 %int_0
+         %22 = OpLoad %uint %21
+               OpStore %o %22
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(diag, AnyVUID("VUID-StandaloneSpirv-Result-11346"));
+  EXPECT_THAT(
+      diag, HasSubstr("The result type operand of OpBufferPointerEXT "
+                      "must have a Type operand that is explicitly laid out"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, BufferPointerEXTDecorate) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %resource_heap %o
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpName %main "main"
+               OpName %o "o"
+               OpName %resource_heap "resource_heap"
+               OpName %U "U"
+               OpMemberName %U 0 "inputData"
+               OpDecorate %o Location 0
+               OpDecorate %resource_heap BuiltIn SamplerHeapEXT
+               OpDecorate %U Block
+               OpMemberDecorate %U 0 Offset 0
+               OpDecorateId %_runtimearr_17 ArrayStrideIdEXT %18
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+%_ptr_Output_uint = OpTypePointer Output %uint
+          %o = OpVariable %_ptr_Output_uint Output
+%_ptr_Uniform = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_Uniform UniformConstant
+        %int = OpTypeInt 32 1
+      %int_9 = OpConstant %int 9
+          %U = OpTypeStruct %uint
+      %int_0 = OpConstant %int 0
+         %17 = OpTypeBufferEXT Uniform
+         %18 = OpConstantSizeOfEXT %int %17
+%_runtimearr_17 = OpTypeRuntimeArray %17
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %16 = OpUntypedAccessChainKHR %_ptr_Uniform %_runtimearr_17 %resource_heap %int_9
+         %20 = OpBufferPointerEXT %_ptr_Uniform %16
+         %21 = OpUntypedAccessChainKHR %_ptr_Uniform %U %20 %int_0
+         %22 = OpLoad %uint %21
+               OpStore %o %22
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(diag, HasSubstr("OpBufferPointerEXT's buffer must be an untyped "
+                              "pointer into a variable declared with the "
+                              "ResourceHeapEXT built-in"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, BufferPointerEXTNonWritable) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %resource_heap %fragColor
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 460
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpSourceExtension "GL_EXT_nonuniform_qualifier"
+               OpName %main "main"
+               OpName %fragColor "fragColor"
+               OpName %resource_heap "resource_heap"
+               OpName %U "U"
+               OpMemberName %U 0 "uv"
+               OpDecorate %fragColor Location 0
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorate %U Block
+               OpMemberDecorate %U 0 Offset 0
+               OpDecorateId %_runtimearr_18 ArrayStrideIdEXT %uint_2
+               OpDecorate %22 NonWritable
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+%_ptr_Output_v2float = OpTypePointer Output %v2float
+  %fragColor = OpVariable %_ptr_Output_v2float Output
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+        %int = OpTypeInt 32 1
+      %int_3 = OpConstant %int 3
+          %U = OpTypeStruct %v2float
+      %int_0 = OpConstant %int 0
+%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform
+         %18 = OpTypeBufferEXT Uniform
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+%_runtimearr_18 = OpTypeRuntimeArray %18
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %17 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_18 %resource_heap %int_3
+         %22 = OpBufferPointerEXT %_ptr_Uniform %17
+         %23 = OpUntypedAccessChainKHR %_ptr_Uniform %U %22 %int_0
+         %24 = OpLoad %v2float %23
+               OpStore %fragColor %24
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(diag, HasSubstr("Target of NonWritable decoration is invalid"));
+  EXPECT_THAT(
+      diag,
+      HasSubstr(
+          "cannot be used to OpBufferPointerEXT with Uniform storage class"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, BufferPointerEXTNonWritableWithSSBO) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %resource_heap %fragColor
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 460
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpSourceExtension "GL_EXT_nonuniform_qualifier"
+               OpName %main "main"
+               OpName %fragColor "fragColor"
+               OpName %resource_heap "resource_heap"
+               OpName %U "U"
+               OpMemberName %U 0 "uv"
+               OpDecorate %fragColor Location 0
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorate %U Block
+               OpMemberDecorate %U 0 Offset 0
+               OpDecorateId %_runtimearr_18 ArrayStrideIdEXT %uint_2
+               OpDecorate %22 NonWritable
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+%_ptr_Output_v2float = OpTypePointer Output %v2float
+  %fragColor = OpVariable %_ptr_Output_v2float Output
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+        %int = OpTypeInt 32 1
+      %int_3 = OpConstant %int 3
+          %U = OpTypeStruct %v2float
+      %int_0 = OpConstant %int 0
+%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform
+%_ptr_Buffer = OpTypeUntypedPointerKHR StorageBuffer
+         %18 = OpTypeBufferEXT Uniform
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+%_runtimearr_18 = OpTypeRuntimeArray %18
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %17 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_18 %resource_heap %int_3
+         %22 = OpBufferPointerEXT %_ptr_Buffer %17
+         %23 = OpUntypedAccessChainKHR %_ptr_Buffer %U %22 %int_0
+         %24 = OpLoad %v2float %23
+               OpStore %fragColor %24
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, MemberDecorateIdExtStruct) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %2 "main" %3 %4
+               OpExecutionMode %2 LocalSize 1 1 1
+               OpSource GLSL 450
+               OpMemberDecorate %5 0 Offset 0
+               OpDecorate %3 DescriptorSet 0
+               OpDecorate %3 Binding 0
+               OpDecorate %4 DescriptorSet 0
+               OpDecorate %4 Binding 0
+               OpDecorate %5 Block
+               OpDecorate %6 Block
+               OpDecorate %7 SpecId 0
+               OpMemberDecorateIdEXT %11 0 OffsetIdEXT %7
+               OpMemberDecorate %6 1 Offset 4
+               OpMemberDecorateIdEXT %6 2 OffsetIdEXT %8
+          %9 = OpTypeVoid
+         %10 = OpTypeFunction %9
+         %11 = OpTypeInt 32 0
+          %5 = OpTypeStruct %11
+         %12 = OpTypePointer StorageBuffer %5
+         %13 = OpTypeImage %11 2D 0 0 0 1 Unknown
+         %14 = OpTypeBufferEXT Uniform
+         %15 = OpConstantSizeOfEXT %11 %13
+         %16 = OpConstantSizeOfEXT %11 %14
+         %17 = OpConstant %11 1
+         %18 = OpConstant %11 8
+         %19 = OpSpecConstantOp %11 IAdd %18 %15
+         %20 = OpSpecConstantOp %11 ISub %19 %17
+         %21 = OpSpecConstantOp %11 UDiv %20 %15
+         %22 = OpSpecConstantOp %11 IMul %21 %15
+         %23 = OpSpecConstantOp %11 IAdd %22 %15
+         %24 = OpSpecConstantOp %11 IAdd %23 %16
+         %25 = OpSpecConstantOp %11 ISub %24 %17
+         %26 = OpSpecConstantOp %11 UDiv %25 %16
+         %27 = OpSpecConstantOp %11 IMul %26 %16
+          %8 = OpSpecConstantOp %11 IAdd %27 %16
+          %7 = OpSpecConstant %11 4
+          %3 = OpVariable %12 StorageBuffer
+         %28 = OpConstant %11 0
+          %6 = OpTypeStruct %11 %11 %11
+         %29 = OpTypePointer Uniform %6
+          %4 = OpVariable %29 Uniform
+         %30 = OpTypePointer StorageBuffer %11
+         %31 = OpConstant %11 2
+          %2 = OpFunction %9 None %10
+         %32 = OpLabel
+         %33 = OpAccessChain %30 %4 %28
+         %34 = OpLoad %11 %33
+         %35 = OpAccessChain %30 %4 %31
+         %36 = OpLoad %11 %35
+         %37 = OpIAdd %11 %34 %36
+         %38 = OpAccessChain %30 %3 %28
+               OpStore %38 %37
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(diag, HasSubstr("MemberDecorateIdEXT Structure type"));
+  EXPECT_THAT(diag, HasSubstr("is not a struct type."));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, DecorateIdArrayStrideIdEXT) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %2 "main" %3 %4
+               OpExecutionMode %2 LocalSize 1 1 1
+               OpSource GLSL 450
+               OpMemberDecorate %5 0 Offset 0
+               OpDecorate %3 DescriptorSet 0
+               OpDecorate %3 Binding 0
+               OpDecorate %4 DescriptorSet 0
+               OpDecorate %4 Binding 0
+               OpDecorate %5 Block
+               OpDecorate %6 Block
+               OpDecorate %7 SpecId 0
+               OpDecorateId %5 ArrayStrideIdEXT %7
+               OpMemberDecorate %6 1 Offset 4
+               OpMemberDecorateIdEXT %6 2 OffsetIdEXT %8
+          %9 = OpTypeVoid
+         %10 = OpTypeFunction %9
+         %11 = OpTypeInt 32 0
+          %5 = OpTypeStruct %11
+         %12 = OpTypePointer StorageBuffer %5
+         %13 = OpTypeImage %11 2D 0 0 0 1 Unknown
+         %14 = OpTypeBufferEXT Uniform
+         %15 = OpConstantSizeOfEXT %11 %13
+         %16 = OpConstantSizeOfEXT %11 %14
+         %17 = OpConstant %11 1
+         %18 = OpConstant %11 8
+         %19 = OpSpecConstantOp %11 IAdd %18 %15
+         %20 = OpSpecConstantOp %11 ISub %19 %17
+         %21 = OpSpecConstantOp %11 UDiv %20 %15
+         %22 = OpSpecConstantOp %11 IMul %21 %15
+         %23 = OpSpecConstantOp %11 IAdd %22 %15
+         %24 = OpSpecConstantOp %11 IAdd %23 %16
+         %25 = OpSpecConstantOp %11 ISub %24 %17
+         %26 = OpSpecConstantOp %11 UDiv %25 %16
+         %27 = OpSpecConstantOp %11 IMul %26 %16
+          %8 = OpSpecConstantOp %11 IAdd %27 %16
+          %7 = OpSpecConstant %11 4
+          %3 = OpVariable %12 StorageBuffer
+         %28 = OpConstant %11 0
+          %6 = OpTypeStruct %11 %11 %11
+         %29 = OpTypePointer Uniform %6
+          %4 = OpVariable %29 Uniform
+         %30 = OpTypePointer StorageBuffer %11
+         %31 = OpConstant %11 2
+          %2 = OpFunction %9 None %10
+         %32 = OpLabel
+         %33 = OpAccessChain %30 %4 %28
+         %34 = OpLoad %11 %33
+         %35 = OpAccessChain %30 %4 %31
+         %36 = OpLoad %11 %35
+         %37 = OpIAdd %11 %34 %36
+         %38 = OpAccessChain %30 %3 %28
+               OpStore %38 %37
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(
+      diag,
+      HasSubstr(
+          "ArrayStrideIdEXT decoration must only be applied to array types."));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, ArrayStrideNonDescriptor) {
+  const std::string str = R"(
+              OpCapability Shader
+              OpCapability UntypedPointersKHR
+              OpCapability DescriptorHeapEXT
+              OpExtension "SPV_EXT_descriptor_heap"
+              OpExtension "SPV_KHR_untyped_pointers"
+              OpMemoryModel Logical GLSL450
+              OpEntryPoint GLCompute %main "main" %resource_heap
+              OpExecutionMode %main LocalSize 1 1 1
+              OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+              OpDecorate %ssbo Block
+              OpMemberDecorate %ssbo 0 BuiltIn ResourceHeapEXT
+              OpMemberDecorate %ssbo 0 Offset 0
+              OpDecorateId %_runtimearr_13 ArrayStrideIdEXT %16
+              OpDecorateId %_runtimearr_24 ArrayStrideIdEXT %25
+      %void = OpTypeVoid
+        %3 = OpTypeFunction %void
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+      %int = OpTypeInt 32 1
+    %int_16 = OpConstant %int 16
+    %v4int = OpTypeVector %int 4
+      %ssbo = OpTypeStruct %v4int
+    %int_0 = OpConstant %int 0
+        %13 = OpTypeImage %int 2D 0 0 0 2 Rgba8i
+%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform
+        %16 = OpConstantSizeOfEXT %int %13
+%_runtimearr_13 = OpTypeRuntimeArray %13
+        %24 = OpTypeBufferEXT StorageBuffer
+        %25 = OpConstantSizeOfEXT %int %24
+%_runtimearr_24 = OpTypeRuntimeArray %int
+      %main = OpFunction %void None %3
+        %5 = OpLabel
+              OpReturn
+              OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(diag,
+              HasSubstr("ArrayStrideIdEXT decoration must only be applied to "
+                        "array type containing a Descriptor type."));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, MemberDecorateIdArrayStrideIdEXT) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %2 "main" %3 %4
+               OpExecutionMode %2 LocalSize 1 1 1
+               OpSource GLSL 450
+               OpMemberDecorate %5 0 Offset 0
+               OpDecorate %3 DescriptorSet 0
+               OpDecorate %3 Binding 0
+               OpDecorate %4 DescriptorSet 0
+               OpDecorate %4 Binding 0
+               OpDecorate %5 Block
+               OpDecorate %6 Block
+               OpDecorate %7 SpecId 0
+               OpMemberDecorateIdEXT %6 0 ArrayStrideIdEXT %7
+               OpMemberDecorate %6 1 Offset 4
+               OpMemberDecorateIdEXT %6 2 OffsetIdEXT %8
+          %9 = OpTypeVoid
+         %10 = OpTypeFunction %9
+         %11 = OpTypeInt 32 0
+          %5 = OpTypeStruct %11
+         %12 = OpTypePointer StorageBuffer %5
+         %13 = OpTypeImage %11 2D 0 0 0 1 Unknown
+         %14 = OpTypeBufferEXT Uniform
+         %15 = OpConstantSizeOfEXT %11 %13
+         %16 = OpConstantSizeOfEXT %11 %14
+         %17 = OpConstant %11 1
+         %18 = OpConstant %11 8
+         %19 = OpSpecConstantOp %11 IAdd %18 %15
+         %20 = OpSpecConstantOp %11 ISub %19 %17
+         %21 = OpSpecConstantOp %11 UDiv %20 %15
+         %22 = OpSpecConstantOp %11 IMul %21 %15
+         %23 = OpSpecConstantOp %11 IAdd %22 %15
+         %24 = OpSpecConstantOp %11 IAdd %23 %16
+         %25 = OpSpecConstantOp %11 ISub %24 %17
+         %26 = OpSpecConstantOp %11 UDiv %25 %16
+         %27 = OpSpecConstantOp %11 IMul %26 %16
+          %8 = OpSpecConstantOp %11 IAdd %27 %16
+          %7 = OpSpecConstant %11 4
+          %3 = OpVariable %12 StorageBuffer
+         %28 = OpConstant %11 0
+          %6 = OpTypeStruct %11 %11 %11
+         %29 = OpTypePointer Uniform %6
+          %4 = OpVariable %29 Uniform
+         %30 = OpTypePointer StorageBuffer %11
+         %31 = OpConstant %11 2
+          %2 = OpFunction %9 None %10
+         %32 = OpLabel
+         %33 = OpAccessChain %30 %4 %28
+         %34 = OpLoad %11 %33
+         %35 = OpAccessChain %30 %4 %31
+         %36 = OpLoad %11 %35
+         %37 = OpIAdd %11 %34 %36
+         %38 = OpAccessChain %30 %3 %28
+               OpStore %38 %37
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(diag, HasSubstr("ArrayStrideIdEXT could only be directly applied "
+                              "to array type using OpDecorateId."));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, MemberDecorateIdOffsetIdEXT) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %2 "main" %3 %4
+               OpExecutionMode %2 LocalSize 1 1 1
+               OpSource GLSL 450
+               OpMemberDecorate %5 0 Offset 0
+               OpDecorate %3 DescriptorSet 0
+               OpDecorate %3 Binding 0
+               OpDecorate %4 DescriptorSet 0
+               OpDecorate %4 Binding 0
+               OpDecorate %5 Block
+               OpDecorate %6 Block
+               OpDecorate %7 SpecId 0
+               OpMemberDecorateIdEXT %6 0 OffsetIdEXT %7
+               OpMemberDecorate %6 1 Offset 4
+               OpMemberDecorateIdEXT %6 2 ArrayStrideIdEXT %8
+          %9 = OpTypeVoid
+         %10 = OpTypeFunction %9
+         %11 = OpTypeInt 32 0
+          %5 = OpTypeStruct %11
+         %12 = OpTypePointer StorageBuffer %5
+         %13 = OpTypeImage %11 2D 0 0 0 1 Unknown
+         %14 = OpTypeBufferEXT Uniform
+         %15 = OpConstantSizeOfEXT %11 %13
+         %16 = OpConstantSizeOfEXT %11 %14
+         %17 = OpConstant %11 1
+         %18 = OpConstant %11 8
+         %19 = OpSpecConstantOp %11 IAdd %18 %15
+         %20 = OpSpecConstantOp %11 ISub %19 %17
+         %21 = OpSpecConstantOp %11 UDiv %20 %15
+         %22 = OpSpecConstantOp %11 IMul %21 %15
+         %23 = OpSpecConstantOp %11 IAdd %22 %15
+         %24 = OpSpecConstantOp %11 IAdd %23 %16
+         %25 = OpSpecConstantOp %11 ISub %24 %17
+         %26 = OpSpecConstantOp %11 UDiv %25 %16
+         %27 = OpSpecConstantOp %11 IMul %26 %16
+          %8 = OpSpecConstantOp %11 IAdd %27 %16
+          %7 = OpSpecConstant %11 4
+          %3 = OpVariable %12 StorageBuffer
+         %28 = OpConstant %11 0
+          %6 = OpTypeStruct %11 %11 %11
+         %29 = OpTypePointer Uniform %6
+          %4 = OpVariable %29 Uniform
+         %30 = OpTypePointer StorageBuffer %11
+         %31 = OpConstant %11 2
+          %2 = OpFunction %9 None %10
+         %32 = OpLabel
+         %33 = OpAccessChain %30 %4 %28
+         %34 = OpLoad %11 %33
+         %35 = OpAccessChain %30 %4 %31
+         %36 = OpLoad %11 %35
+         %37 = OpIAdd %11 %34 %36
+         %38 = OpAccessChain %30 %3 %28
+               OpStore %38 %37
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(
+      diag,
+      HasSubstr(
+          "OffsetIdEXT decoration in MemberDecorateIdEXT must only be applied "
+          "to members of structs where the struct contains descriptor types."));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, OffsetIdAllSingleImages) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpCapability ImageBuffer
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main" %2
+               OpExecutionMode %1 LocalSize 1 1 1
+               OpDecorate %2 BuiltIn ResourceHeapEXT
+               OpMemberDecorate %_struct_3 0 Offset 0
+               OpMemberDecorateIdEXT %_struct_3 1 OffsetIdEXT %uint_0
+               OpMemberDecorate %_struct_3 2 Offset 16
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_2 = OpConstant %uint 2
+ %uint_51966 = OpConstant %uint 51966
+   %uint_0_0 = OpConstant %uint 0
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+          %2 = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+         %14 = OpTypeImage %uint Buffer 0 0 0 2 R32ui
+  %_struct_3 = OpTypeStruct %14 %14 %14
+          %1 = OpFunction %void None %7
+         %15 = OpLabel
+         %16 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_struct_3 %2 %uint_2
+         %17 = OpLoad %14 %16
+               OpImageWrite %17 %uint_0 %uint_51966
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, OffsetIdAllImages) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpCapability ImageBuffer
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main" %2
+               OpExecutionMode %1 LocalSize 1 1 1
+               OpDecorate %2 BuiltIn ResourceHeapEXT
+               OpDecorate %4 SpecId 0
+               OpMemberDecorate %_struct_3 0 Offset 0
+               OpMemberDecorateIdEXT %_struct_3 1 OffsetIdEXT %4
+               OpMemberDecorateIdEXT %_struct_3 2 OffsetIdEXT %5
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_2 = OpConstant %uint 2
+ %uint_51966 = OpConstant %uint 51966
+   %uint_0_0 = OpConstant %uint 0
+          %4 = OpSpecConstant %uint 0
+          %5 = OpSpecConstantOp %uint IMul %4 %uint_2
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+          %2 = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+         %14 = OpTypeImage %uint Buffer 0 0 0 2 R32ui
+  %_struct_3 = OpTypeStruct %14 %14 %14
+          %1 = OpFunction %void None %7
+         %15 = OpLabel
+         %16 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_struct_3 %2 %uint_2
+         %17 = OpLoad %14 %16
+               OpImageWrite %17 %uint_0 %uint_51966
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, OffsetIdStructOfStructOfImage) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpCapability ImageBuffer
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main" %2
+               OpExecutionMode %1 LocalSize 1 1 1
+               OpDecorate %2 BuiltIn ResourceHeapEXT
+               OpMemberDecorate %struct 0 Offset 0
+               OpMemberDecorateIdEXT %struct 1 OffsetIdEXT %uint_0
+               OpMemberDecorate %image_struct 0 Offset 0
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+ %uint_51966 = OpConstant %uint 51966
+   %uint_0_0 = OpConstant %uint 0
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+          %2 = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+         %14 = OpTypeImage %uint Buffer 0 0 0 2 R32ui
+  %image_struct = OpTypeStruct %14
+  %struct = OpTypeStruct %uint %image_struct
+          %1 = OpFunction %void None %7
+         %15 = OpLabel
+         %16 = OpUntypedAccessChainKHR %_ptr_UniformConstant %struct %2 %uint_1 %uint_0
+         %17 = OpLoad %14 %16
+               OpImageWrite %17 %uint_0 %uint_51966
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, MemberDecorateIdDecorator) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpCapability BindlessTextureNV
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpExtension "SPV_NV_bindless_texture"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpSamplerImageAddressingModeNV 64
+               OpEntryPoint Fragment %main "main" %resource_heap %fragColor
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 460
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpSourceExtension "GL_EXT_nonuniform_qualifier"
+               OpName %main "main"
+               OpName %fragColor "fragColor"
+               OpName %resource_heap "resource_heap"
+               OpName %U "U"
+               OpMemberName %U 0 "uv"
+               OpMemberName %U 1 "k"
+               OpMemberName %U 2 "g"
+               OpDecorate %fragColor Location 0
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorate %U Block
+               OpMemberDecorate %U 0 Offset 0
+               OpMemberDecorate %U 1 Offset 8
+               OpMemberDecorateIdEXT %U 2 Offset 0
+               OpDecorateId %_runtimearr_20 ArrayStrideIdEXT %uint_2
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+%_ptr_Output_v2float = OpTypePointer Output %v2float
+  %fragColor = OpVariable %_ptr_Output_v2float Output
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+        %int = OpTypeInt 32 1
+      %int_3 = OpConstant %int 3
+       %uint = OpTypeInt 32 0
+     %v3uint = OpTypeVector %uint 3
+      %specC = OpSpecConstant %int 4
+  %samplerTy = OpTypeSampler
+%_runtimearr_sampler = OpTypeRuntimeArray %samplerTy
+          %U = OpTypeStruct %v2float %int %_runtimearr_sampler
+      %int_0 = OpConstant %int 0
+%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform
+         %20 = OpTypeBufferEXT Uniform
+     %uint_2 = OpConstant %uint 2
+%_runtimearr_20 = OpTypeRuntimeArray %20
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %19 = OpUntypedAccessChainKHR %_ptr_Uniform %_runtimearr_20 %resource_heap %int_3
+         %23 = OpBufferPointerEXT %_ptr_Uniform %19
+         %24 = OpUntypedAccessChainKHR %_ptr_Uniform %U %23 %int_0
+         %25 = OpLoad %v2float %24
+               OpStore %fragColor %25
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(diag, HasSubstr("Decoration operand could only be OffsetIdEXT."));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, MemberDecorateIdExtraIdOrder) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpCapability BindlessTextureNV
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpExtension "SPV_NV_bindless_texture"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpSamplerImageAddressingModeNV 64
+               OpEntryPoint Fragment %main "main" %resource_heap %fragColor
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 460
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpSourceExtension "GL_EXT_nonuniform_qualifier"
+               OpName %main "main"
+               OpName %fragColor "fragColor"
+               OpName %resource_heap "resource_heap"
+               OpName %U "U"
+               OpMemberName %U 0 "uv"
+               OpMemberName %U 1 "k"
+               OpMemberName %U 2 "g"
+               OpDecorate %fragColor Location 0
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorate %U Block
+               OpMemberDecorate %U 0 Offset 0
+               OpMemberDecorate %U 1 Offset 8
+               OpMemberDecorateIdEXT %U 2 OffsetIdEXT %specC
+               OpDecorateId %_runtimearr_20 ArrayStrideIdEXT %uint_2
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+%_ptr_Output_v2float = OpTypePointer Output %v2float
+  %fragColor = OpVariable %_ptr_Output_v2float Output
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+        %int = OpTypeInt 32 1
+      %int_3 = OpConstant %int 3
+       %uint = OpTypeInt 32 0
+     %v3uint = OpTypeVector %uint 3
+  %samplerTy = OpTypeSampler
+%_runtimearr_sampler = OpTypeRuntimeArray %samplerTy
+          %U = OpTypeStruct %v2float %int %_runtimearr_sampler
+      %specC = OpSpecConstant %int 4
+      %int_0 = OpConstant %int 0
+%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform
+         %20 = OpTypeBufferEXT Uniform
+     %uint_2 = OpConstant %uint 2
+%_runtimearr_20 = OpTypeRuntimeArray %20
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %19 = OpUntypedAccessChainKHR %_ptr_Uniform %_runtimearr_20 %resource_heap %int_3
+         %23 = OpBufferPointerEXT %_ptr_Uniform %19
+         %24 = OpUntypedAccessChainKHR %_ptr_Uniform %U %23 %int_0
+         %25 = OpLoad %v2float %24
+               OpStore %fragColor %25
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(
+      diag,
+      HasSubstr("All <id> Extra Operands must appear before Structure Type."));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, OpMemberDecorateIdDuplicateOffset) {
+  const std::string str = R"(
+          OpCapability Shader
+          OpCapability SampledBuffer
+          OpCapability DescriptorHeapEXT
+          OpExtension "SPV_EXT_descriptor_heap"
+          OpMemoryModel Logical GLSL450
+          OpEntryPoint GLCompute %main "main"
+          OpExecutionMode %main LocalSize 1 1 1
+          OpMemberDecorateIdEXT %struct 0 OffsetIdEXT %uint_0
+          OpMemberDecorateIdEXT %struct 1 OffsetIdEXT %uint_0
+          OpMemberDecorateIdEXT %struct 1 OffsetIdEXT %uint_0
+          OpMemberDecorateIdEXT %struct 2 OffsetIdEXT %uint_0
+          %uint = OpTypeInt 32 0
+          %uint_0 = OpConstant %uint 0
+          %14 = OpTypeImage %uint Buffer 0 0 0 2 R32ui
+%struct = OpTypeStruct %14 %uint %uint
+  %void = OpTypeVoid
+     %3 = OpTypeFunction %void
+  %main = OpFunction %void None %3
+     %5 = OpLabel
+          OpReturn
+          OpFunctionEnd
+
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("member '1' decorated with OffsetIdEXT multiple times "
+                        "is not allowed"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, OffsetAndOffsetId) {
+  const std::string str = R"(
+          OpCapability Shader
+          OpCapability SampledBuffer
+          OpCapability DescriptorHeapEXT
+          OpExtension "SPV_EXT_descriptor_heap"
+          OpMemoryModel Logical GLSL450
+          OpEntryPoint GLCompute %main "main"
+          OpExecutionMode %main LocalSize 1 1 1
+          OpMemberDecorate %struct 0 Offset 0
+          OpMemberDecorateIdEXT %struct 0 OffsetIdEXT %uint_0
+          OpMemberDecorateIdEXT %struct 1 OffsetIdEXT %uint_0
+          OpMemberDecorateIdEXT %struct 2 OffsetIdEXT %uint_0
+          %uint = OpTypeInt 32 0
+          %uint_0 = OpConstant %uint 0
+          %14 = OpTypeImage %uint Buffer 0 0 0 2 R32ui
+%struct = OpTypeStruct %14 %uint %uint
+  %void = OpTypeVoid
+     %3 = OpTypeFunction %void
+  %main = OpFunction %void None %3
+     %5 = OpLabel
+          OpReturn
+          OpFunctionEnd
+
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("member '0' decorated with both OffsetIdEXT and Offset "
+                        "is not allowed"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, AtomicImageType) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability Image1D
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %resource_heap
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 460
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpName %main "main"
+               OpName %resource_heap "resource_heap"
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorateId %_runtimearr_10 ArrayStrideIdEXT %13
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+         %10 = OpTypeImage %int 1D 0 0 0 2 Rgba16i
+         %13 = OpConstantSizeOfEXT %int %10
+%_runtimearr_10 = OpTypeRuntimeArray %10
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+%_ptr_Image_int = OpTypePointer Image %int
+ %_ptr_Image = OpTypeUntypedPointerKHR Image
+     %uint_1 = OpConstant %uint 1
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %12 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_10 %resource_heap %int_1
+         %19 = OpUntypedImageTexelPointerEXT %_ptr_Image %10 %12 %int_1 %uint_0
+         %21 = OpAtomicIAdd %int %19 %uint_1 %uint_0 %int_1
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(
+      diag,
+      AnyVUID("VUID-StandaloneSpirv-OpUntypedImageTexelPointerEXT-11416"));
+  EXPECT_THAT(diag,
+              HasSubstr("Expected the Image Format in Image to be R64i, R64ui, "
+                        "R32f, R32i, or R32ui for Vulkan environment using "
+                        "OpUntypedImageTexelPointerEXT"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, UntypedPointerStorageClass) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability Image1D
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %resource_heap
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 460
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpName %main "main"
+               OpName %resource_heap "resource_heap"
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorateId %_runtimearr_10 ArrayStrideIdEXT %13
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+         %10 = OpTypeImage %int 1D 0 0 0 2 R32i
+%_ptr_Uniform = OpTypeUntypedPointerKHR Private
+         %13 = OpConstantSizeOfEXT %int %10
+%_runtimearr_10 = OpTypeRuntimeArray %10
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+%_ptr_Image_int = OpTypePointer Image %int
+ %_ptr_Image = OpTypeUntypedPointerKHR Image
+     %uint_1 = OpConstant %uint 1
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %12 = OpUntypedAccessChainKHR %_ptr_Uniform %_runtimearr_10 %resource_heap %int_1
+         %19 = OpUntypedImageTexelPointerEXT %_ptr_Image %10 %12 %int_1 %uint_0
+         %21 = OpAtomicIAdd %int %19 %uint_1 %uint_0 %int_1
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(diag,
+              AnyVUID("VUID-StandaloneSpirv-OpTypeUntypedPointerKHR-11417"));
+  EXPECT_THAT(diag, HasSubstr("In Vulkan, untyped pointers can only be used "
+                              "in an explicitly laid out storage class"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, InvalidStoreToHeap) {
+  const std::string str = R"(
+                              OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %resource_heap %_
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 450
+               OpSourceExtension "GL_EXT_descriptor_heap"
+               OpName %main "main"
+               OpName %resource_heap "resource_heap"
+               OpName %A "A"
+               OpMemberName %A 0 "a"
+               OpName %PushConstant "PushConstant"
+               OpMemberName %PushConstant 0 "b"
+               OpName %_ ""
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorate %A Block
+               OpMemberDecorate %A 0 BuiltIn ResourceHeapEXT
+               OpMemberDecorate %A 0 Offset 0
+               OpDecorate %PushConstant Block
+               OpMemberDecorate %PushConstant 0 Offset 0
+               OpDecorateId %_runtimearr_20 ArrayStrideIdEXT %21
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+       %uint = OpTypeInt 32 0
+          %A = OpTypeStruct %uint
+%PushConstant = OpTypeStruct %uint
+%_ptr_PushConstant_PushConstant = OpTypePointer PushConstant %PushConstant
+          %_ = OpVariable %_ptr_PushConstant_PushConstant PushConstant
+%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
+%_ptr_StorageBuffer = OpTypeUntypedPointerKHR StorageBuffer
+         %20 = OpTypeBufferEXT StorageBuffer
+         %21 = OpConstantSizeOfEXT %int %20
+%_runtimearr_20 = OpTypeRuntimeArray %20
+     %v3uint = OpTypeVector %uint 3
+     %uint_1 = OpConstant %uint 1
+         %27 = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %16 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
+         %17 = OpLoad %uint %16
+               OpStore %resource_heap %17
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(diag, HasSubstr("OpStore Pointer <id>"));
+  EXPECT_THAT(diag, HasSubstr("storage class is read-only"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, NonDescriptorOpaqueType) {
+  std::string spirv = R"(
+        OpCapability Shader
+        OpCapability DescriptorHeapEXT
+        OpExtension "SPV_EXT_descriptor_heap"
+        OpMemoryModel Logical GLSL450
+        OpEntryPoint Vertex %main "main"
+%float = OpTypeFloat 32
+%image = OpTypeImage %float 2D 0 0 0 1 Unknown
+%sampled = OpTypeSampledImage %image
+%struct = OpTypeStruct %sampled
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%main = OpFunction %void None %func
+   %l = OpLabel
+        OpReturn
+        OpFunctionEnd
+)";
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(diag, AnyVUID("VUID-StandaloneSpirv-DescriptorHeapEXT-11482"));
+  EXPECT_THAT(
+      diag,
+      HasSubstr(
+          "In Vulkan, OpTypeStruct must not contain an invalid opaque type."));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, ResourceHeapBuiltinOnMemeber) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability DescriptorHeapEXT
+               OpExtension "SPV_EXT_descriptor_heap"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %resource_heap %_
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+               OpDecorate %A Block
+               OpMemberDecorate %A 0 BuiltIn ResourceHeapEXT
+               OpMemberDecorate %A 0 Offset 0
+               OpDecorate %PushConstant Block
+               OpMemberDecorate %PushConstant 0 Offset 0
+               OpDecorateId %_runtimearr_21 ArrayStrideIdEXT %22
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+        %int = OpTypeInt 32 1
+      %int_2 = OpConstant %int 2
+       %uint = OpTypeInt 32 0
+          %A = OpTypeStruct %uint
+      %int_0 = OpConstant %int 0
+%PushConstant = OpTypeStruct %uint
+%_ptr_PushConstant_PushConstant = OpTypePointer PushConstant %PushConstant
+          %_ = OpVariable %_ptr_PushConstant_PushConstant PushConstant
+%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint
+%_ptr_StorageBuffer = OpTypeUntypedPointerKHR StorageBuffer
+         %21 = OpTypeBufferEXT StorageBuffer
+         %22 = OpConstantSizeOfEXT %int %21
+%_runtimearr_21 = OpTypeRuntimeArray %21
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %17 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0
+         %18 = OpLoad %uint %17
+         %20 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_21 %resource_heap %int_2
+         %24 = OpBufferPointerEXT %_ptr_StorageBuffer %20
+         %25 = OpUntypedAccessChainKHR %_ptr_StorageBuffer %A %24 %int_0
+               OpStore %25 %18
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "BuiltIn ResourceHeapEXT cannot be used as a member decoration"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, OffsetIdFloat) {
+  const std::string str = R"(
+          OpCapability Shader
+          OpCapability SampledBuffer
+          OpCapability DescriptorHeapEXT
+          OpExtension "SPV_EXT_descriptor_heap"
+          OpMemoryModel Logical GLSL450
+          OpEntryPoint GLCompute %main "main"
+          OpExecutionMode %main LocalSize 1 1 1
+          OpMemberDecorateIdEXT %struct 0 OffsetIdEXT %float_0
+          OpMemberDecorateIdEXT %struct 1 OffsetIdEXT %uint_0
+          OpMemberDecorateIdEXT %struct 2 OffsetIdEXT %uint_0
+          %uint = OpTypeInt 32 0
+          %uint_0 = OpConstant %uint 0
+          %float = OpTypeFloat 32
+          %float_0 = OpConstant %float 0
+          %14 = OpTypeImage %uint Buffer 0 0 0 2 R32ui
+%struct = OpTypeStruct %14 %uint %uint
+  %void = OpTypeVoid
+     %3 = OpTypeFunction %void
+  %main = OpFunction %void None %3
+     %5 = OpLabel
+          OpReturn
+          OpFunctionEnd
+
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OffsetIdEXT extra operand must be a 32-bit int scalar type"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, OffsetIdNonConstant) {
+  const std::string str = R"(
+          OpCapability Shader
+          OpCapability SampledBuffer
+          OpCapability DescriptorHeapEXT
+          OpExtension "SPV_EXT_descriptor_heap"
+          OpMemoryModel Logical GLSL450
+          OpEntryPoint GLCompute %main "main"
+          OpExecutionMode %main LocalSize 1 1 1
+          OpMemberDecorateIdEXT %struct 0 OffsetIdEXT %void
+          OpMemberDecorateIdEXT %struct 1 OffsetIdEXT %uint_0
+          OpMemberDecorateIdEXT %struct 2 OffsetIdEXT %uint_0
+          %uint = OpTypeInt 32 0
+          %uint_0 = OpConstant %uint 0
+          %float = OpTypeFloat 32
+          %float_0 = OpConstant %float 0
+          %14 = OpTypeImage %uint Buffer 0 0 0 2 R32ui
+%struct = OpTypeStruct %14 %uint %uint
+  %void = OpTypeVoid
+     %3 = OpTypeFunction %void
+  %main = OpFunction %void None %3
+     %5 = OpLabel
+          OpReturn
+          OpFunctionEnd
+
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OffsetIdEXT extra operand must be a 32-bit int scalar type"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, ArrayStrideFloat) {
+  const std::string str = R"(
+      OpCapability Shader
+      OpCapability DescriptorHeapEXT
+      OpExtension "SPV_EXT_descriptor_heap"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint GLCompute %main "main"
+      OpExecutionMode %main LocalSize 1 1 1
+      OpDecorateId %array ArrayStrideIdEXT %float_4
+%float = OpTypeFloat 32
+%float_4 = OpConstant %float 4
+%int = OpTypeInt 32 0
+%int_2 = OpConstant %int 2
+%sampler = OpTypeSampler
+%array = OpTypeArray %sampler %int_2
+%void = OpTypeVoid
+  %3 = OpTypeFunction %void
+%main = OpFunction %void None %3
+  %5 = OpLabel
+      OpReturn
+      OpFunctionEnd
+
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "ArrayStrideIdEXT extra operand must be a 32-bit int scalar type"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, ArrayStrideConstantZero) {
+  const std::string str = R"(
+      OpCapability Shader
+      OpCapability DescriptorHeapEXT
+      OpExtension "SPV_EXT_descriptor_heap"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint GLCompute %main "main"
+      OpExecutionMode %main LocalSize 1 1 1
+      OpDecorateId %array ArrayStrideIdEXT %int_0
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_2 = OpConstant %int 2
+%sampler = OpTypeSampler
+%array = OpTypeArray %sampler %int_2
+%void = OpTypeVoid
+  %3 = OpTypeFunction %void
+%main = OpFunction %void None %3
+  %5 = OpLabel
+      OpReturn
+      OpFunctionEnd
+
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("ArrayStrideIdEXT contains a stride of zero"));
+}
+
+TEST_F(ValidateSpvEXTDescriptorHeap, ArrayStrideSpecConstantZero) {
+  const std::string str = R"(
+      OpCapability Shader
+      OpCapability DescriptorHeapEXT
+      OpExtension "SPV_EXT_descriptor_heap"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint GLCompute %main "main"
+      OpExecutionMode %main LocalSize 1 1 1
+      OpDecorate %spec SpecId 1
+      OpDecorateId %array ArrayStrideIdEXT %spec
+%int = OpTypeInt 32 0
+%int_2 = OpConstant %int 2
+%spec = OpSpecConstant %int 0
+%sampler = OpTypeSampler
+%array = OpTypeArray %sampler %int_2
+%void = OpTypeVoid
+  %3 = OpTypeFunction %void
+%main = OpFunction %void None %3
+  %5 = OpLabel
+      OpReturn
+      OpFunctionEnd
+
+  )";
+  CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+}
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/val_extension_spv_intel_arbitrary_precision_integers_test.cpp b/test/val/val_extension_spv_intel_arbitrary_precision_integers_test.cpp
new file mode 100644
index 0000000..c21bfc8
--- /dev/null
+++ b/test/val/val_extension_spv_intel_arbitrary_precision_integers_test.cpp
@@ -0,0 +1,178 @@
+// Copyright (c) 2025 The Khronos Group 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.
+
+// Tests for SPV_INTEL_arbitrary_precision_integers extension
+
+#include <string>
+
+#include "gmock/gmock.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+
+using ValidateIntelArbitraryPrecisionIntegers = spvtest::ValidateBase<bool>;
+
+TEST_F(ValidateIntelArbitraryPrecisionIntegers,
+       ArbitraryPrecisionIntegerWithoutExtension) {
+  const std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %int19 = OpTypeInt 19 1
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid number of bits (19) used for OpTypeInt"));
+}
+
+TEST_F(ValidateIntelArbitraryPrecisionIntegers,
+       ArbitraryPrecisionIntegerWithExtension) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability ArbitraryPrecisionIntegersINTEL
+               OpExtension "SPV_INTEL_arbitrary_precision_integers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %int19 = OpTypeInt 19 1
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateIntelArbitraryPrecisionIntegers,
+       ArbitraryPrecisionIntegerVariousBitWidths) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability ArbitraryPrecisionIntegersINTEL
+               OpExtension "SPV_INTEL_arbitrary_precision_integers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int1 = OpTypeInt 1 1
+        %int3 = OpTypeInt 3 0
+        %int7 = OpTypeInt 7 1
+       %int13 = OpTypeInt 13 0
+       %int19 = OpTypeInt 19 1
+       %int33 = OpTypeInt 33 0
+       %int65 = OpTypeInt 65 1
+      %int128 = OpTypeInt 128 0
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateIntelArbitraryPrecisionIntegers,
+       ArbitraryPrecisionIntegerWithCapabilityOnly) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability ArbitraryPrecisionIntegersINTEL
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %int19 = OpTypeInt 19 1
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_MISSING_EXTENSION, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("SPV_INTEL_arbitrary_precision_integers"));
+}
+
+TEST_F(ValidateIntelArbitraryPrecisionIntegers, StandardIntegerTypesStillWork) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability ArbitraryPrecisionIntegersINTEL
+               OpExtension "SPV_INTEL_arbitrary_precision_integers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %int32 = OpTypeInt 32 1
+       %uint32 = OpTypeInt 32 0
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateIntelArbitraryPrecisionIntegers, ZeroBitIntegerRejected) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability ArbitraryPrecisionIntegersINTEL
+               OpExtension "SPV_INTEL_arbitrary_precision_integers"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int0 = OpTypeInt 0 0
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("OpTypeInt has 0 bits"));
+}
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/val_extension_spv_intel_function_variants.cpp b/test/val/val_extension_spv_intel_function_variants.cpp
new file mode 100644
index 0000000..adb243f
--- /dev/null
+++ b/test/val/val_extension_spv_intel_function_variants.cpp
@@ -0,0 +1,123 @@
+// Copyright 2025 The Khronos Group 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.
+
+// Tests for SPV_INTEL_inline_assembly
+
+#include <string>
+
+#include "gmock/gmock.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+
+using ValidateSpvINTELFunctionVariants = spvtest::ValidateBase<bool>;
+
+TEST_F(ValidateSpvINTELFunctionVariants, Valid) {
+  const std::string str = R"(
+         OpCapability Kernel
+         OpCapability Linkage
+         OpCapability SpecConditionalINTEL
+         OpCapability FunctionVariantsINTEL
+         OpConditionalCapabilityINTEL %1 Int8
+         OpExtension "SPV_INTEL_function_variants"
+         OpMemoryModel Logical OpenCL
+ %bool = OpTypeBool
+    %1 = OpSpecConstantTrue %bool
+  )";
+  CompileSuccessfully(str.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateSpvINTELFunctionVariants, RequiresExtension1) {
+  const std::string str = R"(
+         OpCapability Kernel
+         OpCapability Linkage
+         OpCapability SpecConditionalINTEL
+         OpMemoryModel Logical OpenCL
+  )";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(
+      diag, HasSubstr("1st operand of Capability: operand "
+                      "SpecConditionalINTEL(6245) requires "
+                      "one of these extensions: SPV_INTEL_function_variants"));
+  EXPECT_THAT(diag, HasSubstr("OpCapability SpecConditionalINTEL"));
+}
+
+TEST_F(ValidateSpvINTELFunctionVariants, RequiresExtension2) {
+  const std::string str = R"(
+         OpCapability Kernel
+         OpCapability Linkage
+         OpCapability FunctionVariantsINTEL
+         OpMemoryModel Logical OpenCL
+  )";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(
+      diag, HasSubstr("1st operand of Capability: operand "
+                      "FunctionVariantsINTEL(6246) requires "
+                      "one of these extensions: SPV_INTEL_function_variants"));
+  EXPECT_THAT(diag, HasSubstr("OpCapability FunctionVariantsINTEL"));
+}
+
+TEST_F(ValidateSpvINTELFunctionVariants, RequiresCapability1) {
+  const std::string str = R"(
+         OpCapability Kernel
+         OpCapability Linkage
+         OpConditionalCapabilityINTEL %1 Int8
+         OpExtension "SPV_INTEL_function_variants"
+         OpMemoryModel Logical OpenCL
+ %bool = OpTypeBool
+    %1 = OpSpecConstantTrue %bool
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(
+      diag, HasSubstr("Opcode ConditionalCapabilityINTEL requires one of these "
+                      "capabilities: SpecConditionalINTEL"));
+  EXPECT_THAT(diag, HasSubstr("OpConditionalCapabilityINTEL %1 Int8"));
+}
+
+TEST_F(ValidateSpvINTELFunctionVariants, RequiresCapability2) {
+  const std::string str = R"(
+         OpCapability Kernel
+         OpCapability Linkage
+         OpCapability SpecConditionalINTEL
+         OpConditionalCapabilityINTEL %1 Int8
+         OpExtension "SPV_INTEL_function_variants"
+         OpMemoryModel Logical OpenCL
+ %bool = OpTypeBool
+    %1 = OpSpecConstantArchitectureINTEL %bool 0 0 170 0
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(
+      diag,
+      HasSubstr("Opcode SpecConstantArchitectureINTEL requires one of these "
+                "capabilities: FunctionVariantsINTEL"));
+  EXPECT_THAT(
+      diag, HasSubstr("%1 = OpSpecConstantArchitectureINTEL %bool 0 0 170 0"));
+}
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/val_extension_spv_intel_inline_assembly.cpp b/test/val/val_extension_spv_intel_inline_assembly.cpp
new file mode 100644
index 0000000..a683afd
--- /dev/null
+++ b/test/val/val_extension_spv_intel_inline_assembly.cpp
@@ -0,0 +1,109 @@
+// Copyright 2025 The Khronos Group 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.
+
+// Tests for SPV_INTEL_inline_assembly
+
+#include <string>
+
+#include "gmock/gmock.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+
+using ValidateSpvINTELInlineAssembly = spvtest::ValidateBase<bool>;
+
+TEST_F(ValidateSpvINTELInlineAssembly, Valid) {
+  const std::string str = R"(
+         OpCapability Kernel
+         OpCapability Addresses
+         OpCapability Linkage
+         OpCapability AsmINTEL
+         OpExtension "SPV_INTEL_inline_assembly"
+         OpMemoryModel Physical32 OpenCL
+         OpDecorate %1 SideEffectsINTEL
+    %2 = OpTypeVoid
+    %3 = OpTypeFunction %2
+    %4 = OpAsmTargetINTEL "spirv64-unknown-unknown"
+    %1 = OpAsmINTEL %2 %3 %4 "nop" ""
+    %5 = OpFunction %2 None %3
+    %6 = OpLabel
+    %7 = OpAsmCallINTEL %2 %1
+         OpReturn
+         OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateSpvINTELInlineAssembly, RequiresExtension) {
+  const std::string str = R"(
+         OpCapability Kernel
+         OpCapability Addresses
+         OpCapability Linkage
+         OpCapability AsmINTEL
+         OpMemoryModel Physical32 OpenCL
+         OpDecorate %1 SideEffectsINTEL
+    %2 = OpTypeVoid
+    %3 = OpTypeFunction %2
+    %4 = OpAsmTargetINTEL "spirv64-unknown-unknown"
+    %1 = OpAsmINTEL %2 %3 %4 "nop" ""
+    %5 = OpFunction %2 None %3
+    %6 = OpLabel
+    %7 = OpAsmCallINTEL %2 %1
+         OpReturn
+         OpFunctionEnd
+  )";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(
+      diag,
+      HasSubstr("1st operand of Capability: operand AsmINTEL(5606) requires "
+                "one of these extensions: SPV_INTEL_inline_assembly"));
+  EXPECT_THAT(diag, HasSubstr("OpCapability AsmINTEL"));
+}
+
+TEST_F(ValidateSpvINTELInlineAssembly, RequiresCapability) {
+  const std::string str = R"(
+         OpCapability Kernel
+         OpCapability Addresses
+         OpCapability Linkage
+         OpExtension "SPV_INTEL_inline_assembly"
+         OpMemoryModel Physical32 OpenCL
+         OpDecorate %1 SideEffectsINTEL
+    %2 = OpTypeVoid
+    %3 = OpTypeFunction %2
+    %4 = OpAsmTargetINTEL "spirv64-unknown-unknown"
+    %1 = OpAsmINTEL %2 %3 %4 "nop" ""
+    %5 = OpFunction %2 None %3
+    %6 = OpLabel
+    %7 = OpAsmCallINTEL %2 %1
+         OpReturn
+         OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  const std::string diag = getDiagnosticString();
+  EXPECT_THAT(diag, HasSubstr("Operand 2 of Decorate requires one of these "
+                              "capabilities: AsmINTEL"));
+  EXPECT_THAT(diag, HasSubstr("OpDecorate %1 SideEffectsINTEL"));
+}
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/val_extension_spv_khr_abort_test.cpp b/test/val/val_extension_spv_khr_abort_test.cpp
new file mode 100644
index 0000000..1c4eae5
--- /dev/null
+++ b/test/val/val_extension_spv_khr_abort_test.cpp
@@ -0,0 +1,690 @@
+// Copyright (c) 2026 The Khronos Group 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.
+
+// Tests for OpExtension validator rules.
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "source/spirv_target_env.h"
+#include "test/unit_spirv.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+using ::testing::Values;
+using ::testing::ValuesIn;
+
+using ValidateSpvKHRAbort = spvtest::ValidateBase<bool>;
+
+TEST_F(ValidateSpvKHRAbort, Valid) {
+  const std::string str = R"(
+OpCapability Shader
+OpCapability AbortKHR
+OpExtension "SPV_KHR_abort"
+OpMemoryModel Logical Simple
+OpEntryPoint GLCompute %main "main"
+
+%void    = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%uint32_t = OpTypeInt 32 0
+%payload = OpConstant %uint32_t 6
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpAbortKHR %uint32_t %payload
+OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateSpvKHRAbort, RequireFinalInstructionInBlock) {
+  const std::string str = R"(
+            OpCapability Shader
+            OpCapability AbortKHR
+            OpExtension "SPV_KHR_abort"
+            OpMemoryModel Logical Simple
+            OpEntryPoint GLCompute %main "main"
+
+%void     = OpTypeVoid
+%void_fn  = OpTypeFunction %void
+%uint32_t = OpTypeInt 32 0
+%payload  = OpConstant %uint32_t 6
+%main     = OpFunction %void None %void_fn
+%entry    = OpLabel
+            OpAbortKHR %uint32_t %payload
+            OpReturn
+            OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Return must appear in a block"));
+}
+
+TEST_F(ValidateSpvKHRAbort, RequiresCapability) {
+  const std::string str = R"(
+            OpCapability Shader
+            OpExtension "SPV_KHR_abort"
+            OpMemoryModel Logical Simple
+            OpEntryPoint GLCompute %main "main"
+
+%void     = OpTypeVoid
+%void_fn  = OpTypeFunction %void
+%uint32_t = OpTypeInt 32 0
+%payload  = OpConstant %uint32_t 6
+%main     = OpFunction %void None %void_fn
+%entry    = OpLabel
+            OpAbortKHR %uint32_t %payload
+            OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Opcode AbortKHR requires one of these capabilities: AbortKHR"));
+}
+
+TEST_F(ValidateSpvKHRAbort, RequiresExtension) {
+  const std::string str = R"(
+            OpCapability Shader
+            OpCapability AbortKHR
+            OpMemoryModel Logical Simple
+            OpEntryPoint GLCompute %main "main"
+
+%void     = OpTypeVoid
+%void_fn  = OpTypeFunction %void
+%uint32_t = OpTypeInt 32 0
+%payload  = OpConstant %uint32_t 6
+%main     = OpFunction %void None %void_fn
+%entry    = OpLabel
+            OpAbortKHR %uint32_t %payload
+            OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("1st operand of Capability: operand AbortKHR(5120) "
+                        "requires one of these extensions: SPV_KHR_abort"));
+}
+
+TEST_F(ValidateSpvKHRAbort, MismatchedOperandTypes) {
+  const std::string str = R"(
+            OpCapability Shader
+            OpCapability AbortKHR
+            OpExtension "SPV_KHR_abort"
+            OpMemoryModel Logical Simple
+            OpEntryPoint GLCompute %main "main"
+
+%void     = OpTypeVoid
+%void_fn  = OpTypeFunction %void
+%uint32_t = OpTypeInt 32 0
+%f32_t    = OpTypeFloat 32
+%payload  = OpConstant %uint32_t 6
+%main     = OpFunction %void None %void_fn
+%entry    = OpLabel
+            OpAbortKHR %f32_t %payload
+            OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Type of Message operand does not logically match "
+                        "the type of the Message Type operand"));
+}
+
+TEST_F(ValidateSpvKHRAbort, ValidCompositeOperandTypes) {
+  const std::string str = R"(
+             OpCapability Shader
+             OpCapability Int8
+             OpCapability AbortKHR
+             OpCapability ConstantDataKHR
+             OpExtension "SPV_KHR_abort"
+             OpExtension "SPV_KHR_constant_data"
+             OpMemoryModel Logical Simple
+             OpEntryPoint GLCompute %main "main"
+
+             OpDecorate %string1_t UTFEncodedKHR
+             OpDecorate %string2_t UTFEncodedKHR
+
+             OpDecorate %string1_x UTFEncodedKHR
+             OpDecorate %string1_x ArrayStride 1
+             OpDecorate %string2_x UTFEncodedKHR
+             OpDecorate %string2_x ArrayStride 1
+             OpMemberDecorate %message_x 0 Offset 0
+             OpMemberDecorate %message_x 1 Offset 6
+             OpMemberDecorate %message_x 2 Offset 8
+
+%void      = OpTypeVoid
+%void_fn   = OpTypeFunction %void
+
+   %char_t = OpTypeInt 8 0
+ %uint32_t = OpTypeInt 32 0
+  %str1len = OpConstant %uint32_t 6
+%string1_t = OpTypeArray %char_t %str1len
+  ; "test: "
+  %string1 = OpConstantDataKHR %string1_t 0x74736574 0x0000203A
+  %str2len = OpSpecConstant %uint32_t 2
+%string2_t = OpTypeArray %char_t %str2len
+  ; "%u"
+  %string2 = OpSpecConstantDataKHR %string2_t 0x00007525
+%message_t = OpTypeStruct %string1_t %string2_t %uint32_t
+%uintval   = OpConstant %uint32_t 6
+
+%string1_x = OpTypeArray %char_t %str1len
+%string2_x = OpTypeArray %char_t %str2len
+%message_x = OpTypeStruct %string1_t %string2_t %uint32_t
+
+%main      = OpFunction %void None %void_fn
+%entry     = OpLabel
+  %message = OpCompositeConstruct %message_t %string1 %string2 %uintval
+             OpAbortKHR %message_x %message
+             OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateSpvKHRAbort, MismatchedCompositeOperandTypes) {
+  const std::string str = R"(
+             OpCapability Shader
+             OpCapability Int8
+             OpCapability AbortKHR
+             OpCapability ConstantDataKHR
+             OpExtension "SPV_KHR_abort"
+             OpExtension "SPV_KHR_constant_data"
+             OpMemoryModel Logical Simple
+             OpEntryPoint GLCompute %main "main"
+
+             OpDecorate %string1_t UTFEncodedKHR
+             OpDecorate %string2_t UTFEncodedKHR
+
+             OpDecorate %string1_x UTFEncodedKHR
+             OpDecorate %string1_x ArrayStride 1
+             OpDecorate %string2_x UTFEncodedKHR
+             OpDecorate %string2_x ArrayStride 1
+             OpMemberDecorate %message_x 0 Offset 0
+             OpMemberDecorate %message_x 1 Offset 6
+             OpMemberDecorate %message_x 2 Offset 8
+
+%void      = OpTypeVoid
+%void_fn   = OpTypeFunction %void
+
+   %char_t = OpTypeInt 8 0
+ %uint32_t = OpTypeInt 32 0
+  %str1len = OpConstant %uint32_t 6
+%string1_t = OpTypeArray %char_t %str1len
+  ; "test: "
+  %string1 = OpConstantDataKHR %string1_t 0x74736574 0x0000203A
+  %str2len = OpSpecConstant %uint32_t 2
+%string2_t = OpTypeArray %char_t %str2len
+  ; "%u"
+  %string2 = OpSpecConstantDataKHR %string2_t 0x00007525
+%message_t = OpTypeStruct %string1_t %string2_t
+%uintval   = OpConstant %uint32_t 6
+
+%string1_x = OpTypeArray %char_t %str1len
+%string2_x = OpTypeArray %char_t %str2len
+%message_x = OpTypeStruct %string1_t %string2_t %uint32_t
+
+%main      = OpFunction %void None %void_fn
+%entry     = OpLabel
+  %message = OpCompositeConstruct %message_t %string1 %string2
+             OpAbortKHR %message_x %message
+             OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Type of Message operand does not logically match "
+                        "the type of the Message Type operand"));
+}
+
+TEST_F(ValidateSpvKHRAbort, ConstantDataNonArray) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability ConstantDataKHR
+               OpExtension "SPV_KHR_constant_data"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+       %data = OpConstantDataKHR %uint 1
+  %void_func = OpTypeFunction %void
+       %main = OpFunction %void None %void_func
+ %main_label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result type must be an array."));
+}
+
+TEST_F(ValidateSpvKHRAbort, ConstantDataFloat) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability ConstantDataKHR
+               OpExtension "SPV_KHR_constant_data"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+       %float = OpTypeFloat 32
+  %uint_size = OpConstant %uint 1
+ %uint_array = OpTypeArray %float %uint_size
+       %data = OpConstantDataKHR %uint_array 1
+  %void_func = OpTypeFunction %void
+       %main = OpFunction %void None %void_func
+ %main_label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result type must be an array of integer scalar type"));
+}
+
+TEST_F(ValidateSpvKHRAbort, ConstantDataIntVector) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability ConstantDataKHR
+               OpExtension "SPV_KHR_constant_data"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+      %uvec3 = OpTypeVector %uint 3
+  %uint_size = OpConstant %uint 1
+ %uint_array = OpTypeArray %uvec3 %uint_size
+       %data = OpConstantDataKHR %uint_array 1
+  %void_func = OpTypeFunction %void
+       %main = OpFunction %void None %void_func
+ %main_label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result type must be an array of integer scalar type"));
+}
+
+TEST_F(ValidateSpvKHRAbort, ConstantDataMultiLength) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability ConstantDataKHR
+               OpCapability Int8
+               OpExtension "SPV_KHR_constant_data"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+       %char = OpTypeInt 8 1
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+     %uint_3 = OpConstant %uint 3
+     %uint_4 = OpConstant %uint 4
+ %char_array_1 = OpTypeArray %char %uint_1
+ %char_array_2 = OpTypeArray %char %uint_2
+ %char_array_3 = OpTypeArray %char %uint_3
+ %char_array_4 = OpTypeArray %char %uint_4
+       %data_1 = OpConstantDataKHR %char_array_1 0
+       %data_2 = OpConstantDataKHR %char_array_2 0
+       %data_3 = OpConstantDataKHR %char_array_3 0
+       %data_4 = OpConstantDataKHR %char_array_4 0
+  %void_func = OpTypeFunction %void
+       %main = OpFunction %void None %void_func
+ %main_label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateSpvKHRAbort, ConstantDataSpecLength) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability ConstantDataKHR
+               OpExtension "SPV_KHR_constant_data"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+  %uint_size = OpSpecConstant %uint 4444
+ %uint_array = OpTypeArray %uint %uint_size
+       %data = OpConstantDataKHR %uint_array 0
+  %void_func = OpTypeFunction %void
+       %main = OpFunction %void None %void_func
+ %main_label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateSpvKHRAbort, ConstantDataSpecLengthAndData) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability ConstantDataKHR
+               OpExtension "SPV_KHR_constant_data"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %uint_size SpecId 1
+               OpDecorate %data SpecId 2
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+  %uint_size = OpSpecConstant %uint 4444
+ %uint_array = OpTypeArray %uint %uint_size
+       %data = OpSpecConstantDataKHR %uint_array 1
+    ; No SpecID for this one
+     %data_2 = OpSpecConstantDataKHR %uint_array 2
+  %void_func = OpTypeFunction %void
+       %main = OpFunction %void None %void_func
+ %main_label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateSpvKHRAbort, ConstantDataNull) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability ConstantDataKHR
+               OpExtension "SPV_KHR_constant_data"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+  %uint_size = OpConstant %uint 1
+ %uint_array = OpTypeArray %uint %uint_size
+       %data = OpConstantDataKHR %uint_array
+  %void_func = OpTypeFunction %void
+       %main = OpFunction %void None %void_func
+ %main_label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("There must be at least 1 literal integer"));
+}
+
+TEST_F(ValidateSpvKHRAbort, ConstantDataLengthOverUint32) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability ConstantDataKHR
+               OpExtension "SPV_KHR_constant_data"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+  %uint_size = OpConstant %uint 2
+ %uint_array = OpTypeArray %uint %uint_size
+       %data = OpConstantDataKHR %uint_array 1
+  %void_func = OpTypeFunction %void
+       %main = OpFunction %void None %void_func
+ %main_label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("contains 1 words of data, but needs to have 2 words "
+                        "to match the array of 2 of 32-bit ints"));
+}
+
+TEST_F(ValidateSpvKHRAbort, ConstantDataLengthUnderUint32) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability ConstantDataKHR
+               OpExtension "SPV_KHR_constant_data"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+  %uint_size = OpConstant %uint 2
+ %uint_array = OpTypeArray %uint %uint_size
+       %data = OpConstantDataKHR %uint_array 1 2 3
+  %void_func = OpTypeFunction %void
+       %main = OpFunction %void None %void_func
+ %main_label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("contains 3 words of data, but needs to have 2 words "
+                        "to match the array of 2 of 32-bit ints"));
+}
+
+TEST_F(ValidateSpvKHRAbort, ConstantDataLengthOverUint8) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability ConstantDataKHR
+               OpCapability Int8
+               OpExtension "SPV_KHR_constant_data"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+       %char = OpTypeInt 8 1
+  %uint_size = OpConstant %uint 5
+ %char_array = OpTypeArray %char %uint_size
+       %data = OpConstantDataKHR %char_array 1
+  %void_func = OpTypeFunction %void
+       %main = OpFunction %void None %void_func
+ %main_label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("contains 1 words of data, but needs to have 2 words "
+                        "to match the array of 5 of 8-bit ints"));
+}
+
+TEST_F(ValidateSpvKHRAbort, ConstantDataLengthUnderUint8) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability ConstantDataKHR
+               OpCapability Int8
+               OpExtension "SPV_KHR_constant_data"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+       %char = OpTypeInt 8 1
+  %uint_size = OpConstant %uint 4
+ %char_array = OpTypeArray %char %uint_size
+       %data = OpConstantDataKHR %char_array 1 2
+  %void_func = OpTypeFunction %void
+       %main = OpFunction %void None %void_func
+ %main_label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("contains 2 words of data, but needs to have 1 words "
+                        "to match the array of 4 of 8-bit ints"));
+}
+
+TEST_F(ValidateSpvKHRAbort, ConstantDataLengthUint64Good) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability ConstantDataKHR
+               OpCapability Int64
+               OpExtension "SPV_KHR_constant_data"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+        %u64 = OpTypeInt 64 0
+  %uint_size = OpConstant %uint 2
+ %u64_array = OpTypeArray %u64 %uint_size
+       %data = OpConstantDataKHR %u64_array 1 2 3 4
+  %void_func = OpTypeFunction %void
+       %main = OpFunction %void None %void_func
+ %main_label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateSpvKHRAbort, ConstantDataLengthUint64Short) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability ConstantDataKHR
+               OpCapability Int64
+               OpExtension "SPV_KHR_constant_data"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+        %u64 = OpTypeInt 64 0
+  %uint_size = OpConstant %uint 1
+ %u64_array = OpTypeArray %u64 %uint_size
+       %data = OpConstantDataKHR %u64_array 1
+  %void_func = OpTypeFunction %void
+       %main = OpFunction %void None %void_func
+ %main_label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("contains 1 words of data, but needs to have 2 words "
+                        "to match the array of 1 of 64-bit ints"));
+}
+
+TEST_F(ValidateSpvKHRAbort, ConstantDataLengthUint64Short2) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability ConstantDataKHR
+               OpCapability Int64
+               OpExtension "SPV_KHR_constant_data"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+        %u64 = OpTypeInt 64 0
+  %uint_size = OpConstant %uint 2
+ %u64_array = OpTypeArray %u64 %uint_size
+       %data = OpConstantDataKHR %u64_array 1 2 3
+  %void_func = OpTypeFunction %void
+       %main = OpFunction %void None %void_func
+ %main_label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("contains 3 words of data, but needs to have 4 words "
+                        "to match the array of 2 of 64-bit ints"));
+}
+
+TEST_F(ValidateSpvKHRAbort, ConstantDataLengthOverUint64) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability ConstantDataKHR
+               OpCapability Int64
+               OpExtension "SPV_KHR_constant_data"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+        %u64 = OpTypeInt 64 0
+  %uint_size = OpConstant %uint 1
+ %u64_array = OpTypeArray %u64 %uint_size
+       %data = OpConstantDataKHR %u64_array 1 2 3 4
+  %void_func = OpTypeFunction %void
+       %main = OpFunction %void None %void_func
+ %main_label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("contains 4 words of data, but needs to have 2 words "
+                        "to match the array of 1 of 64-bit ints"));
+}
+
+TEST_F(ValidateSpvKHRAbort, ConstantDataLengthUnderUint64) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability ConstantDataKHR
+               OpCapability Int64
+               OpExtension "SPV_KHR_constant_data"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+        %u64 = OpTypeInt 64 0
+  %uint_size = OpConstant %uint 2
+ %u64_array = OpTypeArray %u64 %uint_size
+       %data = OpConstantDataKHR %u64_array 1 2
+  %void_func = OpTypeFunction %void
+       %main = OpFunction %void None %void_func
+ %main_label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str());
+  EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("contains 2 words of data, but needs to have 4 words "
+                        "to match the array of 2 of 64-bit ints"));
+}
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/val_extension_spv_khr_integer_dot_product_test.cpp b/test/val/val_extension_spv_khr_integer_dot_product_test.cpp
index 5b3a309..9e3d53a 100644
--- a/test/val/val_extension_spv_khr_integer_dot_product_test.cpp
+++ b/test/val/val_extension_spv_khr_integer_dot_product_test.cpp
@@ -18,9 +18,7 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/extensions.h"
-#include "source/spirv_target_env.h"
-#include "test/unit_spirv.h"
+#include "spirv-tools/libspirv.h"
 #include "test/val/val_fixtures.h"
 
 namespace spvtools {
@@ -739,7 +737,7 @@
                            "%uchar",  // match width
                            "%v4uchar",
                            "%v4uchar",
-                           "%uint",
+                           "%uchar",
                            false,
                            ""},
                       Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"},
@@ -1247,80 +1245,365 @@
                            true,
                            ""}));
 
-using ValidateSpvKHRIntegerDotProductSimple = ::testing::Test;
+using ValidateIntegerDotProductSimple = spvtest::ValidateBase<bool>;
 
-TEST(ValidateSpvKHRIntegerDotProductSimple, DISABLED_RequiresExtension) {
-  FAIL();
+TEST_F(ValidateIntegerDotProductSimple, RequiresExtension) {
+  const std::string ss = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%fn = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%v2int = OpTypeVector %int 2
+%int_2 = OpConstant %int 2
+%vec_a = OpConstantComposite %v2int %int_2 %int_2
+%vec_b = OpConstantComposite %v2int %int_2 %int_2
+%main = OpFunction %void None %fn
+%label = OpLabel
+%x = OpSDot %int %vec_a %vec_b
+OpReturn
+OpFunctionEnd
+  )";
+  CompileSuccessfully(ss);
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Opcode SDot requires one of these capabilities: DotProduct"));
 }
 
-TEST(ValidateSpvKHRIntegerDotProductSimple, DISABLED_Invalid_ResultTooNarrow) {
-  // Test across all the instructions.
-  FAIL();
+std::string GenerateShaderCode(const std::string& body) {
+  std::ostringstream ss;
+  ss << R"(
+OpCapability Shader
+OpCapability DotProductKHR
+OpCapability DotProductInputAll
+OpCapability DotProductInput4x8BitPacked
+OpCapability Int16
+OpCapability Int64
+OpCapability LongVectorEXT
+OpExtension "SPV_EXT_long_vector"
+OpExtension "SPV_KHR_integer_dot_product"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %spec_5 SpecId 0
+OpDecorate %spec_6 SpecId 1
+
+%void = OpTypeVoid
+%fn = OpTypeFunction %void
+
+%float = OpTypeFloat 32
+%int16 = OpTypeInt 16 1
+%uint16 = OpTypeInt 16 0
+%uint = OpTypeInt 32 0
+%int = OpTypeInt 32 1
+%int64 = OpTypeInt 64 1
+%v2int = OpTypeVector %int 2
+%v3int = OpTypeVector %int 3
+%v2uint = OpTypeVector %uint 2
+%v2int16 = OpTypeVector %int16 2
+%v2uint16 = OpTypeVector %uint16 2
+%v2int64 = OpTypeVector %int64 2
+
+%float_1 = OpConstant %float 1
+%int_1 = OpConstant %int 1
+%uint_1 = OpConstant %uint 1
+%int64_1 = OpConstant %int64 1
+%int16_1 = OpConstant %int16 1
+%uint16_1 = OpConstant %uint16 1
+
+%ivec2_1 = OpConstantComposite %v2int %int_1 %int_1
+%ivec3_1 = OpConstantComposite %v3int %int_1 %int_1 %int_1
+%uvec2_1 = OpConstantComposite %v2uint %uint_1 %uint_1
+%i16vec2_1 = OpConstantComposite %v2int16 %int16_1 %int16_1
+%u16vec2_1 = OpConstantComposite %v2uint16 %uint16_1 %uint16_1
+%i64vec2_1 = OpConstantComposite %v2int64 %int64_1 %int64_1
+
+%uint_5 = OpConstant %uint 5
+%uint_6 = OpConstant %uint 6
+%spec_5 = OpSpecConstant %uint 5
+%spec_6 = OpSpecConstant %uint 6
+
+%uvec5 = OpTypeVectorIdEXT %uint %uint_5
+%uvec6 = OpTypeVectorIdEXT %uint %uint_6
+%spec_uvec5 = OpTypeVectorIdEXT %uint %spec_5
+%spec_uvec6 = OpTypeVectorIdEXT %uint %spec_6
+
+%uvec5_1 = OpConstantComposite %uvec5 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1
+%uvec6_1 = OpConstantComposite %uvec6 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1
+%spec_uvec5_1 = OpConstantComposite %spec_uvec5 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1
+%spec_uvec6_1 = OpConstantComposite %spec_uvec6 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1
+
+%main = OpFunction %void None %fn
+%label = OpLabel
+)";
+
+  ss << body;
+
+  ss << R"(
+OpReturn
+OpFunctionEnd)";
+  return ss.str();
 }
 
-TEST(ValidateSpvKHRIntegerDotProductSimple,
-     DISABLED_Invalid_UDot_OperandTypesMatch) {
-  FAIL();
+TEST_F(ValidateIntegerDotProductSimple, Dot16BitGood) {
+  const std::string ss = R"(
+     %x = OpSDot %int %i16vec2_1 %i16vec2_1
+     %y = OpUDot %uint %u16vec2_1 %u16vec2_1
+     %z = OpSUDot %uint %i16vec2_1 %u16vec2_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST(ValidateSpvKHRIntegerDotProductSimple,
-     DISABLED_Invalid_SDot_OperandTypesMatchExceptSignedness) {
-  FAIL();
+TEST_F(ValidateIntegerDotProductSimple, SDotDifferentVectors) {
+  const std::string ss = R"(
+     %x = OpSDot %int %ivec2_1 %ivec3_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("'Vector 1' is 2 components but 'Vector 2' is 3 components"));
 }
 
-TEST(ValidateSpvKHRIntegerDotProductSimple,
-     DISABLED_Invalid_SUDot_OperandTypesMatchExceptSignedness) {
-  FAIL();
+TEST_F(ValidateIntegerDotProductSimple, SDotFloat) {
+  const std::string ss = R"(
+     %x = OpSDot %int %float_1 %float_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected 'Vector 1' to be an int scalar or vector"));
 }
 
-TEST(ValidateSpvKHRIntegerDotProductSimple,
-     DISABLED_Invalid_UDotAccSat_OperandTypesMatch) {
-  FAIL();
+TEST_F(ValidateIntegerDotProductSimple, SDot16BitScalar) {
+  const std::string ss = R"(
+     %x = OpSDot %int %int16_1 %int16_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected 'Vector 1' to be an int scalar or vector"));
 }
 
-TEST(ValidateSpvKHRIntegerDotProductSimple,
-     DISABLED_Invalid_SDotAccSat_OperandTypesMatchExceptSignedness) {
-  FAIL();
+TEST_F(ValidateIntegerDotProductSimple, SDotResultVector) {
+  const std::string ss = R"(
+     %x = OpSDot %v2int %ivec2_1 %ivec2_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result must be an int scalar type"));
 }
 
-TEST(ValidateSpvKHRIntegerDotProductSimple,
-     DISABLED_Invalid_SUDotAccSat_OperandTypesMatchExceptSignedness) {
-  FAIL();
+TEST_F(ValidateIntegerDotProductSimple, SDotResultSmall) {
+  const std::string ss = R"(
+     %x = OpSDot %int %i64vec2_1 %i64vec2_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result width (32) must be greater than or equal to "
+                        "the vectors width (64)"));
 }
 
-TEST(ValidateSpvKHRIntegerDotProductSimple,
-     DISABLED_Invalid_UDot_RequiresUnsigned) {
-  FAIL();
+TEST_F(ValidateIntegerDotProductSimple, SDotNoPackedVectorFormat) {
+  const std::string ss = R"(
+     %x = OpSDot %int %int_1 %int_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("'Vector 1' and 'Vector 2' are a 32-bit int scalar, "
+                        "but no Packed Vector Format was provided"));
 }
 
-TEST(ValidateSpvKHRIntegerDotProductSimple,
-     DISABLED_Invalid_SUDot_RequiresUnsignedSecondArg) {
-  FAIL();
+TEST_F(ValidateIntegerDotProductSimple, UDotResultSigned) {
+  const std::string ss = R"(
+     %x = OpUDot %int %uvec2_1 %uvec2_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result must be an unsigned int scalar type"));
 }
 
-TEST(ValidateSpvKHRIntegerDotProductSimple,
-     DISABLED_Invalid_UDotAccSat_RequiresUnsigned) {
-  FAIL();
+TEST_F(ValidateIntegerDotProductSimple, UDotVectorSigned) {
+  const std::string ss = R"(
+     %x = OpUDot %uint %ivec2_1 %ivec2_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected 'Vector 1' to be an vector of unsigned integers"));
 }
 
-TEST(ValidateSpvKHRIntegerDotProductSimple,
-     DISABLED_Invalid_SUDotAccSat_RequiresUnsignedSecondArg) {
-  FAIL();
+TEST_F(ValidateIntegerDotProductSimple, SUDotVectorSigned) {
+  const std::string ss = R"(
+     %x = OpSUDot %uint %ivec2_1 %ivec2_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected 'Vector 2' to be an vector of unsigned integers"));
 }
 
-TEST(ValidateSpvKHRIntegerDotProductSimple,
-     DISABLED_Invalid_VectorOperandsDisallowPackedFormat) {
-  FAIL();
+TEST_F(ValidateIntegerDotProductSimple, SDotLongVectorGood) {
+  const std::string ss = R"(
+     %x = OpSDot %uint %uvec5_1 %uvec5_1
+     %y = OpSDot %uint %uvec6_1 %uvec6_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST(ValidateSpvKHRIntegerDotProductSimple,
-     DISABLED_Invalid_ScalarOperandsRequirePackedFormat) {
-  FAIL();
+TEST_F(ValidateIntegerDotProductSimple, SDotLongVectorSpec) {
+  const std::string ss = R"(
+     %x = OpSDot %uint %spec_uvec5_1 %spec_uvec6_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-// TODO(dneto): Test valid cases with other scalar integer types
-// TODO(dneto): Test valid cases of length-8 vectors
-// TODO(dneto): Test valid cases of length-16 vectors
+TEST_F(ValidateIntegerDotProductSimple, SDotLongVector) {
+  const std::string ss = R"(
+     %x = OpSDot %uint %uvec5_1 %uvec6_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("'Vector 1' is 5 components but 'Vector 2' is 6 components"));
+}
+
+TEST_F(ValidateIntegerDotProductSimple, UDotAccSat) {
+  const std::string ss = R"(
+     %x = OpUDotAccSat %uint %uvec2_1 %uvec2_1 %int_1
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result must be the same as the Accumulator type"));
+}
+
+TEST_F(ValidateIntegerDotProductSimple, CapabilityDotProductInput4x8BitPacked) {
+  const std::string ss = R"(
+     OpCapability Shader
+     OpCapability DotProductKHR
+     OpExtension "SPV_KHR_integer_dot_product"
+     OpMemoryModel Logical GLSL450
+     OpEntryPoint GLCompute %main "main"
+     OpExecutionMode %main LocalSize 1 1 1
+     %void = OpTypeVoid
+     %fn = OpTypeFunction %void
+     %int = OpTypeInt 32 1
+     %v2int = OpTypeVector %int 2
+     %int_1 = OpConstant %int 1
+     %main = OpFunction %void None %fn
+     %label = OpLabel
+     %x = OpSDot %int %int_1 %int_1
+     OpReturn
+     OpFunctionEnd
+  )";
+  CompileSuccessfully(ss);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DotProductInput4x8BitPacked capability is required to "
+                        "use scalar integers"));
+}
+
+TEST_F(ValidateIntegerDotProductSimple, CapabilityDotProductInput4x8Bit) {
+  const std::string ss = R"(
+     OpCapability Shader
+     OpCapability DotProductKHR
+     OpCapability Int8
+     OpExtension "SPV_KHR_integer_dot_product"
+     OpMemoryModel Logical GLSL450
+     OpEntryPoint GLCompute %main "main"
+     OpExecutionMode %main LocalSize 1 1 1
+     %void = OpTypeVoid
+     %fn = OpTypeFunction %void
+     %char = OpTypeInt 8 1
+     %v4char = OpTypeVector %char 4
+     %char_1 = OpConstant %char 1
+     %v4char_1 = OpConstantComposite %v4char %char_1 %char_1 %char_1 %char_1
+     %main = OpFunction %void None %fn
+     %label = OpLabel
+     %x = OpSDot %char %v4char_1 %v4char_1
+     OpReturn
+     OpFunctionEnd
+  )";
+  CompileSuccessfully(ss);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("DotProductInput4x8Bit or DotProductInputAll capability is "
+                "required to use 4-component vectors of 8-bit integers"));
+}
+
+TEST_F(ValidateIntegerDotProductSimple, CapabilityDotProductInputAll) {
+  const std::string ss = R"(
+     OpCapability Shader
+     OpCapability DotProductKHR
+     OpExtension "SPV_KHR_integer_dot_product"
+     OpMemoryModel Logical GLSL450
+     OpEntryPoint GLCompute %main "main"
+     OpExecutionMode %main LocalSize 1 1 1
+     %void = OpTypeVoid
+     %fn = OpTypeFunction %void
+     %int = OpTypeInt 32 1
+     %int_1 = OpConstant %int 1
+     %v4int = OpTypeVector %int 4
+     %v4int_1 = OpConstantComposite %v4int %int_1 %int_1 %int_1 %int_1
+     %main = OpFunction %void None %fn
+     %label = OpLabel
+     %x = OpSDot %int %v4int_1 %v4int_1
+     OpReturn
+     OpFunctionEnd
+  )";
+  CompileSuccessfully(ss);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("DotProductInputAll capability is additionally required to the "
+                "DotProduct capability to use vectors. (It is possible to set "
+                "DotProductInput4x8BitPacked to only use 32-bit scalars packed "
+                "as a 4-wide 8-byte vector)"));
+}
+
+TEST_F(ValidateIntegerDotProductSimple, CapabilityDotProductInputAll2) {
+  const std::string ss = R"(
+     OpCapability Shader
+     OpCapability DotProductKHR
+     OpCapability DotProductInput4x8BitPacked
+     OpExtension "SPV_KHR_integer_dot_product"
+     OpMemoryModel Logical GLSL450
+     OpEntryPoint GLCompute %main "main"
+     OpExecutionMode %main LocalSize 1 1 1
+     %void = OpTypeVoid
+     %fn = OpTypeFunction %void
+     %int = OpTypeInt 32 1
+     %int_1 = OpConstant %int 1
+     %v4int = OpTypeVector %int 4
+     %v4int_1 = OpConstantComposite %v4int %int_1 %int_1 %int_1 %int_1
+     %main = OpFunction %void None %fn
+     %label = OpLabel
+     %x = OpSDot %int %v4int_1 %v4int_1
+     OpReturn
+     OpFunctionEnd
+  )";
+  CompileSuccessfully(ss);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DotProductInputAll capability is required use "
+                        "vectors. (DotProductInput4x8BitPacked capability "
+                        "declared allows for only 32-bit int scalars)"));
+}
 
 }  // namespace
 }  // namespace val
diff --git a/test/val/val_extension_spv_khr_subgroup_uniform_control_flow.cpp b/test/val/val_extension_spv_khr_subgroup_uniform_control_flow.cpp
index f528cb9..c4c8355 100644
--- a/test/val/val_extension_spv_khr_subgroup_uniform_control_flow.cpp
+++ b/test/val/val_extension_spv_khr_subgroup_uniform_control_flow.cpp
@@ -18,9 +18,9 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-#include "source/enum_string_mapping.h"
 #include "source/extensions.h"
 #include "source/spirv_target_env.h"
+#include "source/table2.h"
 #include "test/test_fixture.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
diff --git a/test/val/val_extensions_test.cpp b/test/val/val_extensions_test.cpp
index bc8e972..555fd31 100644
--- a/test/val/val_extensions_test.cpp
+++ b/test/val/val_extensions_test.cpp
@@ -61,8 +61,8 @@
         "SPV_AMD_shader_image_load_store_lod", "SPV_AMD_shader_fragment_mask",
         "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1",
         "SPV_NV_shader_subgroup_partitioned", "SPV_EXT_descriptor_indexing",
-        "SPV_KHR_terminate_invocation",
-        "SPV_KHR_relaxed_extended_instruction"));
+        "SPV_KHR_terminate_invocation", "SPV_KHR_relaxed_extended_instruction",
+        "SPV_EXT_float8"));
 
 INSTANTIATE_TEST_SUITE_P(FailSilently, ValidateUnknownExtensions,
                          Values("ERROR_unknown_extension", "SPV_KHR_",
@@ -132,6 +132,134 @@
   EXPECT_THAT(getDiagnosticString(), HasSubstr("SPV_KHR_device_group"));
 }
 
+TEST_F(ValidateExtensionCapabilities, SpirvVersionImageProcessingQCOM) {
+  const std::string str = R"(
+               OpCapability Shader
+               OpCapability TextureBlockMatch2QCOM
+               OpExtension "SPV_QCOM_image_processing"
+               OpExtension "SPV_QCOM_image_processing2"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %v_texcoord %fragColor %target_samp %ref_samp
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpSourceExtension "GL_QCOM_image_processing"
+               OpSourceExtension "GL_QCOM_image_processing2"
+               OpDecorate %v_texcoord Location 0
+               OpDecorate %fragColor Location 0
+               OpDecorate %target_samp DescriptorSet 0
+               OpDecorate %target_samp Binding 4
+               OpDecorate %ref_samp DescriptorSet 0
+               OpDecorate %ref_samp Binding 5
+               OpDecorate %target_samp BlockMatchTextureQCOM
+               OpDecorate %target_samp BlockMatchSamplerQCOM
+               OpDecorate %ref_samp BlockMatchTextureQCOM
+               OpDecorate %ref_samp BlockMatchSamplerQCOM
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %v2uint = OpTypeVector %uint 2
+%_ptr_Function_v2uint = OpTypePointer Function %v2uint
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+ %v_texcoord = OpVariable %_ptr_Input_v4float Input
+     %uint_0 = OpConstant %uint 0
+%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+     %uint_3 = OpConstant %uint 3
+     %uint_4 = OpConstant %uint 4
+         %39 = OpConstantComposite %v2uint %uint_4 %uint_4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+  %fragColor = OpVariable %_ptr_Output_v4float Output
+         %42 = OpTypeImage %float 2D 0 0 0 1 Unknown
+         %43 = OpTypeSampledImage %42
+%_ptr_UniformConstant_43 = OpTypePointer UniformConstant %43
+%target_samp = OpVariable %_ptr_UniformConstant_43 UniformConstant
+   %ref_samp = OpVariable %_ptr_UniformConstant_43 UniformConstant
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+ %tgt_coords = OpVariable %_ptr_Function_v2uint Function
+ %ref_coords = OpVariable %_ptr_Function_v2uint Function
+  %blockSize = OpVariable %_ptr_Function_v2uint Function
+         %16 = OpAccessChain %_ptr_Input_float %v_texcoord %uint_0
+         %17 = OpLoad %float %16
+         %18 = OpConvertFToU %uint %17
+         %20 = OpAccessChain %_ptr_Function_uint %tgt_coords %uint_0
+               OpStore %20 %18
+         %22 = OpAccessChain %_ptr_Input_float %v_texcoord %uint_1
+         %23 = OpLoad %float %22
+         %24 = OpConvertFToU %uint %23
+         %25 = OpAccessChain %_ptr_Function_uint %tgt_coords %uint_0
+               OpStore %25 %24
+         %28 = OpAccessChain %_ptr_Input_float %v_texcoord %uint_2
+         %29 = OpLoad %float %28
+         %30 = OpConvertFToU %uint %29
+         %31 = OpAccessChain %_ptr_Function_uint %ref_coords %uint_0
+               OpStore %31 %30
+         %33 = OpAccessChain %_ptr_Input_float %v_texcoord %uint_3
+         %34 = OpLoad %float %33
+         %35 = OpConvertFToU %uint %34
+         %36 = OpAccessChain %_ptr_Function_uint %ref_coords %uint_1
+               OpStore %36 %35
+               OpStore %blockSize %39
+         %46 = OpLoad %43 %target_samp
+         %47 = OpLoad %v2uint %tgt_coords
+         %49 = OpLoad %43 %ref_samp
+         %50 = OpLoad %v2uint %ref_coords
+         %51 = OpLoad %v2uint %blockSize
+         %52 = OpImageBlockMatchWindowSADQCOM %v4float %46 %47 %49 %50 %51
+               OpStore %fragColor %52
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_WRONG_VERSION,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("SPV_QCOM_image_processing extension requires SPIR-V "
+                        "version 1.4 or later."));
+}
+
+TEST_F(ValidateExtensionCapabilities, SpirvVersionCoopMatConversionQCOM) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
+OpCapability CooperativeMatrixConversionQCOM
+OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_QCOM_cooperative_matrix_conversion"
+OpMemoryModel Logical VulkanKHR
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%f16 = OpTypeFloat 16
+%uint_0 = OpConstant %uint 0
+%uint_8 = OpConstant %uint 8
+%uint_64 = OpConstant %uint 64
+%_arr_f16_uint_8 = OpTypeArray %f16 %uint_8
+%_arr_f16_uint_64 = OpTypeArray %f16 %uint_64
+%_ptr_Function__arr_f16_uint_64 = OpTypePointer Function %_arr_f16_uint_64
+%main = OpFunction %void None %3
+%5 = OpLabel
+%f16vec64Acc = OpVariable %_ptr_Function__arr_f16_uint_64 Function
+%83 = OpLoad %_arr_f16_uint_64 %f16vec64Acc
+%86 = OpExtractSubArrayQCOM %_arr_f16_uint_8 %83 %uint_0
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str());
+  ASSERT_EQ(SPV_ERROR_WRONG_VERSION, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("SPV_KHR_vulkan_memory_model extension requires SPIR-V "
+                        "version 1.3 or later."));
+}
+
 TEST_F(ValidateExtensionCapabilities,
        DeclCapabilityFailureBlockMatchWIndowSAD) {
   const std::string str = R"(
@@ -223,8 +351,9 @@
                OpReturn
                OpFunctionEnd
 )";
-  CompileSuccessfully(str.c_str());
-  ASSERT_EQ(SPV_ERROR_MISSING_EXTENSION, ValidateInstructions());
+  CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_4);
+  ASSERT_EQ(SPV_ERROR_MISSING_EXTENSION,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
   EXPECT_THAT(getDiagnosticString(), HasSubstr("2nd operand of Decorate"));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("requires one of these extensions"));
@@ -332,8 +461,9 @@
                OpReturn
                OpFunctionEnd
 )";
-  CompileSuccessfully(str.c_str());
-  ASSERT_EQ(SPV_ERROR_MISSING_EXTENSION, ValidateInstructions());
+  CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_4);
+  ASSERT_EQ(SPV_ERROR_MISSING_EXTENSION,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
   EXPECT_THAT(getDiagnosticString(), HasSubstr("2nd operand of Decorate"));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("requires one of these extensions"));
@@ -482,7 +612,8 @@
                            GetParam().cap + R"(
                OpMemoryModel Logical GLSL450
                OpEntryPoint Vertex %main "main" %builtin
-               OpDecorate %builtin BuiltIn )" + GetParam().builtin + R"(
+               OpDecorate %builtin BuiltIn )" +
+                           GetParam().builtin + R"(
        %void = OpTypeVoid
           %3 = OpTypeFunction %void
         %int = OpTypeInt 32 1
diff --git a/test/val/val_fixtures.h b/test/val/val_fixtures.h
index db9d045..9c10682 100644
--- a/test/val/val_fixtures.h
+++ b/test/val/val_fixtures.h
@@ -56,6 +56,13 @@
   // This function overwrites the word at the given index with a new word.
   void OverwriteAssembledBinary(uint32_t index, uint32_t word);
 
+  // Overwrites the ID bound.
+  void OverwriteIdBound(uint32_t bound) {
+    // The ID bound is in the header at word index 3.
+    // SPIR-V section 2.3 Physical Layout of a sPIR-V Module and Instruction.
+    OverwriteAssembledBinary(3, bound);
+  }
+
   // Performs validation on the SPIR-V code.
   spv_result_t ValidateInstructions(spv_target_env env = SPV_ENV_UNIVERSAL_1_0);
 
diff --git a/test/val/val_function_test.cpp b/test/val/val_function_test.cpp
index 119edd3..ea53ca8 100644
--- a/test/val/val_function_test.cpp
+++ b/test/val/val_function_test.cpp
@@ -836,6 +836,200 @@
               HasSubstr("type does not match Function <id>"));
 }
 
+TEST_F(ValidateFunctionCall, PointerReturnTypeStorageBuffer) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%ptr = OpTypePointer StorageBuffer %int
+%null = OpConstantNull %ptr
+%foo_ty = OpTypeFunction %ptr
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%call = OpFunctionCall %ptr %foo
+OpReturn
+OpFunctionEnd
+%foo = OpFunction %ptr None %foo_ty
+%foo_entry = OpLabel
+OpReturnValue %null
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("In Logical addressing, functions may only return a storage "
+                "buffer pointer if the VariablePointersStorageBuffer "
+                "capability is declared"));
+}
+
+TEST_F(ValidateFunctionCall, PointerReturnTypeStorageBufferUntyped) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%ptr = OpTypeUntypedPointerKHR StorageBuffer
+%null = OpConstantNull %ptr
+%foo_ty = OpTypeFunction %ptr
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%call = OpFunctionCall %ptr %foo
+OpReturn
+OpFunctionEnd
+%foo = OpFunction %ptr None %foo_ty
+%foo_entry = OpLabel
+OpReturnValue %null
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("In Logical addressing, functions may only return a storage "
+                "buffer pointer if the VariablePointersStorageBuffer "
+                "capability is declared"));
+}
+
+TEST_F(ValidateFunctionCall, PointerReturnTypeWorkgroup) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointersStorageBuffer
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%ptr = OpTypePointer Workgroup %int
+%null = OpConstantNull %ptr
+%foo_ty = OpTypeFunction %ptr
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%call = OpFunctionCall %ptr %foo
+OpReturn
+OpFunctionEnd
+%foo = OpFunction %ptr None %foo_ty
+%foo_entry = OpLabel
+OpReturnValue %null
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("In Logical addressing, functions may only return a workgroup "
+                "pointer if the VariablePointers capability is declared"));
+}
+
+TEST_F(ValidateFunctionCall, PointerReturnTypeWorkgroupUntyped) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointersStorageBuffer
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%ptr = OpTypeUntypedPointerKHR Workgroup
+%null = OpConstantNull %ptr
+%foo_ty = OpTypeFunction %ptr
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%call = OpFunctionCall %ptr %foo
+OpReturn
+OpFunctionEnd
+%foo = OpFunction %ptr None %foo_ty
+%foo_entry = OpLabel
+OpReturnValue %null
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("In Logical addressing, functions may only return a workgroup "
+                "pointer if the VariablePointers capability is declared"));
+}
+
+TEST_F(ValidateFunctionCall, PointerReturnTypePrivate) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%ptr = OpTypePointer Private %int
+%var = OpVariable %ptr Private
+%foo_ty = OpTypeFunction %ptr
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%call = OpFunctionCall %ptr %foo
+OpReturn
+OpFunctionEnd
+%foo = OpFunction %ptr None %foo_ty
+%foo_entry = OpLabel
+OpReturnValue %var
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("In Logical addressing, functions may not return a "
+                        "pointer in this storage class"));
+}
+
+TEST_F(ValidateFunctionCall, PointerReturnTypePrivateUntyped) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%ptr = OpTypeUntypedPointerKHR Private
+%var = OpUntypedVariableKHR %ptr Private %int
+%foo_ty = OpTypeFunction %ptr
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%call = OpFunctionCall %ptr %foo
+OpReturn
+OpFunctionEnd
+%foo = OpFunction %ptr None %foo_ty
+%foo_entry = OpLabel
+OpReturnValue %var
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("In Logical addressing, functions may not return a "
+                        "pointer in this storage class"));
+}
+
 TEST_F(ValidateFunctionCall, UntypedPointerParameterMismatch) {
   const std::string spirv = R"(
 OpCapability Shader
diff --git a/test/val/val_graph_test.cpp b/test/val/val_graph_test.cpp
new file mode 100644
index 0000000..72bbde8
--- /dev/null
+++ b/test/val/val_graph_test.cpp
@@ -0,0 +1,1362 @@
+// Copyright (c) 2023-2025 Arm Ltd.
+//
+// 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 "gmock/gmock.h"
+#include "test/unit_spirv.h"
+#include "test/val/val_fixtures.h"
+
+// TODO(kpet) Tests with spec constants
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::ContainsRegex;
+using ::testing::HasSubstr;
+
+using ValidateGraph = spvtest::ValidateBase<std::string>;
+
+constexpr spv_target_env SPVENV = SPV_ENV_VULKAN_1_3;
+
+std::string GenerateModule(const std::string& src) {
+  const std::string boilerplate = R"(
+                     OpCapability Shader
+                     OpCapability VulkanMemoryModel
+                     OpCapability Int8
+                     OpCapability GraphARM
+                     OpCapability TensorsARM
+                     OpCapability RuntimeDescriptorArray
+                     OpExtension "SPV_ARM_graph"
+                     OpExtension "SPV_ARM_tensors"
+                     OpMemoryModel Logical Vulkan
+                     OpDecorate %var_int8tensor DescriptorSet 0
+                     OpDecorate %var_int8tensor Binding 0
+                     OpDecorate %var_int32tensor DescriptorSet 0
+                     OpDecorate %var_int32tensor Binding 1
+                     OpDecorate %var_int8tensor_array3 DescriptorSet 0
+                     OpDecorate %var_int8tensor_array3 Binding 2
+                     OpDecorate %var_int8tensor_runtime_array DescriptorSet 0
+                     OpDecorate %var_int8tensor_runtime_array Binding 3
+             %void = OpTypeVoid
+             %uint = OpTypeInt 32 0
+             %int8 = OpTypeInt 8 1
+            %int32 = OpTypeInt 32 1
+            %float = OpTypeFloat 32
+           %uint_0 = OpConstant %uint 0
+           %uint_1 = OpConstant %uint 1
+           %uint_2 = OpConstant %uint 2
+           %uint_3 = OpConstant %uint 3
+           %uint_4 = OpConstant %uint 4
+          %float_1 = OpConstant %float 1.0
+       %int8tensor = OpTypeTensorARM %int8 %uint_4
+     %int8r3tensor = OpTypeTensorARM %int8 %uint_3
+      %int32tensor = OpTypeTensorARM %int32 %uint_4
+%int8tensor_array3 = OpTypeArray %int8tensor %uint_3
+%int32tensor_array3 = OpTypeArray %int32tensor %uint_3
+%int8tensor_runtime_array = OpTypeRuntimeArray %int8tensor
+%int32tensor_runtime_array = OpTypeRuntimeArray %int32tensor
+%ptr_Input_int8tensor = OpTypePointer Input %int8tensor
+%var_int8tensor_wrong_storage_class = OpVariable %ptr_Input_int8tensor Input
+%ptr_UniformConstant_int8tensor = OpTypePointer UniformConstant %int8tensor
+%ptr_UniformConstant_int32tensor = OpTypePointer UniformConstant %int32tensor
+%ptr_UniformConstant_int8tensor_array3 = OpTypePointer UniformConstant %int8tensor_array3
+%ptr_UniformConstant_int8tensor_runtime_array = OpTypePointer UniformConstant %int8tensor_runtime_array
+   %var_int8tensor = OpVariable %ptr_UniformConstant_int8tensor UniformConstant
+   %var_int32tensor = OpVariable %ptr_UniformConstant_int32tensor UniformConstant
+%var_int8tensor_array3 = OpVariable %ptr_UniformConstant_int8tensor_array3 UniformConstant
+%var_int8tensor_runtime_array = OpVariable %ptr_UniformConstant_int8tensor_runtime_array UniformConstant
+)";
+  return boilerplate + src;
+}
+
+std::string GenerateModuleWithGraphEntryPoint(const std::string& header) {
+  const std::string src = R"(
+%default_graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+              OpGraphEntryPointARM %default_graph "default_entry_point" %var_int8tensor %var_int8tensor
+     %default_graph = OpGraphARM %default_graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphEndARM
+    )";
+  return GenerateModule(header) + src;
+}
+
+//
+// Layout tests
+//
+
+TEST_F(ValidateGraph, InvalidNoGraphEntryPoint) {
+  std::string spvasm = GenerateModule("");
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("No OpGraphEntryPointARM instruction was found but the "
+                        "GraphARM capability is declared."));
+}
+
+TEST_F(ValidateGraph, InvalidGraphInGraph) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+            %graph = OpGraphARM %graph_type
+           %graph2 = OpGraphARM %graph_type
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Cannot define a graph in a graph"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphEndOutsideGraph) {
+  const std::string src = R"(
+                     OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GraphEndARM must be preceded by at least one "
+                        "OpGraphSetOutputARM instruction"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphEntryPointInsideGraph) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+            %graph = OpGraphARM %graph_type
+               %in = OpGraphInputARM %int8tensor %uint_0
+                     OpGraphEntryPointARM %graph "main" %in %in
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "GraphEntryPointARM cannot appear in the definition of a graph"));
+}
+
+TEST_F(ValidateGraph, InvalidNonGraphInstructionInGraphSection) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+            %ftype = OpTypeFunction %void %void
+            %graph = OpGraphARM %graph_type
+               %in = OpGraphInputARM %int8tensor %uint_0
+                     OpGraphSetOutputARM %in %uint_0
+                     OpGraphEndARM
+               %fn = OpFunction %void None %ftype
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Function cannot appear in the graph definitions section"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphInputOusideGraph) {
+  const std::string src = R"(
+               %in = OpGraphInputARM %int8tensor %uint_0
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpGraphInputARM must immediately follow an OpGraphARM "
+                        "or OpGraphInputARM instruction."));
+}
+
+TEST_F(ValidateGraph, InvalidGraphSetOutputsOusideGraph) {
+  const std::string src = R"(
+                     OpGraphSetOutputARM %uint_0 %uint_0
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpGraphSetOutputARM must immediately precede an "
+                        "OpGraphEndARM or OpGraphSetOutputARM instruction"));
+}
+
+TEST_F(ValidateGraph, ValidGraphConstantOusideGraph) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+       %cst = OpGraphConstantARM %int8tensor 1
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, ValidGraphWithNoInputsNoBody) {
+  const std::string src = R"(
+              %cst = OpGraphConstantARM %int8tensor 1
+       %graph_type = OpTypeGraphARM 0 %int8tensor
+                     OpGraphEntryPointARM %graph "longname" %var_int8tensor
+            %graph = OpGraphARM %graph_type
+                     OpGraphSetOutputARM %cst %uint_0
+                     OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, InvalidGraphWithDisallowedBodyInstructions) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+                     OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor
+            %graph = OpGraphARM %graph_type
+               %in = OpGraphInputARM %int8tensor %uint_0
+              %val = OpCompositeExtract %int8r3tensor %in 0
+              %out = OpCompositeInsert %int8tensor %val %in 0
+                     OpGraphSetOutputARM %out %uint_0
+                     OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpCompositeInsert cannot appear in the graph definitions section"));
+}
+
+TEST_F(ValidateGraph, InvalidInstructionOutsideGraphAfterGraph) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+       %cst = OpGraphConstantARM %int8tensor 1
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphEndARM
+       %val = OpCompositeExtract %int8r3tensor %cst 0
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpCompositeExtract must appear in a graph body"));
+}
+
+//
+// Type tests
+//
+TEST_F(ValidateGraph, ValidGraphTypeOneTensorOutputNoInputs) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 0 %int8tensor
+)";
+  std::string spvasm = GenerateModuleWithGraphEntryPoint(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, ValidGraphTypeOneTensorArrayOutputNoInputs) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 0 %int8tensor_array3
+)";
+  std::string spvasm = GenerateModuleWithGraphEntryPoint(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, ValidGraphTypeOneTensorRuntimeArrayOutputNoInputs) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 0 %int8tensor_runtime_array
+)";
+  std::string spvasm = GenerateModuleWithGraphEntryPoint(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, ValidGraphTypeOneTensorOutputOneTensorInput) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 1 %int8tensor %int32tensor
+)";
+  std::string spvasm = GenerateModuleWithGraphEntryPoint(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, ValidGraphTypeOneTensorOutputOneTensorArrayInput) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int32tensor
+)";
+  std::string spvasm = GenerateModuleWithGraphEntryPoint(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, ValidGraphTypeOneTensorOutputOneTensorRuntimeArrayInput) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 1 %int8tensor_runtime_array %int32tensor
+)";
+  std::string spvasm = GenerateModuleWithGraphEntryPoint(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph,
+       ValidGraphTypeOneTensorOutputOneTensorInputOneTensorArrayInput) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 2 %int8tensor %int8tensor_array3 %int32tensor
+)";
+  std::string spvasm = GenerateModuleWithGraphEntryPoint(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(
+    ValidateGraph,
+    ValidGraphTypeOneTensorOutputOneTensorRuntimeArrayInputOneTensorArrayInput) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 1 %int8tensor_runtime_array %int8tensor_array3 %int32tensor
+)";
+  std::string spvasm = GenerateModuleWithGraphEntryPoint(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, ValidGraphTypeOneTensorArrayOutputOneTensorInput) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 1 %int8tensor %int32tensor_array3
+)";
+  std::string spvasm = GenerateModuleWithGraphEntryPoint(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, ValidGraphTypeOneTensorRuntimeArrayOutputOneTensorInput) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 1 %int8tensor %int32tensor_runtime_array
+)";
+  std::string spvasm = GenerateModuleWithGraphEntryPoint(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph,
+       ValidGraphTypeOneTensorRuntimeArrayOutputOneTensorOuputOneTensorInput) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 1 %int8tensor %int32tensor_runtime_array %int32tensor
+)";
+  std::string spvasm = GenerateModuleWithGraphEntryPoint(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(
+    ValidateGraph,
+    ValidGraphTypeOneTensorRuntimeArrayOutputOneTensorArrayOutputOneTensorOuputOneTensorInput) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 1 %int8tensor %int32tensor_runtime_array %int32tensor_array3 %int32tensor
+)";
+  std::string spvasm = GenerateModuleWithGraphEntryPoint(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+TEST_F(ValidateGraph,
+       ValidGraphTypeOneTensorRuntimeArrayOutputOnteTensorOuputOneTensorInput) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int32tensor_array3
+)";
+  std::string spvasm = GenerateModuleWithGraphEntryPoint(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, InvalidGraphTypeWithNotEnoughIOTypes) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 10 %int8tensor %int8tensor
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("2 I/O types were provided but the graph has 10 inputs"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphTypeWithNonGraphInterfaceTypeIO) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 1 %int8tensor %uint
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex("I/O type '.*' is not a Graph Interface Type.*"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphTypeWithOneInputZeroOutputs) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 1 %int8tensor
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("A graph type must have at least one output"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphTypeWithZeroInputsZeroOutputs) {
+  const std::string src = R"(
+       %graph_type = OpTypeGraphARM 0
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("A graph type must have at least one output"));
+}
+
+//
+// Constant tests
+//
+TEST_F(ValidateGraph, ValidGraphConstantTensorUnranked) {
+  const std::string src = R"(
+   %tensor_type =  OpTypeTensorARM %uint
+           %cst = OpGraphConstantARM %tensor_type 25
+)";
+  std::string spvasm = GenerateModuleWithGraphEntryPoint(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, ValidGraphConstantTensorRanked) {
+  const std::string src = R"(
+   %tensor_type =  OpTypeTensorARM %uint %uint_4
+           %cst = OpGraphConstantARM %tensor_type 25
+)";
+  std::string spvasm = GenerateModuleWithGraphEntryPoint(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, ValidGraphConstantTensorShaped) {
+  const std::string src = R"(
+   %uint_array2 = OpTypeArray %uint %uint_2
+  %tensor_shape = OpConstantComposite %uint_array2 %uint_1 %uint_4
+   %tensor_type = OpTypeTensorARM %uint %uint_2 %tensor_shape
+           %cst = OpGraphConstantARM %tensor_type 25
+)";
+  std::string spvasm = GenerateModuleWithGraphEntryPoint(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, InvalidGraphConstantDuplicateIDs) {
+  const std::string src = R"(
+           %cst = OpGraphConstantARM %int8tensor 25
+          %cst2 = OpGraphConstantARM %int32tensor 25
+)";
+  std::string spvasm = GenerateModuleWithGraphEntryPoint(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("No two OpGraphConstantARM instructions may have the "
+                        "same GraphConstantID"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphConstantWithNonTensorType) {
+  const std::string src = R"(
+           %cst = OpGraphConstantARM %uint 25
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "GraphConstantARM must have a Result Type that is a tensor type"));
+}
+
+//
+// EntryPoint tests
+//
+TEST_F(ValidateGraph, InvalidModuleWithNoGraphEntryPoint) {
+  std::string spvasm = GenerateModule("");
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("No OpGraphEntryPointARM instruction was found but the "
+                        "GraphARM capability is declared."));
+}
+TEST_F(ValidateGraph, InvalidGraphEntryPointNotAGraph) {
+  const std::string src = R"(
+           OpGraphEntryPointARM %uint_0 "longname"
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "GraphEntryPointARM Graph must be a OpGraphARM but found Constant"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphEntryPointInterfaceIDNotOpVariable) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %uint_0 %uint_0
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex(
+                  "GraphEntryPointARM Interface ID '.*' must "
+                  "come from OpVariable with UniformConstant Storage Class.*"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphEntryPointInterfaceIDWrongStorageClass) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor_wrong_storage_class %var_int8tensor_wrong_storage_class
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex(
+                  "GraphEntryPointARM Interface ID '.*' must "
+                  "come from OpVariable with UniformConstant Storage Class.*"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphEntryPointNotEnoughInterfaceIDs) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      ContainsRegex(
+          "GraphEntryPointARM Interface list contains 1 IDs but Graph's type "
+          "'.*' has 2 inputs and outputs.*"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphEntryPointTooManyInterfaceIDs) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      ContainsRegex(
+          "GraphEntryPointARM Interface list contains 3 IDs but Graph's type "
+          "'.*' has 2 inputs and outputs.*"));
+}
+
+TEST_F(ValidateGraph,
+       InvalidGraphEntryPointInterfaceIDTypeMismatchesGraphIOType) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int32tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex("GraphEntryPointARM Interface ID type "
+                            "'.*' must match the type of the "
+                            "corresponding graph I/O '.*'.*"));
+}
+
+//
+// Graph tests
+//
+TEST_F(ValidateGraph, InvalidGraphResultType) {
+  const std::string src = R"(
+           OpGraphEntryPointARM %graph "foo"
+  %graph = OpGraphARM %uint
+     %in = OpGraphInputARM %int8tensor %uint_0
+           OpGraphSetOutputARM %in %uint_0
+           OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GraphARM Result Type must be an OpTypeGraphARM"));
+}
+
+//
+// Input tests
+//
+TEST_F(ValidateGraph, ValidTensorInput) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, ValidTensorArrayInputCompositeExtract) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor_array3 %var_int8tensor
+     %graph = OpGraphARM %graph_type
+%in_tensors = OpGraphInputARM %int8tensor_array3 %uint_0
+       %out = OpCompositeExtract %int8tensor %in_tensors 1
+              OpGraphSetOutputARM %out %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, ValidTensorArrayInputWithElementIndex) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor_array3 %var_int8tensor
+     %graph = OpGraphARM %graph_type
+       %out = OpGraphInputARM %int8tensor %uint_0 %uint_1
+              OpGraphSetOutputARM %out %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, ValidTensorRuntimeArrayInputCompositeExtract) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor_runtime_array %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor_runtime_array %var_int8tensor
+     %graph = OpGraphARM %graph_type
+%in_tensors = OpGraphInputARM %int8tensor_runtime_array %uint_0
+       %out = OpCompositeExtract %int8tensor %in_tensors 1
+              OpGraphSetOutputARM %out %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, ValidTensorRuntimeArrayInputWithElementIndex) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor_runtime_array %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor_runtime_array %var_int8tensor
+     %graph = OpGraphARM %graph_type
+       %out = OpGraphInputARM %int8tensor %uint_0 %uint_1
+              OpGraphSetOutputARM %out %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, InvalidGraphInputIndexWrongType) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %float_1
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GraphInputARM InputIndex must be a 32-bit integer"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphInputElementIndexWrongType) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor_array3 %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0 %float_1
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GraphInputARM ElementIndex must be a 32-bit integer"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphInputIndexDuplicate) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+       %in2 = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Two OpGraphInputARM instructions with the same "
+                        "InputIndex must not be part of the same "
+                        "graph definition unless ElementIndex is present in "
+                        "both with different values."));
+}
+
+TEST_F(ValidateGraph, InvalidGraphInputAndElementIndexDuplicate) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor_array3 %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0 %uint_1
+       %in2 = OpGraphInputARM %int8tensor %uint_0 %uint_1
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Two OpGraphInputARM instructions with the same "
+                        "InputIndex must not be part of the same "
+                        "graph definition unless ElementIndex is present in "
+                        "both with different values."));
+}
+
+TEST_F(ValidateGraph,
+       InvalidGraphInputIndexDuplicateWithAndWithoutElementIndex) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor_array3 %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor_array3 %uint_0
+       %in2 = OpGraphInputARM %int8tensor %uint_0 %uint_1
+              OpGraphSetOutputARM %in2 %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Two OpGraphInputARM instructions with the same "
+                        "InputIndex must not be part of the same "
+                        "graph definition unless ElementIndex is present in "
+                        "both with different values."));
+}
+
+TEST_F(ValidateGraph, ValidGraphInputIndexDuplicateWithDifferentElementIndex) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor_runtime_array %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor_runtime_array %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0 %uint_2
+       %in2 = OpGraphInputARM %int8tensor %uint_0 %uint_1
+              OpGraphSetOutputARM %in2 %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, InvalidGraphInputElementIndexWithNonArrayInput) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0 %uint_1
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpGraphInputARM ElementIndex not allowed when the graph input "
+                "selected by "
+                "InputIndex is not an OpTypeArray or OpTypeRuntimeArray"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphInputElementIndexOutOfRange) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor_array3 %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0 %uint_3
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex("OpGraphInputARM ElementIndex out of range. The "
+                            "type of the graph input being accessed '.*' is an "
+                            "array of 3 elements but ElementIndex is 3.*"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphInputIndexOutOfRange) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 2 %int8tensor %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor %var_int8tensor
+     %graph = OpGraphARM %graph_type
+       %in0 = OpGraphInputARM %int8tensor %uint_0
+       %in1 = OpGraphInputARM %int8tensor %uint_1
+       %in2 = OpGraphInputARM %int8tensor %uint_2
+              OpGraphSetOutputARM %in0 %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex("Type '.*' for graph '.*' "
+                            "has 2 inputs but found an OpGraphInputARM "
+                            "instruction with an InputIndex that is 2.*"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphFirstInputTypeDoesNotMatchGraphType) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 2 %int8tensor %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor %var_int8tensor
+     %graph = OpGraphARM %graph_type
+       %in0 = OpGraphInputARM %int32tensor %uint_0
+       %in1 = OpGraphInputARM %int8tensor %uint_1
+              OpGraphSetOutputARM %in1 %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex("Result Type '.*' of graph input instruction "
+                            "'.*' does not match the type "
+                            "'.*' of input 0 in the graph type.*"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphLastInputTypeDoesNotMatchGraphType) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 2 %int8tensor %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor %var_int8tensor
+     %graph = OpGraphARM %graph_type
+       %in0 = OpGraphInputARM %int8tensor %uint_0
+       %in1 = OpGraphInputARM %int32tensor %uint_1
+              OpGraphSetOutputARM %in0 %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex("Result Type '.*' of graph input instruction "
+                            "'.*' does not match the type "
+                            "'.*' of input 1 in the graph type.*"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphInputAfterNonGraphInputOrGraph) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 2 %int8tensor_array3 %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor_array3 %var_int8tensor %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor_array3 %uint_0
+       %val = OpCompositeExtract %int8tensor %in 0
+       %in2 = OpGraphInputARM %int8tensor %uint_1
+              OpGraphSetOutputARM %val %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpGraphInputARM must immediately follow an OpGraphARM "
+                        "or OpGraphInputARM instruction"));
+}
+
+//
+// Output tests
+//
+TEST_F(ValidateGraph, ValidTensorOutput) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, ValidTensorOutputWholeArray) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int8tensor_array3
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor_array3 %var_int8tensor_array3
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor_array3 %uint_0
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, ValidTensorOutputArraySingleElement) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor_array3
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor_array3
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0 %uint_1
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, ValidTensorOutputArrayMultipleElements) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor_array3
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor_array3
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0 %uint_1
+              OpGraphSetOutputARM %in %uint_0 %uint_2
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, ValidTensorOutputWholeRuntimeArray) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor_runtime_array %int8tensor_runtime_array
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor_runtime_array %var_int8tensor_runtime_array
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor_runtime_array %uint_0
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, ValidTensorOutputRuntimeArraySingleElement) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor_runtime_array
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor_runtime_array
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0 %uint_1
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, ValidTensorOutputRuntimeArrayMultipleElements) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor_runtime_array
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor_runtime_array
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0 %uint_1
+              OpGraphSetOutputARM %in %uint_0 %uint_2
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, InvalidGraphOutputIndexWrongType) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %float_1
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("GraphSetOutputARM OutputIndex must be a 32-bit integer"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphOutputElementIndexWrongType) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor_array3
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor_array3
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0 %float_1
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("GraphSetOutputARM ElementIndex must be a 32-bit integer"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphOutputIndexDuplicate) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Two OpGraphSetOutputARM instructions with the same "
+                        "OutputIndex must not be part of the same "
+                        "graph definition unless ElementIndex is present in "
+                        "both with different values."));
+}
+
+TEST_F(ValidateGraph, InvalidGraphOutputAndElementIndexDuplicate) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor_array3
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor_array3
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0 %uint_1
+              OpGraphSetOutputARM %in %uint_0 %uint_1
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Two OpGraphSetOutputARM instructions with the same "
+                        "OutputIndex must not be part of the same "
+                        "graph definition unless ElementIndex is present in "
+                        "both with different values."));
+}
+
+TEST_F(ValidateGraph,
+       InvalidGraphOutputIndexDuplicateWithAndWithoutElementIndex) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1  %int8tensor_array3 %int8tensor_array3
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor_array3 %var_int8tensor_array3
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor_array3 %uint_0
+ %in_single = OpCompositeExtract %int8tensor %in 0
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphSetOutputARM %in_single %uint_0 %uint_1
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Two OpGraphSetOutputARM instructions with the same "
+                        "OutputIndex must not be part of the same "
+                        "graph definition unless ElementIndex is present in "
+                        "both with different values."));
+}
+
+TEST_F(ValidateGraph, ValidGraphOutputIndexDuplicateWithDifferentElementIndex) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor_runtime_array
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor_runtime_array
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0 %uint_1
+              OpGraphSetOutputARM %in %uint_0 %uint_4
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateGraph, InvalidGraphOutputElementIndexWithNonArrayOutput) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0 %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpGraphSetOutputARM ElementIndex not allowed when the graph "
+                "output selected by "
+                "OutputIndex is not an OpTypeArray or OpTypeRuntimeArray"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphOutputElementIndexOutOfRange) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor_array3
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor_array3
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0 %uint_3
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      ContainsRegex("OpGraphSetOutputARM ElementIndex out of range. The "
+                    "type of the graph output being accessed '.*' is an "
+                    "array of 3 elements but ElementIndex is 3.*"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphOutputNotBeforeEndOrOutput) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor_array3 %int8tensor_array3 %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor_array3 %var_int8tensor_array3 %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor_array3 %uint_0
+              OpGraphSetOutputARM %in %uint_0
+       %val = OpCompositeExtract %int8tensor %in 0
+              OpGraphSetOutputARM %val %uint_1
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "CompositeExtract cannot appear after a graph output instruction"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphOutputIndexOutOfRange) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphSetOutputARM %in %uint_2
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GraphSetOutputARM setting OutputIndex 2 but graph "
+                        "only has 2 outputs."));
+}
+
+TEST_F(ValidateGraph, InvalidGraphFirstOutputValueTypeDoesNotMatchOutputType) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int32tensor %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int32tensor %var_int8tensor %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int32tensor %uint_0
+              OpGraphSetOutputARM %in %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      ContainsRegex(
+          "The type '.*' of Value provided to the graph output instruction "
+          "'.*' does not match the type '.*' of output 0 in the graph type.*"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphLastOutputValueTypeDoesNotMatchOutputType) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int32tensor %int32tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int32tensor %var_int32tensor %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int32tensor %uint_0
+              OpGraphSetOutputARM %in %uint_1
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      ContainsRegex(
+          "The type '.*' of Value provided to the graph output instruction "
+          "'.*' does not match the type '.*' of output 1 in the graph type.*"));
+}
+
+TEST_F(ValidateGraph, InvalidGraphNoSetOuputBeforeEnd) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+              OpGraphEndARM
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GraphEndARM must be preceded by at least one "
+                        "OpGraphSetOutputARM instruction"));
+}
+
+//
+// End tests
+//
+TEST_F(ValidateGraph, InvalidGraphNoEnd) {
+  const std::string src = R"(
+%graph_type = OpTypeGraphARM 1 %int8tensor %int8tensor
+              OpGraphEntryPointARM %graph "longname" %var_int8tensor %var_int8tensor
+     %graph = OpGraphARM %graph_type
+        %in = OpGraphInputARM %int8tensor %uint_0
+)";
+  std::string spvasm = GenerateModule(src);
+
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Missing OpGraphEndARM at end of module"));
+}
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/val_group_test.cpp b/test/val/val_group_test.cpp
new file mode 100644
index 0000000..4a78f54
--- /dev/null
+++ b/test/val/val_group_test.cpp
@@ -0,0 +1,448 @@
+// Copyright 2026 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 <string>
+
+#include "gmock/gmock.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+
+using ValidateGroup = spvtest::ValidateBase<bool>;
+
+std::string GenerateShaderCode(const std::string& body, bool is_64_bit = true) {
+  std::ostringstream ss;
+  ss << R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Groups
+OpCapability Float64
+OpCapability Int64
+)";
+  if (is_64_bit) {
+    ss << "OpMemoryModel Physical64 OpenCL";
+  } else {
+    ss << "OpMemoryModel Physical32 OpenCL";
+  }
+  ss << R"(
+OpEntryPoint Kernel %main "main"
+%bool = OpTypeBool
+%float = OpTypeFloat 32
+%float64 = OpTypeFloat 64
+%uint = OpTypeInt 32 0
+%uint64 = OpTypeInt 64 0
+%null_uint = OpConstantNull %uint
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%uint_2 = OpConstant %uint 2
+%uint64_1 = OpConstant %uint64 1
+%float_2 = OpConstant %float 2
+%uint_array = OpTypeArray %uint %uint_2
+%void = OpTypeVoid
+%event = OpTypeEvent
+%null_event = OpConstantNull %event
+
+%workgroup_float_ptr = OpTypePointer Workgroup %float
+%workgroup_float_var = OpVariable %workgroup_float_ptr Workgroup
+%workgroup_bool_ptr = OpTypePointer Workgroup %bool
+%workgroup_bool_var = OpVariable %workgroup_bool_ptr Workgroup
+%cross_float_ptr = OpTypePointer CrossWorkgroup %float
+%cross_float_var = OpVariable %cross_float_ptr CrossWorkgroup
+%cross_uint_ptr = OpTypePointer CrossWorkgroup %uint
+%cross_uint_var = OpVariable %cross_uint_ptr CrossWorkgroup
+%uniform_float_ptr = OpTypePointer UniformConstant %float
+%uniform_float_var = OpVariable %uniform_float_ptr UniformConstant
+%func_event_ptr = OpTypePointer Function %event
+
+%fn = OpTypeFunction %void
+%true = OpConstantTrue %bool
+%main = OpFunction %void None %fn
+%label = OpLabel
+)";
+
+  ss << body;
+
+  ss << R"(
+OpReturn
+OpFunctionEnd)";
+  return ss.str();
+}
+
+TEST_F(ValidateGroup, AllAnyGood) {
+  const std::string ss = R"(
+    %x = OpGroupAll %bool %uint_2 %true
+    %y = OpGroupAny %bool %uint_2 %true
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateGroup, FloatGood) {
+  const std::string ss = R"(
+    %a = OpGroupFAdd %float %uint_2 Reduce %float_2
+    %b = OpGroupFMin %float %uint_2 Reduce %float_2
+    %c = OpGroupFMax %float %uint_2 Reduce %float_2
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateGroup, IntGood) {
+  const std::string ss = R"(
+    %a = OpGroupIAdd %uint %uint_2 Reduce %uint_2
+    %b = OpGroupSMin %uint %uint_2 Reduce %uint_2
+    %c = OpGroupSMax %uint %uint_2 Reduce %uint_2
+    %d = OpGroupUMin %uint %uint_2 Reduce %uint_2
+    %e = OpGroupUMax %uint %uint_2 Reduce %uint_2
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateGroup, BroadcastGood) {
+  const std::string ss = R"(
+    %a = OpGroupBroadcast %uint %uint_2 %uint_2 %uint_0
+    %b = OpGroupBroadcast %float %uint_2 %float_2 %uint_0
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateGroup, AllResult) {
+  const std::string ss = R"(
+    %x = OpGroupAll %uint %uint_2 %true
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result must be a boolean scalar type"));
+}
+
+TEST_F(ValidateGroup, AllPredicate) {
+  const std::string ss = R"(
+    %x = OpGroupAll %bool %uint_2 %uint_2
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Predicate must be a boolean scalar type"));
+}
+
+TEST_F(ValidateGroup, AnyResult) {
+  const std::string ss = R"(
+    %x = OpGroupAny %uint %uint_2 %true
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result must be a boolean scalar type"));
+}
+
+TEST_F(ValidateGroup, AnyPredicate) {
+  const std::string ss = R"(
+    %x = OpGroupAny %bool %uint_2 %uint_2
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Predicate must be a boolean scalar type"));
+}
+
+TEST_F(ValidateGroup, FAddWithInt) {
+  const std::string ss = R"(
+    %a = OpGroupFAdd %uint %uint_2 Reduce %uint_2
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result must be a scalar or vector of float type"));
+}
+
+TEST_F(ValidateGroup, FMaxWidthMismatch) {
+  const std::string ss = R"(
+    %a = OpGroupFAdd %float64 %uint_2 Reduce %float_2
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("The type of X must match the Result type"));
+}
+
+TEST_F(ValidateGroup, IAddWithFloat) {
+  const std::string ss = R"(
+     %a = OpGroupIAdd %float %uint_2 Reduce %float_2
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result must be a scalar or vector of integer type"));
+}
+
+TEST_F(ValidateGroup, UMinWithArray) {
+  const std::string ss = R"(
+    %a = OpGroupUMin %uint_array %uint_2 Reduce %float_2
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result must be a scalar or vector of integer type"));
+}
+
+TEST_F(ValidateGroup, SMaxWidthMismatch) {
+  const std::string ss = R"(
+    %c = OpGroupSMax %uint64 %uint_2 Reduce %uint_2
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("The type of X must match the Result type"));
+}
+
+TEST_F(ValidateGroup, BroadcastArray) {
+  const std::string ss = R"(
+    %a = OpGroupBroadcast %uint_array %uint_2 %uint_2 %uint_0
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result must be a scalar or vector of integer, "
+                        "floating-point, or boolean type"));
+}
+
+TEST_F(ValidateGroup, BroadcastMismatch) {
+  const std::string ss = R"(
+    %b = OpGroupBroadcast %uint %uint_2 %float_2 %uint_0
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("The type of Value must match the Result type"));
+}
+
+TEST_F(ValidateGroup, AsyncCopyWaitEventsGood) {
+  const std::string ss = R"(
+               OpCapability Kernel
+               OpCapability Addresses
+               OpCapability Int64
+               OpCapability Int8
+               OpCapability Linkage
+               OpMemoryModel Physical64 OpenCL
+               OpEntryPoint Kernel %async_example "async_example"
+               OpExecutionMode %async_example ContractionOff
+               OpDecorate %26 Alignment 4
+               OpDecorate %29 Alignment 8
+               OpDecorate %async_example_local_data Alignment 4
+      %float = OpTypeFloat 32
+%_ptr_CrossWorkgroup_float = OpTypePointer CrossWorkgroup %float
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void %_ptr_CrossWorkgroup_float
+%spirv_Event = OpTypeEvent
+%_ptr_Workgroup_float = OpTypePointer Workgroup %float
+      %ulong = OpTypeInt 64 0
+       %uint = OpTypeInt 32 0
+%_ptr_Function_spirv_Event = OpTypePointer Function %spirv_Event
+    %uint_64 = OpConstant %uint 64
+%_arr_float_uint_64 = OpTypeArray %float %uint_64
+%_ptr_Workgroup__arr_float_uint_64 = OpTypePointer Workgroup %_arr_float_uint_64
+    %ulong_1 = OpConstant %ulong 1
+   %ulong_64 = OpConstant %ulong 64
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+      %uchar = OpTypeInt 8 0
+%_ptr_Function_uchar = OpTypePointer Function %uchar
+%async_example_local_data = OpVariable %_ptr_Workgroup__arr_float_uint_64 Workgroup
+         %24 = OpConstantNull %spirv_Event
+%async_example = OpFunction %void None %5
+         %26 = OpFunctionParameter %_ptr_CrossWorkgroup_float
+         %54 = OpLabel
+         %29 = OpVariable %_ptr_Function_spirv_Event Function
+         %30 = OpBitcast %_ptr_Workgroup_float %async_example_local_data
+         %31 = OpBitcast %_ptr_Function_uchar %29
+         %32 = OpGroupAsyncCopy %spirv_Event %uint_2 %30 %26 %ulong_64 %ulong_1 %24
+               OpStore %29 %32 Aligned 8
+               OpGroupWaitEvents %uint_2 %uint_1 %29
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(ss);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateGroup, AsyncCopyResultType) {
+  const std::string ss = R"(
+    %a = OpGroupAsyncCopy %uint %uint_2 %workgroup_float_var %cross_float_var %uint64_1 %uint64_1 %null_event
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("The result type must be OpTypeEvent"));
+}
+
+TEST_F(ValidateGroup, AsyncCopyDestinationPointer) {
+  const std::string ss = R"(
+    %a = OpGroupAsyncCopy %event %uint_2 %null_uint %cross_float_var %uint64_1 %uint64_1 %null_event
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Destination to be a pointer"));
+}
+
+TEST_F(ValidateGroup, AsyncCopyDestinationUniform) {
+  const std::string ss = R"(
+    %a = OpGroupAsyncCopy %event %uint_2 %uniform_float_var %cross_float_var %uint64_1 %uint64_1 %null_event
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Destination to be a pointer with storage "
+                        "class Workgroup or CrossWorkgroup"));
+}
+
+TEST_F(ValidateGroup, AsyncCopyDestinationBool) {
+  const std::string ss = R"(
+      %a = OpGroupAsyncCopy %event %uint_2 %workgroup_bool_var %cross_float_var %uint64_1 %uint64_1 %null_event
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Destination to be a pointer to scalar or "
+                        "vector of floating-point type or integer type"));
+}
+
+TEST_F(ValidateGroup, AsyncCopyDestinationSourceTypes) {
+  const std::string ss = R"(
+      %a = OpGroupAsyncCopy %event %uint_2 %workgroup_float_var %cross_uint_var %uint64_1 %uint64_1 %null_event
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Destination and Source to be the same type"));
+}
+
+TEST_F(ValidateGroup, AsyncCopyBothWorkgroup) {
+  const std::string ss = R"(
+      %a = OpGroupAsyncCopy %event %uint_2 %workgroup_float_var %workgroup_float_var %uint64_1 %uint64_1 %null_event
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("If Destination storage class is Workgroup, then the "
+                        "Source storage class must be CrossWorkgroup."));
+}
+
+TEST_F(ValidateGroup, AsyncCopyBothCrossWorkgroup) {
+  const std::string ss = R"(
+      %a = OpGroupAsyncCopy %event %uint_2 %cross_float_var %cross_float_var %uint64_1 %uint64_1 %null_event
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("If Destination storage class is CrossWorkgroup, then "
+                        "the Source storage class must be Workgroup"));
+}
+
+TEST_F(ValidateGroup, AsyncCopyEventType) {
+  const std::string ss = R"(
+      %a = OpGroupAsyncCopy %event %uint_2 %workgroup_float_var %cross_float_var %uint64_1 %uint64_1 %null_uint
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Event to be type OpTypeEvent"));
+}
+
+TEST_F(ValidateGroup, AsyncCopyNumElement32Bit) {
+  const std::string ss = R"(
+      %a = OpGroupAsyncCopy %event %uint_2 %workgroup_float_var %cross_float_var %uint_1 %uint64_1 %null_event
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("NumElements must be a 64-bit int scalar when "
+                        "Addressing Model is Physical64"));
+}
+
+TEST_F(ValidateGroup, AsyncCopyStride32Bit) {
+  const std::string ss = R"(
+      %a = OpGroupAsyncCopy %event %uint_2 %workgroup_float_var %cross_float_var %uint64_1 %uint_1 %null_event
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Stride must be a 64-bit int scalar when Addressing "
+                        "Model is Physical64"));
+}
+
+TEST_F(ValidateGroup, AsyncCopyNumElement64Bit) {
+  const std::string ss = R"(
+      %a = OpGroupAsyncCopy %event %uint_2 %workgroup_float_var %cross_float_var %uint64_1 %uint_1 %null_event
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss, false));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("NumElements must be a 32-bit int scalar when "
+                        "Addressing Model is Physical32"));
+}
+
+TEST_F(ValidateGroup, AsyncCopyStride64Bit) {
+  const std::string ss = R"(
+      %a = OpGroupAsyncCopy %event %uint_2 %workgroup_float_var %cross_float_var %uint_1 %uint64_1 %null_event
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss, false));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Stride must be a 32-bit int scalar when Addressing "
+                        "Model is Physical32"));
+}
+
+TEST_F(ValidateGroup, GroupWaitEventsNumEvents) {
+  const std::string ss = R"(
+    %a = OpVariable %func_event_ptr Function
+    OpGroupWaitEvents %uint_2 %uint64_1 %a
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Num Events to be a 32-bit int scalar"));
+}
+
+TEST_F(ValidateGroup, GroupWaitEventsEventList) {
+  const std::string ss = R"(
+    OpGroupWaitEvents %uint_2 %uint_1 %null_uint
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Events List to be a pointer"));
+}
+
+TEST_F(ValidateGroup, GroupWaitEventsEventListType) {
+  const std::string ss = R"(
+    OpGroupWaitEvents %uint_2 %uint_1 %uniform_float_var
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Events List to be a pointer to OpTypeEvent"));
+}
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/val_id_test.cpp b/test/val/val_id_test.cpp
index 1e05018..824ac8d 100644
--- a/test/val/val_id_test.cpp
+++ b/test/val/val_id_test.cpp
@@ -1,4 +1,6 @@
 // Copyright (c) 2015-2016 The Khronos Group Inc.
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -77,8 +79,7 @@
      OpExtension "SPV_KHR_variable_pointers"
 )";
 
-std::string kGLSL450MemoryModel =
-    kOpCapabilitySetup + kOpVariablePtrSetUp + R"(
+std::string kGLSL450MemoryModel = kOpCapabilitySetup + kOpVariablePtrSetUp + R"(
      OpMemoryModel Logical GLSL450
 )";
 
@@ -707,6 +708,24 @@
                   "'2[%_ptr_UniformConstant_float]' is not a scalar type.")));
 }
 
+TEST_P(ValidateIdWithMessage, OpTypeVectorComponentTypeCanBePointerType) {
+  std::string spirv = R"(
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Kernel
+OpCapability Int64
+OpCapability GenericPointer
+OpCapability MaskedGatherScatterINTEL
+OpExtension "SPV_INTEL_masked_gather_scatter"
+OpMemoryModel Physical64 OpenCL
+
+%2 = OpTypeInt 32 0
+%3 = OpTypePointer Generic %2
+%4 = OpTypeVector %3 4)";
+  CompileSuccessfully(spirv.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
 TEST_P(ValidateIdWithMessage, OpTypeVectorColumnCountLessThanTwoBad) {
   std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeFloat 32
@@ -742,7 +761,8 @@
   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr(make_message(
-                  "Having 8 components for TypeVector requires the Vector16 "
+                  "Having 8 components for TypeVector requires the Vector16 or "
+                  "LongVectorEXT "
                   "capability\n  %v8float = OpTypeVector %float 8\n")));
 }
 
@@ -757,6 +777,7 @@
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr(make_message(
                   "Having 16 components for TypeVector requires the Vector16 "
+                  "or LongVectorEXT "
                   "capability\n  %v16float = OpTypeVector %float 16\n")));
 }
 
@@ -1141,9 +1162,9 @@
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(getDiagnosticString(),
               AnyVUID("VUID-StandaloneSpirv-None-04667"));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(make_message("OpTypeStruct must not contain an opaque type")));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr(make_message(
+                  "OpTypeStruct must not contain an invalid opaque type")));
 }
 
 TEST_P(ValidateIdWithMessage, OpTypePointerGood) {
@@ -1602,21 +1623,6 @@
               HasSubstr(make_message("Operand '1[%uint]' cannot be a "
                                      "type")));
 }
-TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayConstConstituentBad) {
-  std::string spirv = kGLSL450MemoryModel + R"(
-%1 = OpTypeInt 32 0
-%2 = OpConstant %1 4
-%3 = OpTypeArray %1 %2
-%4 = OpTypePointer Uniform %1
-%5 = OpVariable %4 Uniform
-%6 = OpConstantComposite %3 %2 %2 %2 %5)";
-  CompileSuccessfully(spirv.c_str());
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr(make_message(
-                  "OpConstantComposite Constituent <id> '5[%5]' is not a "
-                  "constant or undef.")));
-}
 TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayConstituentTypeBad) {
   std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeInt 32 0
@@ -1736,7 +1742,7 @@
  %4 = OpConstantNull %3
  %5 = OpTypeFloat 32
  %6 = OpConstantNull %5
- %7 = OpTypePointer UniformConstant %3
+ %7 = OpTypePointer Workgroup %3
  %8 = OpConstantNull %7
  %9 = OpTypeEvent
 %10 = OpConstantNull %9
@@ -1935,25 +1941,6 @@
                   "'2[%v4float]'s vector element type.")));
 }
 
-// Invalid: Constituent is not a constant
-TEST_P(ValidateIdWithMessage,
-       OpSpecConstantCompositeVectorConstituentNotConstantBad) {
-  std::string spirv = kGLSL450MemoryModel + R"(
-%1 = OpTypeFloat 32
-%2 = OpTypeVector %1 4
-%3 = OpTypeInt 32 0
-%4 = OpSpecConstant %1 3.14
-%5 = OpTypePointer Uniform %1
-%6 = OpVariable %5 Uniform
-%7 = OpSpecConstantComposite %2 %6 %4 %4 %4)";
-  CompileSuccessfully(spirv.c_str());
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr(make_message(
-                  "OpSpecConstantComposite Constituent <id> '6[%6]' is "
-                  "not a constant or undef.")));
-}
-
 // Invalid: Vector contains a mix of Undef-int and Float.
 TEST_P(ValidateIdWithMessage,
        OpSpecConstantCompositeVectorConstituentUndefTypeBad) {
@@ -2068,26 +2055,6 @@
                   "count.")));
 }
 
-// Invalid: Composite contains a non-const/undef component
-TEST_P(ValidateIdWithMessage,
-       OpSpecConstantCompositeMatrixConstituentNotConstBad) {
-  std::string spirv = kGLSL450MemoryModel + R"(
- %1 = OpTypeFloat 32
- %2 = OpConstant %1 0.0
- %3 = OpTypeVector %1 4
- %4 = OpTypeMatrix %3 4
- %5 = OpSpecConstantComposite %3 %2 %2 %2 %2
- %6 = OpTypePointer Uniform %1
- %7 = OpVariable %6 Uniform
- %8 = OpSpecConstantComposite %4 %5 %5 %5 %7)";
-  CompileSuccessfully(spirv.c_str());
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr(make_message(
-                  "OpSpecConstantComposite Constituent <id> '7[%7]' is "
-                  "not a constant or undef.")));
-}
-
 // Invalid: Composite contains a column that is *not* a vector (it's an array)
 TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixColTypeBad) {
   std::string spirv = kGLSL450MemoryModel + R"(
@@ -2198,23 +2165,6 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-// Invalid: Array uses a type as operand.
-TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeArrayConstConstituentBad) {
-  std::string spirv = kGLSL450MemoryModel + R"(
-%1 = OpTypeInt 32 0
-%2 = OpConstant %1 4
-%3 = OpTypeArray %1 %2
-%4 = OpTypePointer Uniform %1
-%5 = OpVariable %4 Uniform
-%6 = OpSpecConstantComposite %3 %2 %2 %2 %5)";
-  CompileSuccessfully(spirv.c_str());
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr(make_message(
-                  "OpSpecConstantComposite Constituent <id> '5[%5]' is "
-                  "not a constant or undef.")));
-}
-
 // Invalid: Array has a mix of Int and Float components.
 TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeArrayConstituentTypeBad) {
   std::string spirv = kGLSL450MemoryModel + R"(
@@ -2296,25 +2246,6 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-// Invalid: Composite contains non-const/undef component.
-TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeStructNonConstBad) {
-  std::string spirv = kGLSL450MemoryModel + R"(
-%1 = OpTypeInt 32 0
-%2 = OpTypeInt 64 0
-%3 = OpTypeStruct %1 %1 %2
-%4 = OpSpecConstant %1 42
-%5 = OpUndef %2
-%6 = OpTypePointer Uniform %1
-%7 = OpVariable %6 Uniform
-%8 = OpSpecConstantComposite %3 %4 %7 %5)";
-  CompileSuccessfully(spirv.c_str());
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr(make_message(
-                  "OpSpecConstantComposite Constituent <id> '7[%7]' is "
-                  "not a constant or undef.")));
-}
-
 // Invalid: Struct component type does not match expected specialization type.
 // Second component was expected to be Int32, but got Int64.
 TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeStructMemberTypeBad) {
@@ -2372,12 +2303,18 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 TEST_P(ValidateIdWithMessage, OpVariableInitializerGlobalVariableGood) {
-  std::string spirv = kGLSL450MemoryModel + R"(
-%1 = OpTypeInt 32 0
-%2 = OpTypePointer Uniform %1
-%3 = OpVariable %2 Uniform
-%4 = OpTypePointer Private %2 ; pointer to pointer
-%5 = OpVariable %4 Private %3
+  std::string spirv = kOpenCLMemoryModel64 + R"(
+%2 = OpTypeInt 32 0
+%3 = OpTypePointer CrossWorkgroup %2
+%4 = OpVariable %3 CrossWorkgroup
+%5 = OpTypePointer Function %3 ; pointer to pointer
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+%6 = OpVariable %5 Function %4
+OpReturn
+OpFunctionEnd
 )";
   CompileSuccessfully(spirv.c_str());
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
@@ -2428,10 +2365,10 @@
 }
 
 TEST_P(ValidateIdWithMessage, OpVariableInitializerIsModuleVarGood) {
-  std::string spirv = kGLSL450MemoryModel + R"(
+  std::string spirv = kOpenCLMemoryModel64 + R"(
 %int = OpTypeInt 32 0
-%ptrint = OpTypePointer Uniform %int
-%mvar = OpVariable %ptrint Uniform
+%ptrint = OpTypePointer CrossWorkgroup %int
+%mvar = OpVariable %ptrint CrossWorkgroup
 %ptrptrint = OpTypePointer Function %ptrint
 %void = OpTypeVoid
 %fnty = OpTypeFunction %void
@@ -2469,7 +2406,8 @@
           "be used with non-externally visible shader Storage Classes: "
           "Workgroup, CrossWorkgroup, Private, Function, Input, Output, "
           "RayPayloadKHR, IncomingRayPayloadKHR, HitAttributeKHR, "
-          "CallableDataKHR, IncomingCallableDataKHR, or UniformConstant")));
+          "CallableDataKHR, IncomingCallableDataKHR, NodePayloadAMDX, or "
+          "UniformConstant")));
 }
 
 TEST_P(ValidateIdWithMessage, OpVariableContainsBoolPrivateGood) {
@@ -2513,11 +2451,11 @@
 TEST_P(ValidateIdWithMessage, OpVariableContainsBoolPointerGood) {
   std::string spirv = kGLSL450MemoryModel + R"(
 %bool = OpTypeBool
-%boolptr = OpTypePointer Uniform %bool
+%boolptr = OpTypePointer Workgroup %bool
 %int = OpTypeInt 32 0
 %block = OpTypeStruct %boolptr %int
-%_ptr_Uniform_block = OpTypePointer Uniform %block
-%var = OpVariable %_ptr_Uniform_block Uniform
+%_ptr_Private_block = OpTypePointer Private %block
+%var = OpVariable %_ptr_Private_block Private
 %void = OpTypeVoid
 %fnty = OpTypeFunction %void
 %main = OpFunction %void None %fnty
@@ -2646,7 +2584,8 @@
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(make_message(
-          "In Logical addressing, variables may not allocate a pointer type")));
+          "In Logical addressing, variables can only allocate a workgroup "
+          "pointer if the VariablePointers capability is declared")));
 }
 
 TEST_P(ValidateIdWithMessage,
@@ -2724,8 +2663,8 @@
 OpMemoryModel Logical GLSL450
 %void = OpTypeVoid
 %int = OpTypeInt 32 0
-%_ptr_workgroup_int = OpTypePointer Workgroup %int
-%_ptr_function_ptr = OpTypePointer Function %_ptr_workgroup_int
+%_ptr_storagebuffer_int = OpTypePointer StorageBuffer %int
+%_ptr_function_ptr = OpTypePointer Function %_ptr_storagebuffer_int
 %voidfn = OpTypeFunction %void
 %func = OpFunction %void None %voidfn
 %entry = OpLabel
@@ -2772,6 +2711,11 @@
 %_ptr_workgroup_int = OpTypePointer Workgroup %int
 %_ptr_uniform_ptr = OpTypePointer Uniform %_ptr_workgroup_int
 %var = OpVariable %_ptr_uniform_ptr Uniform
+%voidfn = OpTypeFunction %void
+%func = OpFunction %void None %voidfn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
 )";
 
   CompileSuccessfully(spirv);
@@ -4012,6 +3956,7 @@
               OpReturn
               OpFunctionEnd
           )";
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
@@ -4021,8 +3966,7 @@
   const std::string instr = GetParam();
   const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
   std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
-%float_entry = )" +
-                      instr +
+%float_entry = )" + instr +
                       R"( %float %my_matrix )" + elem +
                       R"(%int_0 %int_1
 OpReturn
@@ -4032,6 +3976,7 @@
   const std::string expected_err = "The Result Type of " + instr +
                                    " <id> '36[%36]' must be "
                                    "OpTypePointer. Found OpTypeFloat.";
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
@@ -4042,12 +3987,13 @@
   const std::string instr = GetParam();
   const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
   std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
-%float_entry = )" +
-                      instr + " %_ptr_Private_float %void " + elem +
+%float_entry = )" + instr +
+                      " %_ptr_Private_float %void " + elem +
                       R"(%int_0 %int_1
 OpReturn
 OpFunctionEnd
   )";
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand '1[%void]' cannot be a "
@@ -4059,13 +4005,13 @@
   const std::string instr = GetParam();
   const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
   std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
-%entry = )" +
-                      instr + R"( %_ptr_Private_float %_ptr_Private_float )" +
+%entry = )" + instr + R"( %_ptr_Private_float %_ptr_Private_float )" +
                       elem +
                       R"(%int_0 %int_1
 OpReturn
 OpFunctionEnd
   )";
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
@@ -4078,8 +4024,8 @@
   const std::string instr = GetParam();
   const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
   std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
-%entry = )" +
-                      instr + R"( %_ptr_Function_float %my_matrix )" + elem +
+%entry = )" + instr + R"( %_ptr_Function_float %my_matrix )" +
+                      elem +
                       R"(%int_0 %int_1
 OpReturn
 OpFunctionEnd
@@ -4087,6 +4033,7 @@
   const std::string expected_err =
       "The result pointer storage class and base pointer storage class in " +
       instr + " do not match.";
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
@@ -4099,8 +4046,8 @@
   const std::string instr = GetParam();
   const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
   std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
-%entry = )" +
-                      instr + R"( %_ptr_Private_float %my_float_var )" + elem +
+%entry = )" + instr + R"( %_ptr_Private_float %my_float_var )" +
+                      elem +
                       R"(%int_0
 OpReturn
 OpFunctionEnd
@@ -4108,6 +4055,7 @@
   const std::string expected_err = instr +
                                    " reached non-composite type while "
                                    "indexes still remain to be traversed.";
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
@@ -4119,12 +4067,13 @@
   const std::string instr = GetParam();
   const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
   std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
-%entry = )" +
-                      instr + R"( %_ptr_Private_float %my_float_var )" + elem +
+%entry = )" + instr + R"( %_ptr_Private_float %my_float_var )" +
+                      elem +
                       R"(
 OpReturn
 OpFunctionEnd
   )";
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
@@ -4135,18 +4084,55 @@
   const std::string instr = GetParam();
   const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
   std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
-%entry = )" +
-                      instr + R"( %_ptr_Private_mat4x3 %my_float_var )" + elem +
+%entry = )" + instr + R"( %_ptr_Private_mat4x3 %my_float_var )" +
+                      elem +
                       R"(
 OpReturn
 OpFunctionEnd
   )";
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("result type (OpTypeMatrix) does not match the type that "
-                "results from indexing into the base <id> (OpTypeFloat)."));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("result type <id> '6[%mat4v3float]' (OpTypeMatrix) "
+                        "does not match the type that results from indexing "
+                        "into the base <id> '4[%float]' (OpTypeFloat)"));
+}
+
+TEST_P(AccessChainInstructionTest, AccessChainDifferentIntTypes) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %_
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %_arr_uint_uint_32 ArrayStride 4
+               OpDecorate %SSBO Block
+               OpMemberDecorate %SSBO 0 Offset 0
+               OpDecorate %_ Binding 0
+               OpDecorate %_ DescriptorSet 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+    %uint_32 = OpConstant %uint 32
+%_arr_uint_uint_32 = OpTypeArray %uint %uint_32
+       %SSBO = OpTypeStruct %_arr_uint_uint_32
+%_ptr_StorageBuffer_SSBO = OpTypePointer StorageBuffer %SSBO
+          %_ = OpVariable %_ptr_StorageBuffer_SSBO StorageBuffer
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+     %uint_1 = OpConstant %uint 1
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+         %18 = OpAccessChain %ptr_ssbo_int %_ %int_0 %int_0
+               OpReturn
+               OpFunctionEnd
+  )";
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("The types must be the exact same Id, so the two types "
+                        "referenced are slighlty different"));
 }
 
 // Valid: 255 indexes passed to the access chain instruction. Limit is 255.
@@ -4190,6 +4176,7 @@
     OpReturn
     OpFunctionEnd
   )";
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(spirv.str());
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
@@ -4210,6 +4197,7 @@
   )";
   const std::string expected_err = "The number of indexes in " + instr +
                                    " may not exceed 255. Found 256 indexes.";
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(spirv.str());
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
@@ -4257,6 +4245,7 @@
     OpFunctionEnd
   )";
 
+  getValidatorOptions()->relax_logical_pointer = true;
   spvValidatorOptionsSetUniversalLimit(
       options_, spv_validator_limit_max_access_chain_indexes, 10u);
   CompileSuccessfully(spirv.str());
@@ -4279,6 +4268,7 @@
   )";
   const std::string expected_err = "The number of indexes in " + instr +
                                    " may not exceed 10. Found 11 indexes.";
+  getValidatorOptions()->relax_logical_pointer = true;
   spvValidatorOptionsSetUniversalLimit(
       options_, spv_validator_limit_max_access_chain_indexes, 10u);
   CompileSuccessfully(spirv.str());
@@ -4292,14 +4282,15 @@
   const std::string instr = GetParam();
   const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
   std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
-%entry = )" +
-                      instr + R"( %_ptr_Private_float %my_matrix )" + elem +
+%entry = )" + instr + R"( %_ptr_Private_float %my_matrix )" +
+                      elem +
                       R"(%float_0 %int_1
 OpReturn
 OpFunctionEnd
   )";
   const std::string expected_err =
       "Indexes passed to " + instr + " must be of type integer.";
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
@@ -4311,18 +4302,18 @@
   const std::string instr = GetParam();
   const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
   std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
-%f = )" +
-                      instr + R"( %_ptr_Uniform_float %blockName_var )" + elem +
+%f = )" + instr + R"( %_ptr_Uniform_float %blockName_var )" +
+                      elem +
                       R"(%int_0 %spec_int %int_2
 OpReturn
 OpFunctionEnd
   )";
-  const std::string expected_err =
-      "The <id> passed to " + instr +
-      " to index into a structure must be an OpConstant.";
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("The <id> passed to " + instr));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("into a structure must be an OpConstant"));
 }
 
 // Invalid: Indexing up to a vec4 granularity, but result type expected float.
@@ -4331,16 +4322,18 @@
   const std::string instr = GetParam();
   const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
   std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
-%entry = )" +
-                      instr + R"( %_ptr_Uniform_float %blockName_var )" + elem +
+%entry = )" + instr + R"( %_ptr_Uniform_float %blockName_var )" +
+                      elem +
                       R"(%int_0 %int_1 %int_2
 OpReturn
 OpFunctionEnd
   )";
-  const std::string expected_err = instr +
-                                   " result type (OpTypeFloat) does not match "
-                                   "the type that results from indexing into "
-                                   "the base <id> (OpTypeVector).";
+  const std::string expected_err =
+      instr +
+      " result type <id> '4[%float]' (OpTypeFloat) does not match the type "
+      "that results from indexing into the base <id> '18[%v4float]' "
+      "(OpTypeVector).";
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
@@ -4351,8 +4344,8 @@
   const std::string instr = GetParam();
   const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
   std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
-%entry = )" +
-                      instr + R"( %_ptr_Uniform_float %blockName_var )" + elem +
+%entry = )" + instr + R"( %_ptr_Uniform_float %blockName_var )" +
+                      elem +
                       R"(%int_0 %int_2 %int_2
 OpReturn
 OpFunctionEnd
@@ -4360,6 +4353,7 @@
   const std::string expected_err = instr +
                                    " reached non-composite type while "
                                    "indexes still remain to be traversed.";
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
@@ -4370,16 +4364,17 @@
   const std::string instr = GetParam();
   const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
   std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
-%entry = )" +
-                      instr + R"( %_ptr_Uniform_float %blockName_var )" + elem +
+%entry = )" + instr + R"( %_ptr_Uniform_float %blockName_var )" +
+                      elem +
                       R"(%int_3 %int_2 %int_2
 OpReturn
 OpFunctionEnd
   )";
-  const std::string expected_err = "Index is out of bounds: " + instr +
+  const std::string expected_err = "is out of bounds: " + instr +
                                    " cannot find index 3 into the structure "
                                    "<id> '25[%_struct_25]'. This structure "
                                    "has 3 members. Largest valid index is 2.";
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
@@ -4414,6 +4409,7 @@
 OpReturn
 OpFunctionEnd
   )";
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(spirv.str());
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
@@ -4426,12 +4422,13 @@
       " OpDecorate %_ptr_Uniform_blockName ArrayStride 8 ";
   std::string spirv = kGLSL450MemoryModel + arrayStride +
                       kDeeplyNestedStructureSetup + R"(
-%runtime_arr_entry = )" + instr +
-                      R"( %_ptr_Uniform_float %blockName_var )" + elem +
+%runtime_arr_entry = )" +
+                      instr + R"( %_ptr_Uniform_float %blockName_var )" + elem +
                       R"(%int_2 %int_0
 OpReturn
 OpFunctionEnd
   )";
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
@@ -4450,6 +4447,7 @@
   const std::string expected_err =
       instr +
       " reached non-composite type while indexes still remain to be traversed.";
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
@@ -4461,8 +4459,8 @@
   const std::string instr = GetParam();
   const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
   std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
-%entry = )" +
-                      instr + R"( %_ptr_Private_float %my_matrix )" + elem +
+%entry = )" + instr + R"( %_ptr_Private_float %my_matrix )" +
+                      elem +
                       R"(%int_0 %int_1 %int_0
 OpReturn
 OpFunctionEnd
@@ -4470,6 +4468,7 @@
   const std::string expected_err = instr +
                                    " reached non-composite type while "
                                    "indexes still remain to be traversed.";
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
@@ -4481,16 +4480,18 @@
   const std::string instr = GetParam();
   const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
   std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
-%entry = )" +
-                      instr + R"( %_ptr_Private_mat4x3 %my_matrix )" + elem +
+%entry = )" + instr + R"( %_ptr_Private_mat4x3 %my_matrix )" +
+                      elem +
                       R"(%int_0 %int_1
 OpReturn
 OpFunctionEnd
   )";
-  const std::string expected_err = instr +
-                                   " result type (OpTypeMatrix) does not match "
-                                   "the type that results from indexing into "
-                                   "the base <id> (OpTypeFloat).";
+  const std::string expected_err =
+      instr +
+      " result type <id> '6[%mat4v3float]' (OpTypeMatrix) does not match the "
+      "type that results from indexing into the base <id> '4[%float]' "
+      "(OpTypeFloat).";
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
@@ -4935,6 +4936,40 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_P(ValidateIdWithMessage, OpVectorIdShuffleFloatGood) {
+  std::string spirv = "OpCapability LongVectorEXT\n" + kOpCapabilitySetup +
+                      kOpVariablePtrSetUp +
+                      "OpExtension \"SPV_EXT_long_vector\"" + R"(
+OpMemoryModel Logical GLSL450
+%float = OpTypeFloat 32
+%uint = OpTypeInt 32 0
+%u2 = OpConstant %uint 2
+%u3 = OpConstant %uint 3
+%u4 = OpConstant %uint 4
+%vec2 = OpTypeVectorIdEXT %float %u2
+%vec3 = OpTypeVectorIdEXT %float %u3
+%vec4 = OpTypeVectorIdEXT %float %u4
+%ptr_vec2 = OpTypePointer Function %vec2
+%ptr_vec3 = OpTypePointer Function %vec3
+%float_1 = OpConstant %float 1
+%float_2 = OpConstant %float 2
+%1 = OpConstantComposite %vec2 %float_2 %float_1
+%2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2
+%3 = OpTypeFunction %vec4
+%4 = OpFunction %vec4 None %3
+%5 = OpLabel
+%var = OpVariable %ptr_vec2 Function %1
+%var2 = OpVariable %ptr_vec3 Function %2
+%6 = OpLoad %vec2 %var
+%7 = OpLoad %vec3 %var2
+%8 = OpVectorShuffle %vec4 %6 %7 4 3 1 0xffffffff
+     OpReturnValue %8
+     OpFunctionEnd)";
+
+  CompileSuccessfully(spirv.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
 TEST_P(ValidateIdWithMessage, OpVectorShuffleScalarResultType) {
   std::string spirv = kGLSL450MemoryModel + R"(
 %float = OpTypeFloat 32
@@ -4955,7 +4990,7 @@
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr(make_message(
-                  "Result Type of OpVectorShuffle must be OpTypeVector.")));
+                  "Result Type of OpVectorShuffle must be a vector type.")));
 }
 
 TEST_P(ValidateIdWithMessage, OpVectorShuffleComponentCount) {
@@ -5002,7 +5037,7 @@
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr(make_message("The type of Vector 1 must be OpTypeVector.")));
+      HasSubstr(make_message("The type of Vector 1 must be a vector type.")));
 }
 
 TEST_P(ValidateIdWithMessage, OpVectorShuffleVector2Type) {
@@ -5025,7 +5060,7 @@
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr(make_message("The type of Vector 2 must be OpTypeVector.")));
+      HasSubstr(make_message("The type of Vector 2 must be a vector type.")));
 }
 
 TEST_P(ValidateIdWithMessage, OpVectorShuffleVector1ComponentType) {
@@ -5762,7 +5797,7 @@
  %2 = OpFunction %8 None %11
  %4 = OpFunctionParameter %10
 %15 = OpLabel
-%16 = OpLoad %6 %3 Aligned 0
+%16 = OpLoad %6 %3 Aligned 1
 %17 = OpCompositeExtract %5 %16 0
 %18 = OpInBoundsPtrAccessChain %13 %4 %17 %12
       OpStore %18 %14 Aligned 4
@@ -5796,7 +5831,7 @@
  %2 = OpFunction %8 None %12
  %4 = OpFunctionParameter %11
 %17 = OpLabel
-%18 = OpLoad %6 %3 Aligned 0
+%18 = OpLoad %6 %3 Aligned 1
 %19 = OpCompositeExtract %5 %18 0
 %20 = OpBitwiseAnd %5 %19 %13
 %21 = OpPtrAccessChain %15 %4 %20 %14
@@ -5960,9 +5995,10 @@
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr(make_message("SpecId decoration on target <id> "
-                             "'1[%uint_3]' must be a scalar specialization "
-                             "constant")));
+      HasSubstr(make_message(
+          "SpecId decoration on target <id> "
+          "'1[%uint_3]' must be OpSpecConstantTrue, OpSpecConstantFalse, "
+          "OpSpecConstant, or OpSpecConstantDataKHR")));
 }
 
 TEST_P(ValidateIdWithMessage, SpecIdTargetOpSpecConstantOpBad) {
@@ -5984,7 +6020,8 @@
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(make_message("SpecId decoration on target <id> '1[%1]' "
-                             "must be a scalar specialization constant")));
+                             "must be OpSpecConstantTrue, OpSpecConstantFalse, "
+                             "OpSpecConstant, or OpSpecConstantDataKHR")));
 }
 
 TEST_P(ValidateIdWithMessage, SpecIdTargetOpSpecConstantCompositeBad) {
@@ -6005,7 +6042,8 @@
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(make_message("SpecId decoration on target <id> '1[%1]' "
-                             "must be a scalar specialization constant")));
+                             "must be OpSpecConstantTrue, OpSpecConstantFalse, "
+                             "OpSpecConstant, or OpSpecConstantDataKHR")));
 }
 
 TEST_P(ValidateIdWithMessage, SpecIdTargetGood) {
@@ -7174,6 +7212,126 @@
               HasSubstr(make_message("ID '11[%11]' has not been defined")));
 }
 
+TEST_P(ValidateIdWithMessage, OpAliasScopeDeclINTELDoesNotRequireType) {
+  std::string spirv = R"(
+     OpCapability Shader
+     OpCapability MemoryAccessAliasingINTEL
+     OpExtension "SPV_INTEL_memory_access_aliasing"
+     OpMemoryModel Logical GLSL450
+     OpEntryPoint Fragment %main "main"
+     OpExecutionMode %main OriginUpperLeft
+%alias_domain = OpAliasDomainDeclINTEL
+%alias_scope = OpAliasScopeDeclINTEL %alias_domain
+%void = OpTypeVoid
+%func_type = OpTypeFunction %void
+%main = OpFunction %void None %func_type
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateIdWithMessage, OpAliasScopeListDeclINTELDoesNotRequireType) {
+  std::string spirv = R"(
+     OpCapability Shader
+     OpCapability MemoryAccessAliasingINTEL
+     OpExtension "SPV_INTEL_memory_access_aliasing"
+     OpMemoryModel Logical GLSL450
+     OpEntryPoint Fragment %main "main"
+     OpExecutionMode %main OriginUpperLeft
+%alias_domain = OpAliasDomainDeclINTEL
+%alias_scope1 = OpAliasScopeDeclINTEL %alias_domain
+%alias_scope2 = OpAliasScopeDeclINTEL %alias_domain
+%alias_list = OpAliasScopeListDeclINTEL %alias_scope1 %alias_scope2
+%void = OpTypeVoid
+%func_type = OpTypeFunction %void
+%main = OpFunction %void None %func_type
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+// Test that would have failed before the fix - showing what happens when
+// these instructions reference operands without types
+TEST_P(ValidateIdWithMessage, OpAliasScopeINTELWithNonTypedOperands) {
+  std::string spirv = R"(
+     OpCapability Shader
+     OpCapability MemoryAccessAliasingINTEL
+     OpExtension "SPV_INTEL_memory_access_aliasing"
+     OpMemoryModel Logical GLSL450
+     OpEntryPoint Fragment %main "main"
+     OpExecutionMode %main OriginUpperLeft
+%alias_domain = OpAliasDomainDeclINTEL
+%alias_scope = OpAliasScopeDeclINTEL %alias_domain
+%alias_list = OpAliasScopeListDeclINTEL %alias_scope
+%void = OpTypeVoid
+%func_type = OpTypeFunction %void
+%main = OpFunction %void None %func_type
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateIdWithMessage,
+       OpLoadStoreWithAliasScopeINTELMaskMemoryOperands) {
+  std::string spirv = R"(
+     OpCapability Shader
+     OpCapability MemoryAccessAliasingINTEL
+     OpExtension "SPV_INTEL_memory_access_aliasing"
+     OpMemoryModel Logical GLSL450
+     OpEntryPoint Fragment %main "main"
+     OpExecutionMode %main OriginUpperLeft
+%alias_domain = OpAliasDomainDeclINTEL
+%alias_scope = OpAliasScopeDeclINTEL %alias_domain
+%alias_list = OpAliasScopeListDeclINTEL %alias_scope
+%void = OpTypeVoid
+%uint = OpTypeInt 32 0
+%ptr_uint = OpTypePointer Function %uint
+%func_type = OpTypeFunction %void
+%main = OpFunction %void None %func_type
+%entry = OpLabel
+%var = OpVariable %ptr_uint Function
+%val = OpLoad %uint %var Aligned|AliasScopeINTELMask 4 %alias_list
+OpStore %var %val Aligned|NoAliasINTELMask 4 %alias_list
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateIdWithMessage, OpDecorateIdAfterAliasScopeListDeclINTEL) {
+  std::string spirv = R"(
+     OpCapability Shader
+     OpCapability MemoryAccessAliasingINTEL
+     OpExtension "SPV_INTEL_memory_access_aliasing"
+     OpMemoryModel Logical GLSL450
+     OpEntryPoint Fragment %main "main"
+     OpExecutionMode %main OriginUpperLeft
+%alias_domain = OpAliasDomainDeclINTEL
+%alias_scope1 = OpAliasScopeDeclINTEL %alias_domain
+%alias_scope2 = OpAliasScopeDeclINTEL %alias_domain
+%alias_list = OpAliasScopeListDeclINTEL %alias_scope1 %alias_scope2
+OpDecorateId %main NoAliasINTEL %alias_list
+%void = OpTypeVoid
+%func_type = OpTypeFunction %void
+%main = OpFunction %void None %func_type
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+}
+
 INSTANTIATE_TEST_SUITE_P(, ValidateIdWithMessage, ::testing::Bool());
 
 }  // namespace
diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp
index 93fab04..15880c9 100644
--- a/test/val/val_image_test.cpp
+++ b/test/val/val_image_test.cpp
@@ -20,6 +20,7 @@
 #include <string>
 
 #include "gmock/gmock.h"
+#include "spirv-tools/libspirv.h"
 #include "test/unit_spirv.h"
 #include "test/val/val_fixtures.h"
 
@@ -49,7 +50,9 @@
 OpCapability MinLod
 OpCapability Sampled1D
 OpCapability ImageQuery
+OpCapability Int16
 OpCapability Int64
+OpCapability Float16
 OpCapability Float64
 OpCapability SparseResidency
 OpCapability ImageBuffer
@@ -82,7 +85,7 @@
 %uniform_sampler
 %private_image_u32_buffer_0002_r32ui
 %private_image_u32_spd_0002
-%private_image_f32_buffer_0002_r32ui
+%private_image_f32_buffer_0002_r32f
 %input_flat_u32
 )";
 
@@ -115,6 +118,8 @@
 OpDecorate %uniform_image_f32_2d_0002 Binding 3
 OpDecorate %uniform_image_s32_2d_0002 DescriptorSet 1
 OpDecorate %uniform_image_s32_2d_0002 Binding 4
+OpDecorate %uniform_image_u32_3d_0001 DescriptorSet 1
+OpDecorate %uniform_image_u32_3d_0001 Binding 5
 OpDecorate %uniform_image_f32_spd_0002 DescriptorSet 2
 OpDecorate %uniform_image_f32_spd_0002 Binding 0
 OpDecorate %uniform_image_f32_3d_0111 DescriptorSet 2
@@ -123,6 +128,8 @@
 OpDecorate %uniform_image_f32_cube_0101 Binding 2
 OpDecorate %uniform_image_f32_cube_0102_rgba32f DescriptorSet 2
 OpDecorate %uniform_image_f32_cube_0102_rgba32f Binding 3
+OpDecorate %uniform_image_f32_3d_0001 DescriptorSet 2
+OpDecorate %uniform_image_f32_3d_0001 Binding 4
 OpDecorate %uniform_sampler DescriptorSet 3
 OpDecorate %uniform_sampler Binding 0
 OpDecorate %input_flat_u32 Flat
@@ -134,12 +141,15 @@
 %void = OpTypeVoid
 %func = OpTypeFunction %void
 %bool = OpTypeBool
+%f16 = OpTypeFloat 16
 %f32 = OpTypeFloat 32
 %f64 = OpTypeFloat 64
+%u16 = OpTypeInt 16 0
 %u32 = OpTypeInt 32 0
 %s32 = OpTypeInt 32 1
 %u64 = OpTypeInt 64 0
 %s64 = OpTypeInt 64 1
+%f16vec2 = OpTypeVector %f16 2
 %s32vec2 = OpTypeVector %s32 2
 %u32vec2 = OpTypeVector %u32 2
 %f32vec2 = OpTypeVector %f32 2
@@ -151,6 +161,8 @@
 %f32vec4 = OpTypeVector %f32 4
 %boolvec4 = OpTypeVector %bool 4
 
+%f16_0 = OpConstant %f16 0
+
 %f32_0 = OpConstant %f32 0
 %f32_1 = OpConstant %f32 1
 %f32_0_5 = OpConstant %f32 0.5
@@ -167,6 +179,8 @@
 %s32_4 = OpConstant %s32 4
 %s32_m1 = OpConstant %s32 -1
 
+%u16_0 = OpConstant %u16 0
+
 %u32_0 = OpConstant %u32 0
 %u32_1 = OpConstant %u32 1
 %u32_2 = OpConstant %u32 2
@@ -182,6 +196,7 @@
 %u32vec2arr3 = OpTypeArray %u32vec2 %u32_3
 %u32arr4 = OpTypeArray %u32 %u32_4
 %u32vec3arr4 = OpTypeArray %u32vec3 %u32_4
+%f16vec2arr4 = OpTypeArray %f16vec2 %u32_4
 
 %struct_u32_f32vec4 = OpTypeStruct %u32 %f32vec4
 %struct_u64_f32vec4 = OpTypeStruct %u64 %f32vec4
@@ -195,6 +210,7 @@
 %struct_u32_f32vec4_u32 = OpTypeStruct %u32 %f32vec4 %u32
 %struct_u32_u32arr4 = OpTypeStruct %u32 %u32arr4
 
+%u32vec2_00 = OpConstantComposite %u32vec2 %u32_0 %u32_0
 %u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1
 %u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2
 %u32vec3_012 = OpConstantComposite %u32vec3 %u32_0 %u32_1 %u32_2
@@ -209,6 +225,8 @@
 %s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3
 %s32vec4_1234 = OpConstantComposite %s32vec4 %s32_1 %s32_2 %s32_3 %s32_4
 
+%f16vec2_00 = OpConstantComposite %f16vec2 %f16_0 %f16_0
+
 %f32vec2_00 = OpConstantComposite %f32vec2 %f32_0 %f32_0
 %f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1
 %f32vec2_10 = OpConstantComposite %f32vec2 %f32_1 %f32_0
@@ -226,6 +244,7 @@
 %const_offsets3x2 = OpConstantComposite %u32vec2arr3 %u32vec2_01 %u32vec2_12 %u32vec2_01
 %const_offsets4xu = OpConstantComposite %u32arr4 %u32_0 %u32_0 %u32_0 %u32_0
 %const_offsets4x3 = OpConstantComposite %u32vec3arr4 %u32vec3_012 %u32vec3_012 %u32vec3_012 %u32vec3_012
+%const_offsets4f16 = OpConstantComposite %f16vec2arr4 %f16vec2_00 %f16vec2_00 %f16vec2_00 %f16vec2_00
 
 %type_image_f32_1d_0001 = OpTypeImage %f32 1D 0 0 0 1 Unknown
 %ptr_image_f32_1d_0001 = OpTypePointer UniformConstant %type_image_f32_1d_0001
@@ -311,10 +330,10 @@
 %ptr_image_u32_spd_0002 = OpTypePointer Private %type_image_u32_spd_0002
 %private_image_u32_spd_0002 = OpVariable %ptr_image_u32_spd_0002 Private
 
-%type_image_f32_buffer_0002_r32ui = OpTypeImage %f32 Buffer 0 0 0 2 R32ui
+%type_image_f32_buffer_0002_r32f = OpTypeImage %f32 Buffer 0 0 0 2 R32f
 %ptr_Image_f32 = OpTypePointer Image %f32
-%ptr_image_f32_buffer_0002_r32ui = OpTypePointer Private %type_image_f32_buffer_0002_r32ui
-%private_image_f32_buffer_0002_r32ui = OpVariable %ptr_image_f32_buffer_0002_r32ui Private
+%ptr_image_f32_buffer_0002_r32f = OpTypePointer Private %type_image_f32_buffer_0002_r32f
+%private_image_f32_buffer_0002_r32f = OpVariable %ptr_image_f32_buffer_0002_r32f Private
 
 %ptr_input_flat_u32 = OpTypePointer Input %u32
 %input_flat_u32 = OpVariable %ptr_input_flat_u32 Input
@@ -367,6 +386,7 @@
 OpCapability ImageGatherExtended
 OpCapability InputAttachment
 OpCapability SampledRect
+OpCapability Int16
 )";
 
   ss << capabilities_and_extensions;
@@ -376,6 +396,7 @@
 %func = OpTypeFunction %void
 %bool = OpTypeBool
 %f32 = OpTypeFloat 32
+%u16 = OpTypeInt 16 0
 %u32 = OpTypeInt 32 0
 %u32vec2 = OpTypeVector %u32 2
 %f32vec2 = OpTypeVector %f32 2
@@ -390,6 +411,8 @@
 %f32_0_25 = OpConstant %f32 0.25
 %f32_0_75 = OpConstant %f32 0.75
 
+%u16_0 = OpConstant %u16 0
+
 %u32_0 = OpConstant %u32 0
 %u32_1 = OpConstant %u32 1
 %u32_2 = OpConstant %u32 2
@@ -1074,6 +1097,258 @@
           "Image Dim TileImageDataEXT cannot be used with ImageSparseRead"));
 }
 
+TEST_F(ValidateImage, ColorAttachmentReadEXTWrongResultType) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_tid_0002 %uniform_image_f32_tid_0002
+%res1 = OpColorAttachmentReadEXT %bool %img
+)";
+
+  const std::string decl = R"(
+%type_image_f32_tid_0002 = OpTypeImage %f32 TileImageDataEXT 0 0 0 2 Unknown
+%ptr_image_f32_tid_0002 = OpTypePointer UniformConstant %type_image_f32_tid_0002
+%uniform_image_f32_tid_0002 = OpVariable %ptr_image_f32_tid_0002 UniformConstant
+)";
+
+  const std::string extra = R"(
+OpCapability StorageImageReadWithoutFormat
+OpCapability TileImageColorReadAccessEXT
+OpExtension "SPV_EXT_shader_tile_image"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
+                                         SPV_ENV_UNIVERSAL_1_5, "GLSL450", decl)
+                          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected Result Type to be int or float scalar or vector type"));
+}
+
+TEST_F(ValidateImage, ColorAttachmentReadEXTWrongSampledType) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_tid_0002 %uniform_image_f32_tid_0002
+%res1 = OpColorAttachmentReadEXT %f64 %img
+)";
+
+  const std::string decl = R"(
+%type_image_f32_tid_0002 = OpTypeImage %f32 TileImageDataEXT 0 0 0 2 Unknown
+%ptr_image_f32_tid_0002 = OpTypePointer UniformConstant %type_image_f32_tid_0002
+%uniform_image_f32_tid_0002 = OpVariable %ptr_image_f32_tid_0002 UniformConstant
+)";
+
+  const std::string extra = R"(
+OpCapability StorageImageReadWithoutFormat
+OpCapability TileImageColorReadAccessEXT
+OpExtension "SPV_EXT_shader_tile_image"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
+                                         SPV_ENV_UNIVERSAL_1_5, "GLSL450", decl)
+                          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Image 'Sampled Type' to be the same as "
+                        "Result Type components"));
+}
+
+TEST_F(ValidateImage, ColorAttachmentReadEXTWrongAttachment) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_0002
+%res1 = OpColorAttachmentReadEXT %f32 %img
+)";
+
+  const std::string decl = R"(
+%ptr_image_f32_0002 = OpTypePointer UniformConstant %type_image_f32_2d_0002
+%uniform_image_f32_0002 = OpVariable %ptr_image_f32_0002 UniformConstant
+)";
+
+  const std::string extra = R"(
+OpCapability StorageImageReadWithoutFormat
+OpCapability TileImageColorReadAccessEXT
+OpExtension "SPV_EXT_shader_tile_image"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
+                                         SPV_ENV_UNIVERSAL_1_5, "GLSL450", decl)
+                          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Image 'Dim' must be TileImageDataEXT"));
+}
+
+TEST_F(ValidateImage, ColorAttachmentReadEXTWrongSample) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_tid_0002 %uniform_image_f32_tid_0002
+%res1 = OpColorAttachmentReadEXT %f32 %img %f32_0
+)";
+
+  const std::string decl = R"(
+%type_image_f32_tid_0002 = OpTypeImage %f32 TileImageDataEXT 0 0 0 2 Unknown
+%ptr_image_f32_tid_0002 = OpTypePointer UniformConstant %type_image_f32_tid_0002
+%uniform_image_f32_tid_0002 = OpVariable %ptr_image_f32_tid_0002 UniformConstant
+)";
+
+  const std::string extra = R"(
+OpCapability StorageImageReadWithoutFormat
+OpCapability TileImageColorReadAccessEXT
+OpExtension "SPV_EXT_shader_tile_image"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
+                                         SPV_ENV_UNIVERSAL_1_5, "GLSL450", decl)
+                          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Sample to be a 32-bit int scalar"));
+}
+
+TEST_F(ValidateImage, ColorAttachmentReadEXTWrongExecutionModel) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_tid_0002 %uniform_image_f32_tid_0002
+%res1 = OpColorAttachmentReadEXT %f32 %img
+)";
+
+  const std::string decl = R"(
+%type_image_f32_tid_0002 = OpTypeImage %f32 TileImageDataEXT 0 0 0 2 Unknown
+%ptr_image_f32_tid_0002 = OpTypePointer UniformConstant %type_image_f32_tid_0002
+%uniform_image_f32_tid_0002 = OpVariable %ptr_image_f32_tid_0002 UniformConstant
+)";
+
+  const std::string extra = R"(
+OpCapability StorageImageReadWithoutFormat
+OpCapability TileImageColorReadAccessEXT
+OpExtension "SPV_EXT_shader_tile_image"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra, "Vertex", "",
+                                         SPV_ENV_UNIVERSAL_1_5, "GLSL450", decl)
+                          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("ColorAttachmentReadEXT requires Fragment execution model"));
+}
+
+TEST_F(ValidateImage, DepthAttachmentReadEXTWrongResultType) {
+  const std::string body = R"(
+%res1 = OpDepthAttachmentReadEXT %s32
+)";
+
+  const std::string extra = R"(
+OpCapability StorageImageReadWithoutFormat
+OpCapability TileImageDepthReadAccessEXT
+OpExtension "SPV_EXT_shader_tile_image"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
+                                         SPV_ENV_UNIVERSAL_1_5, "GLSL450", "")
+                          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected Result Type to be a 32-bit floating-point type scalar"));
+}
+
+TEST_F(ValidateImage, DepthAttachmentReadEXTWrongSample) {
+  const std::string body = R"(
+%res1 = OpDepthAttachmentReadEXT %f32 %f32_0
+)";
+
+  const std::string extra = R"(
+OpCapability StorageImageReadWithoutFormat
+OpCapability TileImageDepthReadAccessEXT
+OpExtension "SPV_EXT_shader_tile_image"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
+                                         SPV_ENV_UNIVERSAL_1_5, "GLSL450", "")
+                          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Sample to be a 32-bit int scalar"));
+}
+
+TEST_F(ValidateImage, DepthAttachmentReadEXTWrongExecutionModel) {
+  const std::string body = R"(
+%res1 = OpDepthAttachmentReadEXT %f32
+)";
+
+  const std::string extra = R"(
+OpCapability StorageImageReadWithoutFormat
+OpCapability TileImageDepthReadAccessEXT
+OpExtension "SPV_EXT_shader_tile_image"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra, "Vertex", "",
+                                         SPV_ENV_UNIVERSAL_1_5, "GLSL450", "")
+                          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("DepthAttachmentReadEXT requires Fragment execution model"));
+}
+
+TEST_F(ValidateImage, StencilAttachmentReadEXTWrongResultType) {
+  const std::string body = R"(
+%res1 = OpStencilAttachmentReadEXT %f32
+)";
+
+  const std::string extra = R"(
+OpCapability StorageImageReadWithoutFormat
+OpCapability TileImageStencilReadAccessEXT
+OpExtension "SPV_EXT_shader_tile_image"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
+                                         SPV_ENV_UNIVERSAL_1_5, "GLSL450", "")
+                          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Result Type to be a 32-bit integer type scalar"));
+}
+
+TEST_F(ValidateImage, StencilAttachmentReadEXTWrongSample) {
+  const std::string body = R"(
+%res1 = OpStencilAttachmentReadEXT %u32 %f32_0
+)";
+
+  const std::string extra = R"(
+OpCapability StorageImageReadWithoutFormat
+OpCapability TileImageStencilReadAccessEXT
+OpExtension "SPV_EXT_shader_tile_image"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
+                                         SPV_ENV_UNIVERSAL_1_5, "GLSL450", "")
+                          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Sample to be a 32-bit int scalar"));
+}
+
+TEST_F(ValidateImage, StencilAttachmentReadEXTWrongExecutionModel) {
+  const std::string body = R"(
+%res1 = OpStencilAttachmentReadEXT %u32
+)";
+
+  const std::string extra = R"(
+OpCapability StorageImageReadWithoutFormat
+OpCapability TileImageStencilReadAccessEXT
+OpExtension "SPV_EXT_shader_tile_image"
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, extra, "Vertex", "",
+                                         SPV_ENV_UNIVERSAL_1_5, "GLSL450", "")
+                          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("StencilAttachmentReadEXT requires Fragment execution model"));
+}
+
 TEST_F(ValidateImage, TypeImage_OpenCL_Sampled0_OK) {
   const std::string code = GetKernelHeader() + R"(
 %img_type = OpTypeImage %void 2D 0 0 0 0 Unknown ReadOnly
@@ -1412,14 +1687,14 @@
 
 TEST_F(ValidateImage, ImageTexelPointerImageNotResultTypePointer) {
   const std::string body = R"(
-%texel_ptr = OpImageTexelPointer %ptr_Image_u32 %type_image_f32_buffer_0002_r32ui %u32_0 %u32_0
+%texel_ptr = OpImageTexelPointer %ptr_Image_u32 %type_image_f32_buffer_0002_r32f %u32_0 %u32_0
 %sum = OpAtomicIAdd %u32 %texel_ptr %u32_1 %u32_0 %u32_1
 )";
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Operand '148[%148]' cannot be a "
+              HasSubstr("Operand '157[%157]' cannot be a "
                         "type"));
 }
 
@@ -1465,14 +1740,15 @@
 
 TEST_F(ValidateImage, ImageTexelPointerImageCoordTypeBad) {
   const std::string body = R"(
-%texel_ptr = OpImageTexelPointer %ptr_Image_f32 %private_image_f32_buffer_0002_r32ui %f32_0 %f32_0
+%texel_ptr = OpImageTexelPointer %ptr_Image_f32 %private_image_f32_buffer_0002_r32f %f32_0 %f32_0
 %sum = OpAtomicIAdd %f32 %texel_ptr %f32_1 %f32_0 %f32_1
 )";
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected Coordinate to be integer scalar or vector"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Coordinate to be a 32-bit integer scalar or vector"));
 }
 
 TEST_F(ValidateImage, ImageTexelPointerImageCoordSizeBad) {
@@ -1533,7 +1809,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -1629,8 +1906,9 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected Coordinate to be float scalar or vector"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Coordinate to be a 32-bit float scalar or vector"));
 }
 
 TEST_F(ValidateImage, SampleImplicitLodCoordinateSizeTooSmall) {
@@ -1667,7 +1945,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -1792,7 +2071,22 @@
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected Coordinate to be float scalar or vector"));
+              HasSubstr("Expected Coordinate to be a 32-bit integer or float "
+                        "scalar or vector"));
+}
+
+TEST_F(ValidateImage, SampleExplicitLodWrongCoordinateType16Bit) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
+%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f16vec2_00 Lod %f32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Coordinate to be a 32-bit scalar or vector"));
 }
 
 TEST_F(ValidateImage, SampleExplicitLodCoordinateSizeTooSmall) {
@@ -1867,9 +2161,25 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Image Operand Lod to be a 32-bit float scalar when "
+                "used with ExplicitLod"));
+}
+
+TEST_F(ValidateImage, LodWithHalf) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
+%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec2_00 Lod %f16_0
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected Image Operand Lod to be float scalar when "
-                        "used with ExplicitLod"));
+              HasSubstr("Expected Image Operand Lod to be a 32-bit float "
+                        "scalar when used with ExplicitLod"));
 }
 
 TEST_F(ValidateImage, LodWrongDim) {
@@ -1886,6 +2196,18 @@
                         "2D, 3D or Cube"));
 }
 
+TEST_F(ValidateImage, LodWrongMultisample) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011
+%res1 = OpImageFetch %f32vec4 %img %u32vec2_01 Lod|Sample %u32_1 %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Image Operand Lod requires 'MS' parameter to be 0"));
+}
+
 TEST_F(ValidateImage, MinLodIncompatible) {
   const std::string body = R"(
 %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
@@ -1943,8 +2265,24 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected Image Operand Bias to be float scalar"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Image Operand Bias to be a 32-bit float scalar"));
+}
+
+TEST_F(ValidateImage, SampleImplicitLodBias16Bit) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
+%res2 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh Bias %f16_0
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Image Operand Bias to be a 32-bit float scalar"));
 }
 
 TEST_F(ValidateImage, SampleImplicitLodBiasWrongDim) {
@@ -1972,9 +2310,10 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected both Image Operand Grad ids to be float "
-                        "scalars or vectors"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected both Image Operand Grad ids to be 32-bit float "
+                "scalars or vectors"));
 }
 
 TEST_F(ValidateImage, SampleExplicitLodGradDyWrongType) {
@@ -1987,9 +2326,25 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected both Image Operand Grad ids to be 32-bit float "
+                "scalars or vectors"));
+}
+
+TEST_F(ValidateImage, SampleExplicitLodGrad16Bit) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler
+%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec4_0000 Grad %f16vec2_00 %f32vec2_00
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected both Image Operand Grad ids to be float "
-                        "scalars or vectors"));
+              HasSubstr("Expected both Image Operand Grad ids to be 32-bit "
+                        "float scalars or vectors"));
 }
 
 TEST_F(ValidateImage, SampleExplicitLodGradDxWrongSize) {
@@ -2050,10 +2405,24 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Expected Image Operand ConstOffset to be int scalar or vector"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Image Operand ConstOffset to be a 32-bit int "
+                        "scalar or vector"));
+}
+
+TEST_F(ValidateImage, SampleImplicitLodConstOffset16Bit) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
+%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_00 ConstOffset %u16_0
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Image Operand ConstOffset to be a 32-bit int "
+                        "scalar or vector"));
 }
 
 TEST_F(ValidateImage, SampleImplicitLodConstOffsetWrongSize) {
@@ -2114,7 +2483,24 @@
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("Expected Image Operand Offset to be int scalar or vector"));
+      HasSubstr(
+          "Expected Image Operand Offset to be a 32-bit int scalar or vector"));
+}
+
+TEST_F(ValidateImage, SampleImplicitLodOffset16Bit) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
+%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Offset %u16_0
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected Image Operand Offset to be a 32-bit int scalar or vector"));
 }
 
 TEST_F(ValidateImage, SampleImplicitLodOffsetWrongSize) {
@@ -2144,13 +2530,26 @@
   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-Offset-04663"));
+  EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-RuntimeSpirv-Offset-10213"));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Image Operand Offset can only be used with "
                         "OpImage*Gather operations"));
 }
 
+TEST_F(ValidateImage, SampleImplicitLodVulkanOffsetMaintenance8) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
+%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Offset %s32vec2_01
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "", "Fragment", "", SPV_ENV_VULKAN_1_0).c_str());
+  spvValidatorOptionsSetAllowOffsetTextureOperand(getValidatorOptions(), true);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
 TEST_F(ValidateImage, SampleImplicitLodVulkanOffsetWrongBeforeLegalization) {
   const std::string body = R"(
 %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
@@ -2208,8 +2607,24 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected Image Operand MinLod to be float scalar"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Image Operand MinLod to be a 32-bit float scalar"));
+}
+
+TEST_F(ValidateImage, SampleImplicitLodMinLod16Bit) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler
+%res1 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 MinLod %f16_0
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Image Operand MinLod to be a 32-bit float scalar"));
 }
 
 TEST_F(ValidateImage, SampleImplicitLodMinLodWrongDim) {
@@ -2246,7 +2661,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -2263,6 +2679,20 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateImage, GradWrongMultisample) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0011 %uniform_image_f32_2d_0011
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_2d_0011 %img %sampler
+%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec2_00 Grad %f32vec2_01 %f32vec2_01
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Sampling operation is invalid for multisample image"));
+}
+
 TEST_F(ValidateImage, SampleProjExplicitLodWrongResultType) {
   const std::string body = R"(
 %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
@@ -2355,8 +2785,9 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected Coordinate to be float scalar or vector"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Coordinate to be a 32-bit float scalar or vector"));
 }
 
 TEST_F(ValidateImage, SampleProjExplicitLodCoordinateSizeTooSmall) {
@@ -2394,7 +2825,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -2490,8 +2922,9 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected Coordinate to be float scalar or vector"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Coordinate to be a 32-bit float scalar or vector"));
 }
 
 TEST_F(ValidateImage, SampleProjImplicitLodCoordinateSizeTooSmall) {
@@ -2529,7 +2962,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -2615,8 +3049,9 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected Coordinate to be float scalar or vector"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Coordinate to be a 32-bit float scalar or vector"));
 }
 
 TEST_F(ValidateImage, SampleDrefImplicitLodCoordinateSizeTooSmall) {
@@ -2685,7 +3120,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -2771,8 +3207,9 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected Coordinate to be float scalar or vector"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Coordinate to be a 32-bit float scalar or vector"));
 }
 
 TEST_F(ValidateImage, SampleDrefExplicitLodCoordinateSizeTooSmall) {
@@ -2824,7 +3261,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -2910,8 +3348,9 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected Coordinate to be float scalar or vector"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Coordinate to be a 32-bit float scalar or vector"));
 }
 
 TEST_F(ValidateImage, SampleProjDrefImplicitLodCoordinateSizeTooSmall) {
@@ -2962,7 +3401,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -3048,8 +3488,9 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected Coordinate to be float scalar or vector"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Coordinate to be a 32-bit float scalar or vector"));
 }
 
 TEST_F(ValidateImage, SampleProjDrefExplicitLodCoordinateSizeTooSmall) {
@@ -3080,7 +3521,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -3097,7 +3539,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -3210,8 +3653,9 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected Coordinate to be int scalar or vector"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Coordinate to be a 32-bit integer scalar or vector"));
 }
 
 TEST_F(ValidateImage, FetchCoordinateSizeTooSmall) {
@@ -3235,9 +3679,26 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected Image Operand Lod to be int scalar when used "
-                        "with OpImageFetch"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected Image Operand Lod to be a 32-bit int scalar when used "
+          "with OpImageFetch"));
+}
+
+TEST_F(ValidateImage, FetchLod16Int) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
+%res1 = OpImageFetch %f32vec4 %img %u32vec2_01 Lod %u16_0
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected Image Operand Lod to be a 32-bit int scalar when used "
+          "with OpImageFetch"));
 }
 
 TEST_F(ValidateImage, FetchMultisampledMissingSample) {
@@ -3271,7 +3732,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -3367,8 +3829,9 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected Coordinate to be float scalar or vector"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Coordinate to be a 32-bit float scalar or vector"));
 }
 
 TEST_F(ValidateImage, GatherCoordinateSizeTooSmall) {
@@ -3508,7 +3971,7 @@
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Expected Image Operand ConstOffsets array components "
-                        "to be int vectors of size 2"));
+                        "to be a 32-bit int vectors of size 2"));
 }
 
 TEST_F(ValidateImage, GatherConstOffsetsArrayVectorWrongSize) {
@@ -3523,7 +3986,22 @@
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Expected Image Operand ConstOffsets array components "
-                        "to be int vectors of size 2"));
+                        "to be a 32-bit int vectors of size 2"));
+}
+
+TEST_F(ValidateImage, GatherConstOffsetsArrayVector16Bit) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
+%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %u32_1 ConstOffsets %const_offsets4f16
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Image Operand ConstOffsets array components "
+                        "to be a 32-bit int vectors of size 2"));
 }
 
 TEST_F(ValidateImage, GatherConstOffsetsArrayNotConst) {
@@ -3575,7 +4053,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -3675,7 +4154,7 @@
 TEST_F(ValidateImage, ReadSuccess4) {
   const std::string body = R"(
 %img = OpLoad %type_image_f32_spd_0002 %uniform_image_f32_spd_0002
-%res1 = OpImageRead %f32vec4 %img %u32vec2_01
+%res1 = OpImageRead %f32vec4 %img %u32vec2_00
 )";
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
@@ -3842,7 +4321,7 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(ValidateImage, ReadWrongCoordinateType) {
+TEST_F(ValidateImage, ReadFloatCoordinateType) {
   const std::string body = R"(
 %img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002
 %res1 = OpImageRead %u32vec4 %img %f32vec2_00
@@ -3850,9 +4329,20 @@
 
   const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n";
   CompileSuccessfully(GenerateShaderCode(body, extra).c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateImage, ReadWrongCoordinateType) {
+  const std::string body = R"(
+%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002
+%res1 = OpImageRead %u32vec4 %img %f16vec2_00
+)";
+
+  const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n";
+  CompileSuccessfully(GenerateShaderCode(body, extra).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected Coordinate to be int scalar or vector"));
+              HasSubstr("Expected Coordinate to be a 32-bit scalar or vector"));
 }
 
 TEST_F(ValidateImage, ReadCoordinateSizeTooSmall) {
@@ -4016,7 +4506,7 @@
               HasSubstr("Expected Image 'Sampled' parameter to be 0 or 2"));
 }
 
-TEST_F(ValidateImage, WriteWrongCoordinateType) {
+TEST_F(ValidateImage, WriteFloatCoordinateType) {
   const std::string body = R"(
 %img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002
 OpImageWrite %img %f32vec2_00 %u32vec4_0123
@@ -4024,9 +4514,20 @@
 
   const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n";
   CompileSuccessfully(GenerateShaderCode(body, extra).c_str());
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateImage, WriteWrongCoordinateType) {
+  const std::string body = R"(
+%img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002
+OpImageWrite %img %f16vec2_00 %u32vec4_0123
+)";
+
+  const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n";
+  CompileSuccessfully(GenerateShaderCode(body, extra).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected Coordinate to be int scalar or vector"));
+              HasSubstr("Expected Coordinate to be a 32-bit scalar or vector"));
 }
 
 TEST_F(ValidateImage, WriteCoordinateSizeTooSmall) {
@@ -4115,8 +4616,34 @@
                                          declarations)
                           .c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected Image Operand Sample to be int scalar"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Image Operand Sample to be a 32-bit int scalar"));
+}
+
+TEST_F(ValidateImage, WriteSample16Bit) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0012 %uniform_image_f32_2d_0012
+OpImageWrite %img %u32vec2_01 %f32vec4_0000 Sample %u16_0
+)";
+
+  const std::string extra = R"(
+    OpCapability StorageImageWriteWithoutFormat
+    OpCapability StorageImageMultisample
+    )";
+  const std::string declarations = R"(
+%type_image_f32_2d_0012 = OpTypeImage %f32 2D 0 0 1 2 Unknown
+%ptr_image_f32_2d_0012 = OpTypePointer UniformConstant %type_image_f32_2d_0012
+%uniform_image_f32_2d_0012 = OpVariable %ptr_image_f32_2d_0012 UniformConstant
+    )";
+  CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
+                                         SPV_ENV_UNIVERSAL_1_0, "GLSL450",
+                                         declarations)
+                          .c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Image Operand Sample to be a 32-bit int scalar"));
 }
 
 TEST_F(ValidateImage, WriteSampleNotMultisampled) {
@@ -4421,7 +4948,19 @@
   CompileSuccessfully(GenerateKernelCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected Level of Detail to be int scalar"));
+              HasSubstr("Expected Level of Detail to be a 32-bit int scalar"));
+}
+
+TEST_F(ValidateImage, QuerySizeLodWrong16Bit) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
+%res1 = OpImageQuerySizeLod %u32vec2 %img %u16_0
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Level of Detail to be a 32-bit int scalar"));
 }
 
 TEST_F(ValidateImage, QuerySizeSuccess) {
@@ -4520,13 +5059,27 @@
 %sampler = OpLoad %type_sampler %uniform_sampler
 %simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
 %res1 = OpImageQueryLod %f32vec2 %simg %f32vec2_hh
-%res2 = OpImageQueryLod %f32vec2 %simg %u32vec2_01
 )";
 
   CompileSuccessfully(GenerateKernelCode(body).c_str());
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateImage, QueryLodNonFloatKernel) {
+  const std::string body = R"(
+%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
+%sampler = OpLoad %type_sampler %uniform_sampler
+%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler
+%res1 = OpImageQueryLod %f32vec2 %simg %u32vec2_01
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Coordinate to be a 32-bit float scalar or vector"));
+}
+
 TEST_F(ValidateImage, QueryLodSuccessShader) {
   const std::string body = R"(
 %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001
@@ -4604,8 +5157,9 @@
 
   CompileSuccessfully(GenerateShaderCode(body).c_str());
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Expected Coordinate to be float scalar or vector"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Coordinate to be a 32-bit float scalar or vector"));
 }
 
 TEST_F(ValidateImage, QueryLodCoordinateSizeTooSmall) {
@@ -5011,7 +5565,7 @@
 TEST_F(ValidateImage, ReadSubpassDataWrongExecutionModel) {
   const std::string body = R"(
 %img = OpLoad %type_image_f32_spd_0002 %uniform_image_f32_spd_0002
-%res1 = OpImageRead %f32vec4 %img %u32vec2_01
+%res1 = OpImageRead %f32vec4 %img %u32vec2_00
 )";
 
   const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n";
@@ -5041,7 +5595,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -5169,7 +5724,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -5263,7 +5819,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -5480,7 +6037,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -5621,7 +6179,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -5638,7 +6197,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -5657,7 +6217,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(
@@ -5679,7 +6240,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
@@ -5700,7 +6262,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -5719,7 +6282,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
@@ -5740,7 +6304,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
@@ -5761,7 +6326,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(
@@ -5784,7 +6350,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -5801,7 +6368,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_ERROR_INVALID_DATA,
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(
@@ -5824,7 +6392,8 @@
 )";
   CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "",
                                          SPV_ENV_UNIVERSAL_1_3, "VulkanKHR")
-                          .c_str());
+                          .c_str(),
+                      SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
@@ -5967,7 +6536,7 @@
   EXPECT_THAT(getDiagnosticString(), Eq(""));
 }
 
-TEST_F(ValidateImage, ZeroExtendScalarSIntTexelV14Good) {
+TEST_F(ValidateImage, ZeroExtendScalarSIntTexelV14) {
   // Zeroed int sampled type
   const std::string body = R"(
 %img = OpLoad %type_image_s32_2d_0002 %uniform_image_s32_2d_0002
@@ -5978,8 +6547,11 @@
   CompileSuccessfully(
       GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_4),
       SPV_ENV_UNIVERSAL_1_4);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
-  EXPECT_THAT(getDiagnosticString(), Eq(""));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Using ZeroExtend, but result type is a signed integer type."));
 }
 
 TEST_F(ValidateImage, ZeroExtendScalarVectorUIntTexelV14Good) {
@@ -5996,7 +6568,7 @@
   EXPECT_THAT(getDiagnosticString(), Eq(""));
 }
 
-TEST_F(ValidateImage, ZeroExtendVectorSIntTexelV14Good) {
+TEST_F(ValidateImage, ZeroExtendVectorSIntTexelV14) {
   const std::string body = R"(
 %img = OpLoad %type_image_s32_2d_0002 %uniform_image_s32_2d_0002
 %res1 = OpImageRead %s32vec4 %img %u32vec2_01 ZeroExtend
@@ -6006,8 +6578,11 @@
   CompileSuccessfully(
       GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_4),
       SPV_ENV_UNIVERSAL_1_4);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
-  EXPECT_THAT(getDiagnosticString(), Eq(""));
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Using ZeroExtend, but result type is a signed integer type."));
 }
 
 TEST_F(ValidateImage, ReadLodAMDSuccess1) {
@@ -6407,20 +6982,13 @@
 }
 
 TEST_F(ValidateImage, ImageTexelPointerR32fSuccessVulkan) {
-  const std::string& declarations = R"(
-%type_image_f32_buffer_0002_r32f = OpTypeImage %f32 Buffer 0 0 0 2 R32f
-%ptr_image_f32_buffer_0002_r32f = OpTypePointer Private %type_image_f32_buffer_0002_r32f
-%private_image_f32_buffer_0002_r32f = OpVariable %ptr_image_f32_buffer_0002_r32f Private
-)";
-
   const std::string body = R"(
 %texel_ptr = OpImageTexelPointer %ptr_Image_f32 %private_image_f32_buffer_0002_r32f %u32_0 %u32_0
 )";
 
   spv_target_env env = SPV_ENV_VULKAN_1_0;
   CompileSuccessfully(
-      GenerateShaderCode(body, "", "Fragment", "", env, "GLSL450", declarations)
-          .c_str(),
+      GenerateShaderCode(body, "", "Fragment", "", env, "GLSL450").c_str(),
       env);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(env));
 }
@@ -6447,19 +7015,19 @@
               AnyVUID("VUID-StandaloneSpirv-OpImageTexelPointer-04658"));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Expected the Image Format in Image to be R64i, R64ui, "
-                        "R32f, R32i, or R32ui for Vulkan environment"));
+                        "R32f, R32i, or R32ui for Vulkan environment using "
+                        "OpImageTexelPointer"));
 }
 
 TEST_F(ValidateImage, ImageTexelPointerRgba16fVulkan) {
   const std::string& declarations = R"(
-%type_image_s32_buffer_0002_rgba16f = OpTypeImage %s32 Buffer 0 0 0 2 Rgba16f
-%ptr_Image_s32 = OpTypePointer Image %s32
-%ptr_image_s32_buffer_0002_rgba16f = OpTypePointer Private %type_image_s32_buffer_0002_rgba16f
-%private_image_s32_buffer_0002_rgba16f = OpVariable %ptr_image_s32_buffer_0002_rgba16f Private
+%type_image_f32_buffer_0002_rgba16f = OpTypeImage %f32 Buffer 0 0 0 2 Rgba16f
+%ptr_image_f32_buffer_0002_rgba16f = OpTypePointer Private %type_image_f32_buffer_0002_rgba16f
+%private_image_f32_buffer_0002_rgba16f = OpVariable %ptr_image_f32_buffer_0002_rgba16f Private
 )";
 
   const std::string body = R"(
-%texel_ptr = OpImageTexelPointer %ptr_Image_s32 %private_image_s32_buffer_0002_rgba16f %u32_0 %u32_0
+%texel_ptr = OpImageTexelPointer %ptr_Image_f32 %private_image_f32_buffer_0002_rgba16f %u32_0 %u32_0
 )";
 
   spv_target_env env = SPV_ENV_VULKAN_1_0;
@@ -6472,7 +7040,8 @@
               AnyVUID("VUID-StandaloneSpirv-OpImageTexelPointer-04658"));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Expected the Image Format in Image to be R64i, R64ui, "
-                        "R32f, R32i, or R32ui for Vulkan environment"));
+                        "R32f, R32i, or R32ui for Vulkan environment using "
+                        "OpImageTexelPointer"));
 }
 
 TEST_F(ValidateImage, ImageExecutionModeLimitationNoMode) {
@@ -10897,6 +11466,543 @@
               HasSubstr("Expected Result Type to be a pointer"));
 }
 
+TEST_F(ValidateImage, TileImageNotFragment) {
+  const std::string body = R"(
+    OpCapability Shader
+    OpCapability TileImageColorReadAccessEXT
+    OpExtension "SPV_EXT_shader_tile_image"
+    OpMemoryModel Logical GLSL450
+    OpEntryPoint GLCompute %main "main"
+    OpExecutionMode %main LocalSize 1 1 1
+    %void = OpTypeVoid
+    %func = OpTypeFunction %void
+    %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %ptr = OpTypePointer TileImageEXT %v4float
+    %var = OpVariable %ptr TileImageEXT
+    %main = OpFunction %void None %func
+    %label = OpLabel
+    %val = OpLoad %v4float %var
+    OpReturn
+    OpFunctionEnd
+  )";
+
+  CompileSuccessfully(body.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-08720"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "TileImageEXT Storage Class is limited to Fragment execution model"));
+}
+
+TEST_F(ValidateImage, SubpassDataNonZero) {
+  const std::string body = R"(
+               OpCapability Shader
+               OpCapability InputAttachment
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %outColor %inputColor
+               OpExecutionMode %main OriginUpperLeft
+               OpDecorate %outColor Location 0
+               OpDecorate %inputColor Binding 0
+               OpDecorate %inputColor DescriptorSet 0
+               OpDecorate %inputColor InputAttachmentIndex 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+   %outColor = OpVariable %_ptr_Output_v4float Output
+         %11 = OpTypeImage %float SubpassData 0 0 0 2 Unknown
+%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
+ %inputColor = OpVariable %_ptr_UniformConstant_11 UniformConstant
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_1 = OpConstant %int 1
+      %v2int = OpTypeVector %int 2
+    %v2int_1 = OpConstantComposite %v2int %int_1 %int_1
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+         %14 = OpLoad %11 %inputColor
+         %19 = OpImageRead %v4float %14 %v2int_1
+               OpStore %outColor %19
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(body.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-SubpassData-04660"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Coordinate for a SubpassData image to be a "
+                        "OpConstantComposite of (0,0) or OpConstantNull"));
+}
+
+TEST_F(ValidateImage, SubpassDataNonConstant) {
+  const std::string body = R"(
+               OpCapability Shader
+               OpCapability InputAttachment
+          %2 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %_ %outColor %inputColor
+               OpExecutionMode %main OriginUpperLeft
+               OpDecorate %UBO Block
+               OpMemberDecorate %UBO 0 Offset 0
+               OpDecorate %_ Binding 0
+               OpDecorate %_ DescriptorSet 0
+               OpDecorate %outColor Location 0
+               OpDecorate %inputColor Binding 0
+               OpDecorate %inputColor DescriptorSet 0
+               OpDecorate %inputColor InputAttachmentIndex 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %v2uint = OpTypeVector %uint 2
+%_ptr_Function_v2uint = OpTypePointer Function %v2uint
+        %UBO = OpTypeStruct %v2uint
+%_ptr_Uniform_UBO = OpTypePointer Uniform %UBO
+          %_ = OpVariable %_ptr_Uniform_UBO Uniform
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_Uniform_v2uint = OpTypePointer Uniform %v2uint
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+   %outColor = OpVariable %_ptr_Output_v4float Output
+         %23 = OpTypeImage %float SubpassData 0 0 0 2 Unknown
+%_ptr_UniformConstant_23 = OpTypePointer UniformConstant %23
+ %inputColor = OpVariable %_ptr_UniformConstant_23 UniformConstant
+      %v2int = OpTypeVector %int 2
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+          %x = OpVariable %_ptr_Function_v2uint Function
+         %17 = OpAccessChain %_ptr_Uniform_v2uint %_ %int_0
+         %18 = OpLoad %v2uint %17
+         %26 = OpLoad %23 %inputColor
+         %29 = OpImageRead %v4float %26 %18
+               OpStore %outColor %29
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(body.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-SubpassData-04660"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Coordinate for a SubpassData image to be a "
+                        "OpConstantComposite of (0,0) or OpConstantNull"));
+}
+
+// https://gitlab.khronos.org/spirv/SPIR-V/-/issues/766
+TEST_F(ValidateImage, WriteSignedExtendGood) {
+  const std::string body = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %ii2D %ic2D
+               OpExecutionMode %main OriginUpperLeft
+               OpDecorate %ii2D Binding 0
+               OpDecorate %ii2D DescriptorSet 0
+               OpDecorate %ic2D Flat
+               OpDecorate %ic2D Location 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+          %8 = OpTypeImage %int 2D 0 0 0 2 R32i
+%_ptr_UniformConstant_8 = OpTypePointer UniformConstant %8
+       %ii2D = OpVariable %_ptr_UniformConstant_8 UniformConstant
+      %v2int = OpTypeVector %int 2
+      %int_0 = OpConstant %int 0
+         %14 = OpConstantComposite %v2int %int_0 %int_0
+      %v4int = OpTypeVector %int 4
+         %16 = OpConstantComposite %v4int %int_0 %int_0 %int_0 %int_0
+%_ptr_Input_v2int = OpTypePointer Input %v2int
+       %ic2D = OpVariable %_ptr_Input_v2int Input
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+         %11 = OpLoad %8 %ii2D
+               OpImageWrite %11 %14 %16 SignExtend
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateImage, TypeImageVulkanStorageNotFloat) {
+  const std::string code = GetShaderHeader() + R"(
+%img_type = OpTypeImage %f32 2D 0 0 0 2 R32i
+)" + TrivialMain();
+
+  CompileSuccessfully(code.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Image-04965"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Image Format type does not match Sample Type operand (float)"));
+}
+
+TEST_F(ValidateImage, TypeImageVulkanStorageNotInt) {
+  const std::string code = GetShaderHeader() + R"(
+%img_type = OpTypeImage %s32 2D 0 0 0 2 R32f
+)" + TrivialMain();
+
+  CompileSuccessfully(code.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Image-04965"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Image Format type does not match Sample Type operand (integer)"));
+}
+
+TEST_F(ValidateImage, TypeImageVulkanStorageNot64Width) {
+  const std::string code = GetShaderHeader(
+                               "OpCapability Int64ImageEXT\nOpExtension "
+                               "\"SPV_EXT_shader_image_int64\"\n") +
+                           R"(
+%img_type = OpTypeImage %s64 2D 0 0 0 2 R32i
+)" + TrivialMain();
+
+  CompileSuccessfully(code.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Image-04965"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Image Format width does not match Sample Type operand "
+                        "(bit width of 64)"));
+}
+
+TEST_F(ValidateImage, TypeImageVulkanStorageNot32Width) {
+  const std::string code = GetShaderHeader(
+                               "OpCapability Int64ImageEXT\nOpExtension "
+                               "\"SPV_EXT_shader_image_int64\"\n") +
+                           R"(
+%img_type = OpTypeImage %s32 2D 0 0 0 2 R64i
+)" + TrivialMain();
+
+  CompileSuccessfully(code.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Image-04965"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Image Format width does not match Sample Type operand "
+                        "(bit width of 32)"));
+}
+
+TEST_F(ValidateImage, TypeImageVulkanStorageNot64Signedness) {
+  const std::string code = R"(
+    OpCapability Shader
+    OpCapability Int64
+    OpCapability Int64ImageEXT
+    OpExtension "SPV_EXT_shader_image_int64"
+    OpMemoryModel Logical GLSL450
+    OpEntryPoint Fragment %main "main" %image_var
+    OpExecutionMode %main OriginUpperLeft
+    OpDecorate %image_var Location 0
+     %void = OpTypeVoid
+     %func = OpTypeFunction %void
+      %u32 = OpTypeInt 32 0
+      %s64 = OpTypeInt 64 1
+    %u32_0 = OpConstant %u32 0
+    %u32_1 = OpConstant %u32 1
+  %u32vec2 = OpTypeVector %u32 2
+  %s64vec4 = OpTypeVector %s64 4
+%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1
+ %img_type = OpTypeImage %s64 2D 0 0 0 1 R64ui
+%ptr_image = OpTypePointer Input %img_type
+%image_var = OpVariable %ptr_image Input
+     %main = OpFunction %void None %func
+    %label = OpLabel
+      %img = OpLoad %img_type %image_var
+     %res1 = OpImageFetch %s64vec4 %img %u32vec2_01
+    OpReturn
+    OpFunctionEnd
+)";
+
+  CompileSuccessfully(code.c_str(), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Image-04965"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Image Format signedness (unsigned) does not match "
+                        "Sample Type operand"));
+}
+
+TEST_F(ValidateImage, TypeImageVulkanStorageNot32Signedness) {
+  const std::string code = R"(
+    OpCapability Shader
+    OpMemoryModel Logical GLSL450
+    OpEntryPoint Fragment %main "main" %image_var
+    OpExecutionMode %main OriginUpperLeft
+    OpDecorate %image_var Location 0
+     %void = OpTypeVoid
+     %func = OpTypeFunction %void
+      %u32 = OpTypeInt 32 0
+    %u32_0 = OpConstant %u32 0
+    %u32_1 = OpConstant %u32 1
+  %u32vec2 = OpTypeVector %u32 2
+  %u32vec4 = OpTypeVector %u32 4
+%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1
+ %img_type = OpTypeImage %u32 2D 0 0 0 1 R32i
+%ptr_image = OpTypePointer Input %img_type
+%image_var = OpVariable %ptr_image Input
+     %main = OpFunction %void None %func
+    %label = OpLabel
+      %img = OpLoad %img_type %image_var
+     %res1 = OpImageFetch %u32vec4 %img %u32vec2_01
+    OpReturn
+    OpFunctionEnd
+)";
+
+  CompileSuccessfully(code.c_str());
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Image-04965"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Image Format signedness (signed) does not match "
+                        "Sample Type operand"));
+}
+
+TEST_F(ValidateImage, TypeImageVulkanStorageSignExtendOverride) {
+  const std::string code = R"(
+    OpCapability Shader
+    OpMemoryModel Logical GLSL450
+    OpEntryPoint Fragment %main "main" %uniform_image
+    OpExecutionMode %main OriginUpperLeft
+    OpDecorate %uniform_image DescriptorSet 0
+    OpDecorate %uniform_image Binding 0
+     %void = OpTypeVoid
+     %func = OpTypeFunction %void
+      %u32 = OpTypeInt 32 0
+    %u32_0 = OpConstant %u32 0
+    %u32_1 = OpConstant %u32 1
+  %u32vec2 = OpTypeVector %u32 2
+  %u32vec4 = OpTypeVector %u32 4
+%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1
+ %img_type = OpTypeImage %u32 2D 0 0 0 1 R32i
+%ptr_image = OpTypePointer UniformConstant %img_type
+%uniform_image = OpVariable %ptr_image UniformConstant
+     %main = OpFunction %void None %func
+    %label = OpLabel
+      %img = OpLoad %img_type %uniform_image
+     %res1 = OpImageFetch %u32vec4 %img %u32vec2_01 SignExtend
+    OpReturn
+    OpFunctionEnd
+)";
+
+  CompileSuccessfully(code.c_str(), SPV_ENV_VULKAN_1_2);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateImage, TypeImageVulkanStorageUintToSint) {
+  const std::string code = R"(
+                     OpCapability Shader
+                     OpCapability StorageImageExtendedFormats
+                     OpMemoryModel Logical GLSL450
+                     OpEntryPoint GLCompute %main "main" %image_ptr
+                     OpExecutionMode %main LocalSize 1 1 1
+                     OpSource GLSL 450
+                     OpDecorate %image_ptr DescriptorSet 0
+                     OpDecorate %image_ptr Binding 0
+                     OpDecorate %image_ptr NonReadable
+%type_void         = OpTypeVoid
+%type_u32          = OpTypeInt 32 0
+%type_i32          = OpTypeInt 32 1
+%type_vec3_u32     = OpTypeVector %type_u32 3
+%type_vec4_u32     = OpTypeVector %type_u32 4
+%type_vec2_i32     = OpTypeVector %type_i32 2
+%type_fn_void      = OpTypeFunction %type_void
+%type_ptr_fn       = OpTypePointer Function %type_vec4_u32
+%type_image        = OpTypeImage %type_u32 2D 0 0 0 2 Rgba32ui
+%type_ptr_image    = OpTypePointer UniformConstant %type_image
+%image_ptr         = OpVariable %type_ptr_image UniformConstant
+%const_i32_0       = OpConstant %type_i32 0
+%const_vec2_i32_00 = OpConstantComposite %type_vec2_i32 %const_i32_0 %const_i32_0
+%main              = OpFunction %type_void None %type_fn_void
+%label             = OpLabel
+%store_location    = OpVariable %type_ptr_fn Function
+%image             = OpLoad %type_image %image_ptr
+%value             = OpImageRead %type_vec4_u32 %image %const_vec2_i32_00 SignExtend
+                     OpStore %store_location %value
+                     OpReturn
+                     OpFunctionEnd
+)";
+
+  CompileSuccessfully(code.c_str(), SPV_ENV_VULKAN_1_2);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Image-04965"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Image Format signedness (unsigned) does not match Sample Type "
+                "operand (SignExtend makes the access as signed)"));
+}
+
+TEST_F(ValidateImage, TypeImageVulkanStorageSintToUint) {
+  const std::string code = R"(
+                     OpCapability Shader
+                     OpCapability StorageImageExtendedFormats
+                     OpMemoryModel Logical GLSL450
+                     OpEntryPoint GLCompute %main "main" %image_ptr
+                     OpExecutionMode %main LocalSize 1 1 1
+                     OpSource GLSL 450
+                     OpDecorate %image_ptr DescriptorSet 0
+                     OpDecorate %image_ptr Binding 0
+                     OpDecorate %image_ptr NonReadable
+%type_void         = OpTypeVoid
+%type_u32          = OpTypeInt 32 0
+%type_i32          = OpTypeInt 32 1
+%type_vec3_u32     = OpTypeVector %type_u32 3
+%type_vec4_u32     = OpTypeVector %type_u32 4
+%type_vec2_i32     = OpTypeVector %type_i32 2
+%type_fn_void      = OpTypeFunction %type_void
+%type_ptr_fn       = OpTypePointer Function %type_vec4_u32
+%type_image        = OpTypeImage %type_u32 2D 0 0 0 2 Rgba32i
+%type_ptr_image    = OpTypePointer UniformConstant %type_image
+%image_ptr         = OpVariable %type_ptr_image UniformConstant
+%const_i32_0       = OpConstant %type_i32 0
+%const_vec2_i32_00 = OpConstantComposite %type_vec2_i32 %const_i32_0 %const_i32_0
+%main              = OpFunction %type_void None %type_fn_void
+%label             = OpLabel
+%store_location    = OpVariable %type_ptr_fn Function
+%image             = OpLoad %type_image %image_ptr
+%value             = OpImageRead %type_vec4_u32 %image %const_vec2_i32_00 ZeroExtend
+                     OpStore %store_location %value
+                     OpReturn
+                     OpFunctionEnd
+)";
+
+  CompileSuccessfully(code.c_str(), SPV_ENV_VULKAN_1_2);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Image-04965"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Image Format signedness (signed) does not match Sample Type "
+                "operand (ZeroExtend makes the access as unsigned)"));
+}
+
+TEST_F(ValidateImage, TypeImageVulkanStorageZeroExtendSigned) {
+  const std::string code = R"(
+    OpCapability Shader
+    OpMemoryModel Logical GLSL450
+    OpEntryPoint Fragment %main "main" %uniform_image
+    OpExecutionMode %main OriginUpperLeft
+    OpDecorate %uniform_image DescriptorSet 0
+    OpDecorate %uniform_image Binding 0
+     %void = OpTypeVoid
+     %func = OpTypeFunction %void
+      %i32 = OpTypeInt 32 1
+      %u32 = OpTypeInt 32 0
+    %u32_0 = OpConstant %u32 0
+    %u32_1 = OpConstant %u32 1
+  %u32vec2 = OpTypeVector %u32 2
+  %i32vec4 = OpTypeVector %i32 4
+%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1
+ %img_type = OpTypeImage %i32 2D 0 0 0 2 R32ui
+%ptr_image = OpTypePointer UniformConstant %img_type
+%uniform_image = OpVariable %ptr_image UniformConstant
+     %main = OpFunction %void None %func
+    %label = OpLabel
+      %img = OpLoad %img_type %uniform_image
+     %res1 = OpImageRead %i32vec4 %img %u32vec2_01 ZeroExtend
+       OpReturn
+    OpFunctionEnd
+)";
+
+  CompileSuccessfully(code.c_str(), SPV_ENV_VULKAN_1_2);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Using ZeroExtend, but result type is a signed integer type"));
+}
+
+TEST_F(ValidateImage, TypeImageVulkanStorageZeroExtendRedundant) {
+  // use ZeroExtend when sample type is already unsigned but still has signed
+  // format
+  const std::string code = R"(
+    OpCapability Shader
+    OpMemoryModel Logical GLSL450
+    OpEntryPoint Fragment %main "main" %uniform_image
+    OpExecutionMode %main OriginUpperLeft
+    OpDecorate %uniform_image DescriptorSet 0
+    OpDecorate %uniform_image Binding 0
+     %void = OpTypeVoid
+     %func = OpTypeFunction %void
+      %u32 = OpTypeInt 32 0
+    %u32_0 = OpConstant %u32 0
+    %u32_1 = OpConstant %u32 1
+  %u32vec2 = OpTypeVector %u32 2
+  %u32vec4 = OpTypeVector %u32 4
+%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1
+ %img_type = OpTypeImage %u32 2D 0 0 0 1 R32i
+%ptr_image = OpTypePointer UniformConstant %img_type
+%uniform_image = OpVariable %ptr_image UniformConstant
+     %main = OpFunction %void None %func
+    %label = OpLabel
+      %img = OpLoad %img_type %uniform_image
+     %res1 = OpImageFetch %u32vec4 %img %u32vec2_01 ZeroExtend
+    OpReturn
+    OpFunctionEnd
+)";
+
+  CompileSuccessfully(code.c_str(), SPV_ENV_VULKAN_1_2);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Image-04965"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Image Format signedness (signed) does not match Sample Type "
+                "operand (ZeroExtend makes the access as unsigned)"));
+}
+
+// TODO - Need to validate in ValidateImageOperands()
+TEST_F(ValidateImage, DISABLED_TypeImageVulkanStorageZeroExtendFloat) {
+  // use ZeroExtend on Float image
+  const std::string code = R"(
+            OpCapability Shader
+            OpCapability StorageImageWriteWithoutFormat
+            OpMemoryModel Logical GLSL450
+            OpEntryPoint GLCompute %main "main" %var
+            OpExecutionMode %main LocalSize 1 1 1
+            OpDecorate %var DescriptorSet 0
+            OpDecorate %var Binding 0
+            OpDecorate %var NonReadable
+    %void = OpTypeVoid
+    %func = OpTypeFunction %void
+    %int = OpTypeInt 32 1
+    %float = OpTypeFloat 32
+    %image = OpTypeImage %float 2D 0 0 0 2 Unknown
+    %ptr = OpTypePointer UniformConstant %image
+    %var = OpVariable %ptr UniformConstant
+    %v2int = OpTypeVector %int 2
+    %int_1 = OpConstant %int 1
+    %coord = OpConstantComposite %v2int %int_1 %int_1
+    %v3float = OpTypeVector %float 3
+    %float_1 = OpConstant %float 1
+%texelU3 = OpConstantComposite %v3float %float_1 %float_1 %float_1
+    %main = OpFunction %void None %func
+    %label = OpLabel
+    %load = OpLoad %image %var
+            OpImageWrite %load %coord %texelU3 ZeroExtend
+            OpReturn
+            OpFunctionEnd
+)";
+
+  CompileSuccessfully(code.c_str(), SPV_ENV_VULKAN_1_2);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Using ZeroExtend, but result type is a signed integer type."));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_interfaces_test.cpp b/test/val/val_interfaces_test.cpp
index 50f4557..72c179b 100644
--- a/test/val/val_interfaces_test.cpp
+++ b/test/val/val_interfaces_test.cpp
@@ -160,6 +160,7 @@
 OpFunctionEnd
 )";
 
+  getValidatorOptions()->relax_logical_pointer = true;
   CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3);
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(
@@ -215,9 +216,9 @@
 
   CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Non-unique OpEntryPoint interface '2[%var]' is disallowed"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("In SPIR-V 1.4 or later, non-unique OpEntryPoint "
+                        "interface '2[%var]' is disallowed"));
 }
 
 TEST_F(ValidateInterfacesTest, MissingGlobalVarSPV1p3) {
@@ -623,6 +624,185 @@
                 "at location 1"));
 }
 
+TEST_F(ValidateInterfacesTest,
+       VulkanLocationsGeometryStreamsDifferentStreamsSameLocation) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Geometry
+OpCapability GeometryStreams
+OpMemoryModel Logical GLSL450
+OpEntryPoint Geometry %main "main" %var1 %var2
+OpExecutionMode %main Triangles
+OpExecutionMode %main OutputPoints
+OpExecutionMode %main OutputVertices 1
+OpDecorate %var1 Location 1
+OpDecorate %var1 Stream 0
+OpDecorate %var2 Location 1
+OpDecorate %var2 Stream 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%ptr_output_float = OpTypePointer Output %float
+%var1 = OpVariable %ptr_output_float Output
+%var2 = OpVariable %ptr_output_float Output
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateInterfacesTest,
+       VulkanLocationsGeometryStreamsSameStreamSameLocationConflict) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Geometry
+OpCapability GeometryStreams
+OpMemoryModel Logical GLSL450
+OpEntryPoint Geometry %main "main" %var1 %var2
+OpExecutionMode %main Triangles
+OpExecutionMode %main OutputPoints
+OpExecutionMode %main OutputVertices 1
+OpDecorate %var1 Location 1
+OpDecorate %var1 Stream 1
+OpDecorate %var2 Location 1
+OpDecorate %var2 Stream 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%ptr_output_float = OpTypePointer Output %float
+%var1 = OpVariable %ptr_output_float Output
+%var2 = OpVariable %ptr_output_float Output
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpEntryPoint-08722"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Entry-point has conflicting output location assignment "
+                "at location 1"));
+}
+
+TEST_F(ValidateInterfacesTest,
+       VulkanLocationsGeometryStreamsComponentSharingAcrossStreams) {
+  // Same location and component on different streams must not conflict.
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Geometry
+OpCapability GeometryStreams
+OpMemoryModel Logical GLSL450
+OpEntryPoint Geometry %main "main" %var1 %var2
+OpExecutionMode %main Triangles
+OpExecutionMode %main OutputPoints
+OpExecutionMode %main OutputVertices 1
+OpDecorate %var1 Location 1
+OpDecorate %var1 Component 0
+OpDecorate %var1 Stream 0
+OpDecorate %var2 Location 1
+OpDecorate %var2 Component 0
+OpDecorate %var2 Stream 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%ptr_output_float = OpTypePointer Output %float
+%var1 = OpVariable %ptr_output_float Output
+%var2 = OpVariable %ptr_output_float Output
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateInterfacesTest,
+       VulkanLocationsGeometryNoStreamsCapStillConflicts) {
+  // Without the GeometryStreams capability the per-stream relaxation must not
+  // apply: a Geometry shader with two outputs at the same location still
+  // conflicts, same as any other stage.
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Geometry
+OpMemoryModel Logical GLSL450
+OpEntryPoint Geometry %main "main" %var1 %var2
+OpExecutionMode %main Triangles
+OpExecutionMode %main OutputPoints
+OpExecutionMode %main OutputVertices 1
+OpDecorate %var1 Location 1
+OpDecorate %var2 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%ptr_output_float = OpTypePointer Output %float
+%var1 = OpVariable %ptr_output_float Output
+%var2 = OpVariable %ptr_output_float Output
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpEntryPoint-08722"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Entry-point has conflicting output location assignment "
+                "at location 1"));
+}
+
+TEST_F(ValidateInterfacesTest,
+       VulkanLocationsGeometryStreamsInputStillConflicts) {
+  // The per-stream relaxation only applies to the Output storage class.
+  // Input variables in a Geometry+GeometryStreams entry point must still
+  // have unique locations.
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Geometry
+OpCapability GeometryStreams
+OpMemoryModel Logical GLSL450
+OpEntryPoint Geometry %main "main" %var1 %var2
+OpExecutionMode %main InputPoints
+OpExecutionMode %main OutputPoints
+OpExecutionMode %main OutputVertices 1
+OpDecorate %var1 Location 1
+OpDecorate %var2 Location 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%uint = OpTypeInt 32 0
+%uint_1 = OpConstant %uint 1
+%arr_float = OpTypeArray %float %uint_1
+%ptr_input_arr = OpTypePointer Input %arr_float
+%var1 = OpVariable %ptr_input_arr Input
+%var2 = OpVariable %ptr_input_arr Input
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpEntryPoint-08721"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Entry-point has conflicting input location assignment "
+                        "at location 1"));
+}
+
 TEST_F(ValidateInterfacesTest, VulkanPatchAndNonPatchOverlap) {
   const std::string text = R"(
                OpCapability Tessellation
@@ -1639,7 +1819,7 @@
           "Interface struct has no Block decoration but has BuiltIn members."));
 }
 
-TEST_F(ValidateInterfacesTest, InvalidLocationTypePointer) {
+TEST_F(ValidateInterfacesTest, InvalidLocationTypeSampler) {
   const std::string text = R"(
                OpCapability Shader
                OpMemoryModel Logical Simple
@@ -1648,14 +1828,13 @@
        %void = OpTypeVoid
           %5 = OpTypeFunction %void
       %float = OpTypeFloat 32
-%_ptr_Private_void = OpTypePointer Private %void
+      %sampler = OpTypeSampler
        %uint = OpTypeInt 32 0
 %uint_4278132784 = OpConstant %uint 4278132784
-%_arr__ptr_Private_void_uint_4278132784 = OpTypeArray %_ptr_Private_void %uint_4278132784
-%_ptr_Output__arr__ptr_Private_void_uint_4278132784 = OpTypePointer Output %_arr__ptr_Private_void_uint_4278132784
-          %2 = OpVariable %_ptr_Output__arr__ptr_Private_void_uint_4278132784 Output
-%_ptr_Output__ptr_Private_void = OpTypePointer Output %_ptr_Private_void
-          %3 = OpVariable %_ptr_Output__arr__ptr_Private_void_uint_4278132784 Output
+%_arr__sampler_uint_4278132784 = OpTypeArray %sampler %uint_4278132784
+%_ptr_Output__arr__sampler_uint_4278132784 = OpTypePointer Output %_arr__sampler_uint_4278132784
+          %2 = OpVariable %_ptr_Output__arr__sampler_uint_4278132784 Output
+          %3 = OpVariable %_ptr_Output__arr__sampler_uint_4278132784 Output
           %1 = OpFunction %void None %5
          %15 = OpLabel
                OpReturn
@@ -1668,7 +1847,7 @@
               HasSubstr("Invalid type to assign a location"));
 }
 
-TEST_F(ValidateInterfacesTest, ValidLocationTypePhysicalStorageBufferPointer) {
+TEST_F(ValidateInterfacesTest, PhysicalStorageBufferPointer) {
   const std::string text = R"(
 OpCapability Shader
 OpCapability PhysicalStorageBufferAddresses
@@ -1677,10 +1856,10 @@
 OpDecorate %var Location 0
 OpDecorate %var RestrictPointer
 %void = OpTypeVoid
-%int = OpTypeInt 32 0
-%ptr = OpTypePointer PhysicalStorageBuffer %int
-%ptr2 = OpTypePointer Input %ptr
-%var = OpVariable %ptr2 Input
+%uint = OpTypeInt 32 0
+%psb_ptr = OpTypePointer PhysicalStorageBuffer %uint
+%in_ptr = OpTypePointer Input %psb_ptr
+%var = OpVariable %in_ptr Input
 %void_fn = OpTypeFunction %void
 %main = OpFunction %void None %void_fn
 %entry = OpLabel
@@ -1688,7 +1867,139 @@
 OpFunctionEnd
 )";
   CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Input-09557"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Input/Output interface variable id <2> contains a "
+                        "PhysicalStorageBuffer pointer, which is not allowed"));
+}
+
+TEST_F(ValidateInterfacesTest, PhysicalStorageBufferPointerArray) {
+  const std::string text = R"(
+  OpCapability Shader
+  OpCapability PhysicalStorageBufferAddresses
+  OpMemoryModel PhysicalStorageBuffer64 GLSL450
+  OpEntryPoint Vertex %main "main" %var
+  OpDecorate %var Location 0
+  OpDecorate %var RestrictPointer
+  %void = OpTypeVoid
+  %uint = OpTypeInt 32 0
+  %uint_3 = OpConstant %uint 3
+  %psb_ptr = OpTypePointer PhysicalStorageBuffer %uint
+  %array = OpTypeArray %psb_ptr %uint_3
+  %in_ptr = OpTypePointer Input %array
+  %var = OpVariable %in_ptr Input
+  %void_fn = OpTypeFunction %void
+  %main = OpFunction %void None %void_fn
+  %entry = OpLabel
+  OpReturn
+  OpFunctionEnd
+  )";
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Input-09557"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Input/Output interface variable id <2> contains a "
+                        "PhysicalStorageBuffer pointer, which is not allowed"));
+}
+TEST_F(ValidateInterfacesTest, PhysicalStorageBufferPointerStruct) {
+  const std::string text = R"(
+  OpCapability Shader
+  OpCapability PhysicalStorageBufferAddresses
+  OpMemoryModel PhysicalStorageBuffer64 GLSL450
+  OpEntryPoint Vertex %main "main" %var
+  OpDecorate %var Location 0
+  OpDecorate %var RestrictPointer
+  %void = OpTypeVoid
+  %int = OpTypeInt 32 1
+  OpTypeForwardPointer %psb_ptr PhysicalStorageBuffer
+  %struct_0 = OpTypeStruct %int %psb_ptr
+  %struct_1 = OpTypeStruct %int %int
+  %psb_ptr = OpTypePointer PhysicalStorageBuffer %struct_1
+  %in_ptr = OpTypePointer Input %struct_0
+  %var = OpVariable %in_ptr Input
+  %void_fn = OpTypeFunction %void
+  %main = OpFunction %void None %void_fn
+  %entry = OpLabel
+  OpReturn
+  OpFunctionEnd
+  )";
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Input-09557"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Input/Output interface variable id <2> contains a "
+                        "PhysicalStorageBuffer pointer, which is not allowed"));
+}
+
+TEST_F(ValidateInterfacesTest, PhysicalStorageBufferPointerArrayOfStruct) {
+  const std::string text = R"(
+  OpCapability Shader
+  OpCapability PhysicalStorageBufferAddresses
+  OpMemoryModel PhysicalStorageBuffer64 GLSL450
+  OpEntryPoint Vertex %main "main" %var
+  OpDecorate %var Location 0
+  OpDecorate %var RestrictPointer
+  %void = OpTypeVoid
+  %int = OpTypeInt 32 1
+  %uint = OpTypeInt 32 0
+  %uint_3 = OpConstant %uint 3
+  OpTypeForwardPointer %psb_ptr PhysicalStorageBuffer
+  %array_1 = OpTypeArray %psb_ptr %uint_3
+  %struct_0 = OpTypeStruct %int %array_1
+   %struct_1 = OpTypeStruct %int %int
+  %psb_ptr = OpTypePointer PhysicalStorageBuffer %struct_1
+  %array_0 = OpTypeArray %struct_0 %uint_3
+  %in_ptr = OpTypePointer Input %array_0
+  %var = OpVariable %in_ptr Input
+  %void_fn = OpTypeFunction %void
+  %main = OpFunction %void None %void_fn
+  %entry = OpLabel
+  OpReturn
+  OpFunctionEnd
+  )";
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Input-09557"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Input/Output interface variable id <2> contains a "
+                        "PhysicalStorageBuffer pointer, which is not allowed"));
+}
+
+TEST_F(ValidateInterfacesTest, PhysicalStorageBufferPointerNestedStruct) {
+  const std::string text = R"(
+  OpCapability Shader
+  OpCapability PhysicalStorageBufferAddresses
+  OpMemoryModel PhysicalStorageBuffer64 GLSL450
+  OpEntryPoint Vertex %main "main" %var
+  OpDecorate %var Location 0
+  OpDecorate %var RestrictPointer
+  %void = OpTypeVoid
+  %int = OpTypeInt 32 1
+  OpTypeForwardPointer %psb_ptr PhysicalStorageBuffer
+  %struct_0 = OpTypeStruct %int %psb_ptr
+  %struct_1 = OpTypeStruct %int %int
+  %psb_ptr = OpTypePointer PhysicalStorageBuffer %struct_1
+  %struct_2 = OpTypeStruct %int %struct_0
+  %in_ptr = OpTypePointer Input %struct_2
+  %var = OpVariable %in_ptr Input
+  %void_fn = OpTypeFunction %void
+  %main = OpFunction %void None %void_fn
+  %entry = OpLabel
+  OpReturn
+  OpFunctionEnd
+  )";
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Input-09557"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Input/Output interface variable id <2> contains a "
+                        "PhysicalStorageBuffer pointer, which is not allowed"));
 }
 
 TEST_F(ValidateInterfacesTest, UntypedVariableInputMissing) {
@@ -1807,6 +2118,164 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
 }
 
+TEST_F(ValidateInterfacesTest,
+       InvalidBfloat16VariableWithInputOutputStorageClass) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability BFloat16TypeKHR
+OpExtension "SPV_KHR_bfloat16"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in %out
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %in Location 0
+OpDecorate %out Location 0
+%void = OpTypeVoid
+%bfloat16 = OpTypeFloat 16 BFloat16KHR
+%in_ptr = OpTypePointer Input %bfloat16
+%out_ptr = OpTypePointer Output %bfloat16
+%in = OpVariable %in_ptr Input
+%out = OpVariable %out_ptr Output
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Bfloat16 OpVariable <id> '2[%2]' must not be declared "
+                        "with a Storage Class of Input or Output.\n"));
+}
+
+TEST_F(ValidateInterfacesTest,
+       InvalidFP8E4M3VariableWithInputOutputStorageClass) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Float8EXT
+OpExtension "SPV_EXT_float8"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in %out
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %in Location 0
+OpDecorate %out Location 0
+%void = OpTypeVoid
+%fp8e4m3 = OpTypeFloat 8 Float8E4M3EXT
+%in_ptr = OpTypePointer Input %fp8e4m3
+%out_ptr = OpTypePointer Output %fp8e4m3
+%in = OpVariable %in_ptr Input
+%out = OpVariable %out_ptr Output
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeFloat-10823"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("FP8 E4M3/E5M2 OpVariable <id> '2[%2]' must not be declared "
+                "with a Storage Class of Input or Output.\n"));
+}
+
+TEST_F(ValidateInterfacesTest,
+       InvalidFP8E5M2VariableWithInputOutputStorageClass) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Float8EXT
+OpExtension "SPV_EXT_float8"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in %out
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %in Location 0
+OpDecorate %out Location 0
+%void = OpTypeVoid
+%fp8e5m2 = OpTypeFloat 8 Float8E5M2EXT
+%in_ptr = OpTypePointer Input %fp8e5m2
+%out_ptr = OpTypePointer Output %fp8e5m2
+%in = OpVariable %in_ptr Input
+%out = OpVariable %out_ptr Output
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeFloat-10823"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("FP8 E4M3/E5M2 OpVariable <id> '2[%2]' must not be declared "
+                "with a Storage Class of Input or Output.\n"));
+}
+
+TEST_F(ValidateInterfacesTest, VectorIdFragmentInputOutputPass) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability LongVectorEXT
+OpExtension "SPV_EXT_long_vector"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in %out
+OpExecutionMode %main OriginUpperLeft
+OpDecorate %in Location 0
+OpDecorate %out Location 0
+%void = OpTypeVoid
+%f32 = OpTypeFloat 32
+%u32 = OpTypeInt 32 0
+%u4 = OpConstant %u32 4
+%f32vec = OpTypeVectorIdEXT %f32 %u4
+%in_ptr = OpTypePointer Input %f32vec
+%out_ptr = OpTypePointer Output %f32vec
+%in = OpVariable %in_ptr Input
+%out = OpVariable %out_ptr Output
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateInterfacesTest, VectorIdVertexInputOutputPass) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability LongVectorEXT
+OpExtension "SPV_EXT_long_vector"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main" %in %out
+OpDecorate %in Location 0
+OpDecorate %out Location 0
+%void = OpTypeVoid
+%f32 = OpTypeFloat 32
+%u32 = OpTypeInt 32 0
+%u4 = OpConstant %u32 4
+%f32vec = OpTypeVectorIdEXT %f32 %u4
+%in_ptr = OpTypePointer Input %f32vec
+%out_ptr = OpTypePointer Output %f32vec
+%in = OpVariable %in_ptr Input
+%out = OpVariable %out_ptr Output
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_invalid_type_test.cpp b/test/val/val_invalid_type_test.cpp
new file mode 100644
index 0000000..5f8cf73
--- /dev/null
+++ b/test/val/val_invalid_type_test.cpp
@@ -0,0 +1,296 @@
+// Copyright (c) 2025 Google Inc.
+// Copyright (c) 2025 Arm Ltd.
+//
+// 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.
+
+// Tests for invalid types.
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "source/spirv_target_env.h"
+#include "test/unit_spirv.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+using ::testing::Not;
+using ::testing::Values;
+
+using ValidateInvalidType = spvtest::ValidateBase<bool>;
+
+std::string GenerateBFloatCode(const std::string& main_body) {
+  const std::string prefix =
+      R"(
+OpCapability Shader
+OpCapability BFloat16TypeKHR
+OpCapability AtomicFloat16AddEXT
+OpCapability GroupNonUniformShuffle
+OpExtension "SPV_EXT_shader_atomic_float16_add"
+OpExtension "SPV_KHR_bfloat16"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpSource GLSL 450
+OpName %main "main"
+%bool = OpTypeBool
+%void = OpTypeVoid
+%bfloat16 = OpTypeFloat 16 BFloat16KHR
+%func = OpTypeFunction %void
+%u32 = OpTypeInt 32 0
+%u1 = OpConstant %u32 1
+%u0 = OpConstant %u32 0
+%u3 = OpConstant %u32 3
+%bf16_1 = OpConstant %bfloat16 1
+%_ptr_Function_bfloat16 = OpTypePointer Function %bfloat16
+%v2bfloat16 = OpTypeVector %bfloat16 2
+%_ptr_Function_v2bfloat16 = OpTypePointer Function %v2bfloat16
+%bf16_ptr = OpTypePointer Workgroup %bfloat16
+%bf16_var = OpVariable %bf16_ptr Workgroup
+%main = OpFunction %void None %func
+%main_entry = OpLabel)";
+
+  const std::string suffix =
+      R"(
+OpReturn
+OpFunctionEnd)";
+
+  return prefix + main_body + suffix;
+}
+
+TEST_F(ValidateInvalidType, Bfloat16InvalidArithmeticInstruction) {
+  const std::string body = R"(
+%v1 = OpVariable %_ptr_Function_bfloat16 Function
+%v2 = OpVariable %_ptr_Function_bfloat16 Function
+%12 = OpLoad %bfloat16 %v1
+%14 = OpLoad %bfloat16 %v2
+%15 = OpFMul %bfloat16 %12 %14
+)";
+
+  CompileSuccessfully(GenerateBFloatCode(body).c_str(), SPV_ENV_VULKAN_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("FMul doesn't support BFloat16 type."));
+}
+
+TEST_F(ValidateInvalidType, Bfloat16InvalidRelationalInstruction) {
+  const std::string body = R"(
+%v1 = OpVariable %_ptr_Function_bfloat16 Function
+%v2 = OpVariable %_ptr_Function_bfloat16 Function
+%12 = OpLoad %bfloat16 %v1
+%14 = OpLoad %bfloat16 %v2
+%15 = OpFOrdEqual %bool %12 %14
+)";
+
+  CompileSuccessfully(GenerateBFloatCode(body).c_str(), SPV_ENV_VULKAN_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("FOrdEqual doesn't support BFloat16 type."));
+}
+
+TEST_F(ValidateInvalidType, Bfloat16InvalidAtomicInstruction) {
+  const std::string body = R"(
+%val1 = OpAtomicFAddEXT %bfloat16 %bf16_var %u1 %u0 %bf16_1
+)";
+
+  CompileSuccessfully(GenerateBFloatCode(body).c_str(), SPV_ENV_VULKAN_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicFAddEXT doesn't support BFloat16 type."));
+}
+
+TEST_F(ValidateInvalidType, Bfloat16InvalidGroupNonUniformShuffle) {
+  const std::string body = R"(
+%val1 = OpGroupNonUniformShuffle %bfloat16 %u3 %bf16_1 %u0
+)";
+
+  CompileSuccessfully(GenerateBFloatCode(body).c_str(), SPV_ENV_VULKAN_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("GroupNonUniformShuffle doesn't support BFloat16 type."));
+}
+
+std::string GenerateFP8Code(const std::string& main_body) {
+  const std::string prefix =
+      R"(
+OpCapability Shader
+OpCapability Float8EXT
+OpCapability AtomicFloat16AddEXT
+OpCapability GroupNonUniformShuffle
+OpExtension "SPV_EXT_shader_atomic_float16_add"
+OpExtension "SPV_EXT_float8"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpSource GLSL 450
+OpName %main "main"
+%bool = OpTypeBool
+%void = OpTypeVoid
+%fp8e4m3 = OpTypeFloat 8 Float8E4M3EXT
+%fp8e5m2 = OpTypeFloat 8 Float8E5M2EXT
+%func = OpTypeFunction %void
+%u32 = OpTypeInt 32 0
+%u1 = OpConstant %u32 1
+%u0 = OpConstant %u32 0
+%u3 = OpConstant %u32 3
+%fp8e4m3_1 = OpConstant %fp8e4m3 1
+%fp8e5m2_1 = OpConstant %fp8e5m2 1
+%_ptr_Function_fp8e4m3 = OpTypePointer Function %fp8e4m3
+%_ptr_Function_fp8e5m2 = OpTypePointer Function %fp8e5m2
+%v2fp8e4m3 = OpTypeVector %fp8e4m3 2
+%v2fp8e5m2 = OpTypeVector %fp8e5m2 2
+%_ptr_Function_v2fp8e4m3 = OpTypePointer Function %v2fp8e4m3
+%_ptr_Function_v2fp8e5m2 = OpTypePointer Function %v2fp8e5m2
+%fp8e4m3_ptr = OpTypePointer Workgroup %fp8e4m3
+%fp8e5m2_ptr = OpTypePointer Workgroup %fp8e5m2
+%fp8e4m3_var = OpVariable %fp8e4m3_ptr Workgroup
+%fp8e5m2_var = OpVariable %fp8e5m2_ptr Workgroup
+%main = OpFunction %void None %func
+%main_entry = OpLabel)";
+
+  const std::string suffix =
+      R"(
+OpReturn
+OpFunctionEnd)";
+
+  return prefix + main_body + suffix;
+}
+
+TEST_F(ValidateInvalidType, FP8E4M3InvalidArithmeticInstruction) {
+  const std::string body = R"(
+%v1 = OpVariable %_ptr_Function_fp8e4m3 Function
+%v2 = OpVariable %_ptr_Function_fp8e4m3 Function
+%12 = OpLoad %fp8e4m3 %v1
+%14 = OpLoad %fp8e4m3 %v2
+%15 = OpFMul %fp8e4m3 %12 %14
+)";
+
+  CompileSuccessfully(GenerateFP8Code(body).c_str(), SPV_ENV_VULKAN_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("FMul doesn't support FP8 E4M3/E5M2 types."));
+}
+
+TEST_F(ValidateInvalidType, FP8E5M2InvalidArithmeticInstruction) {
+  const std::string body = R"(
+%v1 = OpVariable %_ptr_Function_fp8e5m2 Function
+%v2 = OpVariable %_ptr_Function_fp8e5m2 Function
+%12 = OpLoad %fp8e5m2 %v1
+%14 = OpLoad %fp8e5m2 %v2
+%15 = OpFMul %fp8e5m2 %12 %14
+)";
+
+  CompileSuccessfully(GenerateFP8Code(body).c_str(), SPV_ENV_VULKAN_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("FMul doesn't support FP8 E4M3/E5M2 types."));
+}
+
+TEST_F(ValidateInvalidType, FP8E4M3InvalidRelationalInstruction) {
+  const std::string body = R"(
+%v1 = OpVariable %_ptr_Function_fp8e4m3 Function
+%v2 = OpVariable %_ptr_Function_fp8e4m3 Function
+%12 = OpLoad %fp8e4m3 %v1
+%14 = OpLoad %fp8e4m3 %v2
+%15 = OpFOrdEqual %bool %12 %14
+)";
+
+  CompileSuccessfully(GenerateFP8Code(body).c_str(), SPV_ENV_VULKAN_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("FOrdEqual doesn't support FP8 E4M3/E5M2 types."));
+}
+
+TEST_F(ValidateInvalidType, FP8E5M2InvalidRelationalInstruction) {
+  const std::string body = R"(
+%v1 = OpVariable %_ptr_Function_fp8e5m2 Function
+%v2 = OpVariable %_ptr_Function_fp8e5m2 Function
+%12 = OpLoad %fp8e5m2 %v1
+%14 = OpLoad %fp8e5m2 %v2
+%15 = OpFOrdEqual %bool %12 %14
+)";
+
+  CompileSuccessfully(GenerateFP8Code(body).c_str(), SPV_ENV_VULKAN_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("FOrdEqual doesn't support FP8 E4M3/E5M2 types."));
+}
+
+TEST_F(ValidateInvalidType, FP8E4M3InvalidAtomicInstruction) {
+  const std::string body = R"(
+%val1 = OpAtomicFAddEXT %fp8e4m3 %fp8e4m3_var %u1 %u0 %fp8e4m3_1
+)";
+
+  CompileSuccessfully(GenerateFP8Code(body).c_str(), SPV_ENV_VULKAN_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicFAddEXT doesn't support FP8 E4M3/E5M2 types."));
+}
+
+TEST_F(ValidateInvalidType, FP8E5M2InvalidAtomicInstruction) {
+  const std::string body = R"(
+%val1 = OpAtomicFAddEXT %fp8e5m2 %fp8e5m2_var %u1 %u0 %fp8e5m2_1
+)";
+
+  CompileSuccessfully(GenerateFP8Code(body).c_str(), SPV_ENV_VULKAN_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicFAddEXT doesn't support FP8 E4M3/E5M2 types."));
+}
+
+TEST_F(ValidateInvalidType, FP8E4M3InvalidGroupNonUniformShuffle) {
+  const std::string body = R"(
+%val1 = OpGroupNonUniformShuffle %fp8e4m3 %u3 %fp8e4m3_1 %u0
+)";
+
+  CompileSuccessfully(GenerateFP8Code(body).c_str(), SPV_ENV_VULKAN_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("GroupNonUniformShuffle doesn't support FP8 E4M3/E5M2 types."));
+}
+
+TEST_F(ValidateInvalidType, FP8E5M2InvalidGroupNonUniformShuffle) {
+  const std::string body = R"(
+%val1 = OpGroupNonUniformShuffle %fp8e5m2 %u3 %fp8e5m2_1 %u0
+)";
+
+  CompileSuccessfully(GenerateFP8Code(body).c_str(), SPV_ENV_VULKAN_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_6));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("GroupNonUniformShuffle doesn't support FP8 E4M3/E5M2 types."));
+}
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/val_layout_test.cpp b/test/val/val_layout_test.cpp
index e809abf..4fd7fe4 100644
--- a/test/val/val_layout_test.cpp
+++ b/test/val/val_layout_test.cpp
@@ -460,9 +460,11 @@
       OpMemoryModel Logical GLSL450)";
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("No OpEntryPoint instruction was found. This is only "
-                        "allowed if the Linkage capability is being used."));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "No OpEntryPoint instruction was found. This is only "
+          "allowed if the Linkage or GraphARM capability is being used."));
 }
 
 // Invalid. A function may not be a target of both OpEntryPoint and
@@ -631,7 +633,8 @@
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("ModuleProcessed cannot appear in a function declaration"));
+      HasSubstr(
+          "ModuleProcessed cannot appear in the graph definitions section"));
 }
 
 TEST_F(ValidateLayout, ModuleProcessedInvalidInBasicBlock) {
@@ -653,7 +656,8 @@
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_1));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("ModuleProcessed cannot appear in a function declaration"));
+      HasSubstr(
+          "ModuleProcessed cannot appear in the graph definitions section"));
 }
 
 // TODO(umar): Test optional instructions
diff --git a/test/val/val_logical_pointers_test.cpp b/test/val/val_logical_pointers_test.cpp
new file mode 100644
index 0000000..3b09b26
--- /dev/null
+++ b/test/val/val_logical_pointers_test.cpp
@@ -0,0 +1,2456 @@
+// Copyright (c) 2025 Google LLC
+//
+// 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 <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "test/unit_spirv.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::Combine;
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using ::testing::Values;
+using ::testing::ValuesIn;
+
+using ValidateLogicalPointersTest = spvtest::ValidateBase<bool>;
+
+enum class MatrixTrace : uint32_t {
+  kNotAMatrix,
+  kColumn,
+  kComponent,
+};
+
+const MatrixTrace traces[] = {MatrixTrace::kNotAMatrix, MatrixTrace::kColumn,
+                              MatrixTrace::kComponent};
+
+using MatrixTraceTypedTest = spvtest::ValidateBase<MatrixTrace>;
+
+TEST_P(MatrixTraceTypedTest, PhiLoopOp1) {
+  const auto trace_type = GetParam();
+  std::string gep, type;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      type = "%ptr_wg_v2float";
+      gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index";
+      break;
+    case MatrixTrace::kComponent:
+      type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index";
+      break;
+    default:
+      type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var %int_1";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg_struct = OpTypePointer Workgroup %struct
+%ptr_wg_v2float = OpTypePointer Workgroup %v2float
+%ptr_wg_float = OpTypePointer Workgroup %float
+%null = OpConstantNull )" + type +
+                            R"(
+%var = OpVariable %ptr_wg_struct Workgroup
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = )" + gep + R"(
+OpBranch %loop
+%loop = OpLabel
+%phi = OpPhi )" + type + R"( %gep %entry %copy %continue
+OpLoopMerge %merge %continue None
+OpBranchConditional %bool_cond %merge %continue
+%continue = OpLabel
+%copy = OpCopyObject )" + type +
+                            R"( %phi
+OpBranch %loop
+%merge = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceTypedTest, PhiLoopOp2) {
+  const auto trace_type = GetParam();
+  std::string gep, type;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      type = "%ptr_wg_v2float";
+      gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index";
+      break;
+    case MatrixTrace::kComponent:
+      type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index";
+      break;
+    default:
+      type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var %int_1";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg_struct = OpTypePointer Workgroup %struct
+%ptr_wg_v2float = OpTypePointer Workgroup %v2float
+%ptr_wg_float = OpTypePointer Workgroup %float
+%null = OpConstantNull )" + type +
+                            R"(
+%var = OpVariable %ptr_wg_struct Workgroup
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = )" + gep + R"(
+OpBranch %loop
+%loop = OpLabel
+%phi = OpPhi )" + type + R"( %copy %continue %gep %entry
+OpLoopMerge %merge %continue None
+OpBranchConditional %bool_cond %merge %continue
+%continue = OpLabel
+%copy = OpCopyObject )" + type +
+                            R"( %phi
+OpBranch %loop
+%merge = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceTypedTest, SelectOp1) {
+  const auto trace_type = GetParam();
+  std::string gep, type;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      type = "%ptr_wg_v2float";
+      gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index";
+      break;
+    case MatrixTrace::kComponent:
+      type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index";
+      break;
+    default:
+      type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var %int_1";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg_struct = OpTypePointer Workgroup %struct
+%ptr_wg_v2float = OpTypePointer Workgroup %v2float
+%ptr_wg_float = OpTypePointer Workgroup %float
+%null = OpConstantNull )" + type +
+                            R"(
+%var = OpVariable %ptr_wg_struct Workgroup
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = )" + gep + R"(
+%copy = OpCopyObject )" + type +
+                            R"( %gep
+%sel = OpSelect )" + type + R"( %bool_cond %copy %null
+OpReturn
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceTypedTest, SelectOp2) {
+  const auto trace_type = GetParam();
+  std::string gep, type;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      type = "%ptr_wg_v2float";
+      gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index";
+      break;
+    case MatrixTrace::kComponent:
+      type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index";
+      break;
+    default:
+      type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var %int_1";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg_struct = OpTypePointer Workgroup %struct
+%ptr_wg_v2float = OpTypePointer Workgroup %v2float
+%ptr_wg_float = OpTypePointer Workgroup %float
+%null = OpConstantNull )" + type +
+                            R"(
+%var = OpVariable %ptr_wg_struct Workgroup
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = )" + gep + R"(
+%copy = OpCopyObject )" + type +
+                            R"( %gep
+%sel = OpSelect )" + type + R"( %bool_cond %null %copy
+OpReturn
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceTypedTest, FunctionVariable) {
+  const auto trace_type = GetParam();
+  std::string gep, type, ld_type;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      type = "%ptr_func_wg_v2float";
+      ld_type = "%ptr_wg_v2float";
+      gep = "OpAccessChain %ptr_wg_v2float %var_mat2x2 %index";
+      break;
+    case MatrixTrace::kComponent:
+      type = "%ptr_func_wg_float";
+      ld_type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var_mat2x2 %index %index";
+      break;
+    default:
+      type = "%ptr_func_wg_float";
+      ld_type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var_float";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg_float = OpTypePointer Workgroup %float
+%ptr_wg_v2float = OpTypePointer Workgroup %v2float
+%ptr_wg_mat2x2 = OpTypePointer Workgroup %mat2x2
+%ptr_func_wg_float = OpTypePointer Function %ptr_wg_float
+%ptr_func_wg_v2float = OpTypePointer Function %ptr_wg_v2float
+%var_mat2x2 = OpVariable %ptr_wg_mat2x2 Workgroup
+%var_float = OpVariable %ptr_wg_float Workgroup
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%func_var = OpVariable )" + type +
+                            R"( Function
+%gep = )" + gep + R"(
+OpStore %func_var %gep
+%ld = OpLoad )" + ld_type + R"( %func_var
+OpReturn
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceTypedTest, PrivateVariable) {
+  const auto trace_type = GetParam();
+  std::string gep, type, ld_type;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      type = "%ptr_priv_wg_v2float";
+      ld_type = "%ptr_wg_v2float";
+      gep = "OpAccessChain %ptr_wg_v2float %var_mat2x2 %index";
+      break;
+    case MatrixTrace::kComponent:
+      type = "%ptr_priv_wg_float";
+      ld_type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var_mat2x2 %index %index";
+      break;
+    default:
+      type = "%ptr_priv_wg_float";
+      ld_type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var_float";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg_float = OpTypePointer Workgroup %float
+%ptr_wg_v2float = OpTypePointer Workgroup %v2float
+%ptr_wg_mat2x2 = OpTypePointer Workgroup %mat2x2
+%ptr_priv_wg_float = OpTypePointer Private %ptr_wg_float
+%ptr_priv_wg_v2float = OpTypePointer Private %ptr_wg_v2float
+%var_mat2x2 = OpVariable %ptr_wg_mat2x2 Workgroup
+%var_float = OpVariable %ptr_wg_float Workgroup
+%priv_var = OpVariable )" + type +
+                            R"( Private
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = )" + gep + R"(
+OpStore %priv_var %gep
+%ld = OpLoad )" + ld_type + R"( %priv_var
+OpReturn
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceTypedTest, FunctionVariableAggregate) {
+  const auto trace_type = GetParam();
+  std::string gep, var_type, gep_type, ld_type;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      var_type = "%ptr_func_wg_struct_v2float";
+      gep_type = "%ptr_func_wg_v2float";
+      ld_type = "%ptr_wg_v2float";
+      gep = "OpAccessChain %ptr_wg_v2float %var_mat2x2 %index";
+      break;
+    case MatrixTrace::kComponent:
+      var_type = "%ptr_func_wg_struct_float";
+      gep_type = "%ptr_func_wg_float";
+      ld_type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var_mat2x2 %index %index";
+      break;
+    default:
+      var_type = "%ptr_func_wg_struct_float";
+      gep_type = "%ptr_func_wg_float";
+      ld_type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var_float";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg_float = OpTypePointer Workgroup %float
+%ptr_wg_v2float = OpTypePointer Workgroup %v2float
+%ptr_wg_mat2x2 = OpTypePointer Workgroup %mat2x2
+%func_array_float = OpTypeArray %ptr_wg_float %int_1
+%func_array_v2float = OpTypeArray %ptr_wg_v2float %int_1
+%func_struct_float = OpTypeStruct %func_array_float
+%func_struct_v2float = OpTypeStruct %func_array_v2float
+%ptr_func_wg_struct_float = OpTypePointer Function %func_struct_float
+%ptr_func_wg_struct_v2float = OpTypePointer Function %func_struct_v2float
+%ptr_func_wg_float = OpTypePointer Function %ptr_wg_float
+%ptr_func_wg_v2float = OpTypePointer Function %ptr_wg_v2float
+%var_mat2x2 = OpVariable %ptr_wg_mat2x2 Workgroup
+%var_float = OpVariable %ptr_wg_float Workgroup
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%func_var = OpVariable )" + var_type +
+                            R"( Function
+%gep = )" + gep + R"(
+%store_gep = OpAccessChain )" +
+                            gep_type + R"( %func_var %int_0 %index
+%store_gep_copy = OpCopyObject )" +
+                            gep_type + R"( %store_gep
+OpStore %store_gep_copy %gep
+%ld_gep = OpAccessChain )" + gep_type +
+                            R"( %func_var %int_0 %index
+%ld = OpLoad )" + ld_type + R"( %ld_gep
+OpReturn
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceTypedTest, FunctionCallParam1) {
+  const auto trace_type = GetParam();
+  std::string gep, type;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      type = "%ptr_wg_v2float";
+      gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index";
+      break;
+    case MatrixTrace::kComponent:
+      type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index";
+      break;
+    default:
+      type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var %int_1";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg_struct = OpTypePointer Workgroup %struct
+%ptr_wg_v2float = OpTypePointer Workgroup %v2float
+%ptr_wg_float = OpTypePointer Workgroup %float
+%null = OpConstantNull )" + type +
+                            R"(
+%var = OpVariable %ptr_wg_struct Workgroup
+%void_fn = OpTypeFunction %void
+%foo_fn = OpTypeFunction %void )" +
+                            type + R"( %bool
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = )" + gep + R"(
+%copy = OpCopyObject )" + type +
+                            R"( %gep
+%call = OpFunctionCall %void %foo %copy %bool_cond
+OpReturn
+OpFunctionEnd
+%foo = OpFunction %void None %foo_fn
+%ptr_param = OpFunctionParameter )" +
+                            type + R"(
+%bool_param = OpFunctionParameter %bool
+%foo_entry = OpLabel
+%sel = OpSelect )" + type + R"( %bool_param %ptr_param %null
+OpReturn
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceTypedTest, FunctionCallParam2) {
+  const auto trace_type = GetParam();
+  std::string gep, type;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      type = "%ptr_wg_v2float";
+      gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index";
+      break;
+    case MatrixTrace::kComponent:
+      type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index";
+      break;
+    default:
+      type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var %int_1";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg_struct = OpTypePointer Workgroup %struct
+%ptr_wg_v2float = OpTypePointer Workgroup %v2float
+%ptr_wg_float = OpTypePointer Workgroup %float
+%null = OpConstantNull )" + type +
+                            R"(
+%var = OpVariable %ptr_wg_struct Workgroup
+%void_fn = OpTypeFunction %void
+%foo_fn = OpTypeFunction %void %bool )" +
+                            type + R"(
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = )" + gep + R"(
+%copy = OpCopyObject )" + type +
+                            R"( %gep
+%call = OpFunctionCall %void %foo %bool_cond %copy
+OpReturn
+OpFunctionEnd
+%foo = OpFunction %void None %foo_fn
+%bool_param = OpFunctionParameter %bool
+%ptr_param = OpFunctionParameter )" +
+                            type + R"(
+%foo_entry = OpLabel
+%sel = OpSelect )" + type + R"( %bool_param %ptr_param %null
+OpReturn
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceTypedTest, FunctionCall) {
+  const auto trace_type = GetParam();
+  std::string gep, type;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      type = "%ptr_wg_v2float";
+      gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index";
+      break;
+    case MatrixTrace::kComponent:
+      type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index";
+      break;
+    default:
+      type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var %int_1";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg_struct = OpTypePointer Workgroup %struct
+%ptr_wg_v2float = OpTypePointer Workgroup %v2float
+%ptr_wg_float = OpTypePointer Workgroup %float
+%null = OpConstantNull )" + type +
+                            R"(
+%var = OpVariable %ptr_wg_struct Workgroup
+%void_fn = OpTypeFunction %void
+%foo_ty = OpTypeFunction )" +
+                            type + R"(
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%call = OpFunctionCall )" + type +
+                            R"( %foo
+OpReturn
+OpFunctionEnd
+%foo = OpFunction )" + type +
+                            R"( None %foo_ty
+%foo_entry = OpLabel
+%gep = )" + gep + R"(
+OpReturnValue %gep
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceTypedTest, FunctionCallMultiReturn1) {
+  const auto trace_type = GetParam();
+  std::string gep, type;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      type = "%ptr_wg_v2float";
+      gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index";
+      break;
+    case MatrixTrace::kComponent:
+      type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index";
+      break;
+    default:
+      type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var %int_1";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg_struct = OpTypePointer Workgroup %struct
+%ptr_wg_v2float = OpTypePointer Workgroup %v2float
+%ptr_wg_float = OpTypePointer Workgroup %float
+%null = OpConstantNull )" + type +
+                            R"(
+%var = OpVariable %ptr_wg_struct Workgroup
+%void_fn = OpTypeFunction %void
+%foo_ty = OpTypeFunction )" +
+                            type + R"(
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%call = OpFunctionCall )" + type +
+                            R"( %foo
+OpReturn
+OpFunctionEnd
+%foo = OpFunction )" + type +
+                            R"( None %foo_ty
+%foo_entry = OpLabel
+%gep = )" + gep + R"(
+OpSelectionMerge %merge None
+OpBranchConditional %bool_cond %then %merge
+%then = OpLabel
+OpReturnValue %gep
+%merge = OpLabel
+OpReturnValue %null
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceTypedTest, FunctionCallMultiReturn2) {
+  const auto trace_type = GetParam();
+  std::string gep, type;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      type = "%ptr_wg_v2float";
+      gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index";
+      break;
+    case MatrixTrace::kComponent:
+      type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index";
+      break;
+    default:
+      type = "%ptr_wg_float";
+      gep = "OpAccessChain %ptr_wg_float %var %int_1";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg_struct = OpTypePointer Workgroup %struct
+%ptr_wg_v2float = OpTypePointer Workgroup %v2float
+%ptr_wg_float = OpTypePointer Workgroup %float
+%null = OpConstantNull )" + type +
+                            R"(
+%var = OpVariable %ptr_wg_struct Workgroup
+%void_fn = OpTypeFunction %void
+%foo_ty = OpTypeFunction )" +
+                            type + R"(
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%call = OpFunctionCall )" + type +
+                            R"( %foo
+OpReturn
+OpFunctionEnd
+%foo = OpFunction )" + type +
+                            R"( None %foo_ty
+%foo_entry = OpLabel
+%gep = )" + gep + R"(
+OpSelectionMerge %merge None
+OpBranchConditional %bool_cond %then %merge
+%then = OpLabel
+OpReturnValue %null
+%merge = OpLabel
+OpReturnValue %gep
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidateLogicalPointersMatrixTraceTyped,
+                         MatrixTraceTypedTest, ValuesIn(traces));
+
+using MatrixTraceUntypedTest = spvtest::ValidateBase<MatrixTrace>;
+
+TEST_P(MatrixTraceUntypedTest, PhiLoopOp1) {
+  const auto trace_type = GetParam();
+  std::string gep;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index";
+      break;
+    case MatrixTrace::kComponent:
+      gep =
+          "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index "
+          "%index";
+      break;
+    default:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg = OpTypeUntypedPointerKHR Workgroup
+%null = OpConstantNull %ptr_wg
+%var = OpUntypedVariableKHR %ptr_wg Workgroup %struct
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = )" + gep + R"(
+OpBranch %loop
+%loop = OpLabel
+%phi = OpPhi %ptr_wg %gep %entry %copy %continue
+OpLoopMerge %merge %continue None
+OpBranchConditional %bool_cond %merge %continue
+%continue = OpLabel
+%copy = OpCopyObject %ptr_wg %phi
+OpBranch %loop
+%merge = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+TEST_P(MatrixTraceUntypedTest, PhiLoopOp2) {
+  const auto trace_type = GetParam();
+  std::string gep;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index";
+      break;
+    case MatrixTrace::kComponent:
+      gep =
+          "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index "
+          "%index";
+      break;
+    default:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg = OpTypeUntypedPointerKHR Workgroup
+%null = OpConstantNull %ptr_wg
+%var = OpUntypedVariableKHR %ptr_wg Workgroup %struct
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = )" + gep + R"(
+OpBranch %loop
+%loop = OpLabel
+%phi = OpPhi %ptr_wg %copy %continue %gep %entry
+OpLoopMerge %merge %continue None
+OpBranchConditional %bool_cond %merge %continue
+%continue = OpLabel
+%copy = OpCopyObject %ptr_wg %phi
+OpBranch %loop
+%merge = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceUntypedTest, SelectOp1) {
+  const auto trace_type = GetParam();
+  std::string gep;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index";
+      break;
+    case MatrixTrace::kComponent:
+      gep =
+          "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index "
+          "%index";
+      break;
+    default:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg = OpTypeUntypedPointerKHR Workgroup
+%null = OpConstantNull %ptr_wg
+%var = OpUntypedVariableKHR %ptr_wg Workgroup %struct
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = )" + gep + R"(
+%copy = OpCopyObject %ptr_wg %gep
+%sel = OpSelect %ptr_wg %bool_cond %copy %null
+OpReturn
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceUntypedTest, SelectOp2) {
+  const auto trace_type = GetParam();
+  std::string gep;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index";
+      break;
+    case MatrixTrace::kComponent:
+      gep =
+          "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index "
+          "%index";
+      break;
+    default:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg = OpTypeUntypedPointerKHR Workgroup
+%null = OpConstantNull %ptr_wg
+%var = OpUntypedVariableKHR %ptr_wg Workgroup %struct
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = )" + gep + R"(
+%copy = OpCopyObject %ptr_wg %gep
+%sel = OpSelect %ptr_wg %bool_cond %null %copy
+OpReturn
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceUntypedTest, FunctionVariable) {
+  const auto trace_type = GetParam();
+  std::string gep;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %mat2x2 %var_mat2x2 %index";
+      break;
+    case MatrixTrace::kComponent:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %mat2x2 %var_mat2x2 %index %index";
+      break;
+    default:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %float %var_float";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg = OpTypeUntypedPointerKHR Workgroup
+%ptr_func_wg = OpTypePointer Function %ptr_wg
+%var_mat2x2 = OpUntypedVariableKHR %ptr_wg Workgroup %mat2x2
+%var_float = OpUntypedVariableKHR %ptr_wg Workgroup %float
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%func_var = OpVariable %ptr_func_wg Function
+%gep = )" + gep + R"(
+OpStore %func_var %gep
+%ld = OpLoad %ptr_wg %func_var
+OpReturn
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceUntypedTest, PrivateVariable) {
+  const auto trace_type = GetParam();
+  std::string gep;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %mat2x2 %var_mat2x2 %index";
+      break;
+    case MatrixTrace::kComponent:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %mat2x2 %var_mat2x2 %index %index";
+      break;
+    default:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %float %var_float";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg = OpTypeUntypedPointerKHR Workgroup
+%ptr_priv_wg = OpTypePointer Private %ptr_wg
+%var_mat2x2 = OpUntypedVariableKHR %ptr_wg Workgroup %mat2x2
+%var_float = OpUntypedVariableKHR %ptr_wg Workgroup %float
+%priv_var = OpVariable %ptr_priv_wg Private
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = )" + gep + R"(
+OpStore %priv_var %gep
+%ld = OpLoad %ptr_wg %priv_var
+OpReturn
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceUntypedTest, FunctionVariableAggregate) {
+  const auto trace_type = GetParam();
+  std::string gep;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %mat2x2 %var_mat2x2 %index";
+      break;
+    case MatrixTrace::kComponent:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %mat2x2 %var_mat2x2 %index %index";
+      break;
+    default:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %float %var_float";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg = OpTypeUntypedPointerKHR Workgroup
+%func_array = OpTypeArray %ptr_wg %int_1
+%func_struct = OpTypeStruct %func_array
+%ptr_func_wg_struct = OpTypePointer Function %func_struct
+%ptr_func_wg = OpTypePointer Function %ptr_wg
+%var_mat2x2 = OpUntypedVariableKHR %ptr_wg Workgroup %mat2x2
+%var_float = OpUntypedVariableKHR %ptr_wg Workgroup %float
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%func_var = OpVariable %ptr_func_wg_struct Function
+%gep = )" + gep + R"(
+%store_gep = OpAccessChain %ptr_func_wg %func_var %int_0 %index
+%store_gep_copy = OpCopyObject %ptr_func_wg %store_gep
+OpStore %store_gep_copy %gep
+%ld_gep = OpAccessChain %ptr_func_wg %func_var %int_0 %index
+%ld = OpLoad %ptr_wg %ld_gep
+OpReturn
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceUntypedTest, FunctionCallParam1) {
+  const auto trace_type = GetParam();
+  std::string gep;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index";
+      break;
+    case MatrixTrace::kComponent:
+      gep =
+          "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index "
+          "%index";
+      break;
+    default:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg = OpTypeUntypedPointerKHR Workgroup
+%null = OpConstantNull %ptr_wg
+%var = OpUntypedVariableKHR %ptr_wg Workgroup %struct
+%void_fn = OpTypeFunction %void
+%foo_fn = OpTypeFunction %void %ptr_wg %bool
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = )" + gep + R"(
+%copy = OpCopyObject %ptr_wg %gep
+%call = OpFunctionCall %void %foo %copy %bool_cond
+OpReturn
+OpFunctionEnd
+%foo = OpFunction %void None %foo_fn
+%ptr_param = OpFunctionParameter %ptr_wg
+%bool_param = OpFunctionParameter %bool
+%foo_entry = OpLabel
+%sel = OpSelect %ptr_wg %bool_param %ptr_param %null
+OpReturn
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceUntypedTest, FunctionCallParam2) {
+  const auto trace_type = GetParam();
+  std::string gep;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index";
+      break;
+    case MatrixTrace::kComponent:
+      gep =
+          "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index "
+          "%index";
+      break;
+    default:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg = OpTypeUntypedPointerKHR Workgroup
+%null = OpConstantNull %ptr_wg
+%var = OpUntypedVariableKHR %ptr_wg Workgroup %struct
+%void_fn = OpTypeFunction %void
+%foo_fn = OpTypeFunction %void %bool %ptr_wg
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = )" + gep + R"(
+%copy = OpCopyObject %ptr_wg %gep
+%call = OpFunctionCall %void %foo %bool_cond %copy
+OpReturn
+OpFunctionEnd
+%foo = OpFunction %void None %foo_fn
+%bool_param = OpFunctionParameter %bool
+%ptr_param = OpFunctionParameter %ptr_wg
+%foo_entry = OpLabel
+%sel = OpSelect %ptr_wg %bool_param %ptr_param %null
+OpReturn
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceUntypedTest, FunctionCall) {
+  const auto trace_type = GetParam();
+  std::string gep;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index";
+      break;
+    case MatrixTrace::kComponent:
+      gep =
+          "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index "
+          "%index";
+      break;
+    default:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg = OpTypeUntypedPointerKHR Workgroup
+%null = OpConstantNull %ptr_wg
+%var = OpUntypedVariableKHR %ptr_wg Workgroup %struct
+%void_fn = OpTypeFunction %void
+%foo_ty = OpTypeFunction %ptr_wg
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%call = OpFunctionCall %ptr_wg %foo
+OpReturn
+OpFunctionEnd
+%foo = OpFunction %ptr_wg None %foo_ty
+%foo_entry = OpLabel
+%gep = )" + gep + R"(
+OpReturnValue %gep
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceUntypedTest, FunctionCallMultiReturn1) {
+  const auto trace_type = GetParam();
+  std::string gep;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index";
+      break;
+    case MatrixTrace::kComponent:
+      gep =
+          "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index "
+          "%index";
+      break;
+    default:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg = OpTypeUntypedPointerKHR Workgroup
+%null = OpConstantNull %ptr_wg
+%var = OpUntypedVariableKHR %ptr_wg Workgroup %struct
+%void_fn = OpTypeFunction %void
+%foo_ty = OpTypeFunction %ptr_wg
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%call = OpFunctionCall %ptr_wg %foo
+OpReturn
+OpFunctionEnd
+%foo = OpFunction %ptr_wg None %foo_ty
+%foo_entry = OpLabel
+%gep = )" + gep + R"(
+OpSelectionMerge %merge None
+OpBranchConditional %bool_cond %then %merge
+%then = OpLabel
+OpReturnValue %gep
+%merge = OpLabel
+OpReturnValue %null
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceUntypedTest, FunctionCallMultiReturn2) {
+  const auto trace_type = GetParam();
+  std::string gep;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index";
+      break;
+    case MatrixTrace::kComponent:
+      gep =
+          "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_0 %index %index "
+          "%index";
+      break;
+    default:
+      gep = "OpUntypedAccessChainKHR %ptr_wg %struct %var %int_1";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg = OpTypeUntypedPointerKHR Workgroup
+%null = OpConstantNull %ptr_wg
+%var = OpUntypedVariableKHR %ptr_wg Workgroup %struct
+%void_fn = OpTypeFunction %void
+%foo_ty = OpTypeFunction %ptr_wg
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%call = OpFunctionCall %ptr_wg %foo
+OpReturn
+OpFunctionEnd
+%foo = OpFunction %ptr_wg None %foo_ty
+%foo_entry = OpLabel
+%gep = )" + gep + R"(
+OpSelectionMerge %merge None
+OpBranchConditional %bool_cond %then %merge
+%then = OpLabel
+OpReturnValue %null
+%merge = OpLabel
+OpReturnValue %gep
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+TEST_P(MatrixTraceUntypedTest, MixedTypes) {
+  const auto trace_type = GetParam();
+  std::string gep;
+  switch (trace_type) {
+    case MatrixTrace::kColumn:
+      gep = "OpAccessChain %ptr_wg_v2float %var %int_0 %index %index";
+      break;
+    case MatrixTrace::kComponent:
+      gep = "OpAccessChain %ptr_wg_float %var %int_0 %index %index %index";
+      break;
+    default:
+      gep = "OpAccessChain %ptr_wg_float %var %int_1";
+      break;
+  }
+
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%index = OpUndef %int
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%array = OpTypeArray %mat2x2 %int_2
+%struct = OpTypeStruct %array %float
+%ptr_wg = OpTypeUntypedPointerKHR Workgroup
+%ptr_wg_struct = OpTypePointer Workgroup %struct
+%ptr_wg_v2float = OpTypePointer Workgroup %v2float
+%ptr_wg_float = OpTypePointer Workgroup %float
+%null = OpConstantNull %ptr_wg
+%var = OpVariable %ptr_wg_struct Workgroup
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = )" + gep + R"(
+%copy = OpUntypedAccessChainKHR %ptr_wg %struct %gep
+%sel = OpSelect %ptr_wg %bool_cond %copy %null
+OpReturn
+OpFunctionEnd
+)";
+
+  const auto expected = trace_type == MatrixTrace::kNotAMatrix
+                            ? SPV_SUCCESS
+                            : SPV_ERROR_INVALID_DATA;
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(expected, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  if (expected) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Variable pointer must not point to a column or a "
+                          "component of a column of a matrix"));
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidateLogicalPointersMatrixTraceUntyped,
+                         MatrixTraceUntypedTest, ValuesIn(traces));
+
+TEST_F(ValidateLogicalPointersTest, SelectDifferentBuffersTyped) {
+  const std::string spirv = R"(
+OpCapability VariablePointersStorageBuffer
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%struct = OpTypeStruct %int
+%ptr_struct = OpTypePointer StorageBuffer %struct
+%v1 = OpVariable %ptr_struct StorageBuffer
+%v2 = OpVariable %ptr_struct StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%select = OpSelect %ptr_struct %bool_cond %v1 %v2
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Variable pointers must point into the same structure "
+                        "(or OpConstantNull)"));
+}
+
+TEST_F(ValidateLogicalPointersTest, SelectDifferentBuffersUntyped) {
+  const std::string spirv = R"(
+OpCapability VariablePointersStorageBuffer
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%struct = OpTypeStruct %int
+%ptr = OpTypeUntypedPointerKHR StorageBuffer
+%v1 = OpUntypedVariableKHR %ptr StorageBuffer %struct
+%v2 = OpUntypedVariableKHR %ptr StorageBuffer %struct
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%select = OpSelect %ptr %bool_cond %v1 %v2
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Variable pointers must point into the same structure "
+                        "(or OpConstantNull)"));
+}
+
+TEST_F(ValidateLogicalPointersTest, PhiDifferentBuffersTyped) {
+  const std::string spirv = R"(
+OpCapability VariablePointersStorageBuffer
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%struct = OpTypeStruct %int
+%ptr_struct = OpTypePointer StorageBuffer %struct
+%v1 = OpVariable %ptr_struct StorageBuffer
+%v2 = OpVariable %ptr_struct StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpSelectionMerge %merge None
+OpBranchConditional %bool_cond %then %merge
+%then = OpLabel
+OpBranch %merge
+%merge = OpLabel
+%phi = OpPhi %ptr_struct %v1 %entry %v2 %then
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Variable pointers must point into the same structure "
+                        "(or OpConstantNull)"));
+}
+
+TEST_F(ValidateLogicalPointersTest, PhiDifferentBuffersUntyped) {
+  const std::string spirv = R"(
+OpCapability VariablePointersStorageBuffer
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%int = OpTypeInt 32 0
+%struct = OpTypeStruct %int
+%ptr = OpTypeUntypedPointerKHR StorageBuffer
+%v1 = OpUntypedVariableKHR %ptr StorageBuffer %struct
+%v2 = OpUntypedVariableKHR %ptr StorageBuffer %struct
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpSelectionMerge %merge None
+OpBranchConditional %bool_cond %then %merge
+%then = OpLabel
+OpBranch %merge
+%merge = OpLabel
+%phi = OpPhi %ptr %v1 %entry %v2 %then
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Variable pointers must point into the same structure "
+                        "(or OpConstantNull)"));
+}
+
+TEST_F(ValidateLogicalPointersTest, BlockArrayTyped) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%struct = OpTypeStruct %int
+%array = OpTypeArray %struct %int_4
+%ptr_array = OpTypePointer StorageBuffer %array
+%ptr_int = OpTypePointer StorageBuffer %int
+%var = OpVariable %ptr_array StorageBuffer
+%null = OpConstantNull %ptr_array
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%select = OpSelect %ptr_array %bool_cond %null %var
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Variable pointer must not point to an array of Block- "
+                        "or BufferBlock-decorated structs"));
+}
+
+TEST_F(ValidateLogicalPointersTest, BlockArrayUntyped) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%bool = OpTypeBool
+%bool_cond = OpUndef %bool
+%struct = OpTypeStruct %int
+%array = OpTypeArray %struct %int_4
+%ptr = OpTypeUntypedPointerKHR StorageBuffer
+%var = OpUntypedVariableKHR %ptr StorageBuffer %array
+%null = OpConstantNull %ptr
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%select = OpSelect %ptr %bool_cond %null %var
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Variable pointer must not point to an array of Block- "
+                        "or BufferBlock-decorated structs"));
+}
+
+TEST_F(ValidateLogicalPointersTest, UntypedMatrixLoad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%struct = OpTypeStruct %mat2x2
+%ptr = OpTypeUntypedPointerKHR StorageBuffer
+%var = OpUntypedVariableKHR %ptr StorageBuffer %struct
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld = OpLoad %struct %var
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+}
+
+TEST_F(ValidateLogicalPointersTest, UntypedMatrixLoadVariablePointer) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%struct = OpTypeStruct %mat2x2
+%ptr = OpTypeUntypedPointerKHR StorageBuffer
+%var = OpUntypedVariableKHR %ptr StorageBuffer %struct
+%null = OpConstantNull %ptr
+%bool = OpTypeBool
+%cond = OpUndef %bool
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%sel = OpSelect %ptr %cond %var %null
+%ld = OpLoad %struct %sel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Variable pointer must not point to an object that is "
+                        "or contains a matrix"));
+}
+
+TEST_F(ValidateLogicalPointersTest, UntypedMatrixStore) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%zero = OpConstantNull %mat2x2
+%struct = OpTypeStruct %mat2x2
+%ptr = OpTypeUntypedPointerKHR StorageBuffer
+%var = OpUntypedVariableKHR %ptr StorageBuffer %struct
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpStore %var %zero
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+}
+
+TEST_F(ValidateLogicalPointersTest, UntypedMatrixStoreVariablePointer) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%mat2x2 = OpTypeMatrix %v2float 2
+%zero = OpConstantNull %mat2x2
+%struct = OpTypeStruct %mat2x2
+%ptr = OpTypeUntypedPointerKHR StorageBuffer
+%var = OpUntypedVariableKHR %ptr StorageBuffer %struct
+%null = OpConstantNull %ptr
+%bool = OpTypeBool
+%cond = OpUndef %bool
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%sel = OpSelect %ptr %cond %var %null
+OpStore %sel %zero
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Variable pointer must not point to an object that is "
+                        "or contains a matrix"));
+}
+
+TEST_F(ValidateLogicalPointersTest, LogicalPointerOperandFailure) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Kernel
+OpCapability Addresses
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%v2int = OpTypeVector %int 2
+%ptr_v2int = OpTypePointer StorageBuffer %v2int
+%var = OpVariable %ptr_v2int StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpInBoundsPtrAccessChain %ptr_v2int %var %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Instruction may not have a logical pointer operand"));
+}
+
+TEST_F(ValidateLogicalPointersTest,
+       LogicalPointerOperandVariablePointerStorageBuffer) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability Addresses
+OpMemoryModel Logical GLSL450
+OpDecorate %ptr_int ArrayStride 4
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%array = OpTypeArray %int %int_4
+%ptr_array = OpTypePointer StorageBuffer %array
+%ptr_int = OpTypePointer StorageBuffer %int
+%var = OpVariable %ptr_array StorageBuffer
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%foo = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpAccessChain %ptr_int %var %int_0
+%ptr_gep = OpPtrAccessChain %ptr_int %gep %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Instruction may only have a logical pointer operand in the "
+                "StorageBuffer or "
+                "Workgroup storage classes with appropriate variable pointers "
+                "capability"));
+}
+
+TEST_F(ValidateLogicalPointersTest,
+       LogicalPointerOperandVariablePointerWorkgroup) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability VariablePointersStorageBuffer
+OpMemoryModel Logical GLSL450
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%array = OpTypeArray %int %int_4
+%ptr_array = OpTypePointer Workgroup %array
+%ptr_int = OpTypePointer Workgroup %int
+%var = OpVariable %ptr_array Workgroup
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%foo = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpAccessChain %ptr_int %var %int_0
+%ptr_gep = OpPtrAccessChain %ptr_int %gep %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Instruction may only have a logical pointer operand in the "
+                "StorageBuffer or "
+                "Workgroup storage classes with appropriate variable pointers "
+                "capability"));
+}
+
+TEST_F(ValidateLogicalPointersTest,
+       LogicalPointerReturnVariablePointerStorageBuffer) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%int = OpTypeInt 32 0
+%ptr = OpTypePointer StorageBuffer %int
+%null = OpConstantNull %ptr
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Instruction may only return a logical pointer in the "
+                "StorageBuffer or "
+                "Workgroup storage classes with appropriate variable pointers "
+                "capability"));
+}
+
+TEST_F(ValidateLogicalPointersTest,
+       LogicalPointerReturnVariablePointerWorkgroup) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability VariablePointersStorageBuffer
+OpMemoryModel Logical GLSL450
+%int = OpTypeInt 32 0
+%ptr = OpTypePointer Workgroup %int
+%null = OpConstantNull %ptr
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Instruction may only return a logical pointer in the "
+                "StorageBuffer or "
+                "Workgroup storage classes with appropriate variable pointers "
+                "capability"));
+}
+
+TEST_F(ValidateLogicalPointersTest, ArrayLengthInvalidVariablePointer) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%array = OpTypeRuntimeArray %int
+%struct = OpTypeStruct %array
+%ptr = OpTypePointer StorageBuffer %struct
+%var = OpVariable %ptr StorageBuffer
+%null = OpConstantNull %ptr
+%bool = OpTypeBool
+%cond = OpUndef %bool
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%sel = OpSelect %ptr %cond %null %var
+%len = OpArrayLength %int %sel 0
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Pointer operand must not be a variable pointer"));
+}
+
+TEST_F(ValidateLogicalPointersTest, ArrayLengthUntypedInvalidVariablePointer) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%array = OpTypeRuntimeArray %int
+%struct = OpTypeStruct %array
+%ptr = OpTypeUntypedPointerKHR StorageBuffer
+%var = OpUntypedVariableKHR %ptr StorageBuffer %struct
+%null = OpConstantNull %ptr
+%bool = OpTypeBool
+%cond = OpUndef %bool
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%sel = OpSelect %ptr %cond %null %var
+%len = OpUntypedArrayLengthKHR %int %struct %sel 0
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Pointer operand must not be a variable pointer"));
+}
+
+TEST_F(ValidateLogicalPointersTest, FunctionParameterOperandOfFunctionCall) {
+  const std::string spirv = R"(
+               OpCapability ClipDistance
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "               "
+               OpExecutionMode %2 OriginUpperLeft
+       %void = OpTypeVoid
+         %32 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+         %35 = OpTypeFunction %float %_ptr_Function_float
+%float_1_35631564en19 = OpConstant %float 1.35631564e-19
+%float_1_35631564en19_0 = OpConstant %float 1.35631564e-19
+          %2 = OpFunction %void None %32
+       %8447 = OpLabel
+               OpUnreachable
+               OpFunctionEnd
+          %9 = OpFunction %float None %35
+         %10 = OpFunctionParameter %_ptr_Function_float
+      %65535 = OpLabel
+        %217 = OpVariable %_ptr_Function_float Function
+        %218 = OpLoad %float %10
+    %2097407 = OpFunctionCall %float %9 %10
+        %231 = OpLoad %float %217
+        %232 = OpFDiv %float %218 %231
+        %233 = OpExtInst %float %1 SmoothStep %float_1_35631564en19 %float_1_35631564en19_0 %232
+               OpReturnValue %233
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateLogicalPointersTest, RecursiveCalls) {
+  const std::string spirv = R"(
+               OpCapability ClipDistance
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %2 "               "
+               OpExecutionMode %2 OriginUpperLeft
+       %void = OpTypeVoid
+         %32 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+         %35 = OpTypeFunction %float %_ptr_Function_float
+%float_1_35631564en19 = OpConstant %float 1.35631564e-19
+%float_1_35631564en19_0 = OpConstant %float 1.35631564e-19
+          %2 = OpFunction %void None %32
+       %8447 = OpLabel
+               OpUnreachable
+               OpFunctionEnd
+          %9 = OpFunction %float None %35
+         %10 = OpFunctionParameter %_ptr_Function_float
+      %65535 = OpLabel
+        %217 = OpVariable %_ptr_Function_float Function
+        %218 = OpLoad %float %10
+    %2097407 = OpFunctionCall %float %20 %10
+        %231 = OpLoad %float %217
+        %232 = OpFDiv %float %218 %231
+        %233 = OpExtInst %float %1 SmoothStep %float_1_35631564en19 %float_1_35631564en19_0 %232
+               OpReturnValue %233
+               OpFunctionEnd
+         %20 = OpFunction %float None %35
+         %21 = OpFunctionParameter %_ptr_Function_float
+         %22 = OpLabel
+         %23 = OpFunctionCall %float %9 %21
+               OpReturnValue %float_1_35631564en19
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/val_logicals_test.cpp b/test/val/val_logicals_test.cpp
index c140672..07b502c 100644
--- a/test/val/val_logicals_test.cpp
+++ b/test/val/val_logicals_test.cpp
@@ -631,38 +631,54 @@
 }
 
 TEST_F(ValidateLogicals, OpSelectPointerWithCapability1) {
-  const std::string body = R"(
-%x = OpVariable %f32vec4ptr Function
-%y = OpVariable %f32vec4ptr Function
-OpStore %x %f32vec4_0123
-OpStore %y %f32vec4_1234
-%val1 = OpSelect %f32vec4ptr %true %x %y
-)";
-
-  const std::string extra_cap_ext = R"(
+  const std::string spirv = R"(
+OpCapability Shader
 OpCapability VariablePointers
 OpExtension "SPV_KHR_variable_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%ptr = OpTypePointer Workgroup %int
+%var = OpVariable %ptr Workgroup
+%null = OpConstantNull %ptr
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%sel = OpSelect %ptr %undef %var %null
+OpReturn
+OpFunctionEnd
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra_cap_ext).c_str());
+  CompileSuccessfully(spirv);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
 TEST_F(ValidateLogicals, OpSelectPointerWithCapability2) {
-  const std::string body = R"(
-%x = OpVariable %f32vec4ptr Function
-%y = OpVariable %f32vec4ptr Function
-OpStore %x %f32vec4_0123
-OpStore %y %f32vec4_1234
-%val1 = OpSelect %f32vec4ptr %true %x %y
-)";
-
-  const std::string extra_cap_ext = R"(
+  const std::string spirv = R"(
+OpCapability Shader
 OpCapability VariablePointersStorageBuffer
 OpExtension "SPV_KHR_variable_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%ptr = OpTypePointer StorageBuffer %int
+%var = OpVariable %ptr StorageBuffer
+%null = OpConstantNull %ptr
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%sel = OpSelect %ptr %undef %var %null
+OpReturn
+OpFunctionEnd
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, extra_cap_ext).c_str());
+  CompileSuccessfully(spirv);
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
diff --git a/test/val/val_memory_semantics_test.cpp b/test/val/val_memory_semantics_test.cpp
new file mode 100644
index 0000000..96f812d
--- /dev/null
+++ b/test/val/val_memory_semantics_test.cpp
@@ -0,0 +1,688 @@
+// Copyright (c) 2025 The Khronos Group 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 "gmock/gmock.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::Combine;
+using ::testing::HasSubstr;
+using ::testing::Values;
+using ::testing::ValuesIn;
+using ::testing::internal::ParamGenerator;
+
+// clang-format off
+#define MEMORY_SEMANTICS_OPERANDS   \
+    OpMemoryBarrier,                \
+    OpControlBarrier,               \
+    OpAtomicLoad,                   \
+    OpAtomicStore,                  \
+    OpAtomicExchange,               \
+    OpAtomicIIncrement,             \
+    OpAtomicIDecrement,             \
+    OpAtomicIAdd,                   \
+    OpAtomicISub,                   \
+    OpAtomicSMin,                   \
+    OpAtomicSMax,                   \
+    OpAtomicUMin,                   \
+    OpAtomicUMax,                   \
+    OpAtomicAnd,                    \
+    OpAtomicOr,                     \
+    OpAtomicXor,                    \
+    OpAtomicCompareExchangeEqual,   \
+    OpAtomicCompareExchangeUnequal
+
+enum Operand { MEMORY_SEMANTICS_OPERANDS };
+const Operand Operands[] = { MEMORY_SEMANTICS_OPERANDS };
+const size_t OperandsCount = sizeof(Operands) / sizeof(Operand);
+#undef MEMORY_SEMANTICS_OPERANDS
+// clang-format on
+
+enum Value {
+  None = uint32_t(spv::MemorySemanticsMask::MaskNone),
+  Acquire = uint32_t(spv::MemorySemanticsMask::Acquire),
+  Release = uint32_t(spv::MemorySemanticsMask::Release),
+  AcqRel = uint32_t(spv::MemorySemanticsMask::AcquireRelease),
+  SeqCst = uint32_t(spv::MemorySemanticsMask::SequentiallyConsistent),
+  Uniform = uint32_t(spv::MemorySemanticsMask::UniformMemory),
+  Subgroup = uint32_t(spv::MemorySemanticsMask::SubgroupMemory),
+  Workgroup = uint32_t(spv::MemorySemanticsMask::WorkgroupMemory),
+  CrossWorkgroup = uint32_t(spv::MemorySemanticsMask::CrossWorkgroupMemory),
+  AtomicCounter = uint32_t(spv::MemorySemanticsMask::AtomicCounterMemory),
+  Image = uint32_t(spv::MemorySemanticsMask::ImageMemory),
+  Output = uint32_t(spv::MemorySemanticsMask::OutputMemory),
+  Available = uint32_t(spv::MemorySemanticsMask::MakeAvailable),
+  Visible = uint32_t(spv::MemorySemanticsMask::MakeVisible),
+  Volatile = uint32_t(spv::MemorySemanticsMask::Volatile)
+};
+
+struct TestResult {
+  explicit TestResult(spv_result_t in_result = SPV_SUCCESS,
+                      const char* in_vuid = nullptr,
+                      const char* in_error = nullptr)
+      : result(in_result), vuid(in_vuid), error(in_error) {}
+  spv_result_t result;
+  const char* vuid;
+  const char* error;
+};
+
+template <typename T, typename... Ts>
+ParamGenerator<T> ValuesInExcept(const T* items, const size_t count,
+                                 const Ts... skip) {
+  std::vector<T> filtered;
+  std::initializer_list<T> excluded = {skip...};
+  std::copy_if(items, items + count, std::back_inserter(filtered),
+               [&excluded](const T& value) {
+                 return std::all_of(
+                     excluded.begin(), excluded.end(),
+                     [&value](const T& other) { return value != other; });
+               });
+  return ValuesIn(filtered);
+}
+
+std::string GenerateInstruction(const Operand operand) {
+  switch (operand) {
+    case OpMemoryBarrier:
+      return "OpMemoryBarrier %scope %semantics";
+    case OpControlBarrier:
+      return "OpControlBarrier %uint_2 %scope %semantics";
+    case OpAtomicLoad:
+      return "%result = OpAtomicLoad %uint %var %scope %semantics";
+    case OpAtomicStore:
+      return "OpAtomicStore %var %scope %semantics %uint_1";
+    case OpAtomicExchange:
+      return "%result = OpAtomicExchange %uint %var %scope %semantics %uint_1";
+    case OpAtomicCompareExchangeEqual:
+      return "%result = OpAtomicCompareExchange %uint %var %scope %semantics "
+             "%semantics_min %uint_1 %uint_0";
+    case OpAtomicCompareExchangeUnequal:
+      return "%result = OpAtomicCompareExchange %uint %var %scope "
+             "%semantics_max %semantics %uint_1 %uint_0";
+    case OpAtomicIIncrement:
+      return "%result = OpAtomicIIncrement %uint %var %scope %semantics";
+    case OpAtomicIDecrement:
+      return "%result = OpAtomicIDecrement %uint %var %scope %semantics";
+    case OpAtomicIAdd:
+      return "%result = OpAtomicIAdd %uint %var %scope %semantics %uint_1";
+    case OpAtomicISub:
+      return "%result = OpAtomicISub %uint %var %scope %semantics %uint_1";
+    case OpAtomicSMin:
+      return "%result = OpAtomicSMin %uint %var %scope %semantics %uint_1";
+    case OpAtomicUMin:
+      return "%result = OpAtomicUMin %uint %var %scope %semantics %uint_1";
+    case OpAtomicSMax:
+      return "%result = OpAtomicSMax %uint %var %scope %semantics %uint_1";
+    case OpAtomicUMax:
+      return "%result = OpAtomicUMax %uint %var %scope %semantics %uint_1";
+    case OpAtomicAnd:
+      return "%result = OpAtomicAnd %uint %var %scope %semantics %uint_1";
+    case OpAtomicOr:
+      return "%result = OpAtomicOr %uint %var %scope %semantics %uint_1";
+    case OpAtomicXor:
+      return "%result = OpAtomicXor %uint %var %scope %semantics %uint_1";
+    default:
+      return "";
+  }
+}
+
+std::string GenerateVulkanCode(const std::string instruction,
+                               const uint32_t semantics,
+                               const uint32_t semantics2 = 0) {
+  std::ostringstream ss;
+  ss << R"(
+OpCapability Shader
+OpCapability VulkanMemoryModel
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpMemoryModel Logical Vulkan
+OpEntryPoint GLCompute %main "main" %var
+OpExecutionMode %main LocalSize 32 1 1
+%void = OpTypeVoid
+%uint = OpTypeInt 32 0
+%func = OpTypeFunction %void
+%uint_ptr = OpTypePointer Workgroup %uint
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%uint_2 = OpConstant %uint 2
+%uint_4 = OpConstant %uint 4
+%scope = OpConstant %uint 5
+%semantics = OpConstant %uint )"
+     << semantics << R"(
+%semantics2 = OpConstant %uint )"
+     << semantics2 << R"(
+%semantics_min = OpConstant %uint )"
+     << (semantics & Volatile) << R"(
+%semantics_max = OpConstant %uint )"
+     << (32712 | (semantics & Volatile)) << R"(
+%var = OpVariable %uint_ptr Workgroup
+%main = OpFunction %void None %func
+%label = OpLabel
+)" << instruction
+     << R"(
+OpReturn
+OpFunctionEnd)";
+
+  return ss.str();
+}
+
+using VulkanMemorySemantics =
+    spvtest::ValidateBase<std::tuple<uint32_t, uint32_t, uint32_t, uint32_t,
+                                     uint32_t, Operand, TestResult>>;
+using VulkanUnequalMemorySemantics = spvtest::ValidateBase<
+    std::tuple<uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t,
+               uint32_t, uint32_t, bool, uint32_t, TestResult>>;
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorMultipleMemoryOrderBits, VulkanMemorySemantics,
+    Combine(
+        Values(Acquire | Release, Acquire | AcqRel, Release | AcqRel,
+               Acquire | SeqCst, Release | SeqCst, AcqRel | SeqCst),
+        Values(None, Uniform | Workgroup | Image | Output),
+        Values(None, Available, Visible, Available | Visible),
+        Values(None, Volatile),
+        Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+        ValuesIn(Operands),
+        Values(TestResult(
+            SPV_ERROR_INVALID_DATA,
+            "VUID-StandaloneSpirv-MemorySemantics-10865",
+            "Memory Semantics must have at most one non-relaxed memory order "
+            "bit set"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorSequentiallyConsistentMemoryOrder, VulkanMemorySemantics,
+    Combine(
+        Values(SeqCst), Values(None, Uniform | Workgroup | Image | Output),
+        Values(None, Available, Visible, Available | Visible),
+        Values(None, Volatile),
+        Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+        ValuesIn(Operands),
+        Values(TestResult(
+            SPV_ERROR_INVALID_DATA,
+            "VUID-StandaloneSpirv-MemorySemantics-10866",
+            "Memory Semantics with SequentiallyConsistent memory order must "
+            "not be used in the Vulkan API"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorAtomicStoreWithAcquireMemoryOrder, VulkanMemorySemantics,
+    Combine(Values(Acquire, AcqRel),
+            Values(None, Uniform, Workgroup, Image, Output,
+                   Uniform | Workgroup | Image | Output),
+            Values(None, Visible, Available, Available | Visible),
+            Values(None, Volatile),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            Values(OpAtomicStore),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "VUID-StandaloneSpirv-MemorySemantics-10867",
+                "MemorySemantics must not use Acquire or AcquireRelease "
+                "memory order with AtomicStore"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorAtomicLoadWithReleaseMemoryOrder, VulkanMemorySemantics,
+    Combine(Values(Release, AcqRel),
+            Values(None, Uniform, Workgroup, Image, Output,
+                   Uniform | Workgroup | Image | Output),
+            Values(None, Available, Visible, Available | Visible),
+            Values(None, Volatile),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            Values(OpAtomicLoad),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "VUID-StandaloneSpirv-MemorySemantics-10868",
+                "MemorySemantics must not use Release or AcquireRelease "
+                "memory order with AtomicLoad"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorMemoryBarrierWithRelaxedMemoryOrder, VulkanMemorySemantics,
+    Combine(Values(None),
+            Values(None, Uniform, Workgroup, Image, Output,
+                   Uniform | Workgroup | Image | Output),
+            Values(None, Available, Visible, Available | Visible),
+            Values(None, Volatile),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            Values(OpMemoryBarrier),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "VUID-StandaloneSpirv-MemorySemantics-10869",
+                "MemorySemantics must not use Relaxed memory order with "
+                "MemoryBarrier"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorNonRelaxedSemanticsWithoutStorageClass, VulkanMemorySemantics,
+    Combine(Values(Acquire, Release, AcqRel), Values(None),
+            Values(None, Available, Visible, Available | Visible),
+            Values(None, Volatile),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            ValuesInExcept(Operands, OperandsCount, OpAtomicLoad,
+                           OpAtomicStore),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "VUID-StandaloneSpirv-MemorySemantics-10870",
+                "Memory Semantics with a non-relaxed memory order (Acquire, "
+                "Release, or AcquireRelease) must have at least one "
+                "Vulkan-supported storage class semantics bit set "
+                "(UniformMemory, WorkgroupMemory, ImageMemory, "
+                "or OutputMemory)"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorNonRelaxedSemanticsWithoutStorageClassLoad, VulkanMemorySemantics,
+    Combine(Values(Acquire), Values(None),
+            Values(None, Available, Visible, Available | Visible),
+            Values(None, Volatile),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            Values(OpAtomicLoad),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "VUID-StandaloneSpirv-MemorySemantics-10870",
+                "Memory Semantics with a non-relaxed memory order (Acquire, "
+                "Release, or AcquireRelease) must have at least one "
+                "Vulkan-supported storage class semantics bit set "
+                "(UniformMemory, WorkgroupMemory, ImageMemory, or "
+                "OutputMemory)"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorNonRelaxedSemanticsWithoutStorageClassStore, VulkanMemorySemantics,
+    Combine(Values(Release), Values(None),
+            Values(None, Available, Visible, Available | Visible),
+            Values(None, Volatile),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            Values(OpAtomicStore),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "VUID-StandaloneSpirv-MemorySemantics-10870",
+                "Memory Semantics with a non-relaxed memory order (Acquire, "
+                "Release, or AcquireRelease) must have at least one "
+                "Vulkan-supported storage class semantics bit set "
+                "(UniformMemory, WorkgroupMemory, ImageMemory, or "
+                "OutputMemory)"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorRelaxedSemanticsWithStorageClass, VulkanMemorySemantics,
+    Combine(
+        Values(None),
+        Values(Uniform, Workgroup, Image, Output,
+               Uniform | Workgroup | Image | Output),
+        Values(None, Available, Visible, Available | Visible),
+        Values(None, Volatile),
+        Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+        ValuesInExcept(Operands, OperandsCount, OpMemoryBarrier),
+        Values(TestResult(
+            SPV_ERROR_INVALID_DATA,
+            "VUID-StandaloneSpirv-MemorySemantics-10871",
+            "Memory Semantics with at least one Vulkan-supported storage class "
+            "semantics bit set (UniformMemory, WorkgroupMemory, ImageMemory, "
+            "or OutputMemory) must use a non-relaxed memory order (Acquire, "
+            "Release, or AcquireRelease)"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorMakeAvailableWithRelaxedMemoryOrder, VulkanMemorySemantics,
+    Combine(Values(None), Values(None), Values(Available, Available | Visible),
+            Values(None, Volatile),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            ValuesInExcept(Operands, OperandsCount, OpMemoryBarrier),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "VUID-StandaloneSpirv-MemorySemantics-10872",
+                "Memory Semantics with MakeAvailable bit set must use Release "
+                "or AcquireRelease memory order"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorMakeAvailableWithAcquireMemoryOrder, VulkanMemorySemantics,
+    Combine(Values(Acquire),
+            Values(Uniform, Workgroup, Image, Output,
+                   Uniform | Workgroup | Image | Output),
+            Values(Available, Available | Visible), Values(None, Volatile),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            ValuesInExcept(Operands, OperandsCount, OpAtomicStore),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "VUID-StandaloneSpirv-MemorySemantics-10872",
+                "Memory Semantics with MakeAvailable bit set must use Release "
+                "or AcquireRelease memory order"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorMakeVisibleWithRelaxedMemoryOrder, VulkanMemorySemantics,
+    Combine(Values(None), Values(None), Values(Visible), Values(None, Volatile),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            ValuesInExcept(Operands, OperandsCount, OpMemoryBarrier),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "VUID-StandaloneSpirv-MemorySemantics-10873",
+                "Memory Semantics with MakeVisible bit set must use Acquire "
+                "or AcquireRelease memory order"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorMakeVisibleWithReleaseMemoryOrder, VulkanMemorySemantics,
+    Combine(Values(Release),
+            Values(Uniform, Workgroup, Image, Output,
+                   Uniform | Workgroup | Image | Output),
+            Values(Visible, Available | Visible), Values(None, Volatile),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            ValuesInExcept(Operands, OperandsCount, OpAtomicLoad),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "VUID-StandaloneSpirv-MemorySemantics-10873",
+                "Memory Semantics with MakeVisible bit set must use Acquire "
+                "or AcquireRelease memory order"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorVolatileBarrierWithRelaxedSemantics, VulkanMemorySemantics,
+    Combine(Values(None), Values(None), Values(None), Values(Volatile),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            Values(OpControlBarrier),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "VUID-StandaloneSpirv-MemorySemantics-10874",
+                "Memory Semantics with Volatile bit set must not be used with "
+                "barrier instructions"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorVolatileBarrierWithNonRelaxedSemantics, VulkanMemorySemantics,
+    Combine(Values(Acquire, Acquire | Visible, Release, Release | Available,
+                   AcqRel, AcqRel | Visible, AcqRel | Available,
+                   AcqRel | Available | Visible),
+            Values(Uniform, Workgroup, Image, Output,
+                   Uniform | Workgroup | Image | Output),
+            Values(None), Values(Volatile),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            Values(OpControlBarrier, OpMemoryBarrier),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "VUID-StandaloneSpirv-MemorySemantics-10874",
+                "Memory Semantics with Volatile bit set must not be used with "
+                "barrier instructions"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorCompareExchangeUnequalSemanticsWithRelease, VulkanMemorySemantics,
+    Combine(Values(Release, AcqRel, AcqRel | Visible),
+            Values(Uniform, Workgroup, Image, Output,
+                   Uniform | Workgroup | Image | Output),
+            Values(None, Available), Values(None, Volatile),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            Values(OpAtomicCompareExchangeUnequal),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "VUID-StandaloneSpirv-UnequalMemorySemantics-10875",
+                "AtomicCompareExchange Unequal Memory Semantics must not use "
+                "Release or AcquireRelease memory order"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    SuccessAtomicsRelaxed, VulkanMemorySemantics,
+    Combine(Values(None), Values(None), Values(None), Values(None, Volatile),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            ValuesInExcept(Operands, OperandsCount, OpMemoryBarrier,
+                           OpControlBarrier),
+            Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+    SuccessAtomicsAcquire, VulkanMemorySemantics,
+    Combine(Values(Acquire),
+            Values(Uniform, Workgroup, Image, Output,
+                   Uniform | Workgroup | Image | Output),
+            Values(None, Visible), Values(None, Volatile),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            ValuesInExcept(Operands, OperandsCount, OpMemoryBarrier,
+                           OpControlBarrier, OpAtomicStore),
+            Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+    SuccessAtomicsRelease, VulkanMemorySemantics,
+    Combine(Values(Release),
+            Values(Uniform, Workgroup, Image, Output,
+                   Uniform | Workgroup | Image | Output),
+            Values(None, Available), Values(None, Volatile),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            ValuesInExcept(Operands, OperandsCount, OpMemoryBarrier,
+                           OpControlBarrier, OpAtomicLoad,
+                           OpAtomicCompareExchangeUnequal),
+            Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+    SuccessAtomicsAcqRel, VulkanMemorySemantics,
+    Combine(Values(AcqRel),
+            Values(Uniform, Workgroup, Image, Output,
+                   Uniform | Workgroup | Image | Output),
+            Values(None, Available, Visible, Available | Visible),
+            Values(None, Volatile),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            ValuesInExcept(Operands, OperandsCount, OpMemoryBarrier,
+                           OpControlBarrier, OpAtomicLoad, OpAtomicStore,
+                           OpAtomicCompareExchangeUnequal),
+            Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+    SuccessBarriersRelaxed, VulkanMemorySemantics,
+    Combine(Values(None), Values(None), Values(None), Values(None),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            Values(OpControlBarrier), Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+    SuccessBarriersNonRelaxed, VulkanMemorySemantics,
+    Combine(Values(Acquire, Acquire | Visible, Release, Release | Available,
+                   AcqRel, AcqRel | Available, AcqRel | Visible,
+                   AcqRel | Available | Visible),
+            Values(Uniform, Workgroup, Image, Output,
+                   Uniform | Workgroup | Image | Output),
+            Values(None), Values(None),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            Values(OpControlBarrier, OpMemoryBarrier), Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorMemoryOrderTooWeak, VulkanUnequalMemorySemantics,
+    Combine(Values(None), Values(None), Values(None),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            Values(Acquire),
+            Values(Uniform, Workgroup, Image, Output,
+                   Uniform | Workgroup | Image | Output),
+            Values(None, Visible),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            Values(true, false), Values(None, Volatile),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "VUID-StandaloneSpirv-UnequalMemorySemantics-10876",
+                "AtomicCompareExchange Unequal Memory Semantics must not use a "
+                "stronger memory order than the corresponding Equal Memory "
+                "Semantics"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorMissingStorageClassSemanticsFlags, VulkanUnequalMemorySemantics,
+    Combine(
+        Values(Acquire, Acquire | Visible, Release, Release | Available, AcqRel,
+               AcqRel | Visible, AcqRel | Available,
+               AcqRel | Available | Visible),
+        Values(Uniform | Workgroup), Values(None),
+        Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+        Values(Acquire), Values(Uniform | Image, Output), Values(None, Visible),
+        Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+        Values(true, false), Values(None, Volatile),
+        Values(TestResult(
+            SPV_ERROR_INVALID_DATA,
+            "VUID-StandaloneSpirv-UnequalMemorySemantics-10877",
+            "AtomicCompareExchange Unequal Memory Semantics must not have any "
+            "Vulkan-supported storage class semantics bit set (UniformMemory, "
+            "WorkgroupMemory, ImageMemory, or OutputMemory) unless this bit is "
+            "also set in the corresponding Equal Memory Semantics"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorMissingMakeVisibleFlag, VulkanUnequalMemorySemantics,
+    Combine(Values(Acquire, Release, Release | Available, AcqRel,
+                   AcqRel | Available),
+            Values(Uniform | Workgroup | Image | Output), Values(None),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            Values(Acquire),
+            Values(Uniform, Workgroup, Image, Output,
+                   Uniform | Workgroup | Image | Output),
+            Values(Visible),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            Values(true, false), Values(None, Volatile),
+            Values(TestResult(
+                SPV_ERROR_INVALID_DATA,
+                "VUID-StandaloneSpirv-UnequalMemorySemantics-10878",
+                "AtomicCompareExchange Unequal Memory Semantics must not have "
+                "MakeVisible bit set unless this bit is also set in the "
+                "corresponding Equal Memory Semantics"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorMismatchingVolatileFlagsRelaxedAndRelaxed,
+    VulkanUnequalMemorySemantics,
+    Combine(
+        Values(None), Values(None), Values(None),
+        Values(None, Subgroup | CrossWorkgroup | AtomicCounter), Values(None),
+        Values(None), Values(None),
+        Values(None, Subgroup | CrossWorkgroup | AtomicCounter), Values(false),
+        Values(None, Volatile),
+        Values(TestResult(
+            SPV_ERROR_INVALID_DATA,
+            "VUID-StandaloneSpirv-UnequalMemorySemantics-10879",
+            "AtomicCompareExchange Unequal Memory Semantics must have Volatile "
+            "bit set if and only if this bit is also set in the corresponding "
+            "Equal Memory Semantics"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorMismatchingVolatileFlagsNonRelaxedAndRelaxed,
+    VulkanUnequalMemorySemantics,
+    Combine(
+        Values(Acquire, Acquire | Visible, Release, Release | Available, AcqRel,
+               AcqRel | Visible, AcqRel | Available,
+               AcqRel | Available | Visible),
+        Values(Uniform, Workgroup, Image, Output,
+               Uniform | Workgroup | Image | Output),
+        Values(None), Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+        Values(None), Values(None), Values(None),
+        Values(None, Subgroup | CrossWorkgroup | AtomicCounter), Values(false),
+        Values(None, Volatile),
+        Values(TestResult(
+            SPV_ERROR_INVALID_DATA,
+            "VUID-StandaloneSpirv-UnequalMemorySemantics-10879",
+            "AtomicCompareExchange Unequal Memory Semantics must have Volatile "
+            "bit set if and only if this bit is also set in the corresponding "
+            "Equal Memory Semantics"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorMismatchingVolatileFlagsNonRelaxedAndAcquire,
+    VulkanUnequalMemorySemantics,
+    Combine(
+        Values(Acquire, Acquire | Visible, Release, Release | Available, AcqRel,
+               AcqRel | Visible, AcqRel | Available,
+               AcqRel | Available | Visible),
+        Values(Uniform | Workgroup | Image | Output), Values(None),
+        Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+        Values(Acquire),
+        Values(Uniform, Workgroup, Image, Output,
+               Uniform | Workgroup | Image | Output),
+        Values(None), Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+        Values(false), Values(None, Volatile),
+        Values(TestResult(
+            SPV_ERROR_INVALID_DATA,
+            "VUID-StandaloneSpirv-UnequalMemorySemantics-10879",
+            "AtomicCompareExchange Unequal Memory Semantics must have Volatile "
+            "bit set if and only if this bit is also set in the corresponding "
+            "Equal Memory Semantics"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    ErrorMismatchingVolatileFlagsNonRelaxedAndAcquireVisible,
+    VulkanUnequalMemorySemantics,
+    Combine(
+        Values(Acquire, AcqRel, AcqRel | Available),
+        Values(Uniform | Workgroup | Image | Output), Values(Visible),
+        Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+        Values(Acquire),
+        Values(Uniform, Workgroup, Image, Output,
+               Uniform | Workgroup | Image | Output),
+        Values(Visible),
+        Values(None, Subgroup | CrossWorkgroup | AtomicCounter), Values(false),
+        Values(None, Volatile),
+        Values(TestResult(
+            SPV_ERROR_INVALID_DATA,
+            "VUID-StandaloneSpirv-UnequalMemorySemantics-10879",
+            "AtomicCompareExchange Unequal Memory Semantics must have Volatile "
+            "bit set if and only if this bit is also set in the corresponding "
+            "Equal Memory Semantics"))));
+
+INSTANTIATE_TEST_SUITE_P(
+    SuccessNonRelaxedAndAcquire, VulkanUnequalMemorySemantics,
+    Combine(Values(Acquire, Acquire | Visible, Release, Release | Available,
+                   AcqRel, AcqRel | Visible, AcqRel | Available,
+                   AcqRel | Available | Visible),
+            Values(Uniform | Workgroup | Image | Output), Values(None),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            Values(Acquire),
+            Values(Uniform, Workgroup, Image, Output,
+                   Uniform | Workgroup | Image | Output),
+            Values(None),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            Values(true), Values(None, Volatile), Values(TestResult())));
+
+INSTANTIATE_TEST_SUITE_P(
+    SuccessNonRelaxedAndAcquireVisible, VulkanUnequalMemorySemantics,
+    Combine(Values(Acquire, AcqRel, AcqRel | Available),
+            Values(Uniform | Workgroup | Image | Output), Values(Visible),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            Values(Acquire),
+            Values(Uniform, Workgroup, Image, Output,
+                   Uniform | Workgroup | Image | Output),
+            Values(Visible),
+            Values(None, Subgroup | CrossWorkgroup | AtomicCounter),
+            Values(true), Values(None, Volatile), Values(TestResult())));
+
+TEST_P(VulkanMemorySemantics, Case) {
+  const uint32_t semantics = std::get<0>(GetParam()) | std::get<1>(GetParam()) |
+                             std::get<2>(GetParam()) | std::get<3>(GetParam()) |
+                             std::get<4>(GetParam());
+  const Operand operand = std::get<5>(GetParam());
+  const TestResult& result = std::get<6>(GetParam());
+  const std::string instruction = GenerateInstruction(operand);
+
+  CompileSuccessfully(GenerateVulkanCode(instruction, semantics),
+                      SPV_ENV_VULKAN_1_4);
+  ASSERT_EQ(result.result, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  if (result.vuid) {
+    EXPECT_THAT(getDiagnosticString(), AnyVUID(result.vuid));
+  }
+  if (result.error) {
+    EXPECT_THAT(getDiagnosticString(), HasSubstr(result.error));
+  }
+}
+
+TEST_P(VulkanUnequalMemorySemantics, Case) {
+  const uint32_t equal_volatile = std::get<9>(GetParam());
+  const uint32_t unequal_volatile =
+      std::get<8>(GetParam()) ? equal_volatile : Volatile ^ equal_volatile;
+  const uint32_t equal = std::get<0>(GetParam()) | std::get<1>(GetParam()) |
+                         std::get<2>(GetParam()) | std::get<3>(GetParam()) |
+                         equal_volatile;
+  const uint32_t unequal = std::get<4>(GetParam()) | std::get<5>(GetParam()) |
+                           std::get<6>(GetParam()) | std::get<7>(GetParam()) |
+                           unequal_volatile;
+  const TestResult& result = std::get<10>(GetParam());
+  const std::string instruction =
+      "%result = OpAtomicCompareExchange %uint %var %scope %semantics "
+      "%semantics2 %uint_1 %uint_0";
+
+  CompileSuccessfully(GenerateVulkanCode(instruction, equal, unequal),
+                      SPV_ENV_VULKAN_1_4);
+  ASSERT_EQ(result.result, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  if (result.vuid) {
+    EXPECT_THAT(getDiagnosticString(), AnyVUID(result.vuid));
+  }
+  if (result.error) {
+    EXPECT_THAT(getDiagnosticString(), HasSubstr(result.error));
+  }
+}
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp
index 0a918c9..eecc2a1 100644
--- a/test/val/val_memory_test.cpp
+++ b/test/val/val_memory_test.cpp
@@ -1,4 +1,6 @@
 // Copyright (c) 2018 Google Inc.
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -597,10 +599,39 @@
       getDiagnosticString(),
       HasSubstr(
           "The Result Type of OpArrayLength <id> '10[%10]' must be OpTypeInt "
-          "with width 32 and signedness 0.\n  %10 = OpArrayLength %float %9 "
+          "with width 32 or 64 and signedness 0.\n  %10 = OpArrayLength %float "
+          "%9 "
           "0\n"));
 }
 
+TEST_F(ValidateMemory, ArrayLenResultUnsizedPointer) {
+  std::string spirv = R"(
+               OpCapability ClipDistance
+               OpMemoryModel Logical GLSL450
+               OpName %65312 "arrayLen"
+       %void = OpTypeVoid
+          %8 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+  %_struct_5 = OpTypeStruct %v2float
+%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5
+          %6 = OpVariable %_ptr_Uniform__struct_5 Uniform
+%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float
+    %2105376 = OpFunction %void None %8
+       %8224 = OpLabel
+      %65312 = OpArrayLength %_ptr_Uniform_v2float %6 538976288
+               OpUnreachable
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv.c_str());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("The Result Type of OpArrayLength <id> '1[%arrayLen]' must be "
+                "OpTypeInt with width 32 or 64 and signedness 0."));
+}
+
 TEST_F(ValidateMemory, ArrayLenResultNot32bits) {
   std::string spirv = R"(
                OpCapability Shader
@@ -630,7 +661,8 @@
       getDiagnosticString(),
       HasSubstr(
           "The Result Type of OpArrayLength <id> '11[%11]' must be OpTypeInt "
-          "with width 32 and signedness 0.\n  %11 = OpArrayLength %ushort %10 "
+          "with width 32 or 64 and signedness 0.\n  %11 = OpArrayLength "
+          "%ushort %10 "
           "0\n"));
 }
 
@@ -662,7 +694,8 @@
       getDiagnosticString(),
       HasSubstr(
           "The Result Type of OpArrayLength <id> '11[%11]' must be OpTypeInt "
-          "with width 32 and signedness 0.\n  %11 = OpArrayLength %int %10 "
+          "with width 32 or 64 and signedness 0.\n  %11 = OpArrayLength %int "
+          "%10 "
           "0\n"));
 }
 
@@ -1816,6 +1849,68 @@
       HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned"));
 }
 
+TEST_F(ValidateMemory, PSBStoreAlignedZero) {
+  const std::string body = R"(
+OpCapability PhysicalStorageBufferAddresses
+OpCapability Shader
+OpExtension "SPV_EXT_physical_storage_buffer"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+%uint = OpTypeInt 32 0
+%uint_1 = OpConstant %uint 1
+%ptr = OpTypePointer PhysicalStorageBuffer %uint
+%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
+OpStore %val2 %uint_1 Aligned 0
+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(),
+      HasSubstr(
+          "Memory accesses Aligned operand value 0 is not a power of two"));
+}
+
+TEST_F(ValidateMemory, PSBStoreAlignedNonPoT) {
+  const std::string body = R"(
+OpCapability PhysicalStorageBufferAddresses
+OpCapability Shader
+OpExtension "SPV_EXT_physical_storage_buffer"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+%uint = OpTypeInt 32 0
+%uint_1 = OpConstant %uint 1
+%ptr = OpTypePointer PhysicalStorageBuffer %uint
+%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
+OpStore %val2 %uint_1 Aligned 3
+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(),
+      HasSubstr(
+          "Memory accesses Aligned operand value 3 is not a power of two."));
+}
+
 TEST_F(ValidateMemory, PSBCopyMemoryAlignedSuccess) {
   const std::string body = R"(
 OpCapability PhysicalStorageBufferAddresses
@@ -1826,7 +1921,6 @@
 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
@@ -1838,8 +1932,8 @@
 %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
+OpCopyMemory %val2 %val3 Aligned 8
+OpCopyMemory %val3 %val2 Aligned 8 Aligned 8
 OpReturn
 OpFunctionEnd
 )";
@@ -1858,7 +1952,6 @@
 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
@@ -1870,7 +1963,7 @@
 %val1 = OpVariable %pptr_f Function
 %val2 = OpLoad %ptr %val1
 %val3 = OpLoad %ptr %val1
-OpCopyMemory %val2 %val3 Volatile Aligned 4
+OpCopyMemory %val2 %val3 Volatile Aligned 8
 OpReturn
 OpFunctionEnd
 )";
@@ -1894,7 +1987,6 @@
 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
@@ -1906,7 +1998,7 @@
 %val1 = OpVariable %pptr_f Function
 %val2 = OpLoad %ptr %val1
 %val3 = OpLoad %ptr %val1
-OpCopyMemory %val2 %val3 Aligned 4 Volatile
+OpCopyMemory %val2 %val3 Aligned 8 Volatile
 OpReturn
 OpFunctionEnd
 )";
@@ -1930,7 +2022,6 @@
 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
@@ -1984,6 +2075,562 @@
       HasSubstr("PhysicalStorageBuffer must not be used with OpVariable"));
 }
 
+TEST_F(ValidateMemory, PSBStoreAlignedOneWithUvec4) {
+  const std::string body = R"(
+               OpCapability Shader
+               OpCapability PhysicalStorageBufferAddresses
+               OpMemoryModel PhysicalStorageBuffer64 GLSL450
+               OpEntryPoint GLCompute %main "main" %_
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %SSBO Block
+               OpMemberDecorate %SSBO 0 Offset 0
+               OpDecorate %Ptr Block
+               OpMemberDecorate %Ptr 0 Offset 0
+               OpDecorate %_ Binding 0
+               OpDecorate %_ DescriptorSet 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+               OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_Ptr PhysicalStorageBuffer
+       %SSBO = OpTypeStruct %_ptr_PhysicalStorageBuffer_Ptr
+       %uint = OpTypeInt 32 0
+     %v4uint = OpTypeVector %uint 4
+        %Ptr = OpTypeStruct %v4uint
+%_ptr_PhysicalStorageBuffer_Ptr = OpTypePointer PhysicalStorageBuffer %Ptr
+%_ptr_StorageBuffer_SSBO = OpTypePointer StorageBuffer %SSBO
+          %_ = OpVariable %_ptr_StorageBuffer_SSBO StorageBuffer
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_Ptr = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_Ptr
+     %uint_0 = OpConstant %uint 0
+         %20 = OpConstantComposite %v4uint %uint_0 %uint_0 %uint_0 %uint_0
+%_ptr_PhysicalStorageBuffer_v4uint = OpTypePointer PhysicalStorageBuffer %v4uint
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+         %17 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_Ptr %_ %int_0
+         %18 = OpLoad %_ptr_PhysicalStorageBuffer_Ptr %17
+         %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_v4uint %18 %int_0
+               OpStore %22 %20 Aligned 1
+               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-06314"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Memory accesses Aligned operand value 1 is too small, "
+                        "the largest scalar type is 4 bytes"));
+}
+
+TEST_F(ValidateMemory, PSBStoreAlignedOneWithUint32) {
+  const std::string body = R"(
+               OpCapability Shader
+               OpCapability PhysicalStorageBufferAddresses
+               OpMemoryModel PhysicalStorageBuffer64 GLSL450
+               OpEntryPoint GLCompute %main "main" %_
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %SSBO Block
+               OpMemberDecorate %SSBO 0 Offset 0
+               OpDecorate %B Block
+               OpMemberDecorate %B 0 Offset 0
+               OpDecorate %_ Binding 0
+               OpDecorate %_ DescriptorSet 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+               OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_B PhysicalStorageBuffer
+       %SSBO = OpTypeStruct %_ptr_PhysicalStorageBuffer_B
+       %uint = OpTypeInt 32 0
+          %B = OpTypeStruct %uint
+%_ptr_PhysicalStorageBuffer_B = OpTypePointer PhysicalStorageBuffer %B
+%_ptr_StorageBuffer_SSBO = OpTypePointer StorageBuffer %SSBO
+          %_ = OpVariable %_ptr_StorageBuffer_SSBO StorageBuffer
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_B = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_B
+     %uint_0 = OpConstant %uint 0
+%_ptr_PhysicalStorageBuffer_uint = OpTypePointer PhysicalStorageBuffer %uint
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+         %16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_B %_ %int_0
+         %17 = OpLoad %_ptr_PhysicalStorageBuffer_B %16
+         %20 = OpAccessChain %_ptr_PhysicalStorageBuffer_uint %17 %int_0
+               OpStore %20 %uint_0 Aligned 2
+               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-06314"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Memory accesses Aligned operand value 2 is too small, "
+                        "the largest scalar type is 4 bytes"));
+}
+
+// https://github.com/KhronosGroup/glslang/issues/4024
+TEST_F(ValidateMemory, PSBStoreAlignedPointerNot8) {
+  const std::string body = R"(
+               OpCapability Shader
+               OpCapability PhysicalStorageBufferAddresses
+               OpMemoryModel PhysicalStorageBuffer64 GLSL450
+               OpEntryPoint GLCompute %main "main" %_
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %SSBO Block
+               OpMemberDecorate %SSBO 0 Offset 0
+               OpDecorate %B Block
+               OpMemberDecorate %B 0 Offset 0
+               OpDecorate %A Block
+               OpMemberDecorate %A 0 Offset 0
+               OpDecorate %_ Binding 0
+               OpDecorate %_ DescriptorSet 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+               OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_B PhysicalStorageBuffer
+       %SSBO = OpTypeStruct %_ptr_PhysicalStorageBuffer_B
+               OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_A PhysicalStorageBuffer
+          %B = OpTypeStruct %_ptr_PhysicalStorageBuffer_A
+       %uint = OpTypeInt 32 0
+          %A = OpTypeStruct %uint
+%_ptr_PhysicalStorageBuffer_A = OpTypePointer PhysicalStorageBuffer %A
+%_ptr_PhysicalStorageBuffer_B = OpTypePointer PhysicalStorageBuffer %B
+%_ptr_StorageBuffer_SSBO = OpTypePointer StorageBuffer %SSBO
+          %_ = OpVariable %_ptr_StorageBuffer_SSBO StorageBuffer
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_B = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_B
+%_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_A = OpTypePointer PhysicalStorageBuffer %_ptr_PhysicalStorageBuffer_A
+     %uint_0 = OpConstant %uint 0
+%_ptr_PhysicalStorageBuffer_uint = OpTypePointer PhysicalStorageBuffer %uint
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+         %18 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_B %_ %int_0
+         %19 = OpLoad %_ptr_PhysicalStorageBuffer_B %18
+         %21 = OpAccessChain %_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_A %19 %int_0
+         %22 = OpLoad %_ptr_PhysicalStorageBuffer_A %21 Aligned 4
+         %25 = OpAccessChain %_ptr_PhysicalStorageBuffer_uint %22 %int_0
+               OpStore %25 %uint_0 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-06314"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Memory accesses Aligned operand value 4 is too small, "
+                        "the largest scalar type is 8 bytes"));
+}
+
+// https://godbolt.org/z/sbGv6a7os
+TEST_F(ValidateMemory, PSBStoreAlignedStructCopyWithDeepDouble) {
+  const std::string body = R"(
+               OpCapability Shader
+               OpCapability Float64
+               OpCapability PhysicalStorageBufferAddresses
+               OpMemoryModel PhysicalStorageBuffer64 GLSL450
+               OpEntryPoint GLCompute %main "main" %_
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %SSBO Block
+               OpMemberDecorate %SSBO 0 Offset 0
+               OpMemberDecorate %A 0 Offset 0
+               OpMemberDecorate %A 1 Offset 8
+               OpMemberDecorate %B 0 Offset 0
+               OpMemberDecorate %B 1 Offset 16
+               OpMemberDecorate %C 0 Offset 0
+               OpMemberDecorate %C 1 Offset 32
+               OpDecorate %Ptr Block
+               OpMemberDecorate %Ptr 0 Offset 0
+               OpDecorate %_ Binding 0
+               OpDecorate %_ DescriptorSet 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+               OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_Ptr PhysicalStorageBuffer
+       %SSBO = OpTypeStruct %_ptr_PhysicalStorageBuffer_Ptr
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+       %uint = OpTypeInt 32 0
+     %double = OpTypeFloat 64
+          %A = OpTypeStruct %uint %double
+          %B = OpTypeStruct %v3float %A
+          %C = OpTypeStruct %B %uint
+        %Ptr = OpTypeStruct %C
+%_ptr_PhysicalStorageBuffer_Ptr = OpTypePointer PhysicalStorageBuffer %Ptr
+%_ptr_StorageBuffer_SSBO = OpTypePointer StorageBuffer %SSBO
+          %_ = OpVariable %_ptr_StorageBuffer_SSBO StorageBuffer
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_Ptr = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_Ptr
+        %A_0 = OpTypeStruct %uint %double
+        %B_0 = OpTypeStruct %v3float %A_0
+        %C_0 = OpTypeStruct %B_0 %uint
+%_ptr_Function_C_0 = OpTypePointer Function %C_0
+%_ptr_PhysicalStorageBuffer_C = OpTypePointer PhysicalStorageBuffer %C
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+       %newC = OpVariable %_ptr_Function_C_0 Function
+         %22 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_Ptr %_ %int_0
+         %23 = OpLoad %_ptr_PhysicalStorageBuffer_Ptr %22
+         %29 = OpLoad %C_0 %newC
+         %31 = OpAccessChain %_ptr_PhysicalStorageBuffer_C %23 %int_0
+         %32 = OpCopyLogical %C %29
+               OpStore %31 %32 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-06314"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Memory accesses Aligned operand value 4 is too small, "
+                        "the largest scalar type is 8 bytes"));
+}
+
+TEST_F(ValidateMemory, PSBStoreAlignedPtrAccessChain) {
+  const std::string body = R"(
+               OpCapability PhysicalStorageBufferAddresses
+               OpCapability Int64
+               OpCapability Shader
+               OpExtension "SPV_KHR_non_semantic_info"
+               OpExtension "SPV_KHR_physical_storage_buffer"
+          %2 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+               OpMemoryModel PhysicalStorageBuffer64 GLSL450
+               OpEntryPoint GLCompute %computeMain "main" %globalParams
+               OpExecutionMode %computeMain LocalSize 1 1 1
+               OpDecorate %_ptr_PhysicalStorageBuffer_ulong ArrayStride 8
+               OpDecorate %GlobalParams_std140 Block
+               OpMemberDecorate %GlobalParams_std140 0 Offset 0
+               OpDecorate %globalParams Binding 0
+               OpDecorate %globalParams DescriptorSet 0
+       %void = OpTypeVoid
+       %uint = OpTypeInt 32 0
+         %12 = OpTypeFunction %void
+      %ulong = OpTypeInt 64 0
+%_ptr_PhysicalStorageBuffer_ulong = OpTypePointer PhysicalStorageBuffer %ulong
+%GlobalParams_std140 = OpTypeStruct %_ptr_PhysicalStorageBuffer_ulong
+%_ptr_Uniform_GlobalParams_std140 = OpTypePointer Uniform %GlobalParams_std140
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_Uniform__ptr_PhysicalStorageBuffer_ulong = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_ulong
+    %ulong_1 = OpConstant %ulong 1
+%globalParams = OpVariable %_ptr_Uniform_GlobalParams_std140 Uniform
+%computeMain = OpFunction %void None %12
+         %13 = OpLabel
+         %36 = OpInBoundsAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_ulong %globalParams %int_0
+         %37 = OpLoad %_ptr_PhysicalStorageBuffer_ulong %36
+         %38 = OpPtrAccessChain %_ptr_PhysicalStorageBuffer_ulong %37 %int_0
+               OpStore %38 %ulong_1 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-06314"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Memory accesses Aligned operand value 4 is too small, "
+                        "the largest scalar type is 8 bytes"));
+}
+
+TEST_F(ValidateMemory, PSBStoreAlignedUntypedStorageBuffer) {
+  const std::string body = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability PhysicalStorageBufferAddresses
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpMemoryModel PhysicalStorageBuffer64 GLSL450
+               OpEntryPoint GLCompute %main "main" %_
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %SSBO Block
+               OpMemberDecorate %SSBO 0 Offset 0
+               OpDecorate %B Block
+               OpMemberDecorate %B 0 Offset 0
+               OpDecorate %_ Binding 0
+               OpDecorate %_ DescriptorSet 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+               OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_B PhysicalStorageBuffer
+       %SSBO = OpTypeStruct %_ptr_PhysicalStorageBuffer_B
+       %uint = OpTypeInt 32 0
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+     %uint_0 = OpConstant %uint 0
+          %B = OpTypeStruct %uint
+%_ptr_PhysicalStorageBuffer_B = OpTypePointer PhysicalStorageBuffer %B
+%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_B = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_B
+%untyped_ptr = OpTypeUntypedPointerKHR StorageBuffer
+          %_ = OpUntypedVariableKHR %untyped_ptr StorageBuffer %SSBO
+%_ptr_PhysicalStorageBuffer_uint = OpTypePointer PhysicalStorageBuffer %uint
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+         %16 = OpUntypedAccessChainKHR %untyped_ptr %SSBO %_ %int_0
+         %17 = OpLoad %_ptr_PhysicalStorageBuffer_B %16
+         %20 = OpAccessChain %_ptr_PhysicalStorageBuffer_uint %17 %int_0
+               OpStore %20 %uint_0 Aligned 2
+               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-06314"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Memory accesses Aligned operand value 2 is too small, "
+                        "the largest scalar type is 4 bytes"));
+}
+
+TEST_F(ValidateMemory, PSBStoreAlignedUntypedPhysicalStorageBuffer) {
+  const std::string body = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability PhysicalStorageBufferAddresses
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpMemoryModel PhysicalStorageBuffer64 GLSL450
+               OpEntryPoint GLCompute %main "main" %_
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %SSBO Block
+               OpMemberDecorate %SSBO 0 Offset 0
+               OpDecorate %B Block
+               OpMemberDecorate %B 0 Offset 0
+               OpDecorate %_ Binding 0
+               OpDecorate %_ DescriptorSet 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+               OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_B PhysicalStorageBuffer
+       %SSBO = OpTypeStruct %_ptr_PhysicalStorageBuffer_B
+       %uint = OpTypeInt 32 0
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+     %uint_0 = OpConstant %uint 0
+          %B = OpTypeStruct %uint
+%_ptr_PhysicalStorageBuffer_B = OpTypePointer PhysicalStorageBuffer %B
+%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_B = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_B
+%untyped_ptr = OpTypeUntypedPointerKHR StorageBuffer
+          %_ = OpUntypedVariableKHR %untyped_ptr StorageBuffer %SSBO
+%_ptr_PhysicalStorageBuffer_uint = OpTypePointer PhysicalStorageBuffer %uint
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+         %16 = OpUntypedAccessChainKHR %untyped_ptr %SSBO %_ %int_0
+         %17 = OpLoad %_ptr_PhysicalStorageBuffer_B %16
+         %20 = OpAccessChain %_ptr_PhysicalStorageBuffer_uint %17 %int_0
+               OpStore %20 %uint_0 Aligned 2
+               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-06314"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Memory accesses Aligned operand value 2 is too small, "
+                        "the largest scalar type is 4 bytes"));
+}
+
+TEST_F(ValidateMemory, PSBStoreAlignedVariousTypeSuccess) {
+  const std::string body = R"(
+               OpCapability Shader
+               OpCapability Float64
+               OpCapability Int8
+               OpCapability StorageBuffer8BitAccess
+               OpCapability PhysicalStorageBufferAddresses
+          %2 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel PhysicalStorageBuffer64 GLSL450
+               OpEntryPoint GLCompute %main "main" %_
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %SSBO Block
+               OpMemberDecorate %SSBO 0 Offset 0
+               OpDecorate %Ptr Block
+               OpMemberDecorate %Ptr 0 Offset 0
+               OpMemberDecorate %Ptr 1 Offset 4
+               OpMemberDecorate %Ptr 2 Offset 8
+               OpDecorate %_ Binding 0
+               OpDecorate %_ DescriptorSet 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+               OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_Ptr PhysicalStorageBuffer
+       %SSBO = OpTypeStruct %_ptr_PhysicalStorageBuffer_Ptr
+      %uchar = OpTypeInt 8 0
+       %uint = OpTypeInt 32 0
+     %double = OpTypeFloat 64
+        %Ptr = OpTypeStruct %uchar %uint %double
+%_ptr_PhysicalStorageBuffer_Ptr = OpTypePointer PhysicalStorageBuffer %Ptr
+%_ptr_StorageBuffer_SSBO = OpTypePointer StorageBuffer %SSBO
+          %_ = OpVariable %_ptr_StorageBuffer_SSBO StorageBuffer
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_Ptr = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_Ptr
+    %uchar_0 = OpConstant %uchar 0
+%_ptr_PhysicalStorageBuffer_uchar = OpTypePointer PhysicalStorageBuffer %uchar
+      %int_1 = OpConstant %int 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_PhysicalStorageBuffer_uint = OpTypePointer PhysicalStorageBuffer %uint
+      %int_2 = OpConstant %int 2
+   %double_0 = OpConstant %double 0
+%_ptr_PhysicalStorageBuffer_double = OpTypePointer PhysicalStorageBuffer %double
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+         %18 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_Ptr %_ %int_0
+         %19 = OpLoad %_ptr_PhysicalStorageBuffer_Ptr %18
+         %22 = OpAccessChain %_ptr_PhysicalStorageBuffer_uchar %19 %int_0
+               OpStore %22 %uchar_0 Aligned 1
+         %23 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_Ptr %_ %int_0
+         %24 = OpLoad %_ptr_PhysicalStorageBuffer_Ptr %23
+         %28 = OpAccessChain %_ptr_PhysicalStorageBuffer_uint %24 %int_1
+               OpStore %28 %uint_0 Aligned 4
+         %29 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_Ptr %_ %int_0
+         %30 = OpLoad %_ptr_PhysicalStorageBuffer_Ptr %29
+         %34 = OpAccessChain %_ptr_PhysicalStorageBuffer_double %30 %int_2
+               OpStore %34 %double_0 Aligned 8
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+// https://github.com/KhronosGroup/SPIRV-Tools/issues/6322
+TEST_F(ValidateMemory, PSBStoreAlignedUntypedBitcaseSuccess) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability VulkanMemoryModel
+OpCapability VulkanMemoryModelDeviceScopeKHR
+OpCapability Int8
+OpCapability PhysicalStorageBufferAddresses
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_KHR_physical_storage_buffer"
+OpMemoryModel PhysicalStorageBuffer64 Vulkan
+OpEntryPoint GLCompute %main "main" %id
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %id BuiltIn GlobalInvocationId
+OpDecorate %untyped_phys_ptr ArrayStride 1
+OpDecorate %data_buffer Block
+OpMemberDecorate %data_buffer 0 Offset 0
+OpDecorate %phys_ptrs_struct Block
+OpMemberDecorate %phys_ptrs_struct 0 Offset 0
+OpMemberDecorate %phys_ptrs_struct 1 Offset 8
+OpDecorate %all_data_var DescriptorSet 0
+OpDecorate %all_data_var Binding 0
+
+%uint32 = OpTypeInt 32 0
+%void = OpTypeVoid
+%uint8 = OpTypeInt 8 0
+%vec3_uint32 = OpTypeVector %uint32 3
+%void_func = OpTypeFunction %void
+%c_uint32_0 = OpConstant %uint32 0
+%c_uint32_1 = OpConstant %uint32 1
+%c_uint32_32 = OpConstant %uint32 32
+
+%uint32_input_ptr = OpTypePointer Input %uint32
+%vec3_uint32_input_ptr = OpTypePointer Input %vec3_uint32
+%data_buffer = OpTypeStruct %uint8
+%untyped_phys_ptr = OpTypeUntypedPointerKHR PhysicalStorageBuffer
+%uint8_phys_ptr = OpTypePointer PhysicalStorageBuffer %uint8
+%data_buffer_phys_ptr = OpTypePointer PhysicalStorageBuffer %data_buffer
+%data_buffer_phys_ptr_ptr = OpTypePointer StorageBuffer %data_buffer_phys_ptr
+%phys_ptrs_struct = OpTypeStruct %data_buffer_phys_ptr %data_buffer_phys_ptr
+%phys_ptrs_struct_ptr = OpTypePointer StorageBuffer %phys_ptrs_struct
+%all_data_var = OpVariable %phys_ptrs_struct_ptr StorageBuffer
+%id = OpVariable %vec3_uint32_input_ptr Input
+%main = OpFunction %void None %void_func
+%label_main = OpLabel
+%input_ptr = OpAccessChain %data_buffer_phys_ptr_ptr %all_data_var %c_uint32_0
+%input = OpLoad %data_buffer_phys_ptr %input_ptr
+%input_loc = OpAccessChain %uint8_phys_ptr %input %c_uint32_0
+%output_ptr = OpAccessChain %data_buffer_phys_ptr_ptr %all_data_var %c_uint32_1
+%output = OpLoad %data_buffer_phys_ptr %output_ptr
+%output_loc = OpUntypedAccessChainKHR %untyped_phys_ptr %data_buffer %output %c_uint32_0
+%bitcasted = OpBitcast %untyped_phys_ptr %input_loc
+%bitcasted_val = OpLoad %uint8 %bitcasted Aligned 1
+OpStore %output_loc %bitcasted_val Aligned 1
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_1);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+}
+
+// https://github.com/KhronosGroup/SPIRV-Tools/issues/6322
+TEST_F(ValidateMemory, PSBStoreAlignedUntypedBitcase) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability VulkanMemoryModel
+OpCapability VulkanMemoryModelDeviceScopeKHR
+OpCapability Int16
+OpCapability PhysicalStorageBufferAddresses
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_KHR_physical_storage_buffer"
+OpMemoryModel PhysicalStorageBuffer64 Vulkan
+OpEntryPoint GLCompute %main "main" %id
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %id BuiltIn GlobalInvocationId
+OpDecorate %untyped_phys_ptr ArrayStride 1
+OpDecorate %data_buffer Block
+OpMemberDecorate %data_buffer 0 Offset 0
+OpDecorate %phys_ptrs_struct Block
+OpMemberDecorate %phys_ptrs_struct 0 Offset 0
+OpMemberDecorate %phys_ptrs_struct 1 Offset 8
+OpDecorate %all_data_var DescriptorSet 0
+OpDecorate %all_data_var Binding 0
+
+%uint32 = OpTypeInt 32 0
+%void = OpTypeVoid
+%uint16 = OpTypeInt 16 0
+%vec3_uint32 = OpTypeVector %uint32 3
+%void_func = OpTypeFunction %void
+%c_uint32_0 = OpConstant %uint32 0
+%c_uint32_1 = OpConstant %uint32 1
+%c_uint32_32 = OpConstant %uint32 32
+
+%uint32_input_ptr = OpTypePointer Input %uint32
+%vec3_uint32_input_ptr = OpTypePointer Input %vec3_uint32
+%data_buffer = OpTypeStruct %uint16
+%untyped_phys_ptr = OpTypeUntypedPointerKHR PhysicalStorageBuffer
+%uint16_phys_ptr = OpTypePointer PhysicalStorageBuffer %uint16
+%data_buffer_phys_ptr = OpTypePointer PhysicalStorageBuffer %data_buffer
+%data_buffer_phys_ptr_ptr = OpTypePointer StorageBuffer %data_buffer_phys_ptr
+%phys_ptrs_struct = OpTypeStruct %data_buffer_phys_ptr %data_buffer_phys_ptr
+%phys_ptrs_struct_ptr = OpTypePointer StorageBuffer %phys_ptrs_struct
+%all_data_var = OpVariable %phys_ptrs_struct_ptr StorageBuffer
+%id = OpVariable %vec3_uint32_input_ptr Input
+%main = OpFunction %void None %void_func
+%label_main = OpLabel
+%input_ptr = OpAccessChain %data_buffer_phys_ptr_ptr %all_data_var %c_uint32_0
+%input = OpLoad %data_buffer_phys_ptr %input_ptr
+%input_loc = OpAccessChain %uint16_phys_ptr %input %c_uint32_0
+%output_ptr = OpAccessChain %data_buffer_phys_ptr_ptr %all_data_var %c_uint32_1
+%output = OpLoad %data_buffer_phys_ptr %output_ptr
+%output_loc = OpUntypedAccessChainKHR %untyped_phys_ptr %data_buffer %output %c_uint32_0
+%bitcasted = OpBitcast %untyped_phys_ptr %input_loc
+%bitcasted_val = OpLoad %uint16 %bitcasted Aligned 1
+OpStore %output_loc %bitcasted_val Aligned 1
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(body.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-PhysicalStorageBuffer64-06314"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Memory accesses Aligned operand value 1 is too small, "
+                        "the largest scalar type is 2 bytes"));
+}
+
 std::string GenCoopMatLoadStoreShader(const std::string& storeMemoryAccess,
                                       const std::string& loadMemoryAccess) {
   std::string s = R"(
@@ -2168,7 +2815,8 @@
 %116 = OpLoad %71 %111
 %121 = OpLoad %6 %60
 %122 = OpAccessChain %82 %120 %79 %121
-OpCooperativeMatrixStoreNV %122 %116 %84 %86 )" + storeMemoryAccess + R"( %81
+OpCooperativeMatrixStoreNV %122 %116 %84 %86 )" +
+                  storeMemoryAccess + R"( %81
 OpReturn
 OpFunctionEnd
 )";
@@ -2350,19 +2998,21 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-std::string GenCoopMatLoadStoreShaderKHR(const std::string& storeMemoryAccess,
-                                         const std::string& loadMemoryAccess,
-                                         unsigned layout = 0,
-                                         bool useSpecConstantLayout = false,
-                                         bool useStoreStride = true,
-                                         bool useLoadStride = true) {
+std::string GenCoopMatLoadStoreShaderKHR(
+    const std::string& storeMemoryAccess, const std::string& loadMemoryAccess,
+    unsigned layout = 0, bool useSpecConstantLayout = false,
+    bool useStoreStride = true, bool useLoadStride = true,
+    bool useConstantStride = true, unsigned stride = 4, unsigned rows = 16,
+    unsigned cols = 16) {
   std::string s = R"(
 OpCapability Shader
 OpCapability GroupNonUniform
 OpCapability VulkanMemoryModelKHR
 OpCapability CooperativeMatrixKHR
+OpCapability CooperativeMatrixLayoutsARM
 OpExtension "SPV_KHR_vulkan_memory_model"
 OpExtension "SPV_KHR_cooperative_matrix"
+OpExtension "SPV_ARM_cooperative_matrix_layouts"
 %1 = OpExtInstImport "GLSL.std.450"
 OpMemoryModel Logical VulkanKHR
 OpEntryPoint GLCompute %4 "main" %11 %21
@@ -2402,6 +3052,7 @@
 %7 = OpTypeVector %6 2
 %8 = OpTypePointer Function %7
 %10 = OpTypePointer Input %6
+%ptr_uint_Function = OpTypePointer Function %6
 %11 = OpVariable %10 Input
 %13 = OpConstant %6 2
 %19 = OpTypeVector %6 3
@@ -2411,7 +3062,8 @@
 %31 = OpTypePointer Function %6
 %33 = OpConstant %6 1024
 %34 = OpConstant %6 1
-%38 = OpConstant %6 8
+%cols = OpConstant %6 )" +
+                  std::to_string(cols) + R"(
 %uint_0 = OpConstant %6 0
 )";
   if (useSpecConstantLayout) {
@@ -2421,9 +3073,10 @@
   }
   s += R"(
 %68 = OpTypeFloat 32
-%69 = OpConstant %6 16
-%70 = OpConstant %6 3
-%71 = OpTypeCooperativeMatrixKHR %68 %70 %69 %38 %uint_0
+%rows = OpConstant %6 )" +
+       std::to_string(rows) + R"(
+%scope = OpConstant %6 3
+%71 = OpTypeCooperativeMatrixKHR %68 %scope %rows %cols %uint_0
 %72 = OpTypePointer Function %71
 %74 = OpTypeRuntimeArray %68
 %75 = OpTypeStruct %74
@@ -2433,7 +3086,11 @@
 %79 = OpConstant %78 0
 %81 = OpConstant %6 5
 %82 = OpTypePointer StorageBuffer %68
-%stride = OpConstant %6 64
+)";
+  if (useConstantStride) {
+    s += "%stride = OpConstant %6 " + std::to_string(stride);
+  }
+  s += R"(
 %88 = OpTypePointer Private %71
 %89 = OpVariable %88 Private
 %92 = OpTypeRuntimeArray %68
@@ -2464,6 +3121,7 @@
 %5 = OpLabel
 %9 = OpVariable %8 Function
 %18 = OpVariable %8 Function
+%stride_var = OpVariable %ptr_uint_Function Function
 %32 = OpVariable %31 Function
 %44 = OpVariable %31 Function
 %52 = OpVariable %31 Function
@@ -2471,6 +3129,11 @@
 %73 = OpVariable %72 Function
 %91 = OpVariable %72 Function
 %101 = OpVariable %72 Function
+)";
+  if (!useConstantStride) {
+    s += "%stride = OpLoad %6 %stride_var";
+  }
+  s += R"(
 %12 = OpLoad %6 %11
 %14 = OpUMod %6 %12 %13
 %15 = OpLoad %6 %11
@@ -2491,7 +3154,7 @@
 %37 = OpIMul %6 %33 %36
 %40 = OpAccessChain %31 %18 %uint_0
 %41 = OpLoad %6 %40
-%42 = OpIMul %6 %38 %41
+%42 = OpIMul %6 %cols %41
 %43 = OpIAdd %6 %37 %42
 OpStore %32 %43
 %45 = OpAccessChain %31 %18 %34
@@ -2499,7 +3162,7 @@
 %47 = OpIMul %6 %33 %46
 %48 = OpAccessChain %31 %18 %uint_0
 %49 = OpLoad %6 %48
-%50 = OpIMul %6 %38 %49
+%50 = OpIMul %6 %cols %49
 %51 = OpIAdd %6 %47 %50
 OpStore %44 %51
 %53 = OpAccessChain %31 %18 %34
@@ -2507,7 +3170,7 @@
 %55 = OpIMul %6 %33 %54
 %56 = OpAccessChain %31 %18 %uint_0
 %57 = OpLoad %6 %56
-%58 = OpIMul %6 %38 %57
+%58 = OpIMul %6 %cols %57
 %59 = OpIAdd %6 %55 %58
 OpStore %52 %59
 %61 = OpAccessChain %31 %18 %34
@@ -2515,7 +3178,7 @@
 %63 = OpIMul %6 %33 %62
 %64 = OpAccessChain %31 %18 %uint_0
 %65 = OpLoad %6 %64
-%66 = OpIMul %6 %38 %65
+%66 = OpIMul %6 %cols %65
 %67 = OpIAdd %6 %63 %66
 OpStore %60 %67
 %80 = OpLoad %6 %32
@@ -2594,7 +3257,19 @@
             true},
         StrideMissingCase{
             (unsigned)spv::CooperativeMatrixLayout::ColumnMajorKHR, true,
-            false}));
+            false},
+        StrideMissingCase{
+            (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM,
+            false, true},
+        StrideMissingCase{
+            (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM,
+            true, false},
+        StrideMissingCase{
+            (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM,
+            false, true},
+        StrideMissingCase{
+            (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM,
+            true, false}));
 
 TEST_P(ValidateCoopMatrixStrideMissing, CoopMatKHRLoadStrideMissingFail) {
   const StrideMissingCase& param = GetParam();
@@ -2643,14 +3318,198 @@
               HasSubstr("MakePointerAvailableKHR cannot be used with OpLoad"));
 }
 
+TEST_F(ValidateMemory,
+       CoopMatLoadArmRowBlockedInterleavedLayoutNonConstantStrideFail) {
+  std::string spirv = GenCoopMatLoadStoreShaderKHR(
+      "MakePointerAvailableKHR|NonPrivatePointerKHR",
+      "MakePointerAvailableKHR|NonPrivatePointerKHR",
+      (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM,
+      false, /* useSpecConstantLayout */
+      true,  /* useStoreStride */
+      true,  /* useLoadStride */
+      false /* useConstantStride */);
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("MemoryLayout 4202 requires Stride come from a "
+                        "constant instruction"));
+}
+
+TEST_F(ValidateMemory,
+       CoopMatLoadArmColumnBlockedInterleavedLayoutNonConstantStrideFail) {
+  std::string spirv = GenCoopMatLoadStoreShaderKHR(
+      "MakePointerAvailableKHR|NonPrivatePointerKHR",
+      "MakePointerAvailableKHR|NonPrivatePointerKHR",
+      (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM,
+      false, /* useSpecConstantLayout */
+      true,  /* useStoreStride */
+      true,  /* useLoadStride */
+      false /* useConstantStride */);
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("MemoryLayout 4203 requires Stride come from a "
+                        "constant instruction"));
+}
+
+struct StrideValueCase {
+  unsigned layout;
+  unsigned stride;
+  bool valid;
+};
+
+using ValidateCoopMatrixStrideValue = spvtest::ValidateBase<StrideValueCase>;
+
+INSTANTIATE_TEST_SUITE_P(
+    CoopMatrixStrideValue, ValidateCoopMatrixStrideValue,
+    Values(
+        StrideValueCase{
+            (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, 1,
+            true},
+        StrideValueCase{
+            (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, 2,
+            true},
+        StrideValueCase{
+            (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, 4,
+            true},
+        StrideValueCase{
+            (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, 8,
+            false},
+        StrideValueCase{
+            (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM,
+            1, true},
+        StrideValueCase{
+            (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM,
+            2, true},
+        StrideValueCase{
+            (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM,
+            4, true},
+        StrideValueCase{
+            (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM,
+            8, false}));
+
+TEST_P(ValidateCoopMatrixStrideValue, CoopMatLoadStoreStrideValue) {
+  const StrideValueCase& param = GetParam();
+  std::string spirv = GenCoopMatLoadStoreShaderKHR(
+      "MakePointerAvailableKHR|NonPrivatePointerKHR",
+      "MakePointerVisibleKHR|NonPrivatePointerKHR", param.layout,
+      false /*useSpecConstantLayout*/, true /*useStoreStride*/,
+      true /*useLoadStride*/, true /*useConstantStride*/, param.stride);
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  if (param.valid) {
+    EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  } else {
+    EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("MemoryLayout " + std::to_string(param.layout) +
+                          " requires Stride be 1, 2, or 4"));
+  }
+}
+
+struct MatrixSizeCase {
+  unsigned layout;
+  unsigned rows;
+  unsigned cols;
+  unsigned stride;
+  bool valid_rows;
+  bool valid_cols;
+};
+
+using ValidateCoopMatrixSize = spvtest::ValidateBase<MatrixSizeCase>;
+
+INSTANTIATE_TEST_SUITE_P(
+    CoopMatrixMatrixSize, ValidateCoopMatrixSize,
+    Values(
+        MatrixSizeCase{
+            (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, 4,
+            4, 1, true, true},
+        MatrixSizeCase{
+            (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, 4,
+            8, 2, true, true},
+        MatrixSizeCase{
+            (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, 2,
+            4, 1, false, true},
+        MatrixSizeCase{
+            (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, 2,
+            4, 2, false, false},
+        MatrixSizeCase{
+            (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, 4,
+            2, 1, true, false},
+        MatrixSizeCase{
+            (unsigned)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM, 4,
+            4, 2, true, false},
+        MatrixSizeCase{
+            (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM,
+            4, 4, 1, true, true},
+        MatrixSizeCase{
+            (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM,
+            8, 4, 2, true, true},
+        MatrixSizeCase{
+            (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM,
+            4, 2, 1, true, false},
+        MatrixSizeCase{
+            (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM,
+            4, 2, 2, false, false},
+        MatrixSizeCase{
+            (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM,
+            2, 4, 1, false, true},
+        MatrixSizeCase{
+            (unsigned)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM,
+            4, 4, 2, false, true}));
+
+TEST_P(ValidateCoopMatrixSize, CoopMatLoadStoreMatrixSize) {
+  const MatrixSizeCase& param = GetParam();
+  std::string spirv = GenCoopMatLoadStoreShaderKHR(
+      "MakePointerAvailableKHR|NonPrivatePointerKHR",
+      "MakePointerVisibleKHR|NonPrivatePointerKHR", param.layout,
+      false /*useSpecConstantLayout*/, true /*useStoreStride*/,
+      true /*useLoadStride*/, true /*useConstantStride*/, param.stride,
+      param.rows, param.cols);
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+
+  uint32_t rows_required_multiple = 4;
+  uint32_t cols_required_multiple =
+      16 / 4;  // The shader always uses FP32 matrix components
+
+  if (param.layout ==
+      (uint64_t)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM) {
+    cols_required_multiple *= param.stride;
+  }
+  if (param.layout ==
+      (uint64_t)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM) {
+    rows_required_multiple *= param.stride;
+  }
+
+  if (param.valid_rows && param.valid_cols) {
+    EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  } else if (param.valid_rows) {
+    EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+    EXPECT_THAT(
+        getDiagnosticString(),
+        HasSubstr("MemoryLayout " + std::to_string(param.layout) +
+                  " with a Stride of " + std::to_string(param.stride) +
+                  " requires that the number of columns be a multiple of " +
+                  std::to_string(cols_required_multiple)));
+  } else {
+    EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+    EXPECT_THAT(
+        getDiagnosticString(),
+        HasSubstr("MemoryLayout " + std::to_string(param.layout) +
+                  " with a Stride of " + std::to_string(param.stride) +
+                  " requires that the number of rows be a multiple of " +
+                  std::to_string(rows_required_multiple)));
+  }
+}
+
 TEST_F(ValidateMemory, CoopMatKHRInvalidStorageClassFail) {
   const std::string body = R"(
 OpCapability Shader
 OpCapability Float16
 OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
 OpExtension "SPV_KHR_cooperative_matrix"
 OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical GLSL450
+OpMemoryModel Logical VulkanKHR
 OpEntryPoint GLCompute %main "main"
 %void = OpTypeVoid
 %func = OpTypeFunction %void
@@ -2673,8 +3532,8 @@
 OpReturn
 OpFunctionEnd)";
 
-  CompileSuccessfully(body.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -2688,9 +3547,10 @@
 OpCapability Shader
 OpCapability Float16
 OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
 OpExtension "SPV_KHR_cooperative_matrix"
 OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical GLSL450
+OpMemoryModel Logical VulkanKHR
 OpEntryPoint GLCompute %main "main"
 %void = OpTypeVoid
 %func = OpTypeFunction %void
@@ -2712,8 +3572,8 @@
 OpReturn
 OpFunctionEnd)";
 
-  CompileSuccessfully(body.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("The Result Type of OpCooperativeMatrixLengthKHR <id> "
@@ -2726,9 +3586,10 @@
 OpCapability Shader
 OpCapability Float16
 OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
 OpExtension "SPV_KHR_cooperative_matrix"
 OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical GLSL450
+OpMemoryModel Logical VulkanKHR
 OpEntryPoint GLCompute %main "main"
 %void = OpTypeVoid
 %func = OpTypeFunction %void
@@ -2750,8 +3611,8 @@
 OpReturn
 OpFunctionEnd)";
 
-  CompileSuccessfully(body.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("The type in OpCooperativeMatrixLengthKHR <id> '5[%uint]' "
@@ -2764,9 +3625,10 @@
 OpCapability Shader
 OpCapability Float16
 OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
 OpExtension "SPV_KHR_cooperative_matrix"
 OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical GLSL450
+OpMemoryModel Logical VulkanKHR
 OpEntryPoint GLCompute %main "main"
 %void = OpTypeVoid
 %func = OpTypeFunction %void
@@ -2788,8 +3650,8 @@
 OpReturn
 OpFunctionEnd)";
 
-  CompileSuccessfully(body.c_str());
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+  CompileSuccessfully(body.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
 TEST_F(ValidateMemory, VulkanRTAOutsideOfStructBad) {
@@ -2820,7 +3682,8 @@
           "OpVariable, <id> '5[%5]', 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 "
-          "be instantiated via OpVariable\n  %5 = OpVariable "
+          "be instantiated via OpVariable, unless the RuntimeDescriptorArray "
+          "Capability is declared\n  %5 = OpVariable "
           "%_ptr_UniformConstant__runtimearr_2 UniformConstant\n"));
 }
 
@@ -2834,6 +3697,10 @@
 OpExecutionMode %func OriginUpperLeft
 OpDecorate %struct Block
 OpMemberDecorate %struct 0 Offset 0
+OpDecorate %2 DescriptorSet 0
+OpDecorate %2 Binding 0
+OpDecorate %3 DescriptorSet 0
+OpDecorate %3 Binding 1
 %sampler_t = OpTypeSampler
 %uint = OpTypeInt 32 0
 %array_t = OpTypeRuntimeArray %sampler_t
@@ -2883,7 +3750,7 @@
               AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("For Vulkan with RuntimeDescriptorArrayEXT, a variable "
+      HasSubstr("For Vulkan with RuntimeDescriptorArray, a variable "
                 "containing OpTypeRuntimeArray must have storage class of "
                 "StorageBuffer, Uniform, or UniformConstant.\n  %5 = "
                 "OpVariable %_ptr_Workgroup__runtimearr_uint Workgroup\n"));
@@ -2898,6 +3765,8 @@
 OpDecorate %array_t ArrayStride 4
 OpMemberDecorate %struct_t 0 Offset 0
 OpDecorate %struct_t Block
+OpDecorate %2 DescriptorSet 0
+OpDecorate %2 Binding 0
 %uint_t = OpTypeInt 32 0
 %array_t = OpTypeRuntimeArray %uint_t
 %struct_t = OpTypeStruct %array_t
@@ -2989,6 +3858,8 @@
 OpDecorate %array_t ArrayStride 4
 OpMemberDecorate %struct_t 0 Offset 0
 OpDecorate %struct_t BufferBlock
+OpDecorate %2 DescriptorSet 0
+OpDecorate %2 Binding 0
 %uint_t = OpTypeInt 32 0
 %array_t = OpTypeRuntimeArray %uint_t
 %struct_t = OpTypeStruct %array_t
@@ -3006,13 +3877,17 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
 }
 
-TEST_F(ValidateMemory, VulkanRTAInsideUniformStructWithoutBufferBlockBad) {
+TEST_F(ValidateMemory, VulkanRTAInsideUniformStructWithoutBufferBlock) {
   std::string spirv = R"(
 OpCapability Shader
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %func "func"
 OpExecutionMode %func OriginUpperLeft
+OpDecorate %array_t ArrayStride 16
 OpDecorate %struct_t Block
+OpMemberDecorate %struct_t 0 Offset 0
+OpDecorate %2 Binding 0
+OpDecorate %2 DescriptorSet 0
 %uint_t = OpTypeInt 32 0
 %array_t = OpTypeRuntimeArray %uint_t
 %struct_t = OpTypeStruct %array_t
@@ -3027,14 +3902,41 @@
 )";
 
   CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+}
+
+TEST_F(ValidateMemory, VulkanArrayLengthUniformRuntimeArray) {
+  std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %func "func"
+OpExecutionMode %func OriginUpperLeft
+OpDecorate %array_t ArrayStride 16
+OpDecorate %struct_t Block
+OpMemberDecorate %struct_t 0 Offset 0
+OpDecorate %2 Binding 0
+OpDecorate %2 DescriptorSet 0
+%uint_t = OpTypeInt 32 0
+%array_t = OpTypeRuntimeArray %uint_t
+%struct_t = OpTypeStruct %array_t
+%struct_ptr = OpTypePointer Uniform %struct_t
+%2 = OpVariable %struct_ptr Uniform
+%void = OpTypeVoid
+%func_t = OpTypeFunction %void
+%func = OpFunction %void None %func_t
+%1 = OpLabel
+%length = OpArrayLength %uint_t %2 0
+OpReturn
+OpFunctionEnd
+)";
+
+  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"));
+              AnyVUID("VUID-StandaloneSpirv-OpArrayLength-11805"));
   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 "
-                        "%_ptr_Uniform__struct_2 Uniform\n"));
+              HasSubstr("OpArrayLength must not be used on the "
+                        "OpTypeRuntimeArray inside a Uniform block"));
 }
 
 TEST_F(ValidateMemory, VulkanRTAInsideRTABad) {
@@ -3112,9 +4014,10 @@
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %func "func"
 OpExecutionMode %func OriginUpperLeft
-OpDecorate %array_t ArrayStride 4
 OpMemberDecorate %struct_t 0 Offset 0
 OpDecorate %struct_t Block
+OpDecorate %2 DescriptorSet 0
+OpDecorate %2 Binding 0
 %uint_t = OpTypeInt 32 0
 %struct_t = OpTypeStruct %uint_t
 %array_t = OpTypeRuntimeArray %struct_t
@@ -3358,9 +4261,10 @@
 OpEntryPoint Fragment %func "func"
 OpExecutionMode %func OriginUpperLeft
 OpDecorate %inner_array_t ArrayStride 4
-OpDecorate %array_t ArrayStride 4
 OpMemberDecorate %struct_t 0 Offset 0
 OpDecorate %struct_t Block
+OpDecorate %2 DescriptorSet 0
+OpDecorate %2 Binding 0
 %uint_t = OpTypeInt 32 0
 %inner_array_t = OpTypeRuntimeArray %uint_t
 %struct_t = OpTypeStruct %inner_array_t
@@ -3388,9 +4292,10 @@
 OpEntryPoint Fragment %func "func"
 OpExecutionMode %func OriginUpperLeft
 OpDecorate %inner_array_t ArrayStride 4
-OpDecorate %array_t ArrayStride 4
 OpMemberDecorate %struct_t 0 Offset 0
 OpDecorate %struct_t Block
+OpDecorate %2 DescriptorSet 0
+OpDecorate %2 Binding 0
 %uint_t = OpTypeInt 32 0
 %inner_array_t = OpTypeRuntimeArray %uint_t
 %struct_t = OpTypeStruct %inner_array_t
@@ -3761,7 +4666,7 @@
     EXPECT_EQ(SPV_ERROR_INVALID_ID,
               ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
     EXPECT_THAT(getDiagnosticString(),
-                HasSubstr("Instruction cannot for logical addressing model be "
+                HasSubstr("Instruction on logical pointers cannot be "
                           "used without a variable pointers capability"));
   }
 }
@@ -3884,8 +4789,11 @@
 %void = OpTypeVoid
 %bool = OpTypeBool
 %int = OpTypeInt 32 0
+%float = OpTypeFloat 32
 %ptr_int = OpTypePointer Private %int
 %var = OpVariable %ptr_int Private
+%ptr_float = OpTypePointer Private %float
+%var2 = OpVariable %ptr_float Private
 %func_ty = OpTypeFunction %void
 %func = OpFunction %void None %func_ty
 %1 = OpLabel
@@ -3898,7 +4806,7 @@
     spirv += " %bool ";
   }
 
-  spirv += R"(%var %ld
+  spirv += R"(%var %var2
 OpReturn
 OpFunctionEnd
 )";
@@ -3909,6 +4817,223 @@
               HasSubstr("The types of Operand 1 and Operand 2 must match"));
 }
 
+TEST_P(ValidatePointerComparisons, GoodUntypedPointerSameType) {
+  const std::string operation = GetParam();
+
+  std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability VariablePointersStorageBuffer
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%bool = OpTypeBool
+%int = OpTypeInt 32 0
+%ptr = OpTypeUntypedPointerKHR StorageBuffer
+%var = OpUntypedVariableKHR %ptr StorageBuffer
+%func_ty = OpTypeFunction %void
+%func = OpFunction %void None %func_ty
+%1 = OpLabel
+%equal = )" + operation;
+
+  if (operation == "OpPtrDiff") {
+    spirv += " %int ";
+  } else {
+    spirv += " %bool ";
+  }
+
+  spirv += R"(%var %var
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+}
+
+TEST_P(ValidatePointerComparisons, GoodUntypedPointerSameStorageClass) {
+  const std::string operation = GetParam();
+
+  std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability VariablePointersStorageBuffer
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%bool = OpTypeBool
+%int = OpTypeInt 32 0
+%ptr1 = OpTypeUntypedPointerKHR StorageBuffer
+%var = OpUntypedVariableKHR %ptr1 StorageBuffer
+%ptr2 = OpTypeUntypedPointerKHR StorageBuffer
+%var2 = OpUntypedVariableKHR %ptr2 StorageBuffer
+%func_ty = OpTypeFunction %void
+%func = OpFunction %void None %func_ty
+%1 = OpLabel
+%equal = )" + operation;
+
+  if (operation == "OpPtrDiff") {
+    spirv += " %int ";
+  } else {
+    spirv += " %bool ";
+  }
+
+  spirv += R"(%var %var2
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  if (operation == "OpPtrDiff") {
+    EXPECT_EQ(SPV_ERROR_INVALID_ID,
+              ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("The types of Operand 1 and Operand 2 must match"));
+  } else {
+    EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  }
+}
+
+TEST_P(ValidatePointerComparisons, BadUntypedPointerDiffStorageClass) {
+  const std::string operation = GetParam();
+
+  std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%bool = OpTypeBool
+%int = OpTypeInt 32 0
+%ptr1 = OpTypeUntypedPointerKHR StorageBuffer
+%var1 = OpUntypedVariableKHR %ptr1 StorageBuffer
+%ptr2 = OpTypeUntypedPointerKHR Workgroup
+%var2 = OpUntypedVariableKHR %ptr2 Workgroup %int
+%func_ty = OpTypeFunction %void
+%func = OpFunction %void None %func_ty
+%1 = OpLabel
+%equal = )" + operation;
+
+  if (operation == "OpPtrDiff") {
+    spirv += " %int ";
+  } else {
+    spirv += " %bool ";
+  }
+
+  spirv += R"(%var1 %var2
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  if (operation == "OpPtrDiff") {
+    EXPECT_EQ(SPV_ERROR_INVALID_ID,
+              ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("The types of Operand 1 and Operand 2 must match"));
+  } else {
+    EXPECT_EQ(SPV_ERROR_INVALID_ID,
+              ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Pointer storage classes must match"));
+  }
+}
+
+TEST_P(ValidatePointerComparisons, GoodMixedPointerSameStorageClass) {
+  const std::string operation = GetParam();
+
+  std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability VariablePointersStorageBuffer
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%bool = OpTypeBool
+%int = OpTypeInt 32 0
+%ptr1 = OpTypeUntypedPointerKHR StorageBuffer
+%var = OpUntypedVariableKHR %ptr1 StorageBuffer
+%ptr2 = OpTypePointer StorageBuffer %int
+%var2 = OpVariable %ptr2 StorageBuffer
+%func_ty = OpTypeFunction %void
+%func = OpFunction %void None %func_ty
+%1 = OpLabel
+%equal = )" + operation;
+
+  if (operation == "OpPtrDiff") {
+    spirv += " %int ";
+  } else {
+    spirv += " %bool ";
+  }
+
+  spirv += R"(%var %var2
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  if (operation == "OpPtrDiff") {
+    EXPECT_EQ(SPV_ERROR_INVALID_ID,
+              ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("The types of Operand 1 and Operand 2 must match"));
+  } else {
+    EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  }
+}
+
+TEST_P(ValidatePointerComparisons, BadMixedPointerDiffStorageClass) {
+  const std::string operation = GetParam();
+
+  std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%bool = OpTypeBool
+%int = OpTypeInt 32 0
+%ptr1 = OpTypeUntypedPointerKHR StorageBuffer
+%var1 = OpUntypedVariableKHR %ptr1 StorageBuffer
+%ptr2 = OpTypePointer Workgroup %int
+%var2 = OpVariable %ptr2 Workgroup
+%func_ty = OpTypeFunction %void
+%func = OpFunction %void None %func_ty
+%1 = OpLabel
+%equal = )" + operation;
+
+  if (operation == "OpPtrDiff") {
+    spirv += " %int ";
+  } else {
+    spirv += " %bool ";
+  }
+
+  spirv += R"(%var1 %var2
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  if (operation == "OpPtrDiff") {
+    EXPECT_EQ(SPV_ERROR_INVALID_ID,
+              ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("The types of Operand 1 and Operand 2 must match"));
+  } else {
+    EXPECT_EQ(SPV_ERROR_INVALID_ID,
+              ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Pointer storage classes must match"));
+  }
+}
+
 INSTANTIATE_TEST_SUITE_P(PointerComparisons, ValidatePointerComparisons,
                          Values("OpPtrEqual", "OpPtrNotEqual", "OpPtrDiff"));
 
@@ -5169,8 +6294,8 @@
 OpReturn
 OpFunctionEnd
 )";
-  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
   EXPECT_THAT(getDiagnosticString(),
               AnyVUID(" VUID-StandaloneSpirv-OpVariable-04734"));
   EXPECT_THAT(getDiagnosticString(),
@@ -5197,8 +6322,8 @@
 OpReturn
 OpFunctionEnd
 )";
-  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
 }
 
 TEST_F(ValidateMemory, LoadRuntimeArray) {
@@ -5431,7 +6556,45 @@
   CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Vulkan requires that data type be specified"));
+              AnyVUID("VUID-StandaloneSpirv-OpUntypedVariableKHR-11167"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Storage class is StorageBuffer, but Vulkan requires that Data Type "
+          "be specified when not using UniformConstant storage class"));
+}
+
+TEST_F(ValidateMemory, UntypedVariableNoDataTypeNonHeapVulkan) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability DescriptorHeapEXT
+OpCapability UntypedPointersKHR
+OpExtension "SPV_EXT_descriptor_heap"
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%ptr = OpTypeUntypedPointerKHR UniformConstant
+%var = OpUntypedVariableKHR %ptr UniformConstant
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = 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(),
+              AnyVUID("VUID-StandaloneSpirv-OpUntypedVariableKHR-11347"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Storage class is UniformConstant, but Vulkan requires "
+                        "that Data Type be specified if the variable is not "
+                        "decorated with SamplerHeapEXT or ResourceHeapEXT"));
 }
 
 TEST_F(ValidateMemory, PtrAccessChainArrayStrideBad) {
@@ -5674,8 +6837,8 @@
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(), HasSubstr("Index is out of bounds"));
-  EXPECT_THAT(getDiagnosticString(), HasSubstr("cannot find index -224"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Index at word 4 may not have a negative value"));
 }
 
 TEST_F(ValidateMemory, AccessChainNegativeStructIndex64) {
@@ -5702,8 +6865,8 @@
 
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(), HasSubstr("Index is out of bounds"));
-  EXPECT_THAT(getDiagnosticString(), HasSubstr("cannot find index -224"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Index at word 4 may not have a negative value"));
 }
 
 TEST_F(ValidateMemory, UntypedVariableFunctionOutsideFunction) {
@@ -5863,6 +7026,7 @@
   const bool ptr = opcode == "OpUntypedPtrAccessChainKHR" ||
                    opcode == "OpUntypedInBoundsPtrAccessChainKHR";
   const std::string extra_param = ptr ? "%int_0" : "";
+  const std::string deco = ptr ? "OpDecorate %ptr_ssbo ArrayStride 4" : "";
 
   const std::string spirv = R"(
 OpCapability Shader
@@ -5873,6 +7037,7 @@
 OpExtension "SPV_KHR_untyped_pointers"
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %main "main"
+)" + deco + R"(
 %void = OpTypeVoid
 %int = OpTypeInt 32 0
 %int_0 = OpConstant %int 0
@@ -5889,6 +7054,8 @@
 OpFunctionEnd
 )";
 
+  getValidatorOptions()->relax_logical_pointer =
+      opcode == "OpUntypedInBoundsPtrAccessChainKHR";
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
@@ -5898,6 +7065,7 @@
   const bool ptr = opcode == "OpUntypedPtrAccessChainKHR" ||
                    opcode == "OpUntypedInBoundsPtrAccessChainKHR";
   const std::string extra_param = ptr ? "%int_0" : "";
+  const std::string deco = ptr ? "OpDecorate %ptr ArrayStride 4" : "";
 
   const std::string spirv = R"(
 OpCapability Shader
@@ -5908,6 +7076,7 @@
 OpExtension "SPV_KHR_untyped_pointers"
 OpMemoryModel Logical GLSL450
 OpEntryPoint GLCompute %main "main"
+)" + deco + R"(
 %void = OpTypeVoid
 %int = OpTypeInt 32 0
 %int_0 = OpConstant %int 0
@@ -5924,6 +7093,8 @@
 OpFunctionEnd
 )";
 
+  getValidatorOptions()->relax_logical_pointer =
+      opcode == "OpUntypedInBoundsPtrAccessChainKHR";
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
@@ -5961,6 +7132,8 @@
 OpFunctionEnd
 )";
 
+  getValidatorOptions()->relax_logical_pointer =
+      opcode == "OpUntypedInBoundsPtrAccessChainKHR";
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
@@ -6000,6 +7173,8 @@
 OpFunctionEnd
 )";
 
+  getValidatorOptions()->relax_logical_pointer =
+      opcode == "OpUntypedInBoundsPtrAccessChainKHR";
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
@@ -6038,6 +7213,8 @@
 OpFunctionEnd
 )";
 
+  getValidatorOptions()->relax_logical_pointer =
+      opcode == "OpUntypedInBoundsPtrAccessChainKHR";
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
@@ -6076,6 +7253,8 @@
 OpFunctionEnd
 )";
 
+  getValidatorOptions()->relax_logical_pointer =
+      opcode == "OpUntypedInBoundsPtrAccessChainKHR";
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
@@ -6114,6 +7293,8 @@
 OpFunctionEnd
 )";
 
+  getValidatorOptions()->relax_logical_pointer =
+      opcode == "OpUntypedInBoundsPtrAccessChainKHR";
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
@@ -6153,6 +7334,8 @@
 OpFunctionEnd
 )";
 
+  getValidatorOptions()->relax_logical_pointer =
+      opcode == "OpUntypedInBoundsPtrAccessChainKHR";
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(
@@ -6194,6 +7377,8 @@
 OpFunctionEnd
 )";
 
+  getValidatorOptions()->relax_logical_pointer =
+      opcode == "OpUntypedInBoundsPtrAccessChainKHR";
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
@@ -6233,6 +7418,8 @@
 OpFunctionEnd
 )";
 
+  getValidatorOptions()->relax_logical_pointer =
+      opcode == "OpUntypedInBoundsPtrAccessChainKHR";
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
@@ -6568,6 +7755,41 @@
   EXPECT_THAT(getDiagnosticString(), HasSubstr("Size must be a multiple of 2"));
 }
 
+TEST_F(ValidateMemory, CopyMemorySizedVulkanConstant) {
+  const std::string spirv = R"(
+        OpCapability Shader
+        OpCapability UntypedPointersKHR
+        OpExtension "SPV_KHR_untyped_pointers"
+        OpMemoryModel Logical GLSL450
+        OpEntryPoint GLCompute %main "main" %v1 %v2
+        OpExecutionMode %main LocalSize 1 1 1
+        OpDecorate %struct Block
+        OpDecorate %v1 DescriptorSet 0
+        OpDecorate %v1 Binding 0
+        OpDecorate %v2 DescriptorSet 0
+        OpDecorate %v2 Binding 0
+        OpMemberDecorate %struct 0 Offset 0
+        %void = OpTypeVoid
+        %int = OpTypeInt 32 0
+        %int_2 = OpConstant %int 2
+        %struct = OpTypeStruct %int
+        %ptr = OpTypeUntypedPointerKHR StorageBuffer
+        %v1 = OpUntypedVariableKHR %ptr StorageBuffer %struct
+        %v2 = OpUntypedVariableKHR %ptr StorageBuffer %struct
+        %void_fn = OpTypeFunction %void
+        %main = OpFunction %void None %void_fn
+        %entry = OpLabel
+        OpCopyMemorySized %v2 %v1 %int_2
+        OpReturn
+        OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Size must be a multiple of 4"));
+  EXPECT_THAT(getDiagnosticString(), AnyVUID("VUID-RuntimeSpirv-Size-11165"));
+}
+
 TEST_F(ValidateMemory, PtrEqualUntypedPointersGood) {
   const std::string spirv = R"(
 OpCapability Shader
@@ -6851,8 +8073,9 @@
 
   CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("must be OpTypeInt with width 32 and signedness 0"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("must be OpTypeInt with width 32 or 64 and signedness 0"));
 }
 
 TEST_F(ValidateMemory, UntypedArrayLengthBadPointer) {
@@ -6886,7 +8109,46 @@
   CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Pointer must be an untyped pointer"));
+              HasSubstr("Pointer must be an untyped pointer object"));
+}
+
+TEST_F(ValidateMemory, UntypedArrayLengthBadPointer2) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability UntypedPointersKHR
+               OpCapability Int64
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %b
+               OpExecutionMode %main LocalSize 1 1 1
+               OpDecorate %_runtimearr_float ArrayStride 4
+               OpDecorate %B Block
+               OpMemberDecorate %B 0 NonWritable
+               OpMemberDecorate %B 0 Offset 0
+               OpDecorate %b NonWritable
+               OpDecorate %b Binding 0
+               OpDecorate %b DescriptorSet 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+%_runtimearr_float = OpTypeRuntimeArray %float
+          %B = OpTypeStruct %_runtimearr_float
+        %ptr = OpTypeUntypedPointerKHR StorageBuffer
+          %b = OpUntypedVariableKHR %ptr StorageBuffer %B
+      %ulong = OpTypeInt 64 0
+       %long = OpTypeInt 64 1
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+         %13 = OpUntypedArrayLengthKHR %ulong %b %ptr 0
+         %15 = OpBitcast %long %13
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Pointer must be an untyped pointer object"));
 }
 
 TEST_F(ValidateMemory, UntypedArrayLengtBadStruct) {
@@ -7171,6 +8433,639 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
 }
 
+TEST_F(ValidateMemory, PtrAccessChainElementNotInteger) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %ptr_int ArrayStride 4
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%int_0 = OpConstant %int 0
+%float = OpTypeFloat 32
+%float_0 = OpConstant %float 0
+%array = OpTypeArray %int %int_4
+%ptr_array = OpTypePointer Workgroup %array
+%ptr_int = OpTypePointer Workgroup %int
+%var = OpVariable %ptr_array Workgroup
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpAccessChain %ptr_int %var %int_0
+%ptr_gep = OpPtrAccessChain %ptr_int %gep %float_0
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Element must be an integer"));
+}
+
+TEST_F(ValidateMemory, PtrAccessChainElementNotIntegerUntyped) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpCapability WorkgroupMemoryExplicitLayoutKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %var
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %ptr_int ArrayStride 4
+OpDecorate %array ArrayStride 4
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%int_0 = OpConstant %int 0
+%float = OpTypeFloat 32
+%float_0 = OpConstant %float 0
+%array = OpTypeArray %int %int_4
+%block = OpTypeStruct %array
+%ptr_block = OpTypeUntypedPointerKHR Workgroup
+%ptr_int = OpTypeUntypedPointerKHR Workgroup
+%var = OpUntypedVariableKHR %ptr_block Workgroup %block
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpUntypedAccessChainKHR %ptr_int %block %var %int_0 %int_0
+%ptr_gep = OpUntypedPtrAccessChainKHR %ptr_int %int %gep %float_0
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Element must be an integer"));
+}
+
+TEST_F(ValidateMemory, PtrAccessChainElementBlockArrayNonZeroConstant) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%int_1 = OpConstant %int 1
+%int_0 = OpConstant %int 0
+%block = OpTypeStruct %int
+%array = OpTypeArray %block %int_4
+%ptr_array = OpTypePointer StorageBuffer %array
+%ptr_block = OpTypePointer StorageBuffer %block
+%var = OpVariable %ptr_array StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpAccessChain %ptr_block %var %int_0
+%ptr_gep = OpPtrAccessChain %ptr_block %gep %int_1
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Element must be 0 if the interpretation type is a "
+                        "Block- or BufferBlock-decorated structure"));
+}
+
+TEST_F(ValidateMemory, PtrAccessChainElementBlockArrayNonZeroConstantUntyped) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%int_1 = OpConstant %int 1
+%int_0 = OpConstant %int 0
+%block = OpTypeStruct %int
+%array = OpTypeArray %block %int_4
+%ptr_array = OpTypeUntypedPointerKHR StorageBuffer
+%ptr_block = OpTypeUntypedPointerKHR StorageBuffer
+%var = OpUntypedVariableKHR %ptr_array StorageBuffer %array
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpUntypedAccessChainKHR %ptr_block %array %var %int_0
+%ptr_gep = OpUntypedPtrAccessChainKHR %ptr_block %block %gep %int_1
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Element must be 0 if the interpretation type is a "
+                        "Block- or BufferBlock-decorated structure"));
+}
+
+TEST_F(ValidateMemory, UntypedAccessChainBlockArrayMismatch1) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability VariablePointers
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_1 = OpConstant %int 1
+%int_4 = OpConstant %int 4
+%int_0 = OpConstant %int 0
+%block = OpTypeStruct %int
+%array1 = OpTypeArray %block %int_4
+%array2 = OpTypeArray %block %int_4
+%ptr = OpTypeUntypedPointerKHR StorageBuffer
+%var = OpUntypedVariableKHR %ptr StorageBuffer %array1
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpUntypedAccessChainKHR %ptr %array2 %var
+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("If Base or Base Type is a Block or BufferBlock array, "
+                        "the other must also be the same array"));
+}
+
+TEST_F(ValidateMemory, UntypedAccessChainBlockArrayMismatch2) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability VariablePointers
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_1 = OpConstant %int 1
+%int_4 = OpConstant %int 4
+%int_0 = OpConstant %int 0
+%block = OpTypeStruct %int
+%array1 = OpTypeArray %block %int_4
+%ptr = OpTypeUntypedPointerKHR StorageBuffer
+%var = OpUntypedVariableKHR %ptr StorageBuffer %array1
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpUntypedAccessChainKHR %ptr %block %var
+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("Both Base Type and Base must be Block or BufferBlock "
+                        "arrays or neither can be"));
+}
+
+TEST_F(ValidateMemory, UntypedAccessChainBlockArrayMismatch3) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability VariablePointers
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_1 = OpConstant %int 1
+%int_4 = OpConstant %int 4
+%int_0 = OpConstant %int 0
+%block = OpTypeStruct %int
+%array1 = OpTypeArray %block %int_4
+%array2 = OpTypeArray %block %int_4
+%ptr = OpTypeUntypedPointerKHR StorageBuffer
+%ptr_block_array = OpTypePointer StorageBuffer %array1
+%var = OpVariable %ptr_block_array StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%copy1 = OpCopyObject %ptr_block_array %var
+%copy2 = OpCopyObject %ptr_block_array %copy1
+%gep = OpUntypedAccessChainKHR %ptr %array2 %copy2
+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("If Base or Base Type is a Block or BufferBlock array, "
+                        "the other must also be the same array"));
+}
+
+TEST_F(ValidateMemory, UntypedAccessChainBlockArrayMismatch4) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability VariablePointers
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_1 = OpConstant %int 1
+%int_4 = OpConstant %int 4
+%int_0 = OpConstant %int 0
+%block = OpTypeStruct %int
+%array1 = OpTypeArray %block %int_4
+%ptr = OpTypeUntypedPointerKHR StorageBuffer
+%ptr_block_array = OpTypePointer StorageBuffer %array1
+%var = OpVariable %ptr_block_array StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%copy = OpCopyObject %ptr_block_array %var
+%gep = OpUntypedAccessChainKHR %ptr %block %copy
+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("Both Base Type and Base must be Block or BufferBlock "
+                        "arrays or neither can be"));
+}
+
+TEST_F(ValidateMemory, VariableFunctionPointer) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%ptr = OpTypePointer Function %int
+%ptr_ptr = OpTypePointer Function %ptr
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpVariable %ptr_ptr Function
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("In Logical addressing, variables can only allocate a pointer "
+                "to the StorageBuffer or Workgroup storage classes"));
+}
+
+TEST_F(ValidateMemory, VariableFunctionPointerUntyped) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%ptr = OpTypeUntypedPointerKHR Function
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpUntypedVariableKHR %ptr Function %ptr
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("In Logical addressing, variables can only allocate a pointer "
+                "to the StorageBuffer or Workgroup storage classes"));
+}
+
+TEST_F(ValidateMemory, VariableStorageBufferPointer) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%ptr = OpTypePointer StorageBuffer %int
+%ptr_ptr = OpTypePointer Function %ptr
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpVariable %ptr_ptr Function
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "In Logical addressing, variables can only allocate a storage "
+          "buffer pointer if the VariablePointersStorageBuffer capability "
+          "is declared"));
+}
+
+TEST_F(ValidateMemory, VariableStorageBufferPointerUntyped) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%ptr_s = OpTypeUntypedPointerKHR StorageBuffer
+%ptr_f = OpTypeUntypedPointerKHR Function
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpUntypedVariableKHR %ptr_f Function %ptr_s
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "In Logical addressing, variables can only allocate a storage "
+          "buffer pointer if the VariablePointersStorageBuffer capability "
+          "is declared"));
+}
+
+TEST_F(ValidateMemory, VariableWorkgroupPointer) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%ptr = OpTypePointer Workgroup %int
+%ptr_ptr = OpTypePointer Function %ptr
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpVariable %ptr_ptr Function
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "In Logical addressing, variables can only allocate a workgroup "
+          "pointer if the VariablePointers capability is declared"));
+}
+
+TEST_F(ValidateMemory, VariableWorkgroupPointerUntyped) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%ptr_w = OpTypeUntypedPointerKHR Workgroup
+%ptr_f = OpTypeUntypedPointerKHR Function
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpUntypedVariableKHR %ptr_f Function %ptr_w
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "In Logical addressing, variables can only allocate a workgroup "
+          "pointer if the VariablePointers capability is declared"));
+}
+
+TEST_F(ValidateMemory, VariablePointerBadStorageClass) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%ptr = OpTypePointer Workgroup %int
+%ptr_ptr = OpTypePointer Workgroup %ptr
+%var = OpVariable %ptr_ptr Workgroup
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "In Logical addressing with variable pointers, variables that "
+          "allocate pointers must be in Function or Private storage classes"));
+}
+
+TEST_F(ValidateMemory, VariablePointerInStruct) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpExtension "SPV_KHR_variable_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%ptr_s = OpTypePointer Workgroup %int
+%struct_t = OpTypeStruct %ptr_s
+%ptr_struct = OpTypePointer Function %struct_t
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpVariable %ptr_struct Function
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+}
+
+TEST_F(ValidateMemory, VariablePointerInArray) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpExtension "SPV_KHR_variable_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%ptr_s = OpTypePointer Workgroup %int
+%array_t = OpTypeArray %ptr_s %int_4
+%ptr_array = OpTypePointer Function %array_t
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpVariable %ptr_array Function
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+}
+
+TEST_F(ValidateMemory, NoVariablePointerInStruct) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%ptr_f = OpTypePointer Function %int
+%struct_t = OpTypeStruct %ptr_f
+%ptr_struct = OpTypePointer Function %struct_t
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpVariable %ptr_struct Function
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("In Logical addressing, variables can only allocate a pointer "
+                "to the StorageBuffer or Workgroup storage classes"));
+}
+
+TEST_F(ValidateMemory, VariablePointerBadStorageClassUntyped) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability VariablePointers
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%ptr_w = OpTypeUntypedPointerKHR Workgroup
+%var = OpUntypedVariableKHR %ptr_w Workgroup %ptr_w
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "In Logical addressing with variable pointers, variables that "
+          "allocate pointers must be in Function or Private storage classes"));
+}
+
+TEST_F(ValidateMemory, AccessChainNegativeSignedIndex) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%int_n1 = OpConstant %int -1
+%int_4 = OpConstant %int 4
+%array = OpTypeArray %int %int_4
+%ptr_array = OpTypePointer Workgroup %array
+%ptr_int = OpTypePointer Workgroup %int
+%var = OpVariable %ptr_array Workgroup
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpAccessChain %ptr_int %var %int_n1
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Index at word 4 may not have a negative value"));
+}
+
 std::string GenCoopMat2Shader(const std::string& extra_types,
                               const std::string& main_body,
                               const std::string& after_main = "",
@@ -7273,8 +9168,8 @@
       R"(
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
 TEST_F(ValidateMemory, CoopMat2TensorLayoutInvalidDimFail) {
@@ -7287,8 +9182,8 @@
       R"(
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(), HasSubstr("must be between 1 and 5"));
 }
 
@@ -7302,8 +9197,8 @@
       R"(
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("must be a valid TensorClampMode"));
 }
@@ -7320,8 +9215,8 @@
       R"(
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(), HasSubstr("must be between 1 and 5"));
 }
 
@@ -7337,8 +9232,8 @@
       R"(
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Permutation values don't form a valid permutation"));
 }
@@ -7355,8 +9250,8 @@
       R"(
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Incorrect number of permutation values."));
 }
@@ -7374,8 +9269,8 @@
       %tl2 = OpTensorLayoutSetBlockSizeNV %layout %tl %b %b %b
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
 TEST_F(ValidateMemory, CoopMat2TensorLayoutBlockSizeFail) {
@@ -7391,8 +9286,8 @@
       %tl2 = OpTensorLayoutSetBlockSizeNV %layout %tl %b %b %b %b
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("unexpected number of operands"));
 }
@@ -7410,8 +9305,8 @@
       %tl2 = OpTensorLayoutSetDimensionNV %layout %tl %b %b %b
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
 TEST_F(ValidateMemory, CoopMat2TensorLayoutDimensionFail) {
@@ -7427,8 +9322,8 @@
       %tl2 = OpTensorLayoutSetDimensionNV %layout %tl %b %b %b %b
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("unexpected number of operands"));
 }
@@ -7446,8 +9341,8 @@
       %tl2 = OpTensorLayoutSetStrideNV %layout %tl %b %b %b
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
 TEST_F(ValidateMemory, CoopMat2TensorLayoutStrideFail) {
@@ -7463,8 +9358,8 @@
       %tl2 = OpTensorLayoutSetStrideNV %layout %tl %b %b %b %b
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("unexpected number of operands"));
 }
@@ -7482,8 +9377,8 @@
       %tl2 = OpTensorLayoutSliceNV %layout %tl %b %b %b %b %b %b
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
 TEST_F(ValidateMemory, CoopMat2TensorLayoutSliceFail) {
@@ -7499,8 +9394,8 @@
       %tl2 = OpTensorLayoutSliceNV %layout %tl %b %b %b
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("unexpected number of operands"));
 }
@@ -7518,8 +9413,8 @@
       %tl2 = OpTensorLayoutSetClampValueNV %layout %tl %b
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
 TEST_F(ValidateMemory, CoopMat2TensorViewDimensionPass) {
@@ -7538,8 +9433,8 @@
       %tv2 = OpTensorViewSetDimensionNV %view %tv %b %b %b
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
 TEST_F(ValidateMemory, CoopMat2TensorViewDimensionFail) {
@@ -7558,8 +9453,8 @@
       %tv2 = OpTensorViewSetDimensionNV %view %tv %b %b %b %b
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("unexpected number of operands"));
 }
@@ -7580,8 +9475,8 @@
       %tv2 = OpTensorViewSetStrideNV %view %tv %b %b %b
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
 TEST_F(ValidateMemory, CoopMat2TensorViewStrideFail) {
@@ -7600,8 +9495,8 @@
       %tv2 = OpTensorViewSetStrideNV %view %tv %b %b %b %b
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("unexpected number of operands"));
 }
@@ -7622,8 +9517,8 @@
       %tv2 = OpTensorViewSetClipNV %view %tv %b %b %b %b
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
 TEST_F(ValidateMemory, CoopMat2LoadStoreTensorPass) {
@@ -7653,8 +9548,8 @@
       OpCooperativeMatrixStoreTensorNV %array_ptr %mat %tl Aligned 4 TensorView %tv
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
 TEST_F(ValidateMemory, CoopMat2LoadTensorWrongLayoutTypeFail) {
@@ -7675,8 +9570,8 @@
       %mat2 = OpCooperativeMatrixLoadTensorNV %f16mat %array_ptr %mat %tv None None
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("does not have a tensor layout type"));
 }
@@ -7699,8 +9594,8 @@
       %mat2 = OpCooperativeMatrixLoadTensorNV %f16mat %array_ptr %mat %tl None None
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("type does not match Result Type"));
 }
@@ -7723,8 +9618,8 @@
       %mat2 = OpCooperativeMatrixLoadTensorNV %f32mat %array_ptr %mat %tl None DecodeFunc %decodefunc
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("return type must match matrix component type"));
 }
@@ -7762,8 +9657,8 @@
       OpDecorate %psb2 Restrict
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("dimension equal to the tensor dimension"));
 }
@@ -7797,13 +9692,777 @@
       OpFunctionEnd
       )");
 
-  CompileSuccessfully(spirv.c_str());
-  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_UNIVERSAL_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("first parameter must be pointer to PhysicalStorageBuffer"));
 }
 
+TEST_F(ValidateMemory, PtrAccessChainNodePayloadArray) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability ShaderEnqueueAMDX
+OpExtension "SPV_AMDX_shader_enqueue"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %input
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%node0 = OpConstantStringAMDX "node0"
+%node1 = OpConstantStringAMDX "node1"
+%node2 = OpConstantStringAMDX "node2"
+%S = OpTypeStruct %uint
+%_payloadarr_S = OpTypeNodePayloadArrayAMDX %S
+%_ptr_NodePayloadAMDX__payloadarr_S = OpTypePointer NodePayloadAMDX %_payloadarr_S
+%_ptr_NodePayloadAMDX_uint = OpTypePointer NodePayloadAMDX %uint
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%input = OpVariable %_ptr_NodePayloadAMDX__payloadarr_S NodePayloadAMDX
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%x = OpAccessChain %_ptr_NodePayloadAMDX_uint %input %uint_0 %uint_0
+OpReturn
+OpFunctionEnd
+)";
+
+  spv_target_env env = SPV_ENV_UNIVERSAL_1_4;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env));
+}
+
+std::string GenCoopVecLoadStoreShader(const std::string& storeMemoryAccess,
+                                      const std::string& loadMemoryAccess) {
+  std::string s = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability StorageBuffer16BitAccess
+OpCapability VulkanMemoryModel
+OpCapability CooperativeVectorNV
+OpCapability ReplicatedCompositesEXT
+OpExtension "SPV_EXT_replicated_composites"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_NV_cooperative_vector"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical Vulkan
+OpEntryPoint GLCompute %4 "main" %48 %73
+OpExecutionMode %4 LocalSize 1 1 1
+
+OpDecorate %45 ArrayStride 2
+OpDecorate %46 Block
+OpMemberDecorate %46 0 Offset 0
+OpDecorate %48 Binding 0
+OpDecorate %48 DescriptorSet 0
+
+%2 = OpTypeVoid
+%3 = OpTypeFunction %2
+%6 = OpTypeInt 32 0
+%49 = OpTypeInt 32 1
+%41 = OpTypeFloat 16
+
+%14 = OpConstant %6 1
+%50 = OpConstant %49 0
+%82 = OpConstant %6 5
+
+%42 = OpTypeCooperativeVectorNV %41 %14
+%43 = OpTypePointer Function %42
+
+%45 = OpTypeRuntimeArray %41
+%46 = OpTypeStruct %45
+%47 = OpTypePointer StorageBuffer %46
+%48 = OpVariable %47 StorageBuffer
+%51 = OpTypePointer StorageBuffer %45
+
+%57 = OpTypePointer Private %42
+%73 = OpVariable %57 Private
+
+%4 = OpFunction %2 None %3
+%5 = OpLabel
+%52 = OpAccessChain %51 %48 %50
+%56 = OpCooperativeVectorLoadNV %42 %52 %50 )" +
+                  loadMemoryAccess + R"( %82
+%77 = OpLoad %42 %73
+OpCooperativeVectorStoreNV %52 %50 %77 )" +
+                  storeMemoryAccess + R"( %82
+OpReturn
+OpFunctionEnd
+)";
+
+  return s;
+}
+
+TEST_F(ValidateMemory, CoopVecLoadStoreSuccess) {
+  std::string spirv =
+      GenCoopVecLoadStoreShader("MakePointerAvailableKHR|NonPrivatePointerKHR",
+                                "MakePointerVisibleKHR|NonPrivatePointerKHR");
+
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+}
+
+TEST_F(ValidateMemory, CoopVecStoreMemoryAccessFail) {
+  std::string spirv =
+      GenCoopVecLoadStoreShader("MakePointerVisibleKHR|NonPrivatePointerKHR",
+                                "MakePointerVisibleKHR|NonPrivatePointerKHR");
+
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("MakePointerVisibleKHR cannot be used with OpStore"));
+}
+
+TEST_F(ValidateMemory, CoopVecLoadMemoryAccessFail) {
+  std::string spirv =
+      GenCoopVecLoadStoreShader("MakePointerAvailableKHR|NonPrivatePointerKHR",
+                                "MakePointerAvailableKHR|NonPrivatePointerKHR");
+
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("MakePointerAvailableKHR cannot be used with OpLoad"));
+}
+
+TEST_F(ValidateMemory, CoopVecInvalidStorageClassFail) {
+  const std::string body = R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability CooperativeVectorNV
+OpCapability ReplicatedCompositesEXT
+OpExtension "SPV_NV_cooperative_vector"
+OpExtension "SPV_EXT_replicated_composites"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%f16 = OpTypeFloat 16
+%u32 = OpTypeInt 32 0
+
+%u32_8 = OpConstant %u32 8
+%use_A = OpConstant %u32 0
+%subgroup = OpConstant %u32 3
+
+%f16vec = OpTypeCooperativeVectorNV %f16 %u32_8
+
+%str = OpTypeStruct %f16vec
+%str_ptr = OpTypePointer Workgroup %str
+%sh = OpVariable %str_ptr Workgroup
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+
+OpReturn
+OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Cooperative vector types (or types containing them) can only be "
+          "allocated in Function or Private storage classes or as function "
+          "parameters"));
+}
+
+std::string GenCoopVecShader(const std::string& extra_types,
+                             const std::string& main_body,
+                             const std::string& execution_modes = "") {
+  const std::string prefix =
+      R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Int64
+OpCapability Int16
+OpCapability StorageBuffer16BitAccess
+OpCapability VulkanMemoryModel
+OpCapability CooperativeVectorNV
+OpCapability CooperativeVectorTrainingNV
+OpCapability ReplicatedCompositesEXT
+OpCapability Shader64BitIndexingEXT
+OpExtension "SPV_EXT_replicated_composites"
+OpExtension "SPV_KHR_vulkan_memory_model"
+OpExtension "SPV_NV_cooperative_vector"
+OpExtension "SPV_EXT_shader_64bit_indexing"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical Vulkan
+OpEntryPoint GLCompute %main "main" %48 %73
+OpExecutionMode %main LocalSize 1 1 1
+
+)" + execution_modes +
+      R"(
+
+OpDecorate %f16_arr ArrayStride 2
+OpDecorate %46 Block
+OpMemberDecorate %46 0 Offset 0
+OpDecorate %48 Binding 0
+OpDecorate %48 DescriptorSet 0
+
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%u32 = OpTypeInt 32 0
+%s32 = OpTypeInt 32 1
+%f16 = OpTypeFloat 16
+%bool = OpTypeBool
+
+%false = OpConstantFalse %bool
+%u32_4 = OpConstant %u32 4
+%u32_8 = OpConstant %u32 8
+%s32_0 = OpConstant %s32 0
+%f16_0 = OpConstant %f16 0
+
+%f16vec4 = OpTypeCooperativeVectorNV %f16 %u32_4
+%f16vec8 = OpTypeCooperativeVectorNV %f16 %u32_8
+
+%f16_arr = OpTypeRuntimeArray %f16
+%46 = OpTypeStruct %f16_arr
+%47 = OpTypePointer StorageBuffer %46
+%48 = OpVariable %47 StorageBuffer
+%51 = OpTypePointer StorageBuffer %f16_arr
+
+%57 = OpTypePointer Private %f16vec4
+%73 = OpVariable %57 Private
+%u32ptr = OpTypePointer Function %u32
+
+%input4 = OpConstantCompositeReplicateEXT %f16vec4 %f16_0
+%input8 = OpConstantCompositeReplicateEXT %f16vec8 %f16_0
+%interp = OpConstant %u32 0
+%offset = OpConstant %u32 0
+
+)";
+
+  const std::string func_begin =
+      R"(
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+%u32var = OpVariable %u32ptr Function
+%array_ptr = OpAccessChain %51 %48 %s32_0
+)";
+
+  const std::string suffix =
+      R"(
+OpReturn
+OpFunctionEnd)";
+
+  return prefix + extra_types + func_begin + main_body + suffix;
+}
+
+TEST_F(ValidateMemory, CoopVecMatMulSuccess) {
+  std::string spirv = GenCoopVecShader("",
+                                       R"(
+%result0 = OpCooperativeVectorMatrixMulAddNV %f16vec4 %input4 %interp %array_ptr %offset %interp %array_ptr %offset %interp %u32_4 %u32_4 %s32_0 %false
+%result1 = OpCooperativeVectorMatrixMulAddNV %f16vec4 %input8 %interp %array_ptr %offset %interp %array_ptr %offset %interp %u32_4 %u32_8 %s32_0 %false
+%result2 = OpCooperativeVectorMatrixMulAddNV %f16vec8 %input4 %interp %array_ptr %offset %interp %array_ptr %offset %interp %u32_8 %u32_4 %s32_0 %false
+%result3 = OpCooperativeVectorMatrixMulNV %f16vec4 %input4 %interp %array_ptr %offset %interp %u32_4 %u32_4 %s32_0 %false
+%result4 = OpCooperativeVectorMatrixMulNV %f16vec4 %input8 %interp %array_ptr %offset %interp %u32_4 %u32_8 %s32_0 %false
+%result5 = OpCooperativeVectorMatrixMulNV %f16vec8 %input4 %interp %array_ptr %offset %interp %u32_8 %u32_4 %s32_0 %false
+
+OpCooperativeVectorReduceSumAccumulateNV %array_ptr %offset %input4
+OpCooperativeVectorOuterProductAccumulateNV %array_ptr %offset %input4 %input8 %interp %interp
+      )");
+
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+}
+
+TEST_F(ValidateMemory, CoopVecMatMulKMismatchFail) {
+  std::string spirv = GenCoopVecShader(R"()",
+                                       R"(
+%result1 = OpCooperativeVectorMatrixMulAddNV %f16vec4 %input8 %interp %array_ptr %offset %interp %array_ptr %offset %interp %u32_4 %u32_4 %s32_0 %false
+      )");
+
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpCooperativeVectorMatrixMulAddNV input number of "
+                        "components 8 does not match K 4"));
+}
+
+TEST_F(ValidateMemory, CoopVecMatMulPackedKMismatchPass) {
+  std::string spirv = GenCoopVecShader(
+      R"(
+%packed = OpConstant %u32 1000491001
+      )",
+      R"(
+%result1 = OpCooperativeVectorMatrixMulAddNV %f16vec4 %input8 %packed %array_ptr %offset %interp %array_ptr %offset %interp %u32_4 %u32_4 %s32_0 %false
+      )");
+
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+}
+
+TEST_F(ValidateMemory, CoopVecMatMulMMismatchFail) {
+  std::string spirv = GenCoopVecShader(R"()",
+                                       R"(
+%result1 = OpCooperativeVectorMatrixMulAddNV %f16vec8 %input8 %interp %array_ptr %offset %interp %array_ptr %offset %interp %u32_4 %u32_8 %s32_0 %false
+      )");
+
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpCooperativeVectorMatrixMulAddNV result type number "
+                        "of components 8 does not match M 4"));
+}
+
+TEST_F(ValidateMemory, CoopVecMatMulTransposeTypeFail) {
+  std::string spirv = GenCoopVecShader(R"()",
+                                       R"(
+%result0 = OpCooperativeVectorMatrixMulAddNV %f16vec4 %input4 %interp %array_ptr %offset %interp %array_ptr %offset %interp %u32_4 %u32_4 %s32_0 %s32_0
+      )");
+
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpCooperativeVectorMatrixMulAddNV Transpose <id> "
+                        "'16[%int_0]' is not a scalar boolean"));
+}
+
+TEST_F(ValidateMemory, CoopVecMatMulInputInterpretationNotConstantFail) {
+  std::string spirv = GenCoopVecShader(
+      R"(
+      )",
+      R"(
+%u32val = OpLoad %u32 %u32var
+%result0 = OpCooperativeVectorMatrixMulAddNV %f16vec4 %input4 %u32val %array_ptr %offset %interp %array_ptr %offset %interp %u32_4 %u32_4 %s32_0 %false
+      )");
+
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpCooperativeVectorMatrixMulAddNV InputInterpretation "
+                        "<id> '31[%31]' is not a constant instruction"));
+}
+
+TEST_F(ValidateMemory, CoopVecMatMulMatrixInterpretationNotConstantFail) {
+  std::string spirv = GenCoopVecShader(
+      R"(
+      )",
+      R"(
+%u32val = OpLoad %u32 %u32var
+%result0 = OpCooperativeVectorMatrixMulAddNV %f16vec4 %input4 %interp %array_ptr %offset %u32val %array_ptr %offset %interp %u32_4 %u32_4 %s32_0 %false
+      )");
+
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpCooperativeVectorMatrixMulAddNV MatrixInterpretation <id> "
+                "'31[%31]' is not a constant instruction"));
+}
+
+TEST_F(ValidateMemory, CoopVecMatMulBiasInterpretationNotConstantFail) {
+  std::string spirv = GenCoopVecShader(
+      R"(
+      )",
+      R"(
+%u32val = OpLoad %u32 %u32var
+%result0 = OpCooperativeVectorMatrixMulAddNV %f16vec4 %input4 %interp %array_ptr %offset %interp %array_ptr %offset %u32val %u32_4 %u32_4 %s32_0 %false
+      )");
+
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpCooperativeVectorMatrixMulAddNV BiasInterpretation "
+                        "<id> '31[%31]' is not a constant instruction"));
+}
+
+TEST_F(ValidateMemory, CoopVecMatMulInputInterpretationNotInt32Fail) {
+  std::string spirv = GenCoopVecShader(
+      R"(
+      )",
+      R"(
+%result0 = OpCooperativeVectorMatrixMulAddNV %f16vec4 %input4 %false %array_ptr %offset %interp %array_ptr %offset %interp %u32_4 %u32_4 %s32_0 %false
+      )");
+
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpCooperativeVectorMatrixMulAddNV InputInterpretation "
+                        "type <id> '12[%bool]' is not a 32 bit integer"));
+}
+
+TEST_F(ValidateMemory, CoopVecOuterProductABMismatchFail) {
+  std::string spirv = GenCoopVecShader(
+      R"(
+%f32 = OpTypeFloat 32
+%f32vec8 = OpTypeCooperativeVectorNV %f32 %u32_8
+%f32_0 = OpConstant %f32 0
+%input8f32 = OpConstantCompositeReplicateEXT %f32vec8 %f32_0
+      )",
+      R"(
+OpCooperativeVectorOuterProductAccumulateNV %array_ptr %offset %input4 %input8f32 %interp %interp
+      )");
+
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpCooperativeVectorOuterProductAccumulateNV A and B component "
+                "types '11[%half]' and '28[%float]' do not match"));
+}
+
+TEST_F(ValidateMemory, CoopVecOuterProductIntOffsetFail) {
+  std::string spirv = GenCoopVecShader(
+      R"(
+%u16 = OpTypeInt 16 0
+%u16_0 = OpConstant %u16 0
+      )",
+      R"(
+OpCooperativeVectorOuterProductAccumulateNV %array_ptr %u16_0 %input4 %input8 %interp %interp
+      )");
+
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpCooperativeVectorOuterProductAccumulateNV Offset "
+                "type <id> '28[%ushort]' is not a 32 or 64 bit integer"));
+}
+
+TEST_F(ValidateMemory, CoopVecOuterProductInt32MatrixStrideFail) {
+  std::string spirv = GenCoopVecShader(
+      R"(
+%u64 = OpTypeInt 64 0
+%u64_0 = OpConstant %u64 0
+      )",
+      R"(
+OpCooperativeVectorOuterProductAccumulateNV %array_ptr %offset %input4 %input8 %interp %interp %u64_0
+      )");
+
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpCooperativeVectorOuterProductAccumulateNV MatrixStride type "
+                "<id> '28[%ulong]' is not a 32 bit integer"));
+}
+
+TEST_F(ValidateMemory, CoopVecOuterProductVectorTypeFail) {
+  std::string spirv = GenCoopVecShader(
+      R"(
+%f16v4 = OpTypeVector %f16 4
+%f16c = OpConstantCompositeReplicateEXT %f16v4 %f16_0
+      )",
+      R"(
+OpCooperativeVectorOuterProductAccumulateNV %array_ptr %offset %f16c %input8 %interp %interp
+      )");
+
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpCooperativeVectorOuterProductAccumulateNV A type "
+                        "<id> '28[%v4half]' is not a cooperative vector type"));
+}
+
+TEST_F(ValidateMemory, CoopVecReduceSumIntOffsetFail) {
+  std::string spirv = GenCoopVecShader(
+      R"(
+%u16 = OpTypeInt 16 0
+%u16_0 = OpConstant %u16 0
+      )",
+      R"(
+OpCooperativeVectorReduceSumAccumulateNV %array_ptr %u16_0 %input4
+      )");
+
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpCooperativeVectorReduceSumAccumulateNV Offset type "
+                        "<id> '28[%ushort]' is not a 32 or 64 bit integer"));
+}
+
+TEST_F(ValidateMemory, CoopVecOuterProductInt64OffsetPass) {
+  std::string spirv = GenCoopVecShader(
+      R"(
+%u64 = OpTypeInt 64 0
+%u64_0 = OpConstant %u64 0
+      )",
+      R"(
+OpCooperativeVectorOuterProductAccumulateNV %array_ptr %u64_0 %input4 %input8 %interp %interp
+      )");
+
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+}
+
+TEST_F(ValidateMemory, CoopVecReduceSumVectorTypeFail) {
+  std::string spirv = GenCoopVecShader(
+      R"(
+%f16v4 = OpTypeVector %f16 4
+%f16c = OpConstantCompositeReplicateEXT %f16v4 %f16_0
+      )",
+      R"(
+OpCooperativeVectorReduceSumAccumulateNV %array_ptr %offset %f16c
+      )");
+
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1_SPIRV_1_4);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpCooperativeVectorReduceSumAccumulateNV V type <id> "
+                        "'28[%v4half]' is not a cooperative vector type."));
+}
+
+TEST_F(ValidateMemory, CoopMatMatrixBFloatFAdd) {
+  const std::string body =
+      R"(
+               OpCapability Shader
+               OpCapability Float16
+               OpCapability BFloat16TypeKHR
+               OpCapability BFloat16CooperativeMatrixKHR
+               OpCapability VulkanMemoryModel
+               OpCapability CooperativeMatrixKHR
+               OpExtension "SPV_KHR_bfloat16"
+               OpExtension "SPV_KHR_vulkan_memory_model"
+               OpExtension "SPV_KHR_cooperative_matrix"
+               OpMemoryModel Logical Vulkan
+               OpEntryPoint GLCompute %main "main" %_ %__0 %__1
+               OpExecutionMode %main LocalSize 32 1 1
+               OpDecorate %_arr_bfloat16_uint_64 ArrayStride 2
+               OpDecorate %A Block
+               OpMemberDecorate %A 0 Offset 0
+               OpDecorate %_ Binding 0
+               OpDecorate %_ DescriptorSet 0
+               OpDecorate %_arr_bfloat16_uint_64_0 ArrayStride 2
+               OpDecorate %B Block
+               OpMemberDecorate %B 0 Offset 0
+               OpDecorate %__0 Binding 1
+               OpDecorate %__0 DescriptorSet 0
+               OpDecorate %_arr_bfloat16_uint_64_1 ArrayStride 2
+               OpDecorate %R Block
+               OpMemberDecorate %R 0 Offset 0
+               OpDecorate %__1 Binding 2
+               OpDecorate %__1 DescriptorSet 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+   %bfloat16 = OpTypeFloat 16 BFloat16KHR
+       %uint = OpTypeInt 32 0
+     %uint_3 = OpConstant %uint 3
+     %uint_8 = OpConstant %uint 8
+     %uint_0 = OpConstant %uint 0
+         %12 = OpTypeCooperativeMatrixKHR %bfloat16 %uint_3 %uint_8 %uint_8 %uint_0
+%_ptr_Function_12 = OpTypePointer Function %12
+    %uint_64 = OpConstant %uint 64
+%_arr_bfloat16_uint_64 = OpTypeArray %bfloat16 %uint_64
+          %A = OpTypeStruct %_arr_bfloat16_uint_64
+%_ptr_StorageBuffer_A = OpTypePointer StorageBuffer %A
+          %_ = OpVariable %_ptr_StorageBuffer_A StorageBuffer
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_StorageBuffer_bfloat16 = OpTypePointer StorageBuffer %bfloat16
+%_arr_bfloat16_uint_64_0 = OpTypeArray %bfloat16 %uint_64
+          %B = OpTypeStruct %_arr_bfloat16_uint_64_0
+%_ptr_StorageBuffer_B = OpTypePointer StorageBuffer %B
+        %__0 = OpVariable %_ptr_StorageBuffer_B StorageBuffer
+     %v3uint = OpTypeVector %uint 3
+    %uint_32 = OpConstant %uint 32
+     %uint_1 = OpConstant %uint 1
+         %35 = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1
+%_arr_bfloat16_uint_64_1 = OpTypeArray %bfloat16 %uint_64
+          %R = OpTypeStruct %_arr_bfloat16_uint_64_1
+%_ptr_StorageBuffer_R = OpTypePointer StorageBuffer %R
+        %__1 = OpVariable %_ptr_StorageBuffer_R StorageBuffer
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+       %matX = OpVariable %_ptr_Function_12 Function
+       %matY = OpVariable %_ptr_Function_12 Function
+         %23 = OpAccessChain %_ptr_StorageBuffer_bfloat16 %_ %int_0 %uint_0
+         %24 = OpCooperativeMatrixLoadKHR %12 %23 %int_0 %uint_8 None
+               OpStore %matX %24
+         %30 = OpAccessChain %_ptr_StorageBuffer_bfloat16 %__0 %int_0 %uint_0
+         %31 = OpCooperativeMatrixLoadKHR %12 %30 %int_0 %uint_8 None
+               OpStore %matY %31
+         %32 = OpLoad %12 %matX
+         %33 = OpLoad %12 %matY
+         %34 = OpFAdd %12 %32 %33
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("FAdd doesn't support BFloat16 type"));
+}
+
+TEST_F(ValidateMemory, CoopMatMatrixFloat8FAdd) {
+  const std::string body =
+      R"(
+               OpCapability Shader
+               OpCapability Float8EXT
+               OpCapability Float8CooperativeMatrixEXT
+               OpCapability VulkanMemoryModel
+               OpCapability CooperativeMatrixKHR
+               OpExtension "SPV_EXT_float8"
+               OpExtension "SPV_KHR_cooperative_matrix"
+               OpExtension "SPV_KHR_vulkan_memory_model"
+               OpMemoryModel Logical Vulkan
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 32 1 1
+               OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+    %fp8e4m3 = OpTypeFloat 8 Float8E4M3EXT
+       %uint = OpTypeInt 32 0
+     %uint_3 = OpConstant %uint 3
+    %uint_16 = OpConstant %uint 16
+     %uint_0 = OpConstant %uint 0
+         %12 = OpTypeCooperativeMatrixKHR %fp8e4m3 %uint_3 %uint_16 %uint_16 %uint_0
+%_ptr_Function_12 = OpTypePointer Function %12
+     %v3uint = OpTypeVector %uint 3
+    %uint_32 = OpConstant %uint 32
+     %uint_1 = OpConstant %uint 1
+%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+       %matR = OpVariable %_ptr_Function_12 Function
+       %matX = OpVariable %_ptr_Function_12 Function
+       %matY = OpVariable %_ptr_Function_12 Function
+         %16 = OpLoad %12 %matX
+         %18 = OpLoad %12 %matY
+         %19 = OpFAdd %12 %16 %18
+               OpStore %matR %19
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_3);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("FAdd doesn't support FP8 E4M3/E5M2 types"));
+}
+
+TEST_F(ValidateMemory, PhysicalStorageBufferArray) {
+  const std::string body =
+      R"(
+              OpCapability Shader
+              OpCapability Int64
+              OpCapability PhysicalStorageBufferAddresses
+              OpExtension "SPV_KHR_storage_buffer_storage_class"
+              OpExtension "SPV_KHR_physical_storage_buffer"
+              OpMemoryModel PhysicalStorageBuffer64 GLSL450
+              OpEntryPoint GLCompute %18 "main"
+              OpExecutionMode %18 LocalSize 1 1 1
+              OpSource OpenCL_C 120
+              OpMemberDecorate %S 0 Offset 0
+              OpMemberDecorate %S 1 Offset 256
+              OpDecorate %array ArrayStride 16
+      %uint = OpTypeInt 32 0
+      %void = OpTypeVoid
+     %ulong = OpTypeInt 64 0
+     %float = OpTypeFloat 32
+   %v4float = OpTypeVector %float 4
+        %17 = OpTypeFunction %void
+   %ulong_0 = OpConstant %ulong 0
+    %uint_3 = OpConstant %uint 3
+     %array = OpTypeArray %v4float %uint_3
+         %S = OpTypeStruct %array %uint
+     %ptr_S = OpTypePointer PhysicalStorageBuffer %S
+   %float_0 = OpConstant %float 0
+ %v4float_0 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+        %23 = OpConstantComposite %array %v4float_0 %v4float_0 %v4float_0
+        %24 = OpConstantComposite %S %23 %uint_3
+        %18 = OpFunction %void None %17
+        %19 = OpLabel
+        %58 = OpConvertUToPtr %ptr_S %ulong_0
+              OpStore %58 %24 Aligned 4
+              OpReturn
+              OpFunctionEnd
+)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateMemory, LongVectorPrivateStorageClassGood) {
+  std::string spirv = R"(
+OpCapability Shader
+OpCapability LongVectorEXT
+OpExtension "SPV_EXT_long_vector"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %func "func"
+%float = OpTypeFloat 32
+%vec5 = OpTypeVector %float 5
+%vec5ptr = OpTypePointer Private %vec5
+%1 = OpVariable %vec5ptr Private
+%void = OpTypeVoid
+%functy = OpTypeFunction %void
+%func = OpFunction %void None %functy
+%2 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+}
+
+TEST_F(ValidateMemory, LongVectorInputStorageClassBad) {
+  std::string spirv = R"(
+OpCapability Shader
+OpCapability LongVectorEXT
+OpExtension "SPV_EXT_long_vector"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %func "func"
+%float = OpTypeFloat 32
+%vec5 = OpTypeVector %float 5
+%vec5ptr = OpTypePointer Input %vec5
+%1 = OpVariable %vec5ptr Input
+%void = OpTypeVoid
+%functy = OpTypeFunction %void
+%func = OpFunction %void None %functy
+%2 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Long vector types with more than 4 components (or types "
+                "containing them) not supported in storage class Input"));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Type-12297"));
+}
+
+TEST_F(ValidateMemory, LongVectorMissingCapabilityBad) {
+  std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %func "func"
+%float = OpTypeFloat 32
+%vec5 = OpTypeVector %float 5
+%vec5ptr = OpTypePointer Private %vec5
+%1 = OpVariable %vec5ptr Private
+%void = OpTypeVoid
+%functy = OpTypeFunction %void
+%func = OpFunction %void None %functy
+%2 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Illegal number of components (5) for TypeVector"));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-12295"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_mesh_shading_test.cpp b/test/val/val_mesh_shading_test.cpp
index a7b96a4..95eb5e8 100644
--- a/test/val/val_mesh_shading_test.cpp
+++ b/test/val/val_mesh_shading_test.cpp
@@ -128,6 +128,104 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
 }
 
+// https://godbolt.org/z/Kvb1rsceP
+TEST_F(ValidateMeshShading, BasicMeshBuiltinSuccess) {
+  const std::string body = R"(
+               OpCapability MeshShadingEXT
+               OpExtension "SPV_EXT_mesh_shader"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %main "main" %gl_MeshVerticesEXT %vertexOutput %gl_MeshPrimitivesEXT %gl_LocalInvocationIndex %gl_PrimitiveTriangleIndicesEXT
+               OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1
+               OpExecutionMode %main OutputVertices 3
+               OpExecutionMode %main OutputPrimitivesEXT 1
+               OpExecutionMode %main OutputTrianglesEXT
+               OpDecorate %gl_MeshPerVertexEXT Block
+               OpMemberDecorate %gl_MeshPerVertexEXT 0 BuiltIn Position
+               OpMemberDecorate %gl_MeshPerVertexEXT 1 BuiltIn PointSize
+               OpMemberDecorate %gl_MeshPerVertexEXT 2 BuiltIn ClipDistance
+               OpMemberDecorate %gl_MeshPerVertexEXT 3 BuiltIn CullDistance
+               OpDecorate %VertexOutput Block
+               OpDecorate %vertexOutput Location 0
+               OpDecorate %gl_MeshPerPrimitiveEXT Block
+               OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 BuiltIn PrimitiveId
+               OpMemberDecorate %gl_MeshPerPrimitiveEXT 0 PerPrimitiveEXT
+               OpMemberDecorate %gl_MeshPerPrimitiveEXT 1 BuiltIn Layer
+               OpMemberDecorate %gl_MeshPerPrimitiveEXT 1 PerPrimitiveEXT
+               OpMemberDecorate %gl_MeshPerPrimitiveEXT 2 BuiltIn ViewportIndex
+               OpMemberDecorate %gl_MeshPerPrimitiveEXT 2 PerPrimitiveEXT
+               OpMemberDecorate %gl_MeshPerPrimitiveEXT 3 BuiltIn CullPrimitiveEXT
+               OpMemberDecorate %gl_MeshPerPrimitiveEXT 3 PerPrimitiveEXT
+               OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex
+               OpDecorate %gl_PrimitiveTriangleIndicesEXT BuiltIn PrimitiveTriangleIndicesEXT
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_3 = OpConstant %uint 3
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_MeshPerVertexEXT = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+%_arr_gl_MeshPerVertexEXT_uint_3 = OpTypeArray %gl_MeshPerVertexEXT %uint_3
+%_ptr_Output__arr_gl_MeshPerVertexEXT_uint_3 = OpTypePointer Output %_arr_gl_MeshPerVertexEXT_uint_3
+%gl_MeshVerticesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerVertexEXT_uint_3 Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+    %float_0 = OpConstant %float 0
+         %20 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+      %int_1 = OpConstant %int 1
+      %int_2 = OpConstant %int 2
+%VertexOutput = OpTypeStruct %v4float
+%_arr_VertexOutput_uint_3 = OpTypeArray %VertexOutput %uint_3
+%_ptr_Output__arr_VertexOutput_uint_3 = OpTypePointer Output %_arr_VertexOutput_uint_3
+%vertexOutput = OpVariable %_ptr_Output__arr_VertexOutput_uint_3 Output
+       %bool = OpTypeBool
+%gl_MeshPerPrimitiveEXT = OpTypeStruct %int %int %int %bool
+%_arr_gl_MeshPerPrimitiveEXT_uint_1 = OpTypeArray %gl_MeshPerPrimitiveEXT %uint_1
+%_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_1 = OpTypePointer Output %_arr_gl_MeshPerPrimitiveEXT_uint_1
+%gl_MeshPrimitivesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerPrimitiveEXT_uint_1 Output
+%_ptr_Input_uint = OpTypePointer Input %uint
+%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input
+      %int_3 = OpConstant %int 3
+      %false = OpConstantFalse %bool
+%_ptr_Output_bool = OpTypePointer Output %bool
+     %v3uint = OpTypeVector %uint 3
+%_arr_v3uint_uint_1 = OpTypeArray %v3uint %uint_1
+%_ptr_Output__arr_v3uint_uint_1 = OpTypePointer Output %_arr_v3uint_uint_1
+%gl_PrimitiveTriangleIndicesEXT = OpVariable %_ptr_Output__arr_v3uint_uint_1 Output
+     %uint_0 = OpConstant %uint 0
+         %52 = OpConstantComposite %v3uint %uint_0 %uint_0 %uint_0
+%_ptr_Output_v3uint = OpTypePointer Output %v3uint
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+               OpSetMeshOutputsEXT %uint_3 %uint_1
+         %22 = OpAccessChain %_ptr_Output_v4float %gl_MeshVerticesEXT %int_0 %int_0
+               OpStore %22 %20
+         %24 = OpAccessChain %_ptr_Output_v4float %gl_MeshVerticesEXT %int_1 %int_0
+               OpStore %24 %20
+         %26 = OpAccessChain %_ptr_Output_v4float %gl_MeshVerticesEXT %int_2 %int_0
+               OpStore %26 %20
+         %31 = OpAccessChain %_ptr_Output_v4float %vertexOutput %int_0 %int_0
+               OpStore %31 %20
+         %32 = OpAccessChain %_ptr_Output_v4float %vertexOutput %int_1 %int_0
+               OpStore %32 %20
+         %33 = OpAccessChain %_ptr_Output_v4float %vertexOutput %int_2 %int_0
+               OpStore %33 %20
+         %41 = OpLoad %uint %gl_LocalInvocationIndex
+         %45 = OpAccessChain %_ptr_Output_bool %gl_MeshPrimitivesEXT %41 %int_3
+               OpStore %45 %false
+         %50 = OpLoad %uint %gl_LocalInvocationIndex
+         %54 = OpAccessChain %_ptr_Output_v3uint %gl_PrimitiveTriangleIndicesEXT %50
+               OpStore %54 %52
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(body, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
 TEST_F(ValidateMeshShading, VulkanBasicMeshAndTaskSuccess) {
   const std::string body = R"(
                OpCapability MeshShadingEXT
@@ -136,6 +234,8 @@
                OpMemoryModel Logical GLSL450
                OpEntryPoint MeshEXT %mainMesh "mainMesh"
                OpEntryPoint TaskEXT %mainTask "mainTask"
+               OpExecutionMode %mainMesh LocalSize 1 1 1
+               OpExecutionMode %mainTask LocalSize 1 1 1
                OpExecutionMode %mainMesh OutputVertices 1
                OpExecutionMode %mainMesh OutputPrimitivesEXT 1
                OpExecutionMode %mainMesh OutputTrianglesEXT
@@ -467,6 +567,80 @@
                         "TaskEXT and MeshKHR execution model"));
 }
 
+TEST_F(ValidateMeshShading, BadMultipleTaskPayloadWorkgroupEXT) {
+  const std::string body = R"(
+               OpCapability MeshShadingEXT
+               OpExtension "SPV_EXT_mesh_shader"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint TaskEXT %main "main" %payload %payload1
+       %void = OpTypeVoid
+       %func = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+%_ptr_TaskPayloadWorkgroupEXT = OpTypePointer TaskPayloadWorkgroupEXT %uint
+    %payload = OpVariable %_ptr_TaskPayloadWorkgroupEXT TaskPayloadWorkgroupEXT
+    %payload1 = OpVariable %_ptr_TaskPayloadWorkgroupEXT TaskPayloadWorkgroupEXT
+       %main = OpFunction %void None %func
+      %label = OpLabel
+       %load = OpLoad %uint %payload
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("There can be at most one "
+                        "OpVariable with storage "
+                        "class TaskPayloadWorkgroupEXT associated with "
+                        "an OpEntryPoint"));
+}
+
+TEST_F(ValidateMeshShading, TaskPayloadWorkgroupTaskExtExecutionModel) {
+  const std::string body = R"(
+               OpCapability MeshShadingEXT
+               OpExtension "SPV_EXT_mesh_shader"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint TaskEXT %main "main" %payload
+       %void = OpTypeVoid
+       %func = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+%_ptr_TaskPayloadWorkgroupEXT = OpTypePointer TaskPayloadWorkgroupEXT %uint
+    %payload = OpVariable %_ptr_TaskPayloadWorkgroupEXT TaskPayloadWorkgroupEXT
+       %main = OpFunction %void None %func
+      %label = OpLabel
+       %load = OpLoad %uint %payload
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+}
+
+TEST_F(ValidateMeshShading, TaskPayloadWorkgroupMeshExtExecutionModel) {
+  const std::string body = R"(
+               OpCapability MeshShadingEXT
+               OpExtension "SPV_EXT_mesh_shader"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %main "main" %payload
+               OpExecutionMode %main OutputVertices 1
+               OpExecutionMode %main OutputPrimitivesEXT 1
+               OpExecutionMode %main OutputTrianglesEXT
+       %void = OpTypeVoid
+       %func = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+%_ptr_TaskPayloadWorkgroupEXT = OpTypePointer TaskPayloadWorkgroupEXT %uint
+    %payload = OpVariable %_ptr_TaskPayloadWorkgroupEXT TaskPayloadWorkgroupEXT
+       %main = OpFunction %void None %func
+      %label = OpLabel
+       %load = OpLoad %uint %payload
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+}
+
 TEST_F(ValidateMeshShading, OpSetMeshOutputsBadVertexCount) {
   const std::string body = R"(
                OpCapability MeshShadingEXT
@@ -598,6 +772,770 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
 }
 
+TEST_F(ValidateMeshShading, MeshOutputScalar) {
+  const std::string body = R"(
+          OpCapability MeshShadingEXT
+          OpExtension "SPV_EXT_mesh_shader"
+          OpMemoryModel Logical GLSL450
+          OpEntryPoint MeshEXT %main "main" %x
+          OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1
+          OpExecutionMode %main OutputVertices 3
+          OpExecutionMode %main OutputPrimitivesEXT 1
+          OpExecutionMode %main OutputTrianglesEXT
+          OpDecorate %x Location 0
+  %void = OpTypeVoid
+     %4 = OpTypeFunction %void
+  %uint = OpTypeInt 32 0
+%uint_1 = OpConstant %uint 1
+%uint_3 = OpConstant %uint 3
+%o_ptr = OpTypePointer Output %uint
+     %x = OpVariable %o_ptr Output
+  %main = OpFunction %void None %4
+     %6 = OpLabel
+          OpSetMeshOutputsEXT %uint_3 %uint_1
+          OpReturn
+          OpFunctionEnd
+)";
+
+  CompileSuccessfully(body, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("In the MeshEXT Execution Mode, all Output Variables "
+                        "must contain an Array."));
+}
+
+TEST_F(ValidateMeshShading, MeshOutputScalarStruct) {
+  const std::string body = R"(
+               OpCapability MeshShadingEXT
+               OpExtension "SPV_EXT_mesh_shader"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %main "main" %vertexOutput
+               OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1
+               OpExecutionMode %main OutputVertices 3
+               OpExecutionMode %main OutputPrimitivesEXT 1
+               OpExecutionMode %main OutputTrianglesEXT
+               OpDecorate %VertexOutput Block
+               OpMemberDecorate %VertexOutput 0 Location 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_3 = OpConstant %uint 3
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%VertexOutput = OpTypeStruct %v4float
+%_ptr_Output_uint_3 = OpTypePointer Output %VertexOutput
+%vertexOutput = OpVariable %_ptr_Output_uint_3 Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+    %float_0 = OpConstant %float 0
+         %19 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+               OpSetMeshOutputsEXT %uint_3 %uint_1
+         %21 = OpAccessChain %_ptr_Output_v4float %vertexOutput %int_0
+               OpStore %21 %19
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(body, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("In the MeshEXT Execution Mode, all Output Variables "
+                        "must contain an Array."));
+}
+
+TEST_F(ValidateMeshShading, BadPerPrimitiveEXTStorageClassInMeshEXT) {
+  const std::string body = R"(
+              OpCapability MeshShadingEXT
+              OpExtension "SPV_EXT_mesh_shader"
+         %1 = OpExtInstImport "GLSL.std.450"
+              OpMemoryModel Logical GLSL450
+              OpEntryPoint MeshEXT %main "main" %gl_LocalInvocationID %blk %triangleNormal
+              OpExecutionMode %main LocalSize 32 1 1
+              OpExecutionMode %main OutputVertices 81
+              OpExecutionMode %main OutputPrimitivesNV 32
+              OpExecutionMode %main OutputTrianglesNV
+              OpSource GLSL 450
+              OpSourceExtension "GL_EXT_mesh_shader"
+              OpName %main "main"
+              OpName %iid "iid"
+              OpName %gl_LocalInvocationID "gl_LocalInvocationID"
+              OpName %myblock "myblock"
+              OpMemberName %myblock 0 "f"
+              OpName %blk "blk"
+              OpName %triangleNormal "triangleNormal"
+              OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId
+              OpMemberDecorate %myblock 0 PerPrimitiveEXT
+              OpDecorate %myblock Block
+              OpDecorate %blk Location 0
+              OpDecorate %triangleNormal PerPrimitiveEXT
+              OpDecorate %triangleNormal Location 0
+              OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+      %void = OpTypeVoid
+         %3 = OpTypeFunction %void
+      %uint = OpTypeInt 32 0
+%_ptr_Function_uint = OpTypePointer Function %uint
+    %v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input
+    %uint_0 = OpConstant %uint 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+     %float = OpTypeFloat 32
+   %myblock = OpTypeStruct %float
+   %uint_32 = OpConstant %uint 32
+%_arr_myblock_uint_32 = OpTypeArray %myblock %uint_32
+%_ptr_Output__arr_myblock_uint_32 = OpTypePointer Output %_arr_myblock_uint_32
+       %blk = OpVariable %_ptr_Output__arr_myblock_uint_32 Output
+       %int = OpTypeInt 32 1
+     %int_0 = OpConstant %int 0
+  %float_11 = OpConstant %float 11
+%_ptr_Output_float = OpTypePointer Output %float
+   %v3float = OpTypeVector %float 3
+%_arr_v3float_uint_32 = OpTypeArray %v3float %uint_32
+%_ptr_Output__arr_v3float_uint_32 = OpTypePointer Input %_arr_v3float_uint_32
+%triangleNormal = OpVariable %_ptr_Output__arr_v3float_uint_32 Input
+        %33 = OpConstantComposite %v3float %float_11 %float_11 %float_11
+%_ptr_Output_v3float = OpTypePointer Output %v3float
+    %uint_1 = OpConstant %uint 1
+%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1
+      %main = OpFunction %void None %3
+         %5 = OpLabel
+       %iid = OpVariable %_ptr_Function_uint Function
+        %14 = OpAccessChain %_ptr_Input_uint %gl_LocalInvocationID %uint_0
+        %15 = OpLoad %uint %14
+              OpStore %iid %15
+        %22 = OpLoad %uint %iid
+        %27 = OpAccessChain %_ptr_Output_float %blk %22 %int_0
+              OpStore %27 %float_11
+              OpReturn
+              OpFunctionEnd
+)";
+
+  CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("PerPrimitiveEXT decoration must be applied only to "
+                        "variables in the Output Storage Class in the Storage "
+                        "Class in the MeshEXT Execution Model."));
+}
+
+TEST_F(ValidateMeshShading, VulkanPerPrimitiveEXTStorageClassInMeshEXT) {
+  const std::string body = R"(
+      OpCapability MeshShadingEXT
+      OpExtension "SPV_EXT_mesh_shader"
+ %1 = OpExtInstImport "GLSL.std.450"
+      OpMemoryModel Logical GLSL450
+      OpEntryPoint MeshEXT %main "main" %gl_LocalInvocationID %blk %triangleNormal
+      OpExecutionMode %main LocalSize 32 1 1
+      OpExecutionMode %main OutputVertices 81
+      OpExecutionMode %main OutputPrimitivesNV 32
+      OpExecutionMode %main OutputTrianglesNV
+      OpSource GLSL 450
+      OpSourceExtension "GL_EXT_mesh_shader"
+      OpName %main "main"
+      OpName %iid "iid"
+      OpName %gl_LocalInvocationID "gl_LocalInvocationID"
+      OpName %myblock "myblock"
+      OpMemberName %myblock 0 "f"
+      OpName %blk "blk"
+      OpName %triangleNormal "triangleNormal"
+      OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId
+      OpMemberDecorate %myblock 0 PerPrimitiveEXT
+      OpDecorate %myblock Block
+      OpDecorate %blk Location 0
+      OpDecorate %triangleNormal PerPrimitiveEXT
+      OpDecorate %triangleNormal Location 0
+      OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+%void = OpTypeVoid
+ %3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%_ptr_Function_uint = OpTypePointer Function %uint
+%v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input
+%uint_0 = OpConstant %uint 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%float = OpTypeFloat 32
+%myblock = OpTypeStruct %float
+%uint_32 = OpConstant %uint 32
+%_arr_myblock_uint_32 = OpTypeArray %myblock %uint_32
+%_ptr_Output__arr_myblock_uint_32 = OpTypePointer Output %_arr_myblock_uint_32
+%blk = OpVariable %_ptr_Output__arr_myblock_uint_32 Output
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%float_11 = OpConstant %float 11
+%_ptr_Output_float = OpTypePointer Output %float
+%v3float = OpTypeVector %float 3
+%_arr_v3float_uint_32 = OpTypeArray %v3float %uint_32
+%_ptr_Output__arr_v3float_uint_32 = OpTypePointer Input %_arr_v3float_uint_32
+%triangleNormal = OpVariable %_ptr_Output__arr_v3float_uint_32 Input
+%33 = OpConstantComposite %v3float %float_11 %float_11 %float_11
+%_ptr_Output_v3float = OpTypePointer Output %v3float
+%uint_1 = OpConstant %uint 1
+%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1
+%main = OpFunction %void None %3
+ %5 = OpLabel
+%iid = OpVariable %_ptr_Function_uint Function
+%14 = OpAccessChain %_ptr_Input_uint %gl_LocalInvocationID %uint_0
+%15 = OpLoad %uint %14
+      OpStore %iid %15
+%22 = OpLoad %uint %iid
+%27 = OpAccessChain %_ptr_Output_float %blk %22 %int_0
+      OpStore %27 %float_11
+      OpReturn
+      OpFunctionEnd
+)";
+
+  CompileSuccessfully(body, SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-PrimitiveId-PrimitiveId-04336"));
+}
+
+TEST_F(ValidateMeshShading, BadPerPrimitiveEXTStorageClassInFrag) {
+  const std::string body = R"(
+             OpCapability Shader
+             OpCapability MeshShadingEXT
+             OpExtension "SPV_EXT_mesh_shader"
+        %1 = OpExtInstImport "GLSL.std.450"
+             OpMemoryModel Logical GLSL450
+             OpEntryPoint Fragment %main "main" %triangleNormal
+             OpExecutionMode %main OriginUpperLeft
+             OpSource GLSL 450
+             OpSourceExtension "GL_EXT_mesh_shader"
+             OpName %main "main"
+             OpName %triangleNormal "triangleNormal"
+             OpDecorate %triangleNormal PerPrimitiveNV
+             OpDecorate %triangleNormal Location 0
+     %void = OpTypeVoid
+        %3 = OpTypeFunction %void
+    %float = OpTypeFloat 32
+  %v3float = OpTypeVector %float 3
+     %uint = OpTypeInt 32 0
+   %uint_3 = OpConstant %uint 3
+%_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3
+%_ptr_Input__arr_v3float_uint_3 = OpTypePointer Output %_arr_v3float_uint_3
+%triangleNormal = OpVariable %_ptr_Input__arr_v3float_uint_3 Output
+      %int = OpTypeInt 32 1
+    %int_0 = OpConstant %int 0
+%_ptr_Input_v3float = OpTypePointer Input %v3float
+     %main = OpFunction %void None %3
+        %5 = OpLabel
+       %18 = OpAccessChain %_ptr_Input_v3float %triangleNormal %int_0
+       %19 = OpLoad %v3float %18
+             OpReturn
+             OpFunctionEnd
+)";
+
+  CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA,
+            ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("PerPrimitiveEXT decoration must be applied only to "
+                        "variables in the Input Storage Class in the Fragment "
+                        "Execution Model."));
+}
+
+TEST_F(ValidateMeshShading, PerPrimitiveEXTStorageClassInFrag) {
+  const std::string body = R"(
+                OpCapability Shader
+                OpCapability MeshShadingEXT
+                OpExtension "SPV_EXT_mesh_shader"
+           %1 = OpExtInstImport "GLSL.std.450"
+                OpMemoryModel Logical GLSL450
+                OpEntryPoint Fragment %main "main" %res3 %triangleNormal
+                OpExecutionMode %main OriginUpperLeft
+                OpSource GLSL 450
+                OpSourceExtension "GL_EXT_mesh_shader"
+                OpName %main "main"
+                OpName %res3 "res3"
+                OpName %triangleNormal "triangleNormal"
+                OpDecorate %res3 Location 0
+                OpDecorate %triangleNormal PerPrimitiveNV
+                OpDecorate %triangleNormal Location 0
+        %void = OpTypeVoid
+           %3 = OpTypeFunction %void
+       %float = OpTypeFloat 32
+     %v3float = OpTypeVector %float 3
+ %_ptr_Output_v3float = OpTypePointer Output %v3float
+        %res3 = OpVariable %_ptr_Output_v3float Output
+        %uint = OpTypeInt 32 0
+      %uint_3 = OpConstant %uint 3
+ %_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3
+ %_ptr_Input__arr_v3float_uint_3 = OpTypePointer Input %_arr_v3float_uint_3
+ %triangleNormal = OpVariable %_ptr_Input__arr_v3float_uint_3 Input
+         %int = OpTypeInt 32 1
+       %int_0 = OpConstant %int 0
+ %_ptr_Input_v3float = OpTypePointer Input %v3float
+        %main = OpFunction %void None %3
+           %5 = OpLabel
+          %18 = OpAccessChain %_ptr_Input_v3float %triangleNormal %int_0
+          %19 = OpLoad %v3float %18
+                OpStore %res3 %19
+                OpReturn
+                OpFunctionEnd
+)";
+
+  CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+}
+
+TEST_F(ValidateMeshShading, PerPrimitiveEXTStorageClassInMeshEXT) {
+  const std::string body = R"(
+               OpCapability MeshShadingEXT
+               OpExtension "SPV_EXT_mesh_shader"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %main "main" %gl_LocalInvocationID %blk %triangleNormal
+               OpExecutionMode %main LocalSize 32 1 1
+               OpExecutionMode %main OutputVertices 81
+               OpExecutionMode %main OutputPrimitivesNV 32
+               OpExecutionMode %main OutputTrianglesNV
+               OpSource GLSL 450
+               OpSourceExtension "GL_EXT_mesh_shader"
+               OpName %main "main"
+               OpName %iid "iid"
+               OpName %gl_LocalInvocationID "gl_LocalInvocationID"
+               OpName %myblock "myblock"
+               OpMemberName %myblock 0 "f"
+               OpName %blk "blk"
+               OpName %triangleNormal "triangleNormal"
+               OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId
+               OpMemberDecorate %myblock 0 PerPrimitiveNV
+               OpDecorate %myblock Block
+               OpDecorate %blk Location 0
+               OpDecorate %triangleNormal PerPrimitiveNV
+               OpDecorate %triangleNormal Location 0
+               OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input
+     %uint_0 = OpConstant %uint 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+      %float = OpTypeFloat 32
+    %myblock = OpTypeStruct %float
+    %uint_32 = OpConstant %uint 32
+%_arr_myblock_uint_32 = OpTypeArray %myblock %uint_32
+%_ptr_Output__arr_myblock_uint_32 = OpTypePointer Output %_arr_myblock_uint_32
+        %blk = OpVariable %_ptr_Output__arr_myblock_uint_32 Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+   %float_11 = OpConstant %float 11
+%_ptr_Output_float = OpTypePointer Output %float
+    %v3float = OpTypeVector %float 3
+%_arr_v3float_uint_32 = OpTypeArray %v3float %uint_32
+%_ptr_Output__arr_v3float_uint_32 = OpTypePointer Output %_arr_v3float_uint_32
+%triangleNormal = OpVariable %_ptr_Output__arr_v3float_uint_32 Output
+         %33 = OpConstantComposite %v3float %float_11 %float_11 %float_11
+%_ptr_Output_v3float = OpTypePointer Output %v3float
+     %uint_1 = OpConstant %uint 1
+%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+        %iid = OpVariable %_ptr_Function_uint Function
+         %14 = OpAccessChain %_ptr_Input_uint %gl_LocalInvocationID %uint_0
+         %15 = OpLoad %uint %14
+               OpStore %iid %15
+         %22 = OpLoad %uint %iid
+         %27 = OpAccessChain %_ptr_Output_float %blk %22 %int_0
+               OpStore %27 %float_11
+         %32 = OpLoad %uint %iid
+         %35 = OpAccessChain %_ptr_Output_v3float %triangleNormal %32
+               OpStore %35 %33
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(body, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
+}
+
+TEST_F(ValidateMeshShading, MeshWriteOutput) {
+  const std::string body = R"(
+               OpCapability MeshShadingEXT
+               OpExtension "SPV_EXT_mesh_shader"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %main "main" %x %y
+               OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1
+               OpExecutionMode %main OutputVertices 3
+               OpExecutionMode %main OutputPrimitivesEXT 1
+               OpExecutionMode %main OutputTrianglesEXT
+               OpDecorate %x Location 1
+               OpDecorate %y Location 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_3 = OpConstant %uint 3
+%_arr_uint_uint_3 = OpTypeArray %uint %uint_3
+%_ptr_Output__arr_uint_uint_3 = OpTypePointer Output %_arr_uint_uint_3
+          %x = OpVariable %_ptr_Output__arr_uint_uint_3 Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_Output_uint = OpTypePointer Output %uint
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3
+%_ptr_Output__arr_v4float_uint_3 = OpTypePointer Output %_arr_v4float_uint_3
+          %y = OpVariable %_ptr_Output__arr_v4float_uint_3 Output
+    %float_0 = OpConstant %float 0
+         %23 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+         %16 = OpAccessChain %_ptr_Output_uint %x %int_0
+               OpStore %16 %uint_1
+         %25 = OpAccessChain %_ptr_Output_v4float %y %int_0
+               OpStore %25 %23
+               OpSetMeshOutputsEXT %uint_3 %uint_1
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(body, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateMeshShading, MeshReadOutputInt) {
+  const std::string body = R"(
+               OpCapability MeshShadingEXT
+               OpExtension "SPV_EXT_mesh_shader"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %main "main" %x
+               OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1
+               OpExecutionMode %main OutputVertices 3
+               OpExecutionMode %main OutputPrimitivesEXT 1
+               OpExecutionMode %main OutputTrianglesEXT
+               OpDecorate %x Location 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_3 = OpConstant %uint 3
+%_arr_uint_uint_3 = OpTypeArray %uint %uint_3
+%_ptr_Output__arr_uint_uint_3 = OpTypePointer Output %_arr_uint_uint_3
+          %x = OpVariable %_ptr_Output__arr_uint_uint_3 Output
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+%_ptr_Output_uint = OpTypePointer Output %uint
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+          %y = OpVariable %_ptr_Function_uint Function
+         %18 = OpAccessChain %_ptr_Output_uint %x %int_1
+         %19 = OpLoad %uint %18
+               OpStore %y %19
+               OpSetMeshOutputsEXT %uint_3 %uint_1
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(body, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-MeshEXT-07107"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("The Output Storage Class in a Mesh Execution Model "
+                        "must not be read from"));
+}
+
+TEST_F(ValidateMeshShading, MeshReadOutputVec) {
+  const std::string body = R"(
+               OpCapability MeshShadingEXT
+               OpExtension "SPV_EXT_mesh_shader"
+          %2 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %main "main" %x
+               OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1
+               OpExecutionMode %main OutputVertices 3
+               OpExecutionMode %main OutputPrimitivesEXT 1
+               OpExecutionMode %main OutputTrianglesEXT
+               OpDecorate %x Location 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+     %uint_3 = OpConstant %uint 3
+%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3
+%_ptr_Output__arr_v4float_uint_3 = OpTypePointer Output %_arr_v4float_uint_3
+          %x = OpVariable %_ptr_Output__arr_v4float_uint_3 Output
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+          %y = OpVariable %_ptr_Function_v4float Function
+         %20 = OpAccessChain %_ptr_Output_v4float %x %int_1
+         %21 = OpLoad %v4float %20
+               OpStore %y %21
+               OpSetMeshOutputsEXT %uint_3 %uint_1
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(body, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-MeshEXT-07107"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("The Output Storage Class in a Mesh Execution Model "
+                        "must not be read from"));
+}
+
+TEST_F(ValidateMeshShading, MeshReadOutputStruct) {
+  const std::string body = R"(
+               OpCapability MeshShadingEXT
+               OpExtension "SPV_EXT_mesh_shader"
+          %2 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %main "main" %x_0
+               OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1
+               OpExecutionMode %main OutputVertices 3
+               OpExecutionMode %main OutputPrimitivesEXT 1
+               OpExecutionMode %main OutputTrianglesEXT
+               OpDecorate %x_0 Location 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+        %Bar = OpTypeStruct %uint
+        %Foo = OpTypeStruct %Bar
+     %uint_3 = OpConstant %uint 3
+%_arr_Foo_uint_3 = OpTypeArray %Foo %uint_3
+%_ptr_Output__arr_Foo_uint_3 = OpTypePointer Output %_arr_Foo_uint_3
+        %x_0 = OpVariable %_ptr_Output__arr_Foo_uint_3 Output
+        %int = OpTypeInt 32 1
+      %int_2 = OpConstant %int 2
+      %int_0 = OpConstant %int 0
+%_ptr_Output_uint = OpTypePointer Output %uint
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+          %x = OpVariable %_ptr_Function_uint Function
+         %21 = OpAccessChain %_ptr_Output_uint %x_0 %int_2 %int_0 %int_0
+         %22 = OpLoad %uint %21
+               OpStore %x %22
+               OpSetMeshOutputsEXT %uint_3 %uint_1
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(body, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-MeshEXT-07107"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("The Output Storage Class in a Mesh Execution Model "
+                        "must not be read from"));
+}
+
+TEST_F(ValidateMeshShading, MeshReadOutputIntUntyped) {
+  const std::string body = R"(
+               OpCapability MeshShadingEXT
+               OpCapability UntypedPointersKHR
+               OpExtension "SPV_EXT_mesh_shader"
+               OpExtension "SPV_KHR_untyped_pointers"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %main "main" %x
+               OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1
+               OpExecutionMode %main OutputVertices 3
+               OpExecutionMode %main OutputPrimitivesEXT 1
+               OpExecutionMode %main OutputTrianglesEXT
+               OpDecorate %x Location 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_3 = OpConstant %uint 3
+%_arr_uint_uint_3 = OpTypeArray %uint %uint_3
+%_ptr_Output__arr_uint_uint_3 = OpTypePointer Output %_arr_uint_uint_3
+          %x = OpVariable %_ptr_Output__arr_uint_uint_3 Output
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+%_ptr_Output_uint = OpTypeUntypedPointerKHR Output
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+          %y = OpVariable %_ptr_Function_uint Function
+         %18 = OpUntypedAccessChainKHR %_ptr_Output_uint %_arr_uint_uint_3 %x %int_1
+         %19 = OpLoad %uint %18
+               OpStore %y %19
+               OpSetMeshOutputsEXT %uint_3 %uint_1
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(body, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("In Vulkan, untyped pointers can only be used in an "
+                        "explicitly laid out storage class"));
+}
+
+// https://godbolt.org/z/8s5W19xoc
+TEST_F(ValidateMeshShading, SetMeshOutputsConstantInCondition) {
+  const std::string body = R"(
+               OpCapability MeshShadingEXT
+               OpExtension "SPV_EXT_mesh_shader"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %main "main" %_ %gl_MeshVerticesEXT %gl_PrimitiveTriangleIndicesEXT
+               OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1
+               OpExecutionMode %main OutputVertices 3
+               OpExecutionMode %main OutputPrimitivesEXT 1
+               OpExecutionMode %main OutputTrianglesEXT
+               OpDecorate %SSBO Block
+               OpMemberDecorate %SSBO 0 Offset 0
+               OpMemberDecorate %SSBO 1 Offset 4
+               OpMemberDecorate %SSBO 2 Offset 8
+               OpDecorate %_ Binding 0
+               OpDecorate %_ DescriptorSet 0
+               OpDecorate %gl_MeshPerVertexEXT Block
+               OpMemberDecorate %gl_MeshPerVertexEXT 0 BuiltIn Position
+               OpMemberDecorate %gl_MeshPerVertexEXT 1 BuiltIn PointSize
+               OpMemberDecorate %gl_MeshPerVertexEXT 2 BuiltIn ClipDistance
+               OpMemberDecorate %gl_MeshPerVertexEXT 3 BuiltIn CullDistance
+               OpDecorate %gl_PrimitiveTriangleIndicesEXT BuiltIn PrimitiveTriangleIndicesEXT
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+       %SSBO = OpTypeStruct %uint %uint %uint
+%_ptr_StorageBuffer_SSBO = OpTypePointer StorageBuffer %SSBO
+          %_ = OpVariable %_ptr_StorageBuffer_SSBO StorageBuffer
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+       %bool = OpTypeBool
+   %uint_500 = OpConstant %uint 500
+      %int_1 = OpConstant %int 1
+      %int_2 = OpConstant %int 2
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_MeshPerVertexEXT = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+     %uint_3 = OpConstant %uint 3
+%_arr_gl_MeshPerVertexEXT_uint_3 = OpTypeArray %gl_MeshPerVertexEXT %uint_3
+%_ptr_Output__arr_gl_MeshPerVertexEXT_uint_3 = OpTypePointer Output %_arr_gl_MeshPerVertexEXT_uint_3
+%gl_MeshVerticesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerVertexEXT_uint_3 Output
+    %float_0 = OpConstant %float 0
+         %38 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+     %v3uint = OpTypeVector %uint 3
+%_arr_v3uint_uint_1 = OpTypeArray %v3uint %uint_1
+%_ptr_Output__arr_v3uint_uint_1 = OpTypePointer Output %_arr_v3uint_uint_1
+%gl_PrimitiveTriangleIndicesEXT = OpVariable %_ptr_Output__arr_v3uint_uint_1 Output
+     %uint_0 = OpConstant %uint 0
+     %uint_2 = OpConstant %uint 2
+         %47 = OpConstantComposite %v3uint %uint_0 %uint_1 %uint_2
+%_ptr_Output_v3uint = OpTypePointer Output %v3uint
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+         %15 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0
+         %16 = OpLoad %uint %15
+         %18 = OpIEqual %bool %16 %uint_1
+               OpSelectionMerge %20 None
+               OpBranchConditional %18 %19 %22
+         %19 = OpLabel
+               OpSetMeshOutputsEXT %uint_500 %uint_500
+               OpBranch %20
+         %22 = OpLabel
+         %24 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_1
+         %25 = OpLoad %uint %24
+         %27 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_2
+         %28 = OpLoad %uint %27
+               OpSetMeshOutputsEXT %25 %28
+               OpBranch %20
+         %20 = OpLabel
+         %40 = OpAccessChain %_ptr_Output_v4float %gl_MeshVerticesEXT %int_0 %int_0
+               OpStore %40 %38
+         %49 = OpAccessChain %_ptr_Output_v3uint %gl_PrimitiveTriangleIndicesEXT %int_0
+               OpStore %49 %47
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(body, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpSetMeshOutputsEXT Vertex Count (500) is larger than the "
+                "OutputVertices in OpExecutionMode (3).\nOpSetMeshOutputsEXT "
+                "Primitive Count (500) is larger than the OutputPrimitivesEXT "
+                "in OpExecutionMode (1)"));
+}
+
+// https://godbolt.org/z/TzaKxYGrY (allowed until spec constants are frozen)
+TEST_F(ValidateMeshShading, SetMeshOutputsSpecConstants) {
+  const std::string body = R"(
+               OpCapability MeshShadingEXT
+               OpExtension "SPV_EXT_mesh_shader"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint MeshEXT %main "main" %gl_MeshVerticesEXT %gl_PrimitiveTriangleIndicesEXT
+               OpExecutionModeId %main LocalSizeId %uint_1 %uint_1 %uint_1
+               OpExecutionMode %main OutputVertices 3
+               OpExecutionMode %main OutputPrimitivesEXT 1
+               OpExecutionMode %main OutputTrianglesEXT
+               OpDecorate %V SpecId 0
+               OpDecorate %P SpecId 1
+               OpDecorate %gl_MeshPerVertexEXT Block
+               OpMemberDecorate %gl_MeshPerVertexEXT 0 BuiltIn Position
+               OpMemberDecorate %gl_MeshPerVertexEXT 1 BuiltIn PointSize
+               OpMemberDecorate %gl_MeshPerVertexEXT 2 BuiltIn ClipDistance
+               OpMemberDecorate %gl_MeshPerVertexEXT 3 BuiltIn CullDistance
+               OpDecorate %gl_PrimitiveTriangleIndicesEXT BuiltIn PrimitiveTriangleIndicesEXT
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+          %V = OpSpecConstant %uint 500
+          %P = OpSpecConstant %uint 500
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_arr_float_uint_1 = OpTypeArray %float %uint_1
+%gl_MeshPerVertexEXT = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1
+     %uint_3 = OpConstant %uint 3
+%_arr_gl_MeshPerVertexEXT_uint_3 = OpTypeArray %gl_MeshPerVertexEXT %uint_3
+%_ptr_Output__arr_gl_MeshPerVertexEXT_uint_3 = OpTypePointer Output %_arr_gl_MeshPerVertexEXT_uint_3
+%gl_MeshVerticesEXT = OpVariable %_ptr_Output__arr_gl_MeshPerVertexEXT_uint_3 Output
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+    %float_0 = OpConstant %float 0
+         %22 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+     %v3uint = OpTypeVector %uint 3
+%_arr_v3uint_uint_1 = OpTypeArray %v3uint %uint_1
+%_ptr_Output__arr_v3uint_uint_1 = OpTypePointer Output %_arr_v3uint_uint_1
+%gl_PrimitiveTriangleIndicesEXT = OpVariable %_ptr_Output__arr_v3uint_uint_1 Output
+     %uint_0 = OpConstant %uint 0
+     %uint_2 = OpConstant %uint 2
+         %31 = OpConstantComposite %v3uint %uint_0 %uint_1 %uint_2
+%_ptr_Output_v3uint = OpTypePointer Output %v3uint
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+               OpSetMeshOutputsEXT %V %P
+         %24 = OpAccessChain %_ptr_Output_v4float %gl_MeshVerticesEXT %int_0 %int_0
+               OpStore %24 %22
+         %33 = OpAccessChain %_ptr_Output_v3uint %gl_PrimitiveTriangleIndicesEXT %int_0
+               OpStore %33 %31
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(body, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_misc_test.cpp b/test/val/val_misc_test.cpp
index 2188e03..f54b20c 100644
--- a/test/val/val_misc_test.cpp
+++ b/test/val/val_misc_test.cpp
@@ -378,9 +378,10 @@
 OpCapability Float16
 OpCapability Int16
 OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
 OpExtension "SPV_KHR_cooperative_matrix"
 OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical GLSL450
+OpMemoryModel Logical VulkanKHR
 OpEntryPoint GLCompute %main "main"
 OpExecutionModeId %main LocalSizeId %u32_16 %u32_16 %u32_16
 %void = OpTypeVoid
@@ -411,9 +412,10 @@
 OpCapability Float16
 OpCapability Int16
 OpCapability CooperativeMatrixKHR
+OpCapability VulkanMemoryModelKHR
 OpExtension "SPV_KHR_cooperative_matrix"
 OpExtension "SPV_KHR_vulkan_memory_model"
-OpMemoryModel Logical GLSL450
+OpMemoryModel Logical VulkanKHR
 OpEntryPoint GLCompute %main "main"
 OpExecutionModeId %main LocalSizeId %u32_16 %u32_8 %u32_16
 %void = OpTypeVoid
@@ -442,6 +444,61 @@
                         "before LocalSizeId constant value"));
 }
 
+TEST_F(ValidateMisc, CoopMatDeviceScope) {
+  const std::string body = R"(
+               OpCapability Shader
+               OpCapability Float16
+               OpCapability VulkanMemoryModel
+               OpCapability CooperativeMatrixKHR
+               OpExtension "SPV_KHR_cooperative_matrix"
+               OpMemoryModel Logical Vulkan
+               OpEntryPoint GLCompute %main "main" %_
+               OpExecutionMode %main LocalSize 32 1 1
+               OpDecorate %_runtimearr_uint ArrayStride 4
+               OpDecorate %InputA Block
+               OpMemberDecorate %InputA 0 Offset 0
+               OpDecorate %_ Binding 0
+               OpDecorate %_ DescriptorSet 0
+               OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+       %half = OpTypeFloat 16
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+    %uint_16 = OpConstant %uint 16
+     %uint_0 = OpConstant %uint 0
+         ;; Using Device Scope
+         %12 = OpTypeCooperativeMatrixKHR %half %uint_1 %uint_16 %uint_16 %uint_0
+%_ptr_Function_12 = OpTypePointer Function %12
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+     %InputA = OpTypeStruct %_runtimearr_uint
+%_ptr_StorageBuffer_InputA = OpTypePointer StorageBuffer %InputA
+          %_ = OpVariable %_ptr_StorageBuffer_InputA StorageBuffer
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+     %uint_5 = OpConstant %uint 5
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+     %v3uint = OpTypeVector %uint 3
+    %uint_32 = OpConstant %uint 32
+%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_32 %uint_1 %uint_1
+       %main = OpFunction %void None %4
+          %6 = OpLabel
+          %A = OpVariable %_ptr_Function_12 Function
+         %23 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0 %uint_0
+         %24 = OpCooperativeMatrixLoadKHR %12 %23 %int_0 %uint_16 MakePointerVisible|NonPrivatePointer %uint_5
+               OpStore %A %24
+               OpReturn
+               OpFunctionEnd)";
+
+  CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-Scope-12243"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpTypeCooperativeMatrixKHR Scope is limited to "
+                        "Workgroup and Subgroup"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_modes_test.cpp b/test/val/val_modes_test.cpp
index 83a0503..8e9fca8 100644
--- a/test/val/val_modes_test.cpp
+++ b/test/val/val_modes_test.cpp
@@ -1,4 +1,6 @@
 // Copyright (c) 2018 Google LLC.
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -62,7 +64,7 @@
   CompileSuccessfully(spirv, env);
   EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
   EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-LocalSize-06426"));
+              AnyVUID("VUID-StandaloneSpirv-None-10685"));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -71,6 +73,53 @@
           "or an object decorated with WorkgroupSize must be specified."));
 }
 
+TEST_F(ValidateMode, MeshNoModeVulkan) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability MeshShadingEXT
+OpExtension "SPV_EXT_mesh_shader"
+OpMemoryModel Logical GLSL450
+OpEntryPoint MeshEXT %main "main"
+OpExecutionMode %main OutputVertices 81
+OpExecutionMode %main OutputPrimitivesEXT 16
+OpExecutionMode %main OutputPoints
+)" + kVoidFunction;
+
+  spv_target_env env = SPV_ENV_VULKAN_1_3;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-10685"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "In the Vulkan environment, MeshEXT execution model entry "
+          "points require either the LocalSize or LocalSizeId execution mode "
+          "or an object decorated with WorkgroupSize must be specified."));
+}
+
+TEST_F(ValidateMode, TaskNoModeVulkan) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability MeshShadingEXT
+OpExtension "SPV_EXT_mesh_shader"
+OpMemoryModel Logical GLSL450
+OpEntryPoint TaskEXT %main "main"
+)" + kVoidFunction;
+
+  spv_target_env env = SPV_ENV_VULKAN_1_3;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-10685"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "In the Vulkan environment, TaskEXT execution model entry "
+          "points require either the LocalSize or LocalSizeId execution mode "
+          "or an object decorated with WorkgroupSize must be specified."));
+}
+
 TEST_F(ValidateMode, GLComputeNoModeVulkanWorkgroupSize) {
   const std::string spirv = R"(
 OpCapability Shader
@@ -88,6 +137,176 @@
   EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env));
 }
 
+TEST_F(ValidateMode, GLComputeZeroWorkgroupSize) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpDecorate %int3_1 BuiltIn WorkgroupSize
+%int = OpTypeInt 32 0
+%int3 = OpTypeVector %int 3
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int3_1 = OpConstantComposite %int3 %int_1 %int_0 %int_0
+)" + kVoidFunction;
+
+  CompileSuccessfully(spirv);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "WorkgroupSize decorations must not have a static product of zero"));
+}
+
+TEST_F(ValidateMode, GLComputeZeroSpecWorkgroupSize) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpDecorate %int3_1 BuiltIn WorkgroupSize
+%int = OpTypeInt 32 0
+%int3 = OpTypeVector %int 3
+%int_0 = OpSpecConstant %int 0
+%int_1 = OpConstant %int 1
+%int3_1 = OpSpecConstantComposite %int3 %int_1 %int_0 %int_0
+)" + kVoidFunction;
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateMode, GLComputeZeroSpecCompositeWorkgroupSize) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpDecorate %int3_1 BuiltIn WorkgroupSize
+%int = OpTypeInt 32 0
+%int3 = OpTypeVector %int 3
+%int_0 = OpSpecConstant %int 0
+%int_1 = OpSpecConstant %int 1
+%int3_1 = OpSpecConstantComposite %int3 %int_1 %int_0 %int_0
+)" + kVoidFunction;
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateMode, KernelZeroWorkgroupSizeConstant) {
+  const std::string spirv = R"(
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Kernel
+OpMemoryModel Physical32 OpenCL
+OpEntryPoint Kernel %main "main"
+OpDecorate %int3_1 BuiltIn WorkgroupSize
+%int = OpTypeInt 32 0
+%int3 = OpTypeVector %int 3
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int3_1 = OpConstantComposite %int3 %int_1 %int_0 %int_0
+)" + kVoidFunction;
+
+  CompileSuccessfully(spirv);
+  EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable"));
+}
+
+TEST_F(ValidateMode, KernelZeroWorkgroupSizeVariable) {
+  const std::string spirv = R"(
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Kernel
+OpMemoryModel Physical32 OpenCL
+OpEntryPoint Kernel %main "main"
+OpDecorate %var BuiltIn WorkgroupSize
+%int = OpTypeInt 32 0
+%int3 = OpTypeVector %int 3
+%ptr = OpTypePointer Input %int3
+%var = OpVariable %ptr Input
+)" + kVoidFunction;
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateMode, GLComputeWorkgroupSizeDerivativeGroupQuads) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability ComputeDerivativeGroupQuadsKHR
+OpExtension "SPV_KHR_compute_shader_derivatives"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main DerivativeGroupQuadsKHR
+OpDecorate %int3_1 BuiltIn WorkgroupSize
+%int = OpTypeInt 32 0
+%int3 = OpTypeVector %int 3
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%int3_1 = OpConstantComposite %int3 %int_1 %int_2 %int_1
+)" + kVoidFunction;
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-DerivativeGroupQuadsKHR-10151"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("WorkgroupSize decorations has a static dimensions of (X = 1, "
+                "Y = 2) but Entry Point id 1 has an DerivativeGroupQuadsKHR "
+                "execution mode, so both dimensions must be a multiple of 2"));
+}
+
+TEST_F(ValidateMode, GLComputeWorkgroupSizeDerivativeGroupLinear) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability ComputeDerivativeGroupLinearKHR
+OpExtension "SPV_KHR_compute_shader_derivatives"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main DerivativeGroupLinearKHR
+OpDecorate %int3_1 BuiltIn WorkgroupSize
+%int = OpTypeInt 32 0
+%uvec3 = OpTypeVector %int 3
+%int_3 = OpConstant %int 3
+%int_2 = OpConstant %int 2
+%int3_1 = OpConstantComposite %uvec3 %int_3 %int_3 %int_2
+)" + kVoidFunction;
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-DerivativeGroupLinearKHR-10152"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "WorkgroupSize decorations has a static dimensions of (X = 3, Y = 3, "
+          "Z = 2) but Entry Point id 1 has an DerivativeGroupLinearKHR "
+          "execution mode, so the product (18) must be a multiple of 4"));
+}
+
+TEST_F(ValidateMode, GLComputeWorkgroupSizeDerivativeGroupQuadsOverride) {
+  // "If an object is decorated with the WorkgroupSize decoration, this takes
+  // precedence over any LocalSize or LocalSizeId execution mode."
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability ComputeDerivativeGroupQuadsKHR
+OpExtension "SPV_KHR_compute_shader_derivatives"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main DerivativeGroupQuadsKHR
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %int3_1 BuiltIn WorkgroupSize
+%int = OpTypeInt 32 0
+%int3 = OpTypeVector %int 3
+%int_2 = OpConstant %int 2
+%int3_1 = OpConstantComposite %int3 %int_2 %int_2 %int_2
+)" + kVoidFunction;
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+}
+
 TEST_F(ValidateMode, GLComputeVulkanLocalSize) {
   const std::string spirv = R"(
 OpCapability Shader
@@ -101,6 +320,97 @@
   EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env));
 }
 
+TEST_F(ValidateMode, GLComputeZeroLocalSize) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 0
+)" + kVoidFunction;
+
+  CompileSuccessfully(spirv);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Local Size execution mode must not have a product of zero"));
+}
+
+TEST_F(ValidateMode, KernelZeroLocalSize) {
+  const std::string spirv = R"(
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Kernel
+OpMemoryModel Physical32 OpenCL
+OpEntryPoint Kernel %main "main"
+OpExecutionMode %main LocalSize 1 1 0
+)" + kVoidFunction;
+
+  CompileSuccessfully(spirv);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Local Size execution mode must not have a product of zero"));
+}
+
+TEST_F(ValidateMode, GLComputeLocalSizeDerivativeGroupQuads) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability ComputeDerivativeGroupQuadsKHR
+OpExtension "SPV_KHR_compute_shader_derivatives"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpExecutionMode %main DerivativeGroupQuadsKHR
+)" + kVoidFunction;
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-DerivativeGroupQuadsKHR-10151"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Local Size execution mode dimensions is (X = 1, Y = 1) but "
+                "Entry Point id 1 also has an DerivativeGroupQuadsKHR "
+                "execution mode, so both dimensions must be a multiple of 2"));
+}
+
+TEST_F(ValidateMode, GLComputeLocalSizeDerivativeGroupLinear) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability ComputeDerivativeGroupLinearKHR
+OpExtension "SPV_KHR_compute_shader_derivatives"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main DerivativeGroupLinearKHR
+OpExecutionMode %main LocalSize 3 3 3
+)" + kVoidFunction;
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-DerivativeGroupLinearKHR-10152"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Local Size execution mode dimensions is (X = 3, Y = 3, Z = 3) "
+                "but Entry Point id 1 also has an DerivativeGroupLinearKHR "
+                "execution mode, so the product (27) must be a multiple of 4"));
+}
+
+TEST_F(ValidateMode, GLComputeLocalSizeDerivativeGroupQuadsCapabiltyOnly) {
+  // Declares capability but doesn't have OpExecutionMode
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability ComputeDerivativeGroupQuadsKHR
+OpExtension "SPV_KHR_compute_shader_derivatives"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+)" + kVoidFunction;
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
 TEST_F(ValidateMode, GLComputeVulkanLocalSizeIdBad) {
   const std::string spirv = R"(
 OpCapability Shader
@@ -135,6 +445,142 @@
   EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env));
 }
 
+TEST_F(ValidateMode, GLComputeZeroLocalSizeId) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionModeId %main LocalSizeId %int_1 %int_0 %int_1
+%int = OpTypeInt 32 0
+%int_1 = OpConstant %int 1
+%int_0 = OpConstant %int 0
+)" + kVoidFunction;
+
+  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("LocalSizeId execution mode must not have a product of zero"));
+}
+
+TEST_F(ValidateMode, GLComputeZeroSpecLocalSizeId) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionModeId %main LocalSizeId %int_1 %int_0 %int_1
+%int = OpTypeInt 32 0
+%int_1 = OpConstant %int 1
+%int_0 = OpSpecConstant %int 0
+)" + kVoidFunction;
+
+  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(env));
+}
+
+TEST_F(ValidateMode, KernelZeroLocalSizeId) {
+  const std::string spirv = R"(
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Kernel
+OpMemoryModel Physical32 OpenCL
+OpEntryPoint Kernel %main "main"
+OpExecutionModeId %main LocalSizeId %int_1 %int_0 %int_1
+%int = OpTypeInt 32 0
+%int_1 = OpConstant %int 1
+%int_0 = OpConstant %int 0
+)" + kVoidFunction;
+
+  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("LocalSizeId execution mode must not have a product of zero"));
+}
+
+// https://github.com/KhronosGroup/SPIRV-Tools/issues/5939
+TEST_F(ValidateMode, KernelZeroLocalSize64) {
+  const std::string spirv = R"(
+               OpCapability Kernel
+               OpCapability Addresses
+               OpCapability Int64
+               OpCapability Linkage
+               OpMemoryModel Physical64 OpenCL
+               OpEntryPoint Kernel %test "test" %__spirv_BuiltInWorkgroupSize
+               OpExecutionMode %test ContractionOff
+               OpDecorate %__spirv_BuiltInWorkgroupSize Constant
+               OpDecorate %__spirv_BuiltInWorkgroupSize LinkageAttributes "__spirv_BuiltInWorkgroupSize" Import
+               OpDecorate %__spirv_BuiltInWorkgroupSize BuiltIn WorkgroupSize
+       %void = OpTypeVoid
+      %ulong = OpTypeInt 64 0
+    %v3ulong = OpTypeVector %ulong 3
+%_ptr_Input_v3ulong = OpTypePointer Input %v3ulong
+          %8 = OpTypeFunction %void
+%__spirv_BuiltInWorkgroupSize = OpVariable %_ptr_Input_v3ulong Input
+       %test = OpFunction %void None %8
+      %entry = OpLabel
+         %11 = OpLoad %v3ulong %__spirv_BuiltInWorkgroupSize Aligned 1
+         %12 = OpCompositeExtract %ulong %11 0
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateMode, GLComputeLocalSizeIdDerivativeGroupQuads) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability ComputeDerivativeGroupQuadsKHR
+OpExtension "SPV_KHR_compute_shader_derivatives"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionModeId %main LocalSizeId %int_2 %int_1 %int_2
+OpExecutionMode %main DerivativeGroupQuadsKHR
+%int = OpTypeInt 32 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+)" + kVoidFunction;
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-DerivativeGroupQuadsKHR-10151"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("LocalSizeId execution mode dimensions is (X = 2, Y = 1) but "
+                "Entry Point id 1 also has an DerivativeGroupQuadsKHR "
+                "execution mode, so both dimensions must be a multiple of 2"));
+}
+
+TEST_F(ValidateMode, GLComputeLocalSizeIdDerivativeGroupLinear) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability ComputeDerivativeGroupLinearKHR
+OpExtension "SPV_KHR_compute_shader_derivatives"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1
+OpExecutionMode %main DerivativeGroupLinearKHR
+%int = OpTypeInt 32 0
+%int_1 = OpConstant %int 1
+)" + kVoidFunction;
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-DerivativeGroupLinearKHR-10152"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("LocalSizeId execution mode dimensions is (X = 1, Y = 1, Z = "
+                "1) but Entry Point id 1 also has an DerivativeGroupLinearKHR "
+                "execution mode, so the product (1) must be a multiple of 4"));
+}
+
 TEST_F(ValidateMode, FragmentOriginLowerLeftVulkan) {
   const std::string spirv = R"(
 OpCapability Shader
@@ -814,6 +1260,109 @@
   EXPECT_THAT(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateModeExecution, MeshEXTOutputVertices) {
+  const std::string spirv = R"(
+OpCapability MeshShadingEXT
+OpExtension "SPV_EXT_mesh_shader"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint MeshEXT %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpExecutionMode %main OutputVertices 3
+OpExecutionMode %main OutputPrimitivesNV 1
+OpExecutionMode %main OutputTrianglesNV
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_mesh_shader"
+OpName %main "main"
+OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_3 = OpConstant %uint 3
+%uint_1 = OpConstant %uint 1
+%v3uint = OpTypeVector %uint 3
+%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
+%main = OpFunction %void None %3
+%5 = OpLabel
+OpSetMeshOutputsEXT %uint_3 %uint_1
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+}
+
+TEST_F(ValidateModeExecution, VulkanBadMeshEXTOutputVertices) {
+  const std::string spirv = R"(
+OpCapability MeshShadingEXT
+OpExtension "SPV_EXT_mesh_shader"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint MeshEXT %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpExecutionMode %main OutputVertices 0
+OpExecutionMode %main OutputPrimitivesNV 1
+OpExecutionMode %main OutputTrianglesNV
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_mesh_shader"
+OpName %main "main"
+OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_3 = OpConstant %uint 3
+%uint_1 = OpConstant %uint 1
+%v3uint = OpTypeVector %uint 3
+%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
+%main = OpFunction %void None %3
+%5 = OpLabel
+OpSetMeshOutputsEXT %uint_3 %uint_1
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-MeshEXT-07330"));
+}
+
+TEST_F(ValidateModeExecution, VulkanBadMeshEXTOutputOutputPrimitivesEXT) {
+  const std::string spirv = R"(
+OpCapability MeshShadingEXT
+OpExtension "SPV_EXT_mesh_shader"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint MeshEXT %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpExecutionMode %main OutputVertices 1
+OpExecutionMode %main OutputPrimitivesNV 0
+OpExecutionMode %main OutputTrianglesNV
+OpSource GLSL 460
+OpSourceExtension "GL_EXT_mesh_shader"
+OpName %main "main"
+OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%uint = OpTypeInt 32 0
+%uint_3 = OpConstant %uint 3
+%uint_1 = OpConstant %uint 1
+%v3uint = OpTypeVector %uint 3
+%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
+%main = OpFunction %void None %3
+%5 = OpLabel
+OpSetMeshOutputsEXT %uint_3 %uint_1
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-MeshEXT-07331"));
+}
+
 TEST_F(ValidateModeExecution, MeshNVOutputVertices) {
   const std::string spirv = R"(
 OpCapability Shader
@@ -1282,6 +1831,23 @@
   EXPECT_THAT(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateMode, FragmentShaderPostDepthCoverageVertexBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability SampleMaskPostDepthCoverage
+OpExtension "SPV_KHR_post_depth_coverage"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main"
+OpExecutionMode %main PostDepthCoverage
+)" + kVoidFunction;
+
+  CompileSuccessfully(spirv);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Execution mode can only be used with the Fragment execution model"));
+}
 
 TEST_F(ValidateMode, FragmentShaderStencilRefFrontTooManyModesBad) {
   const std::string spirv = R"(
@@ -1331,6 +1897,24 @@
                 "execution modes."));
 }
 
+TEST_F(ValidateMode, FragmentShaderStencilRefReplacingVertexBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability StencilExportEXT
+OpExtension "SPV_EXT_shader_stencil_export"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main"
+OpExecutionMode %main StencilRefReplacingEXT
+)" + kVoidFunction;
+
+  CompileSuccessfully(spirv);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Execution mode can only be used with the Fragment execution model"));
+}
+
 TEST_F(ValidateMode, FragmentShaderStencilRefFrontGood) {
   const std::string spirv = R"(
 OpCapability Shader
@@ -2208,6 +2792,396 @@
           "Execution mode can only be used with the Fragment execution model"));
 }
 
+const std::string kNodeShaderPrelude = R"(
+OpCapability Shader
+OpCapability ShaderEnqueueAMDX
+OpExtension "SPV_AMDX_shader_enqueue"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpEntryPoint GLCompute %other "other"
+)";
+
+const std::string kNodeShaderPostlude = R"(
+%uint = OpTypeInt 32 0
+%uint_0 = OpConstant %uint 0
+%uint_1 = OpConstant %uint 1
+%node0 = OpConstantStringAMDX "node0"
+%node1 = OpConstantStringAMDX "node1"
+%node2 = OpConstantStringAMDX "node2"
+%S = OpTypeStruct
+%_payloadarr_S_0 = OpTypeNodePayloadArrayAMDX %S
+%_payloadarr_S = OpTypeNodePayloadArrayAMDX %S
+%bool = OpTypeBool
+%true = OpConstantTrue %bool
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+%other = OpFunction %void None %void_fn
+%entry0 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+TEST_F(ValidateMode, NodeShader) {
+  const std::string spirv = kNodeShaderPrelude + R"(
+OpExecutionModeId %main ShaderIndexAMDX %uint_0
+OpExecutionModeId %main IsApiEntryAMDX %true
+OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1
+OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0
+OpExecutionModeId %other ShaderIndexAMDX %uint_0
+OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1
+OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2
+OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0
+OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1
+OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0
+)" + kNodeShaderPostlude;
+
+  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env));
+}
+
+TEST_F(ValidateMode, NodeShaderModeShaderIndex) {
+  const std::string spirv = kNodeShaderPrelude + R"(
+OpExecutionMode %main ShaderIndexAMDX %uint_0
+OpExecutionModeId %main IsApiEntryAMDX %true
+OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1
+OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0
+OpExecutionMode %other ShaderIndexAMDX %uint_0
+OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1
+OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2
+OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0
+OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1
+OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0
+)" + kNodeShaderPostlude;
+
+  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpExecutionMode is only valid when the Mode operand is an "
+                "execution mode that takes no Extra Operands, or takes Extra "
+                "Operands that are not id operands"));
+}
+
+TEST_F(ValidateMode, NodeShaderModeIsApiEntry) {
+  const std::string spirv = kNodeShaderPrelude + R"(
+OpExecutionModeId %main ShaderIndexAMDX %uint_0
+OpExecutionMode %main IsApiEntryAMDX %true
+OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1
+OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0
+OpExecutionModeId %other ShaderIndexAMDX %uint_0
+OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1
+OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2
+OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0
+OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1
+OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0
+)" + kNodeShaderPostlude;
+
+  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpExecutionMode is only valid when the Mode operand is an "
+                "execution mode that takes no Extra Operands, or takes Extra "
+                "Operands that are not id operands"));
+}
+
+TEST_F(ValidateMode, NodeShaderModeMaxNodeRecursion) {
+  const std::string spirv = kNodeShaderPrelude + R"(
+OpExecutionModeId %main ShaderIndexAMDX %uint_0
+OpExecutionModeId %main IsApiEntryAMDX %true
+OpExecutionMode %main MaxNodeRecursionAMDX %uint_1
+OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0
+OpExecutionModeId %other ShaderIndexAMDX %uint_0
+OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1
+OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2
+OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0
+OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1
+OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0
+)" + kNodeShaderPostlude;
+
+  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpExecutionMode is only valid when the Mode operand is an "
+                "execution mode that takes no Extra Operands, or takes Extra "
+                "Operands that are not id operands"));
+}
+
+TEST_F(ValidateMode, NodeShaderModeMaxNumWorkgroups) {
+  const std::string spirv = kNodeShaderPrelude + R"(
+OpExecutionModeId %main ShaderIndexAMDX %uint_0
+OpExecutionModeId %main IsApiEntryAMDX %true
+OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1
+OpExecutionMode %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0
+OpExecutionModeId %other ShaderIndexAMDX %uint_0
+OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1
+OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2
+OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0
+OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1
+OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0
+)" + kNodeShaderPostlude;
+
+  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpExecutionMode is only valid when the Mode operand is an "
+                "execution mode that takes no Extra Operands, or takes Extra "
+                "Operands that are not id operands"));
+}
+
+TEST_F(ValidateMode, NodeShaderModeStaticNumWorkgroups) {
+  const std::string spirv = kNodeShaderPrelude + R"(
+OpExecutionModeId %main ShaderIndexAMDX %uint_0
+OpExecutionModeId %main IsApiEntryAMDX %true
+OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1
+OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpExecutionModeId %main SharesInputWithAMDX %node0 %uint_0
+OpExecutionModeId %other ShaderIndexAMDX %uint_0
+OpExecutionMode %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1
+OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2
+OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0
+OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1
+OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0
+)" + kNodeShaderPostlude;
+
+  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpExecutionMode is only valid when the Mode operand is an "
+                "execution mode that takes no Extra Operands, or takes Extra "
+                "Operands that are not id operands"));
+}
+
+TEST_F(ValidateMode, NodeShaderModeSharesInputWith) {
+  const std::string spirv = kNodeShaderPrelude + R"(
+OpExecutionModeId %main ShaderIndexAMDX %uint_0
+OpExecutionModeId %main IsApiEntryAMDX %true
+OpExecutionModeId %main MaxNodeRecursionAMDX %uint_1
+OpExecutionModeId %main MaxNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpExecutionMode %main SharesInputWithAMDX %node0 %uint_0
+OpExecutionModeId %other ShaderIndexAMDX %uint_0
+OpExecutionModeId %other StaticNumWorkgroupsAMDX %uint_1 %uint_1 %uint_1
+OpDecorateId %_payloadarr_S PayloadNodeNameAMDX %node1
+OpDecorateId %_payloadarr_S_0 PayloadNodeNameAMDX %node2
+OpDecorateId %_payloadarr_S PayloadNodeBaseIndexAMDX %uint_0
+OpDecorateId %_payloadarr_S PayloadNodeArraySizeAMDX %uint_1
+OpDecorateId %_payloadarr_S NodeSharesPayloadLimitsWithAMDX %_payloadarr_S_0
+)" + kNodeShaderPostlude;
+
+  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpExecutionMode is only valid when the Mode operand is an "
+                "execution mode that takes no Extra Operands, or takes Extra "
+                "Operands that are not id operands"));
+}
+
+TEST_F(ValidateMode, GLComputeNoModeVulkanQCOM) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability TileShadingQCOM
+OpExtension "SPV_QCOM_tile_shading"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+)" + kVoidFunction;
+
+  spv_target_env env = SPV_ENV_VULKAN_1_4;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-10685"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("In the Vulkan environment, GLCompute execution model entry "
+                "points require either the TileShadingRateQCOM, LocalSize or "
+                "LocalSizeId execution mode or an object decorated with "
+                "WorkgroupSize "
+                "must be specified."));
+}
+
+TEST_F(ValidateMode, GLComputeVulkanLocalSizeBadQCOM) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability TileShadingQCOM
+OpExtension "SPV_QCOM_tile_shading"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main TileShadingRateQCOM 2 2 3
+OpExecutionMode %main LocalSize 16 16 1
+)" + kVoidFunction;
+
+  spv_target_env env = SPV_ENV_VULKAN_1_4;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("If the TileShadingRateQCOM execution mode is used, "
+                        "LocalSize and LocalSizeId must not be specified."));
+}
+
+TEST_F(ValidateMode, GLComputeVulkanLocalSizeIdBadQCOM) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability TileShadingQCOM
+OpExtension "SPV_QCOM_tile_shading"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main TileShadingRateQCOM 2 2 3
+OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1
+%int = OpTypeInt 32 0
+%int_1 = OpConstant %int 1
+)" + kVoidFunction;
+
+  spv_target_env env = SPV_ENV_VULKAN_1_4;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("If the TileShadingRateQCOM execution mode is used, "
+                        "LocalSize and LocalSizeId must not be specified."));
+}
+
+TEST_F(ValidateMode, NonCoherentTileAttachmentReadQCOMBad1) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main NonCoherentTileAttachmentReadQCOM
+OpExecutionMode %main OriginUpperLeft
+)" + kVoidFunction;
+
+  spv_target_env env = SPV_ENV_VULKAN_1_4;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions(env));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("requires one of these capabilities: TileShadingQCOM"));
+}
+
+TEST_F(ValidateMode, NonCoherentTileAttachmentReadQCOMBad2) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability TileShadingQCOM
+OpExtension "SPV_QCOM_tile_shading"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
+OpExecutionMode %main LocalSize 16 16 1
+OpExecutionMode %main NonCoherentTileAttachmentReadQCOM
+OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
+%uint = OpTypeInt 32 0
+%v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
+)" + kVoidFunction;
+
+  spv_target_env env = SPV_ENV_VULKAN_1_4;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("The NonCoherentTileAttachmentQCOM execution mode must "
+                        "not be used in any stage other than fragment"));
+}
+
+TEST_F(ValidateMode, TileShadingRateQCOMBad1) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
+OpExecutionMode %main TileShadingRateQCOM 2 2 3
+OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
+%uint = OpTypeInt 32 0
+%v3uint = OpTypeVector %uint 3
+%_ptr_Input_v3uint = OpTypePointer Input %v3uint
+%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
+)" + kVoidFunction;
+
+  spv_target_env env = SPV_ENV_VULKAN_1_4;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions(env));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("requires one of these capabilities: TileShadingQCOM"));
+}
+
+TEST_F(ValidateMode, TileShadingRateQCOMBad2) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability TileShadingQCOM
+OpExtension "SPV_QCOM_tile_shading"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main TileShadingRateQCOM 2 2 3
+OpExecutionMode %main OriginUpperLeft
+)" + kVoidFunction;
+
+  spv_target_env env = SPV_ENV_VULKAN_1_4;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("The TileShadingRateQCOM execution mode must not be "
+                        "used in any stage other than compute"));
+}
+
+TEST_F(ValidateMode, TileShadingRateQCOMBad3) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability TileShadingQCOM
+OpExtension "SPV_QCOM_tile_shading"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main TileShadingRateQCOM 3 2 3
+)" + kVoidFunction;
+
+  spv_target_env env = SPV_ENV_VULKAN_1_4;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("The TileShadingRateQCOM execution mode's x and y "
+                        "values must be powers of 2"));
+}
+
+TEST_F(ValidateMode, TileShadingRateQCOMBad4) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability TileShadingQCOM
+OpExtension "SPV_QCOM_tile_shading"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main TileShadingRateQCOM 2 3 3
+)" + kVoidFunction;
+
+  spv_target_env env = SPV_ENV_VULKAN_1_4;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("The TileShadingRateQCOM execution mode's x and y "
+                        "values must be powers of 2"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_non_uniform_test.cpp b/test/val/val_non_uniform_test.cpp
index 530676d..b9ea5da 100644
--- a/test/val/val_non_uniform_test.cpp
+++ b/test/val/val_non_uniform_test.cpp
@@ -958,7 +958,7 @@
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("Before SPIR-V 1.5, Id must be a constant instruction"));
+      HasSubstr("In SPIR-V 1.4 or earlier, Id must be a constant instruction"));
 }
 
 TEST_F(ValidateGroupNonUniform, BroadcastNonConstantSpv1p5) {
@@ -1028,7 +1028,8 @@
             ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
   EXPECT_THAT(
       getDiagnosticString(),
-      HasSubstr("Before SPIR-V 1.5, Index must be a constant instruction"));
+      HasSubstr(
+          "In SPIR-V 1.4 or earlier, Index must be a constant instruction"));
 }
 
 TEST_F(ValidateGroupNonUniform, QuadBroadcastNonConstantSpv1p5) {
diff --git a/test/val/val_opencl_test.cpp b/test/val/val_opencl_test.cpp
index 9dab931..2f7526e 100644
--- a/test/val/val_opencl_test.cpp
+++ b/test/val/val_opencl_test.cpp
@@ -43,23 +43,6 @@
                         "OpenCL\n"));
 }
 
-TEST_F(ValidateOpenCL, NonOpenCLMemoryModelBad) {
-  std::string spirv = R"(
-     OpCapability Kernel
-     OpCapability Addresses
-     OpCapability VulkanMemoryModelKHR
-     OpExtension "SPV_KHR_vulkan_memory_model"
-     OpMemoryModel Physical32 VulkanKHR
-)";
-
-  CompileSuccessfully(spirv);
-
-  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Memory model must be OpenCL in the OpenCL environment."));
-}
-
 TEST_F(ValidateOpenCL, NonVoidSampledTypeImageBad) {
   std::string spirv = R"(
     OpCapability Addresses
diff --git a/test/val/val_pipe_test.cpp b/test/val/val_pipe_test.cpp
new file mode 100644
index 0000000..c05bc27
--- /dev/null
+++ b/test/val/val_pipe_test.cpp
@@ -0,0 +1,493 @@
+// Copyright 2026 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 <string>
+
+#include "gmock/gmock.h"
+#include "spirv-tools/libspirv.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::HasSubstr;
+
+using ValidatePipe = spvtest::ValidateBase<bool>;
+
+const spv_target_env pipe_version = SPV_ENV_UNIVERSAL_1_1;
+
+std::string GenerateShaderCode(const std::string& body) {
+  std::ostringstream ss;
+  ss << R"(
+OpCapability Kernel
+OpCapability Addresses
+OpCapability Linkage
+OpCapability Pipes
+OpCapability GenericPointer
+OpCapability PipeStorage
+OpCapability Int64
+OpMemoryModel Physical64 OpenCL
+OpEntryPoint Kernel %main "main"
+
+%bool = OpTypeBool
+%uint = OpTypeInt 32 0
+%uint64 = OpTypeInt 64 0
+
+%uint_1 = OpConstant %uint 1
+%uint_2 = OpConstant %uint 2
+%uint_4 = OpConstant %uint 4
+%uint64_4 = OpConstant %uint64 4
+%uint_null = OpConstantNull %uint
+
+%_ptr_Generic_uint = OpTypePointer Generic %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+
+%pipe_storage =  OpTypePipeStorage
+%reserved_id = OpTypeReserveId
+
+%void = OpTypeVoid
+%read_pipe_type = OpTypePipe ReadOnly
+%write_pipe_type = OpTypePipe WriteOnly
+%read_write_pipe_type = OpTypePipe ReadWrite
+%fn = OpTypeFunction %void %read_pipe_type %write_pipe_type %read_write_pipe_type
+
+%main = OpFunction %void None %fn
+%read_pipe = OpFunctionParameter %read_pipe_type
+%write_pipe = OpFunctionParameter %write_pipe_type
+%read_write_pipe = OpFunctionParameter %read_write_pipe_type
+%label = OpLabel
+
+%func_var = OpVariable %_ptr_Function_uint Function
+OpStore %func_var %uint_null Aligned 4
+%generic_ptr = OpPtrCastToGeneric %_ptr_Generic_uint %func_var
+
+%const_pipe_storage = OpConstantPipeStorage %pipe_storage 4 4 32
+)";
+
+  ss << body;
+
+  ss << R"(
+OpReturn
+OpFunctionEnd)";
+  return ss.str();
+}
+
+TEST_F(ValidatePipe, PipeReadWriteGood) {
+  const std::string ss = R"(
+    %x = OpReadPipe %uint %read_pipe %generic_ptr %uint_4 %uint_4
+    %y = OpWritePipe %uint %write_pipe %generic_ptr %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(pipe_version));
+}
+
+TEST_F(ValidatePipe, ReadPipeResultType) {
+  const std::string ss = R"(
+    %x = OpReadPipe %uint64 %read_pipe %generic_ptr %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result Type must be a 32-bit int scalar"));
+}
+
+TEST_F(ValidatePipe, ReadPipePipeType) {
+  const std::string ss = R"(
+    %x = OpReadPipe %uint %func_var %generic_ptr %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Pipe must be a type of OpTypePipe"));
+}
+
+TEST_F(ValidatePipe, ReadPipeAccessQualifier) {
+  const std::string ss = R"(
+    %x = OpReadPipe %uint %write_pipe %generic_ptr %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Pipe must have a OpTypePipe with ReadOnly access qualifier"));
+}
+
+TEST_F(ValidatePipe, WritePipeAccessQualifier) {
+  const std::string ss = R"(
+    %x = OpWritePipe %uint %read_pipe %generic_ptr %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Pipe must have a OpTypePipe with WriteOnly access qualifier"));
+}
+
+TEST_F(ValidatePipe, ReadPipePacketSizeInt64) {
+  const std::string ss = R"(
+    %x = OpReadPipe %uint %read_pipe %generic_ptr %uint64_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Packet Size must be a 32-bit scalar integer"));
+}
+
+TEST_F(ValidatePipe, ReadPipePacketAlignmentInt64) {
+  const std::string ss = R"(
+    %x = OpReadPipe %uint %read_pipe %generic_ptr %uint_4 %uint64_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Packet Alignment must be a 32-bit scalar integer"));
+}
+
+TEST_F(ValidatePipe, ReadPipePointerNotPoint) {
+  const std::string ss = R"(
+    %x = OpReadPipe %uint %read_pipe %read_pipe %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Pointer must be a type of OpTypePointer"));
+}
+
+TEST_F(ValidatePipe, ReadPipePointerNotGeneric) {
+  const std::string ss = R"(
+    %x = OpReadPipe %uint %read_pipe %func_var %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Pointer must be a OpTypePointer with a Generic storage class"));
+}
+
+TEST_F(ValidatePipe, PipeQueryGood) {
+  const std::string ss = R"(
+    %x = OpGetNumPipePackets %uint %read_pipe %uint_4 %uint_4
+    %y = OpGetMaxPipePackets %uint %write_pipe %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(pipe_version));
+}
+
+TEST_F(ValidatePipe, GetNumPipePacketsResult) {
+  const std::string ss = R"(
+    %x = OpGetNumPipePackets %uint64 %read_pipe %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result Type must be a 32-bit int scalar"));
+}
+
+TEST_F(ValidatePipe, GetNumPipePacketsReadWrite) {
+  const std::string ss = R"(
+    %x = OpGetNumPipePackets %uint %read_write_pipe %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Pipe must have a OpTypePipe with ReadOnly or "
+                        "WriteOnly access qualifier"));
+}
+
+TEST_F(ValidatePipe, GetNumPipePacketsPacketSize) {
+  const std::string ss = R"(
+    %x = OpGetNumPipePackets %uint %read_pipe %uint64_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Packet Size must be a 32-bit scalar integer"));
+}
+
+TEST_F(ValidatePipe, GetNumPipePacketsPacketAlignment) {
+  const std::string ss = R"(
+    %x = OpGetNumPipePackets %uint %read_pipe %uint_4 %uint64_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Packet Alignment must be a 32-bit scalar integer"));
+}
+
+TEST_F(ValidatePipe, PipeReservedReadWriteGood) {
+  const std::string ss = R"(
+    %r = OpReserveReadPipePackets %reserved_id %read_pipe %uint_1 %uint_4 %uint_4
+    %w = OpReserveWritePipePackets %reserved_id %write_pipe %uint_1 %uint_4 %uint_4
+
+    %x = OpReservedReadPipe %uint %read_pipe %r %uint_null %generic_ptr %uint_4 %uint_4
+    %y = OpReservedWritePipe %uint %write_pipe %w %uint_null %generic_ptr %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(pipe_version));
+}
+
+TEST_F(ValidatePipe, ReserveReadPipePacketsResult) {
+  const std::string ss = R"(
+    %r = OpReserveReadPipePackets %uint %read_pipe %uint_1 %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result Type must be OpTypeReserveId"));
+}
+
+TEST_F(ValidatePipe, ReserveReadPipePacketsWritePipe) {
+  const std::string ss = R"(
+    %r = OpReserveReadPipePackets %reserved_id %write_pipe %uint_1 %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Pipe must have a OpTypePipe with ReadOnly access qualifier"));
+}
+
+TEST_F(ValidatePipe, ReserveReadPipePacketsNumPacks) {
+  const std::string ss = R"(
+    %r = OpReserveReadPipePackets %reserved_id %read_pipe %uint64_4 %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Num Packets must be a 32-bit scalar integer"));
+}
+
+TEST_F(ValidatePipe, ReservedReadPipeResult) {
+  const std::string ss = R"(
+    %r = OpReserveReadPipePackets %reserved_id %read_pipe %uint_1 %uint_4 %uint_4
+    %x = OpReservedReadPipe %reserved_id %read_pipe %r %uint_null %generic_ptr %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result Type must be a 32-bit int scalar"));
+}
+
+TEST_F(ValidatePipe, ReservedReadPipeReserveType) {
+  const std::string ss = R"(
+    %x = OpReservedReadPipe %uint %read_pipe %func_var %uint_null %generic_ptr %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Reserve Id type must be OpTypeReserveId"));
+}
+
+TEST_F(ValidatePipe, ReservedReadPipeIndexType) {
+  const std::string ss = R"(
+    %r = OpReserveReadPipePackets %reserved_id %read_pipe %uint_1 %uint_4 %uint_4
+    %x = OpReservedReadPipe %uint %read_pipe %r %uint64_4 %generic_ptr %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Index must be a 32-bit scalar integer"));
+}
+
+TEST_F(ValidatePipe, ReservedReadPipePacketAlignment) {
+  const std::string ss = R"(
+    %r = OpReserveReadPipePackets %reserved_id %read_pipe %uint_1 %uint_4 %uint_4
+    %x = OpReservedReadPipe %uint %read_pipe %r %uint_null %generic_ptr %uint_4 %uint64_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Packet Alignment must be a 32-bit scalar integer"));
+}
+
+TEST_F(ValidatePipe, CommitPipeGood) {
+  const std::string ss = R"(
+    %r = OpReserveReadPipePackets %reserved_id %read_pipe %uint_1 %uint_4 %uint_4
+    %w = OpReserveWritePipePackets %reserved_id %write_pipe %uint_1 %uint_4 %uint_4
+
+    OpCommitReadPipe %read_pipe %r %uint_4 %uint_4
+    OpCommitWritePipe %write_pipe %w %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(pipe_version));
+}
+
+TEST_F(ValidatePipe, CommitReadReservedId) {
+  const std::string ss = R"(
+        OpCommitReadPipe %read_pipe %func_var %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Reserve Id type must be OpTypeReserveId"));
+}
+
+TEST_F(ValidatePipe, CommitReadPacketSize) {
+  const std::string ss = R"(
+    %r = OpReserveReadPipePackets %reserved_id %read_pipe %uint_1 %uint_4 %uint_4
+         OpCommitReadPipe %read_pipe %r %uint64_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Packet Size must be a 32-bit scalar integer"));
+}
+
+TEST_F(ValidatePipe, IsValidReserveIdGood) {
+  const std::string ss = R"(
+    %r = OpReserveReadPipePackets %reserved_id %read_pipe %uint_1 %uint_4 %uint_4
+    %x = OpIsValidReserveId %bool %r
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(pipe_version));
+}
+
+TEST_F(ValidatePipe, IsValidReserveIdReservedId) {
+  const std::string ss = R"(
+    %x = OpIsValidReserveId %bool %func_var
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Reserve Id type must be OpTypeReserveId"));
+}
+
+TEST_F(ValidatePipe, IsValidReserveIdResult) {
+  const std::string ss = R"(
+    %r = OpReserveReadPipePackets %reserved_id %read_pipe %uint_1 %uint_4 %uint_4
+    %x = OpIsValidReserveId %uint %r
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result Type must be a bool scalar"));
+}
+
+TEST_F(ValidatePipe, CreatePipeFromPipeStorageGood) {
+  const std::string ss = R"(
+    %x = OpCreatePipeFromPipeStorage %read_pipe_type %const_pipe_storage
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(pipe_version));
+}
+
+TEST_F(ValidatePipe, CreatePipeFromPipeStorageType) {
+  const std::string ss = R"(
+    %x = OpCreatePipeFromPipeStorage %uint %const_pipe_storage
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), SPV_ENV_UNIVERSAL_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result Type must be OpTypePipe"));
+}
+
+TEST_F(ValidatePipe, ConstantPipeStorageType) {
+  const std::string ss = R"(
+    %bad = OpConstantPipeStorage %uint 4 4 32
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), SPV_ENV_UNIVERSAL_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result Type must be OpTypePipeStorage"));
+}
+
+TEST_F(ValidatePipe, ConstantPipeStorageBlock) {
+  const std::string ss = R"(
+    OpCapability Kernel
+    OpCapability Addresses
+    OpCapability Linkage
+    OpCapability Pipes
+    OpCapability PipeStorage
+    OpMemoryModel Physical64 OpenCL
+    OpEntryPoint Kernel %main "main"
+    %pipe_storage =  OpTypePipeStorage
+    %const_pipe_storage = OpConstantPipeStorage %pipe_storage 4 4 32
+  )";
+  CompileSuccessfully(ss, pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("ConstantPipeStorage must appear in a block"));
+}
+
+TEST_F(ValidatePipe, GroupGood) {
+  const std::string ss = R"(
+    %r = OpGroupReserveReadPipePackets %reserved_id %uint_2 %read_pipe %uint_1 %uint_4 %uint_4
+    %w = OpGroupReserveWritePipePackets %reserved_id %uint_2 %write_pipe %uint_1 %uint_4 %uint_4
+
+    OpGroupCommitReadPipe %uint_2 %read_pipe %r %uint_4 %uint_4
+    OpGroupCommitWritePipe %uint_2 %write_pipe %w %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(pipe_version));
+}
+
+TEST_F(ValidatePipe, GroupReserveReadPipePacketsResult) {
+  const std::string ss = R"(
+    %r = OpGroupReserveReadPipePackets %uint %uint_2 %read_pipe %uint_1 %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Result Type must be OpTypeReserveId"));
+}
+
+TEST_F(ValidatePipe, GroupReserveReadPipePacketsWrite) {
+  const std::string ss = R"(
+    %r = OpGroupReserveReadPipePackets %reserved_id %uint_2 %read_write_pipe %uint_1 %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Pipe must have a OpTypePipe with ReadOnly access qualifier"));
+}
+
+TEST_F(ValidatePipe, GroupReserveReadPipePacketsPacketSize) {
+  const std::string ss = R"(
+    %r = OpGroupReserveReadPipePackets %reserved_id %uint_2 %read_pipe %uint_1 %uint64_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Packet Size must be a 32-bit scalar integer"));
+}
+
+TEST_F(ValidatePipe, GroupCommitReadPipeWrite) {
+  const std::string ss = R"(
+    %r = OpGroupReserveReadPipePackets %reserved_id %uint_2 %read_pipe %uint_1 %uint_4 %uint_4
+    OpGroupCommitReadPipe %uint_2 %write_pipe %r %uint_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Pipe must have a OpTypePipe with ReadOnly access qualifier"));
+}
+
+TEST_F(ValidatePipe, GroupCommitReadPipePacketSize) {
+  const std::string ss = R"(
+    %r = OpGroupReserveReadPipePackets %reserved_id %uint_2 %read_pipe %uint_1 %uint_4 %uint_4
+    OpGroupCommitReadPipe %uint_2 %read_pipe %r %uint64_4 %uint_4
+  )";
+  CompileSuccessfully(GenerateShaderCode(ss), pipe_version);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(pipe_version));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Packet Size must be a 32-bit scalar integer"));
+}
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/val_ray_query_test.cpp b/test/val/val_ray_query_test.cpp
index e0eb067..7e86fe2 100644
--- a/test/val/val_ray_query_test.cpp
+++ b/test/val/val_ray_query_test.cpp
@@ -30,20 +30,23 @@
 
 using ValidateRayQuery = spvtest::ValidateBase<bool>;
 
-std::string GenerateShaderCode(
-    const std::string& body,
-    const std::string& capabilities_and_extensions = "",
-    const std::string& declarations = "") {
+std::string GenerateShaderCode(const std::string& body,
+                               const std::string& capabilities = "",
+                               const std::string& extensions = "",
+                               const std::string& declarations = "") {
   std::ostringstream ss;
   ss << R"(
 OpCapability Shader
 OpCapability Int64
 OpCapability Float64
 OpCapability RayQueryKHR
+         )";
+  ss << capabilities;
+  ss << R"(
 OpExtension "SPV_KHR_ray_query"
 )";
 
-  ss << capabilities_and_extensions;
+  ss << extensions;
 
   ss << R"(
 OpMemoryModel Logical GLSL450
@@ -83,12 +86,15 @@
 %u32_0 = OpConstant %u32 0
 %u64_0 = OpConstant %u64 0
 
+%u32_2 = OpConstant %u32 2
+%arr2v3 = OpTypeArray %f32vec3 %u32_2
+%arr2f3 = OpTypeArray %f32 %u32_2
+
 %u32vec3_0 = OpConstantComposite %u32vec3 %u32_0 %u32_0 %u32_0
 %f32vec3_0 = OpConstantComposite %f32vec3 %f32_0 %f32_0 %f32_0
 %f32vec4_0 = OpConstantComposite %f32vec4 %f32_0 %f32_0 %f32_0 %f32_0
 
-%ptr_rq = OpTypePointer Private %type_rq
-%ray_query = OpVariable %ptr_rq Private
+%ptr_rq = OpTypePointer Function %type_rq
 
 %ptr_as = OpTypePointer UniformConstant %type_as
 %top_level_as = OpVariable %ptr_as UniformConstant
@@ -103,6 +109,7 @@
   ss << R"(
 %main = OpFunction %void None %func
 %main_entry = OpLabel
+%ray_query = OpVariable %ptr_rq Function
 )";
 
   ss << body;
@@ -398,7 +405,7 @@
 OpRayQueryInitializeKHR %rq_param %as_2 %u32_0 %u32_0 %f32vec3_0 %f32_0 %f32vec3_0 %f32_0
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body, "", declaration).c_str());
+  CompileSuccessfully(GenerateShaderCode(body, "", "", declaration).c_str());
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
@@ -626,6 +633,191 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateRayQuery, ClusterASNV) {
+  const std::string cap = R"(
+               OpCapability RayTracingClusterAccelerationStructureNV
+                            )";
+
+  const std::string ext = R"(
+               OpExtension "SPV_NV_cluster_acceleration_structure"
+                           )";
+
+  const std::string body = R"(
+               %clusterid = OpRayQueryGetClusterIdNV %s32 %ray_query %s32_0
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body, cap, ext).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+using RayQueryLSSNVCommon = spvtest::ValidateBase<std::string>;
+
+std::string RayQueryLSSNVResultType(std::string opcode, bool valid) {
+  if (opcode.compare("OpRayQueryGetIntersectionLSSPositionsNV") == 0)
+    return valid ? "%arr2v3" : "%f64";
+
+  if (opcode.compare("OpRayQueryGetIntersectionLSSRadiiNV") == 0)
+    return valid ? "%arr2f3" : "%f64";
+
+  if (opcode.compare("OpRayQueryGetIntersectionSphereRadiusNV") == 0 ||
+      opcode.compare("OpRayQueryGetIntersectionLSSHitValueNV") == 0) {
+    return valid ? "%f32" : "%f64";
+  }
+
+  if (opcode.compare("OpRayQueryGetIntersectionSpherePositionNV") == 0) {
+    return valid ? "%f32vec3" : "%f64";
+  }
+
+  if (opcode.compare("OpRayQueryIsSphereHitNV") == 0 ||
+      opcode.compare("OpRayQueryIsLSSHitNV") == 0) {
+    return valid ? "%bool" : "%f64";
+  }
+
+  return "";
+}
+
+TEST_P(RayQueryLSSNVCommon, Success) {
+  const std::string cap = R"(
+               OpCapability RayTracingSpheresGeometryNV
+               OpCapability RayTracingLinearSweptSpheresGeometryNV
+                            )";
+  const std::string ext = R"(
+               OpExtension "SPV_NV_linear_swept_spheres"
+                           )";
+  std::string opcode = GetParam();
+  std::ostringstream ss;
+  ss << "%result = ";
+  ss << " " << opcode << " ";
+  ss << RayQueryLSSNVResultType(opcode, true);
+  ss << " %ray_query ";
+  ss << " %s32_0 ";
+  CompileSuccessfully(GenerateShaderCode(ss.str(), cap, ext).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidateRayQueryLSSNVCommon, RayQueryLSSNVCommon,
+                         Values("OpRayQueryGetIntersectionSpherePositionNV",
+                                "OpRayQueryGetIntersectionLSSPositionsNV",
+                                "OpRayQueryGetIntersectionSphereRadiusNV",
+                                "OpRayQueryGetIntersectionLSSRadiiNV",
+                                "OpRayQueryGetIntersectionLSSHitValueNV",
+                                "OpRayQueryIsSphereHitNV",
+                                "OpRayQueryIsLSSHitNV"));
+
+TEST_F(ValidateRayQuery, RayQueryPositionFetchCapability) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability RayQueryKHR
+               OpCapability RayTracingPositionFetchKHR
+               OpExtension "SPV_KHR_ray_query"
+               OpExtension "SPV_KHR_ray_tracing_position_fetch"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %rayQuery
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+          %6 = OpTypeRayQueryKHR
+%_ptr_Private_6 = OpTypePointer Private %6
+   %rayQuery = OpVariable %_ptr_Private_6 Private
+       %uint = OpTypeInt 32 0
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+     %uint_3 = OpConstant %uint 3
+%_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3
+%_ptr_Function__arr_v3float_uint_3 = OpTypePointer Function %_arr_v3float_uint_3
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %28 = OpRayQueryGetIntersectionTriangleVertexPositionsKHR %_arr_v3float_uint_3 %rayQuery %int_1
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+            ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("RayQueryGetIntersectionTriangleVertexPositionsKHR requires "
+                "one of these capabilities: RayQueryPositionFetchKHR"));
+}
+
+TEST_F(ValidateRayQuery, RayQueryGetIntersectionTriangleVertexPositionsType) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability RayQueryKHR
+               OpCapability RayQueryPositionFetchKHR
+               OpExtension "SPV_KHR_ray_query"
+               OpExtension "SPV_KHR_ray_tracing_position_fetch"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %rayQuery
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+          %6 = OpTypeRayQueryKHR
+%_ptr_Private_6 = OpTypePointer Private %6
+   %rayQuery = OpVariable %_ptr_Private_6 Private
+       %uint = OpTypeInt 32 0
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+     %uint_4 = OpConstant %uint 4
+%_arr_v3float_uint_4 = OpTypeArray %v3float %uint_4
+%_ptr_Function__arr_v3float_uint_4 = OpTypePointer Function %_arr_v3float_uint_4
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %28 = OpRayQueryGetIntersectionTriangleVertexPositionsKHR %_arr_v3float_uint_4 %rayQuery %int_1
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected 3 element array of 32-bit 3 component float "
+                        "point vector as Result Type"));
+}
+
+TEST_F(ValidateRayQuery,
+       RayQueryGetIntersectionTriangleVertexPositionsType32Bit) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability RayQueryKHR
+               OpCapability RayQueryPositionFetchKHR
+               OpCapability Float64
+               OpExtension "SPV_KHR_ray_query"
+               OpExtension "SPV_KHR_ray_tracing_position_fetch"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %rayQuery
+               OpExecutionMode %main LocalSize 1 1 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+          %6 = OpTypeRayQueryKHR
+%_ptr_Private_6 = OpTypePointer Private %6
+   %rayQuery = OpVariable %_ptr_Private_6 Private
+       %uint = OpTypeInt 32 0
+    %float64 = OpTypeFloat 64
+  %v3float64 = OpTypeVector %float64 3
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+     %uint_3 = OpConstant %uint 3
+%_arr_v3float_uint_3 = OpTypeArray %v3float64 %uint_3
+%_ptr_Function__arr_v3float_uint_3 = OpTypePointer Function %_arr_v3float_uint_3
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %28 = OpRayQueryGetIntersectionTriangleVertexPositionsKHR %_arr_v3float_uint_3 %rayQuery %int_1
+               OpReturn
+               OpFunctionEnd
+)";
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected 3 element array of 32-bit 3 component float "
+                        "point vector as Result Type"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_ray_tracing_reorder_test.cpp b/test/val/val_ray_tracing_reorder_test.cpp
index 6038c38..4c6f4f0 100644
--- a/test/val/val_ray_tracing_reorder_test.cpp
+++ b/test/val/val_ray_tracing_reorder_test.cpp
@@ -12,7 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Tests instructions from SPV_NV_shader_invocation_reorder.
+// Tests instructions from SPV_NV_shader_invocation_reorder and
+// SPV_EXT_shader_invocation_reorder.
 
 #include <sstream>
 #include <string>
@@ -30,13 +31,21 @@
 using ValidateRayTracingReorderNV = spvtest::ValidateBase<bool>;
 
 std::string GenerateReorderThreadCode(const std::string& body = "",
-                                      const std::string& declarations = "") {
+                                      const std::string& declarations = "",
+                                      const std::string& extensions = "",
+                                      const std::string& capabilities = "") {
   std::ostringstream ss;
   ss << R"(
             OpCapability RayTracingKHR
             OpCapability ShaderInvocationReorderNV
+         )";
+  ss << capabilities;
+  ss << R"(
             OpExtension "SPV_KHR_ray_tracing"
             OpExtension "SPV_NV_shader_invocation_reorder"
+          )";
+  ss << extensions;
+  ss << R"(
        %1 = OpExtInstImport "GLSL.std.450"
             OpMemoryModel Logical GLSL450
             OpEntryPoint RayGenerationNV %main "main" %hObj
@@ -593,6 +602,968 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
 }
 
+TEST_F(ValidateRayTracingReorderNV, ClusterASNV) {
+  const std::string cap = R"(
+               OpCapability RayTracingClusterAccelerationStructureNV
+                            )";
+
+  const std::string ext = R"(
+               OpExtension "SPV_NV_cluster_acceleration_structure"
+                           )";
+
+  const std::string declarations = R"(
+        %int = OpTypeInt 32 1
+        %_ptr_Function_int = OpTypePointer Function %int
+  )";
+
+  const std::string body = R"(
+      %id = OpVariable %_ptr_Function_int Function
+      %12 = OpHitObjectGetClusterIdNV %int %hObj
+      OpStore %id %12
+  )";
+
+  CompileSuccessfully(
+      GenerateReorderThreadCode(body, declarations, ext, cap).c_str(),
+      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, LSSGetSpherePositionNV) {
+  const std::string cap = R"(
+               OpCapability RayTracingSpheresGeometryNV
+                            )";
+
+  const std::string ext = R"(
+               OpExtension "SPV_NV_linear_swept_spheres"
+                           )";
+
+  const std::string declarations = R"(
+        %float = OpTypeFloat 32
+        %v3float = OpTypeVector %float 3
+        %_ptr_Function_v3float = OpTypePointer Function %v3float
+  )";
+
+  const std::string body = R"(
+      %pos = OpVariable %_ptr_Function_v3float Function
+      %result = OpHitObjectGetSpherePositionNV %v3float %hObj
+      OpStore %pos %result
+  )";
+
+  CompileSuccessfully(
+      GenerateReorderThreadCode(body, declarations, ext, cap).c_str(),
+      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, LSSGetLSSPositionsNV) {
+  const std::string cap = R"(
+               OpCapability RayTracingSpheresGeometryNV
+               OpCapability RayTracingLinearSweptSpheresGeometryNV
+                            )";
+
+  const std::string ext = R"(
+               OpExtension "SPV_NV_linear_swept_spheres"
+                           )";
+
+  const std::string declarations = R"(
+        %float = OpTypeFloat 32
+        %uint = OpTypeInt 32 0
+        %v3float = OpTypeVector %float 3
+        %uint_2 = OpConstant %uint 2
+        %arr = OpTypeArray %v3float %uint_2
+        %_ptr_Function_v3float = OpTypePointer Function %arr
+  )";
+
+  const std::string body = R"(
+      %lsspos = OpVariable %_ptr_Function_v3float Function
+      %result = OpHitObjectGetLSSPositionsNV %arr %hObj
+      OpStore %lsspos %result
+  )";
+
+  CompileSuccessfully(
+      GenerateReorderThreadCode(body, declarations, ext, cap).c_str(),
+      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, LSSGetSphereRadiusNV) {
+  const std::string cap = R"(
+               OpCapability RayTracingSpheresGeometryNV
+                            )";
+
+  const std::string ext = R"(
+               OpExtension "SPV_NV_linear_swept_spheres"
+                           )";
+
+  const std::string declarations = R"(
+        %float = OpTypeFloat 32
+        %_ptr_Function_float = OpTypePointer Function %float
+  )";
+
+  const std::string body = R"(
+      %rad = OpVariable %_ptr_Function_float Function
+      %result = OpHitObjectGetSphereRadiusNV %float %hObj
+      OpStore %rad %result
+  )";
+
+  CompileSuccessfully(
+      GenerateReorderThreadCode(body, declarations, ext, cap).c_str(),
+      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, LSSGetLSSRadiiNV) {
+  const std::string cap = R"(
+               OpCapability RayTracingLinearSweptSpheresGeometryNV
+                            )";
+
+  const std::string ext = R"(
+               OpExtension "SPV_NV_linear_swept_spheres"
+                           )";
+
+  const std::string declarations = R"(
+        %float = OpTypeFloat 32
+        %uint = OpTypeInt 32 0
+        %uint_2 = OpConstant %uint 2
+        %arr = OpTypeArray %float %uint_2
+        %_ptr_Function_float = OpTypePointer Function %arr
+  )";
+
+  const std::string body = R"(
+      %rad = OpVariable %_ptr_Function_float Function
+      %result = OpHitObjectGetLSSRadiiNV %arr %hObj
+      OpStore %rad %result
+  )";
+
+  CompileSuccessfully(
+      GenerateReorderThreadCode(body, declarations, ext, cap).c_str(),
+      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, LSSIsSphereHitNV) {
+  const std::string cap = R"(
+               OpCapability RayTracingSpheresGeometryNV
+                            )";
+
+  const std::string ext = R"(
+               OpExtension "SPV_NV_linear_swept_spheres"
+                           )";
+
+  const std::string declarations = R"(
+        %bool = OpTypeBool
+        %_ptr_Function_bool = OpTypePointer Function %bool
+  )";
+
+  const std::string body = R"(
+      %ishit = OpVariable %_ptr_Function_bool Function
+      %result = OpHitObjectIsSphereHitNV %bool %hObj
+      OpStore %ishit %result
+  )";
+
+  CompileSuccessfully(
+      GenerateReorderThreadCode(body, declarations, ext, cap).c_str(),
+      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderNV, LSSIsLSSHitNV) {
+  const std::string cap = R"(
+               OpCapability RayTracingLinearSweptSpheresGeometryNV
+                            )";
+
+  const std::string ext = R"(
+               OpExtension "SPV_NV_linear_swept_spheres"
+                           )";
+
+  const std::string declarations = R"(
+        %bool = OpTypeBool
+        %_ptr_Function_bool = OpTypePointer Function %bool
+  )";
+
+  const std::string body = R"(
+      %ishit = OpVariable %_ptr_Function_bool Function
+      %result = OpHitObjectIsLSSHitNV %bool %hObj
+      OpStore %ishit %result
+  )";
+
+  CompileSuccessfully(
+      GenerateReorderThreadCode(body, declarations, ext, cap).c_str(),
+      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+// EXT Extension Tests
+using ValidateRayTracingReorderEXT = spvtest::ValidateBase<bool>;
+
+std::string GenerateReorderThreadCodeEXT(const std::string& body = "",
+                                         const std::string& declarations = "",
+                                         const std::string& extensions = "",
+                                         const std::string& capabilities = "") {
+  std::ostringstream ss;
+  ss << R"(
+            OpCapability RayTracingKHR
+            OpCapability ShaderInvocationReorderEXT
+         )";
+  ss << capabilities;
+  ss << R"(
+            OpExtension "SPV_KHR_ray_tracing"
+            OpExtension "SPV_EXT_shader_invocation_reorder"
+          )";
+  ss << extensions;
+  ss << R"(
+       %1 = OpExtInstImport "GLSL.std.450"
+            OpMemoryModel Logical GLSL450
+            OpEntryPoint RayGenerationNV %main "main" %hObj
+            OpSourceExtension "GL_EXT_ray_tracing"
+            OpSourceExtension "GL_EXT_shader_invocation_reorder"
+            OpName %main "main"
+    %void = OpTypeVoid
+       %3 = OpTypeFunction %void
+       %6 = OpTypeHitObjectEXT
+%_ptr_Private_6 = OpTypePointer Private %6
+    %hObj = OpVariable %_ptr_Private_6 Private
+    )";
+  ss << declarations;
+
+  ss << R"(
+   %main  = OpFunction %void None %3
+     %5   = OpLabel 
+    )";
+
+  ss << body;
+
+  ss << R"(
+            OpReturn
+            OpFunctionEnd
+    )";
+  return ss.str();
+}
+
+std::string GenerateReorderShaderCodeEXT(const std::string& body = "",
+                                         const std::string& declarations = "",
+                                         const std::string& extensions = "",
+                                         const std::string& capabilties = "") {
+  std::ostringstream ss;
+  ss << R"(
+               OpCapability RayTracingKHR
+               OpCapability ShaderInvocationReorderEXT
+         )";
+  ss << capabilties;
+  ss << R"(    OpExtension "SPV_KHR_ray_tracing"
+               OpExtension "SPV_EXT_shader_invocation_reorder"
+          )";
+  ss << extensions;
+  ss << R"(
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint RayGenerationKHR %main "main" %attr %_ %hObj %payload %__0 %as %__1
+               OpSource GLSL 460
+               OpSourceExtension "GL_EXT_ray_tracing"
+               OpSourceExtension "GL_EXT_shader_invocation_reorder"
+               OpName %main "main"
+               OpName %attr "attr"
+               OpName %hBlock "hBlock"
+               OpMemberName %hBlock 0 "attrval"
+               OpName %_ ""
+               OpName %hObj "hObj"
+               OpName %payload "payload"
+               OpName %pBlock "pBlock"
+               OpMemberName %pBlock 0 "val1"
+               OpMemberName %pBlock 1 "val2"
+               OpName %__0 ""
+               OpName %as "as"
+               OpName %block "block"
+               OpMemberName %block 0 "op"
+               OpName %__1 ""
+               OpDecorate %hBlock Block
+               OpDecorate %pBlock Block
+               OpDecorate %as DescriptorSet 0
+               OpDecorate %as Binding 0
+               OpMemberDecorate %block 0 Offset 0
+               OpDecorate %block Block
+               OpDecorate %__1 DescriptorSet 0
+               OpDecorate %__1 Binding 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+%_ptr_HitObjectAttributeEXT_v2float = OpTypePointer HitObjectAttributeEXT %v2float
+       %attr = OpVariable %_ptr_HitObjectAttributeEXT_v2float HitObjectAttributeEXT
+    %float_1 = OpConstant %float 1
+         %11 = OpConstantComposite %v2float %float_1 %float_1
+     %hBlock = OpTypeStruct %float
+%_ptr_HitObjectAttributeEXT_hBlock = OpTypePointer HitObjectAttributeEXT %hBlock
+          %_ = OpVariable %_ptr_HitObjectAttributeEXT_hBlock HitObjectAttributeEXT
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+    %float_2 = OpConstant %float 2
+%_ptr_HitObjectAttributeEXT_float = OpTypePointer HitObjectAttributeEXT %float
+             %20 = OpTypeHitObjectEXT
+    %_ptr_Private_20 = OpTypePointer Private %20
+           %hObj = OpVariable %_ptr_Private_20 Private
+             %23 = OpTypeAccelerationStructureKHR
+    %_ptr_UniformConstant_23 = OpTypePointer UniformConstant %23
+             %as = OpVariable %_ptr_UniformConstant_23 UniformConstant
+    %v4float = OpTypeVector %float 4
+%_ptr_RayPayloadKHR_v4float = OpTypePointer RayPayloadKHR %v4float
+    %payload = OpVariable %_ptr_RayPayloadKHR_v4float RayPayloadKHR
+     %pBlock = OpTypeStruct %v2float %v2float
+%_ptr_RayPayloadKHR_pBlock = OpTypePointer RayPayloadKHR %pBlock
+        %__0 = OpVariable %_ptr_RayPayloadKHR_pBlock RayPayloadKHR
+      %block = OpTypeStruct %float
+%_ptr_StorageBuffer_block = OpTypePointer StorageBuffer %block
+        %__1 = OpVariable %_ptr_StorageBuffer_block StorageBuffer
+       )";
+
+  ss << declarations;
+
+  ss << R"(
+        %main = OpFunction %void None %3
+          %5 = OpLabel 
+         )";
+
+  ss << body;
+
+  ss << R"(
+               OpReturn
+               OpFunctionEnd)";
+  return ss.str();
+}
+
+TEST_F(ValidateRayTracingReorderEXT, ReorderThreadWithHintEXT) {
+  const std::string declarations = R"(
+             %uint = OpTypeInt 32 0
+           %uint_4 = OpConstant %uint 4
+  )";
+
+  const std::string body = R"(
+    OpReorderThreadWithHintEXT %uint_4 %uint_4
+  )";
+
+  CompileSuccessfully(GenerateReorderThreadCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, ReorderThreadWithHitObjectEXT) {
+  const std::string declarations = R"(
+      %uint = OpTypeInt 32 0
+    %uint_4 = OpConstant %uint 4
+    %uint_2 = OpConstant %uint 2
+  )";
+
+  const std::string body = R"(
+             OpReorderThreadWithHitObjectEXT %hObj
+             OpReorderThreadWithHitObjectEXT %hObj %uint_4 %uint_2
+  )";
+
+  CompileSuccessfully(GenerateReorderThreadCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectTraceRayEXT) {
+  const std::string declarations = R"(
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+    %v3float = OpTypeVector %float 3
+  %float_0_5 = OpConstant %float 0.5
+         %31 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5
+         %32 = OpConstantComposite %v3float %float_1 %float_1 %float_1
+      %int_1 = OpConstant %int 1
+  )";
+
+  const std::string body = R"(
+  OpStore %attr %11
+  %26 = OpLoad %23 %as
+  OpHitObjectTraceRayEXT %hObj %26 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %31 %float_0_5 %32 %float_1 %payload
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectTraceRayMotionEXT) {
+  const std::string declarations = R"(
+        %uint = OpTypeInt 32 0
+      %uint_1 = OpConstant %uint 1
+     %v3float = OpTypeVector %float 3
+   %float_0_5 = OpConstant %float 0.5
+          %31 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5
+          %32 = OpConstantComposite %v3float %float_1 %float_1 %float_1
+    %float_10 = OpConstant %float 10
+       %int_2 = OpConstant %int 2
+  )";
+
+  const std::string body = R"(
+      OpStore %attr %11
+      %26 = OpLoad %23 %as
+      OpHitObjectTraceRayMotionEXT %hObj %26 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %31 %float_0_5 %32 %float_1 %float_10 %__0
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectRecordEmptyEXT) {
+  const std::string body = R"(
+      OpHitObjectRecordEmptyEXT %hObj
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectRecordMissEXT) {
+  const std::string declarations = R"(
+         %uint = OpTypeInt 32 0
+       %uint_1 = OpConstant %uint 1
+      %v3float = OpTypeVector %float 3
+    %float_0_5 = OpConstant %float 0.5
+           %29 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5
+    %float_1_5 = OpConstant %float 1.5
+           %31 = OpConstantComposite %v3float %float_1_5 %float_1_5 %float_1_5
+      %float_5 = OpConstant %float 5
+  )";
+
+  const std::string body = R"(
+      OpHitObjectRecordMissEXT %hObj %uint_1 %uint_1 %29 %float_2 %31 %float_5
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectIsHitEXT) {
+  const std::string declarations = R"(
+        %bool = OpTypeBool
+        %_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
+  )";
+
+  const std::string body = R"(
+      %26 = OpHitObjectIsHitEXT %bool %hObj
+            OpSelectionMerge %28 None
+            OpBranchConditional %26 %27 %28
+      %27 = OpLabel
+      %33 = OpAccessChain %_ptr_StorageBuffer_float %__1 %int_0
+            OpStore %33 %float_1
+            OpBranch %28
+      %28 = OpLabel
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectIsMissEXT) {
+  const std::string declarations = R"(
+        %bool = OpTypeBool
+        %_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
+  )";
+
+  const std::string body = R"(
+      %26 = OpHitObjectIsMissEXT %bool %hObj
+            OpSelectionMerge %28 None
+            OpBranchConditional %26 %27 %28
+      %27 = OpLabel
+      %33 = OpAccessChain %_ptr_StorageBuffer_float %__1 %int_0
+            OpStore %33 %float_1
+            OpBranch %28
+      %28 = OpLabel
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectIsEmptyEXT) {
+  const std::string declarations = R"(
+        %bool = OpTypeBool
+        %_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
+  )";
+
+  const std::string body = R"(
+      %26 = OpHitObjectIsEmptyEXT %bool %hObj
+            OpSelectionMerge %28 None
+            OpBranchConditional %26 %27 %28
+      %27 = OpLabel
+      %33 = OpAccessChain %_ptr_StorageBuffer_float %__1 %int_0
+            OpStore %33 %float_1
+            OpBranch %28
+      %28 = OpLabel
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectGetGeometryIndexEXT) {
+  const std::string declarations = R"(
+        %_ptr_Function_int = OpTypePointer Function %int
+  )";
+
+  const std::string body = R"(
+      %id = OpVariable %_ptr_Function_int Function
+      %12 = OpHitObjectGetGeometryIndexEXT %int %hObj
+      OpStore %id %12
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectGetPrimitiveIndexEXT) {
+  const std::string declarations = R"(
+        %_ptr_Function_int = OpTypePointer Function %int
+  )";
+
+  const std::string body = R"(
+      %id = OpVariable %_ptr_Function_int Function
+      %12 = OpHitObjectGetPrimitiveIndexEXT %int %hObj
+      OpStore %id %12
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectGetInstanceIdEXT) {
+  const std::string declarations = R"(
+        %_ptr_Function_int = OpTypePointer Function %int
+  )";
+
+  const std::string body = R"(
+      %id = OpVariable %_ptr_Function_int Function
+      %12 = OpHitObjectGetInstanceIdEXT %int %hObj
+      OpStore %id %12
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectGetInstanceCustomIndexEXT) {
+  const std::string declarations = R"(
+        %_ptr_Function_int = OpTypePointer Function %int
+  )";
+
+  const std::string body = R"(
+      %id = OpVariable %_ptr_Function_int Function
+      %12 = OpHitObjectGetInstanceCustomIndexEXT %int %hObj
+      OpStore %id %12
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectGetHitKindEXT) {
+  const std::string declarations = R"(
+    %uint = OpTypeInt 32 0
+    %_ptr_Function_uint = OpTypePointer Function %uint
+  )";
+
+  const std::string body = R"(
+    %uid = OpVariable %_ptr_Function_uint Function
+    %12 = OpHitObjectGetHitKindEXT %uint %hObj
+    OpStore %uid %12
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectGetCurrentTimeEXT) {
+  const std::string declarations = R"(
+        %_ptr_Function_float = OpTypePointer Function %float
+  )";
+
+  const std::string body = R"(
+      %time = OpVariable %_ptr_Function_float Function
+      %12 = OpHitObjectGetCurrentTimeEXT %float %hObj
+      OpStore %time %12
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectGetObjectRayOriginEXT) {
+  const std::string declarations = R"(
+        %v3float = OpTypeVector %float 3
+        %_ptr_Function_v3float = OpTypePointer Function %v3float
+  )";
+
+  const std::string body = R"(
+      %oorig = OpVariable %_ptr_Function_v3float Function
+      %13 = OpHitObjectGetObjectRayOriginEXT %v3float %hObj
+      OpStore %oorig %13
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectGetObjectRayDirectionEXT) {
+  const std::string declarations = R"(
+        %v3float = OpTypeVector %float 3
+        %_ptr_Function_v3float = OpTypePointer Function %v3float
+  )";
+
+  const std::string body = R"(
+      %odir = OpVariable %_ptr_Function_v3float Function
+      %13 = OpHitObjectGetObjectRayDirectionEXT %v3float %hObj
+      OpStore %odir %13
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectGetRayTMaxEXT) {
+  const std::string declarations = R"(
+        %_ptr_Function_float = OpTypePointer Function %float
+  )";
+
+  const std::string body = R"(
+      %tmax = OpVariable %_ptr_Function_float Function
+      %12 = OpHitObjectGetRayTMaxEXT %float %hObj
+      OpStore %tmax %12
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectGetRayTMinEXT) {
+  const std::string declarations = R"(
+        %_ptr_Function_float = OpTypePointer Function %float
+  )";
+
+  const std::string body = R"(
+      %tmin = OpVariable %_ptr_Function_float Function
+      %12 = OpHitObjectGetRayTMinEXT %float %hObj
+      OpStore %tmin %12
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectGetRayFlagsEXT) {
+  const std::string declarations = R"(
+        %_ptr_Function_int = OpTypePointer Function %int
+  )";
+
+  const std::string body = R"(
+      %flags = OpVariable %_ptr_Function_int Function
+      %12 = OpHitObjectGetRayFlagsEXT %int %hObj
+      OpStore %flags %12
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectGetWorldRayOriginEXT) {
+  const std::string declarations = R"(
+        %v3float = OpTypeVector %float 3
+        %_ptr_Function_v3float = OpTypePointer Function %v3float
+  )";
+
+  const std::string body = R"(
+      %orig = OpVariable %_ptr_Function_v3float Function
+      %13 = OpHitObjectGetWorldRayOriginEXT %v3float %hObj
+      OpStore %orig %13
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectGetWorldRayDirectionEXT) {
+  const std::string declarations = R"(
+        %v3float = OpTypeVector %float 3
+        %_ptr_Function_v3float = OpTypePointer Function %v3float
+  )";
+
+  const std::string body = R"(
+      %dir = OpVariable %_ptr_Function_v3float Function
+      %13 = OpHitObjectGetWorldRayDirectionEXT %v3float %hObj
+      OpStore %dir %13
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectGetObjectToWorldEXT) {
+  const std::string declarations = R"(
+        %v3float = OpTypeVector %float 3
+        %mat4v3float = OpTypeMatrix %v3float 4
+        %_ptr_Function_mat4v3float = OpTypePointer Function %mat4v3float
+  )";
+
+  const std::string body = R"(
+      %otw = OpVariable %_ptr_Function_mat4v3float Function
+      %14 = OpHitObjectGetObjectToWorldEXT %mat4v3float %hObj
+      OpStore %otw %14
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectGetWorldToObjectEXT) {
+  const std::string declarations = R"(
+        %v3float = OpTypeVector %float 3
+        %mat4v3float = OpTypeMatrix %v3float 4
+        %_ptr_Function_mat4v3float = OpTypePointer Function %mat4v3float
+  )";
+
+  const std::string body = R"(
+      %wto = OpVariable %_ptr_Function_mat4v3float Function
+      %14 = OpHitObjectGetWorldToObjectEXT %mat4v3float %hObj
+      OpStore %wto %14
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectGetShaderRecordBufferHandleEXT) {
+  const std::string declarations = R"(
+           %uint = OpTypeInt 32 0
+         %v2uint = OpTypeVector %uint 2
+    %_ptr_Function_v2uint = OpTypePointer Function %v2uint
+  )";
+
+  const std::string body = R"(
+    %handle = OpVariable %_ptr_Function_v2uint Function
+        %13 = OpHitObjectGetShaderRecordBufferHandleEXT %v2uint %hObj
+              OpStore %handle %13
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT,
+       HitObjectGetShaderBindingTableRecordIndexEXT) {
+  const std::string declarations = R"(
+    %uint = OpTypeInt 32 0
+    %_ptr_Function_uint = OpTypePointer Function %uint
+  )";
+
+  const std::string body = R"(
+    %rid = OpVariable %_ptr_Function_uint Function
+     %12 = OpHitObjectGetShaderBindingTableRecordIndexEXT %uint %hObj
+           OpStore %rid %12
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT,
+       HitObjectSetShaderBindingTableRecordIndexEXT) {
+  const std::string declarations = R"(
+    %uint = OpTypeInt 32 0
+    %uint_5 = OpConstant %uint 5
+  )";
+
+  const std::string body = R"(
+    OpHitObjectSetShaderBindingTableRecordIndexEXT %hObj %uint_5
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectGetAttributesEXT) {
+  const std::string body = R"(
+    OpHitObjectGetAttributesEXT %hObj %attr
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectExecuteShaderEXT) {
+  const std::string body = R"(
+    OpHitObjectExecuteShaderEXT %hObj %payload
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT,
+       HitObjectGetIntersectionTriangleVertexPositionsEXT) {
+  const std::string declarations = R"(
+        %v3float = OpTypeVector %float 3
+        %uint = OpTypeInt 32 0
+        %uint_3 = OpConstant %uint 3
+        %arr_3_v3float = OpTypeArray %v3float %uint_3
+        %_ptr_Function_arr_3_v3float = OpTypePointer Function %arr_3_v3float
+  )";
+
+  const std::string body = R"(
+      %vertices = OpVariable %_ptr_Function_arr_3_v3float Function
+      %result = OpHitObjectGetIntersectionTriangleVertexPositionsEXT %arr_3_v3float %hObj
+      OpStore %vertices %result
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectRecordFromQueryEXT) {
+  const std::string cap = R"(
+               OpCapability RayQueryKHR
+                            )";
+  const std::string extensions = R"(
+               OpExtension "SPV_KHR_ray_query"
+               )";
+  const std::string declarations = R"(
+        %uint = OpTypeInt 32 0
+        %uint_5 = OpConstant %uint 5
+        %rayquery_type = OpTypeRayQueryKHR
+        %_ptr_Function_rayquery = OpTypePointer Function %rayquery_type
+  )";
+
+  const std::string body = R"(
+      %ray_query = OpVariable %_ptr_Function_rayquery Function
+      OpHitObjectRecordFromQueryEXT %hObj %ray_query %uint_5 %attr
+  )";
+
+  CompileSuccessfully(
+      GenerateReorderShaderCodeEXT(body, declarations, extensions, cap).c_str(),
+      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectRecordMissMotionEXT) {
+  const std::string declarations = R"(
+         %uint = OpTypeInt 32 0
+       %uint_1 = OpConstant %uint 1
+      %v3float = OpTypeVector %float 3
+    %float_0_5 = OpConstant %float 0.5
+           %29 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5
+    %float_1_5 = OpConstant %float 1.5
+           %31 = OpConstantComposite %v3float %float_1_5 %float_1_5 %float_1_5
+      %float_5 = OpConstant %float 5
+     %float_10 = OpConstant %float 10
+  )";
+
+  const std::string body = R"(
+      OpHitObjectRecordMissMotionEXT %hObj %uint_1 %uint_1 %29 %float_2 %31 %float_5 %float_10
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+// Fused Hit Object Instructions Tests
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectReorderExecuteShaderEXT) {
+  const std::string declarations = R"(
+      %uint = OpTypeInt 32 0
+    %uint_4 = OpConstant %uint 4
+    %uint_2 = OpConstant %uint 2
+  )";
+
+  const std::string body = R"(
+      OpHitObjectReorderExecuteShaderEXT %hObj %payload
+      OpHitObjectReorderExecuteShaderEXT %hObj %payload %uint_4 %uint_2
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectTraceReorderExecuteEXT) {
+  const std::string declarations = R"(
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_4 = OpConstant %uint 4
+     %uint_2 = OpConstant %uint 2
+    %v3float = OpTypeVector %float 3
+  %float_0_5 = OpConstant %float 0.5
+         %31 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5
+         %32 = OpConstantComposite %v3float %float_1 %float_1 %float_1
+      %int_1 = OpConstant %int 1
+  )";
+
+  const std::string body = R"(
+  OpStore %attr %11
+  %26 = OpLoad %23 %as
+  OpHitObjectTraceReorderExecuteEXT %hObj %26 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %31 %float_0_5 %32 %float_1 %payload
+  OpHitObjectTraceReorderExecuteEXT %hObj %26 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %31 %float_0_5 %32 %float_1 %payload %uint_4 %uint_2
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateRayTracingReorderEXT, HitObjectTraceMotionReorderExecuteEXT) {
+  const std::string declarations = R"(
+        %uint = OpTypeInt 32 0
+      %uint_1 = OpConstant %uint 1
+      %uint_4 = OpConstant %uint 4
+      %uint_2 = OpConstant %uint 2
+     %v3float = OpTypeVector %float 3
+   %float_0_5 = OpConstant %float 0.5
+          %31 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5
+          %32 = OpConstantComposite %v3float %float_1 %float_1 %float_1
+    %float_10 = OpConstant %float 10
+       %int_2 = OpConstant %int 2
+  )";
+
+  const std::string body = R"(
+      OpStore %attr %11
+      %26 = OpLoad %23 %as
+      OpHitObjectTraceMotionReorderExecuteEXT %hObj %26 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %31 %float_0_5 %32 %float_1 %float_10 %__0
+      OpHitObjectTraceMotionReorderExecuteEXT %hObj %26 %uint_1 %uint_1 %uint_1 %uint_1 %uint_1 %31 %float_0_5 %32 %float_1 %float_10 %__0 %uint_4 %uint_2
+  )";
+
+  CompileSuccessfully(GenerateReorderShaderCodeEXT(body, declarations).c_str(),
+                      SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_ray_tracing_test.cpp b/test/val/val_ray_tracing_test.cpp
index 60f2f89..476b228 100644
--- a/test/val/val_ray_tracing_test.cpp
+++ b/test/val/val_ray_tracing_test.cpp
@@ -667,6 +667,37 @@
                 "IncomingCallableDataKHR storage class in the interface"));
 }
 
+TEST_F(ValidateRayTracing, RayTracingPositionFetchCapability) {
+  const std::string spirv = R"(
+               OpCapability RayTracingKHR
+               OpCapability RayQueryPositionFetchKHR
+               OpExtension "SPV_KHR_ray_tracing"
+               OpExtension "SPV_KHR_ray_tracing_position_fetch"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint AnyHitKHR %main "main" %gl_HitTriangleVertexPositionsEXT
+               OpDecorate %gl_HitTriangleVertexPositionsEXT BuiltIn HitTriangleVertexPositionsKHR
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+       %uint = OpTypeInt 32 0
+     %uint_3 = OpConstant %uint 3
+%_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3
+%_ptr_Input__arr_v3float_uint_3 = OpTypePointer Input %_arr_v3float_uint_3
+%gl_HitTriangleVertexPositionsEXT = OpVariable %_ptr_Input__arr_v3float_uint_3 Input
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpTerminateRayKHR
+               OpFunctionEnd
+)";
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
+            ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Decorate requires one of these capabilities: "
+                        "RayTracingPositionFetchKHR"));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_storage_test.cpp b/test/val/val_storage_test.cpp
index d4170e6..b583fe3 100644
--- a/test/val/val_storage_test.cpp
+++ b/test/val/val_storage_test.cpp
@@ -249,6 +249,194 @@
               HasSubstr("OpFunctionCall Argument <id> '"));
 }
 
+TEST_F(ValidateStorage, TileAttachmentQCOMBad1) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability Sampled1D
+               OpCapability TileShadingQCOM
+               OpExtension "SPV_QCOM_tile_shading"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpDecorate %color1 Binding 2
+               OpDecorate %color1 DescriptorSet 0
+       %void = OpTypeVoid
+        %int = OpTypeInt 32 1
+         %44 = OpTypeImage %int 1D 0 0 0 2 Rgba32i
+%_ptr_TileAttachmentQCOM_44 = OpTypePointer TileAttachmentQCOM %44
+     %color1 = OpVariable %_ptr_TileAttachmentQCOM_44 TileAttachmentQCOM
+          %3 = OpTypeFunction %void
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  spv_target_env env = SPV_ENV_VULKAN_1_4;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Any OpTypeImage variable in the TileAttachmentQCOM "
+                        "Storage Class must have 2D as its dimension"));
+}
+
+TEST_F(ValidateStorage, TileAttachmentQCOMBad2) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability TileShadingQCOM
+               OpExtension "SPV_QCOM_tile_shading"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpDecorate %color1 Binding 2
+       %void = OpTypeVoid 
+        %int = OpTypeInt 32 1
+         %44 = OpTypeImage %int 2D 0 0 0 2 Rgba32i
+%_ptr_TileAttachmentQCOM_44 = OpTypePointer TileAttachmentQCOM %44
+     %color1 = OpVariable %_ptr_TileAttachmentQCOM_44 TileAttachmentQCOM
+          %3 = OpTypeFunction %void
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  spv_target_env env = SPV_ENV_VULKAN_1_4;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Any variable in the TileAttachmentQCOM Storage Class "
+                        "must be decorated with DescriptorSet and Binding"));
+}
+
+TEST_F(ValidateStorage, TileAttachmentQCOMBad3) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability TileShadingQCOM
+               OpExtension "SPV_QCOM_tile_shading"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpDecorate %color1 DescriptorSet 0
+       %void = OpTypeVoid 
+        %int = OpTypeInt 32 1
+         %44 = OpTypeImage %int 2D 0 0 0 2 Rgba32i
+%_ptr_TileAttachmentQCOM_44 = OpTypePointer TileAttachmentQCOM %44
+     %color1 = OpVariable %_ptr_TileAttachmentQCOM_44 TileAttachmentQCOM
+          %3 = OpTypeFunction %void
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  spv_target_env env = SPV_ENV_VULKAN_1_4;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Any variable in the TileAttachmentQCOM Storage Class "
+                        "must be decorated with DescriptorSet and Binding"));
+}
+
+TEST_F(ValidateStorage, TileAttachmentQCOMBad4) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability TileShadingQCOM
+               OpExtension "SPV_QCOM_tile_shading"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpDecorate %color1 Binding 2
+               OpDecorate %color1 DescriptorSet 0
+       %void = OpTypeVoid
+        %int = OpTypeInt 32 1
+         %44 = OpTypeImage %int 2D 0 0 0 0 Rgba32i
+%_ptr_TileAttachmentQCOM_44 = OpTypePointer TileAttachmentQCOM %44
+     %color1 = OpVariable %_ptr_TileAttachmentQCOM_44 TileAttachmentQCOM
+          %3 = OpTypeFunction %void
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  spv_target_env env = SPV_ENV_VULKAN_1_4;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeImage-04657"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Sampled must be 1 or 2 in the Vulkan environment"));
+}
+
+TEST_F(ValidateStorage, TileAttachmentQCOMBad5) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpCapability ImageQuery
+               OpCapability TileShadingQCOM
+               OpExtension "SPV_QCOM_tile_shading"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpDecorate %color1 Binding 2
+               OpDecorate %color1 DescriptorSet 0
+       %void = OpTypeVoid 
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+      %v2int = OpTypeVector %int 2 
+      %int_2 = OpConstant %int 2
+         %44 = OpTypeImage %int 2D 0 0 0 2 Rgba32i
+%_ptr_TileAttachmentQCOM_44 = OpTypePointer TileAttachmentQCOM %44
+     %color1 = OpVariable %_ptr_TileAttachmentQCOM_44 TileAttachmentQCOM
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+        %154 = OpLoad %44 %color1
+        %156 = OpImageQuerySizeLod %v2int %154 %int_2
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  spv_target_env env = SPV_ENV_VULKAN_1_4;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Any variable in the TileAttachmentQCOM Storage Class "
+                "must not be consumed by an OpImageQuery* instruction"));
+}
+
+TEST_F(ValidateStorage, TileAttachmentQCOMBad6) {
+  const std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpDecorate %color1 Binding 2
+               OpDecorate %color1 DescriptorSet 0
+       %void = OpTypeVoid
+        %int = OpTypeInt 32 1
+         %44 = OpTypeImage %int 2D 0 0 0 2 Rgba32i
+%_ptr_TileAttachmentQCOM_44 = OpTypePointer TileAttachmentQCOM %44
+     %color1 = OpVariable %_ptr_TileAttachmentQCOM_44 TileAttachmentQCOM
+          %3 = OpTypeFunction %void
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  spv_target_env env = SPV_ENV_VULKAN_1_4;
+  CompileSuccessfully(spirv, env);
+  EXPECT_THAT(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions(env));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("requires one of these capabilities: TileShadingQCOM"));
+}
+
 std::string GenerateExecutionModelCode(const std::string& execution_model,
                                        const std::string& storage_class,
                                        bool store) {
@@ -265,7 +453,8 @@
               OpMemoryModel Logical GLSL450
               OpEntryPoint )"
      << execution_model << R"( %func "func" %var
-              )" << mode << R"(
+              )"
+     << mode << R"(
               OpDecorate %var Location 0
 %intt       = OpTypeInt 32 0
 %int0       = OpConstant %intt 0
@@ -273,10 +462,12 @@
 %vfunct     = OpTypeFunction %voidt
 %ptr        = OpTypePointer )"
      << storage_class << R"( %intt
-%var        = OpVariable %ptr )" << storage_class << R"(
+%var        = OpVariable %ptr )"
+     << storage_class << R"(
 %func       = OpFunction %voidt None %vfunct
 %funcl      = OpLabel
-              )" << operation << R"(
+              )"
+     << operation << R"(
               OpReturn
               OpFunctionEnd
 )";
diff --git a/test/val/val_tensor_test.cpp b/test/val/val_tensor_test.cpp
new file mode 100644
index 0000000..b9bf424
--- /dev/null
+++ b/test/val/val_tensor_test.cpp
@@ -0,0 +1,1192 @@
+// Copyright (c) 2023-2025 Arm Ltd.
+//
+// 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 "gmock/gmock.h"
+#include "test/unit_spirv.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::ContainsRegex;
+using ::testing::HasSubstr;
+
+using ValidateTensor = spvtest::ValidateBase<std::string>;
+
+constexpr spv_target_env SPVENV = SPV_ENV_VULKAN_1_3;
+
+std::string GenerateModule(const std::string& body) {
+  const std::string header = R"(
+                     OpCapability Shader
+                     OpCapability VulkanMemoryModel
+                     OpCapability Int8
+                     OpCapability TensorsARM
+                     OpExtension "SPV_ARM_tensors"
+                     OpMemoryModel Logical Vulkan
+                     OpEntryPoint GLCompute %fnep "main"
+                     OpExecutionMode %fnep LocalSize 1 1 1
+                     OpDecorate %tensor_var DescriptorSet 0
+                     OpDecorate %tensor_var Binding 0
+                     OpDecorate %tensor_var_float_unranked DescriptorSet 0
+                     OpDecorate %tensor_var_float_unranked Binding 1
+                     OpDecorate %tensor_var_spec_rank DescriptorSet 0
+                     OpDecorate %tensor_var_spec_rank Binding 2
+             %void = OpTypeVoid
+             %uint = OpTypeInt 32 0
+            %float = OpTypeFloat 32
+             %fnty = OpTypeFunction %void
+        %uint_vec4 = OpTypeVector %uint 4
+           %uint_0 = OpConstant %uint 0
+      %uint_0_spec = OpSpecConstant %uint 0
+           %uint_1 = OpConstant %uint 1
+           %uint_2 = OpConstant %uint 2
+           %uint_3 = OpConstant %uint 3
+           %uint_4 = OpConstant %uint 4
+          %uint_42 = OpConstant %uint 42
+       %uint_1_bis = OpConstant %uint 1
+          %float_1 = OpConstant %float 1
+%uint_vec4_1_1_1_1 = OpConstantComposite %uint_vec4 %uint_1 %uint_1 %uint_1 %uint_1
+        %uint_arr1 = OpTypeArray %uint %uint_1
+        %uint_arr2 = OpTypeArray %uint %uint_2
+        %uint_arr3 = OpTypeArray %uint %uint_3
+        %uint_arr4 = OpTypeArray %uint %uint_4
+       %float_arr4 = OpTypeArray %float %uint_4
+%uint_arr4_1_1_1_1 = OpConstantComposite %uint_arr4 %uint_1 %uint_1 %uint_1 %uint_1
+%uint_arr4_0_0_0_0_spec = OpSpecConstantComposite %uint_arr4 %uint_0_spec %uint_0_spec %uint_0_spec %uint_0_spec
+    %uint_arr2_1_1 = OpConstantComposite %uint_arr2 %uint_1 %uint_1
+%float_arr4_1_1_1_1 = OpConstantComposite %float_arr4 %float_1 %float_1 %float_1 %float_1
+%uint_arr4_1_1_0_1 = OpConstantComposite %uint_arr4 %uint_1 %uint_1 %uint_0 %uint_1
+ %uint_ptr_Private = OpTypePointer Private %uint
+%uint_arr4_ptr_Private = OpTypePointer Private %uint_arr4
+       %uint_var_1 = OpVariable %uint_ptr_Private Private %uint_1
+%var_uint_arr4_1_1_1_1 = OpVariable %uint_arr4_ptr_Private Private %uint_arr4_1_1_1_1
+    %tensor_uint_4 = OpTypeTensorARM %uint %uint_4
+ %tensor_uint_spec = OpTypeTensorARM %uint %uint_0_spec
+     %tensor_float = OpTypeTensorARM %float
+%tensor_uint_4_ptr_UniformConstant = OpTypePointer UniformConstant %tensor_uint_4
+%tensor_var = OpVariable %tensor_uint_4_ptr_UniformConstant UniformConstant
+%tensor_float_ptr_UniformConstant = OpTypePointer UniformConstant %tensor_float
+%tensor_var_float_unranked = OpVariable %tensor_float_ptr_UniformConstant UniformConstant
+%tensor_uint_spec_ptr_UniformConstant = OpTypePointer UniformConstant %tensor_uint_spec
+%tensor_var_spec_rank = OpVariable %tensor_uint_spec_ptr_UniformConstant UniformConstant
+)";
+  const std::string footer = R"(
+             %fnep = OpFunction %void None %fnty
+            %label = OpLabel
+                     OpReturn
+                     OpFunctionEnd
+)";
+  return header + body + footer;
+}
+
+//
+// Type tests
+//
+
+TEST_F(ValidateTensor, ValidTypeElementTypeOnly) {
+  const std::string src = R"(
+    %test_type = OpTypeTensorARM %uint
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, InvalidTypeTensorElementTypeNotScalar) {
+  const std::string src = R"(
+    %test_type = OpTypeTensorARM %void
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex("OpTypeTensorARM Element Type <id> "
+                            "'.*' is not a scalar type.*"));
+}
+
+TEST_F(ValidateTensor, InvalidTypeDuplicateElementTypeTensorType) {
+  const std::string src = R"(
+    %typeA = OpTypeTensorARM %uint
+    %typeB = OpTypeTensorARM %uint
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Duplicate non-aggregate type declarations are not allowed."));
+}
+
+TEST_F(ValidateTensor, ValidTypeElementTypeAndRank) {
+  const std::string src = R"(
+    %test_type = OpTypeTensorARM %uint %uint_1
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, ValidTypeElementTypeAndRankUsingSpecConstant) {
+  const std::string src = R"(
+    %rank_spec = OpSpecConstant %uint  0
+    %test_type = OpTypeTensorARM %uint %rank_spec
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, InvalidTypeDuplicateRankedTensorType) {
+  const std::string src = R"(
+    %typeA = OpTypeTensorARM %uint %uint_1
+    %typeB = OpTypeTensorARM %uint %uint_1
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Duplicate non-aggregate type declarations are not allowed."));
+}
+
+TEST_F(ValidateTensor, ValidTypeEquivalentRankedTensorType) {
+  const std::string src = R"(
+    %typeA = OpTypeTensorARM %uint %uint_1
+    %typeB = OpTypeTensorARM %uint %uint_1_bis
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, InvalidTypeRankNotConstant) {
+  const std::string src = R"(
+    %test_type = OpTypeTensorARM %uint %uint_var_1
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex("OpTypeTensorARM Rank <id> '.*' "
+                            "is not a constant instruction.*"));
+}
+
+TEST_F(ValidateTensor, InvalidTypeRankConstantButNotIntegerType) {
+  const std::string src = R"(
+    %test_type = OpTypeTensorARM %uint %float_1
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex("OpTypeTensorARM Rank <id> '.*' "
+                            "does not have a scalar integer type.*"));
+}
+
+TEST_F(ValidateTensor, InvalidTypeRankConstantIntegerTypeButNotScalar) {
+  const std::string src = R"(
+    %test_type = OpTypeTensorARM %uint %uint_vec4_1_1_1_1
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex("OpTypeTensorARM Rank <id> '.*' "
+                            "does not have a scalar integer type.*"));
+}
+
+TEST_F(ValidateTensor, InvalidTypeRank0) {
+  const std::string src = R"(
+    %test_type = OpTypeTensorARM %uint %uint_0
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex("OpTypeTensorARM Rank <id> '.*' must "
+                            "define a value greater than 0.*"));
+}
+
+TEST_F(ValidateTensor, ValidTypeElementTypeAndRankAndShape) {
+  const std::string src = R"(
+    %test_type = OpTypeTensorARM %uint %uint_4 %uint_arr4_1_1_1_1
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, ValidTypeElementTypeAndRankAndShapeUsingSpecConstant) {
+  const std::string src = R"(
+    %test_type = OpTypeTensorARM %uint %uint_0_spec %uint_arr4_0_0_0_0_spec
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, InvalidTypeShapeNotConstant) {
+  const std::string src = R"(
+    %test_type = OpTypeTensorARM %uint %uint_4 %var_uint_arr4_1_1_1_1
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex("OpTypeTensorARM Shape <id> '.*' is not "
+                            "a constant instruction.*"));
+}
+
+TEST_F(ValidateTensor, InvalidTypeShapeConstantButNotArrayOfInteger) {
+  const std::string src = R"(
+    %test_type = OpTypeTensorARM %uint %uint_4 %uint_vec4_1_1_1_1
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex(
+                  "OpTypeTensorARM Shape <id> '.*' is not "
+                  "an array of integer type whose Length is equal to Rank.*"));
+}
+
+TEST_F(ValidateTensor, InvalidTypeShapeConstantArrayOfIntegerWrongLength) {
+  const std::string src = R"(
+    %test_type = OpTypeTensorARM %uint %uint_1 %uint_arr4_1_1_1_1
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex(
+                  "OpTypeTensorARM Shape <id> '.*' is not "
+                  "an array of integer type whose Length is equal to Rank.*"));
+}
+
+TEST_F(ValidateTensor,
+       InvalidTypeShapeConstantArrayOfIntegerWithZeroConstituent) {
+  const std::string src = R"(
+    %test_type = OpTypeTensorARM %uint %uint_4 %uint_arr4_1_1_0_1
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      ContainsRegex(
+          "OpTypeTensorARM Shape constituent 2 is not greater than 0.*"));
+}
+
+//
+// Constants tests
+//
+TEST_F(ValidateTensor, ValidNullTensorConstantRank1) {
+  const std::string src = R"(
+   %uint_arr1_4 = OpConstantComposite %uint_arr1 %uint_4
+    %ts_uint_r1 = OpTypeTensorARM %uint %uint_1 %uint_arr1_4
+           %cst = OpConstantNull %ts_uint_r1
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, ValidNullTensorConstantRank4) {
+  const std::string src = R"(
+%uint_arr4_2_2_2_2 = OpConstantComposite %uint_arr4 %uint_2 %uint_2 %uint_2 %uint_2
+       %ts_uint_r4 = OpTypeTensorARM %uint %uint_4 %uint_arr4_2_2_2_2
+              %cst = OpConstantNull %ts_uint_r4
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, InvalidNullTensorConstantRankedButNotShaped) {
+  const std::string src = R"(
+       %ts_uint_r1 = OpTypeTensorARM %uint %uint_1
+              %cst = OpConstantNull %ts_uint_r1
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex("OpConstantNull Result Type <id> '.*' "
+                            "cannot have a null value.*"));
+}
+
+TEST_F(ValidateTensor, InvalidNullTensorConstantNotRanked) {
+  const std::string src = R"(
+       %ts_uint_r1 = OpTypeTensorARM %uint
+              %cst = OpConstantNull %ts_uint_r1
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex("OpConstantNull Result Type <id> '.*' "
+                            "cannot have a null value.*"));
+}
+
+TEST_F(ValidateTensor, ValidTensorConstantRank1) {
+  const std::string src = R"(
+   %uint_arr1_4 = OpConstantComposite %uint_arr1 %uint_4
+    %ts_uint_r1 = OpTypeTensorARM %uint %uint_1 %uint_arr1_4
+           %cst = OpConstantComposite %ts_uint_r1 %uint_42 %uint_42 %uint_42 %uint_42
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, ValidTensorConstantRank1SpecConstant) {
+  const std::string src = R"(
+   %uint_arr1_4 = OpSpecConstantComposite %uint_arr1 %uint_0_spec
+    %ts_uint_r1 = OpTypeTensorARM %uint %uint_0_spec %uint_arr1_4
+           %cst = OpSpecConstantComposite %ts_uint_r1 %uint_0_spec %uint_0_spec %uint_0_spec %uint_0_spec
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, InvalidTensorConstantRank1NotEnoughConstituents) {
+  const std::string src = R"(
+   %uint_arr1_4 = OpConstantComposite %uint_arr1 %uint_4
+    %ts_uint_r1 = OpTypeTensorARM %uint %uint_1 %uint_arr1_4
+           %cst = OpConstantComposite %ts_uint_r1 %uint_42 %uint_42 %uint_42
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      ContainsRegex("OpConstantComposite Constituent count does not match the "
+                    "shape of Result Type <id> '.*' "
+                    "along its outermost dimension, expected 4 but got 3.*"));
+}
+
+TEST_F(ValidateTensor, InvalidTensorConstantRank1TooManyConstituents) {
+  const std::string src = R"(
+   %uint_arr1_4 = OpConstantComposite %uint_arr1 %uint_4
+    %ts_uint_r1 = OpTypeTensorARM %uint %uint_1 %uint_arr1_4
+           %cst = OpConstantComposite %ts_uint_r1 %uint_42 %uint_42 %uint_42 %uint_42 %uint_42
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      ContainsRegex("OpConstantComposite Constituent count does not match the "
+                    "shape of Result Type <id> '.*' "
+                    "along its outermost dimension, expected 4 but got 5.*"));
+}
+
+TEST_F(ValidateTensor, InvalidTensorConstantRank1WrongConstituentType) {
+  const std::string src = R"(
+   %uint_arr1_4 = OpConstantComposite %uint_arr1 %uint_4
+    %ts_uint_r1 = OpTypeTensorARM %uint %uint_1 %uint_arr1_4
+           %cst = OpConstantComposite %ts_uint_r1 %uint_42 %uint_42 %float_1 %uint_42
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      ContainsRegex("OpConstantComposite Constituent <id> '.*' "
+                    "type does not match the element type of the tensor "
+                    ".*"));
+}
+
+TEST_F(ValidateTensor, ValidTensorConstantRank4) {
+  const std::string src = R"(
+      %uint_arr1_2 = OpConstantComposite %uint_arr1 %uint_2
+    %uint_arr2_2_2 = OpConstantComposite %uint_arr2 %uint_2 %uint_2
+  %uint_arr3_2_2_2 = OpConstantComposite %uint_arr3 %uint_2 %uint_2 %uint_2
+%uint_arr4_2_2_2_2 = OpConstantComposite %uint_arr4 %uint_2 %uint_2 %uint_2 %uint_2
+       %ts_uint_r1 = OpTypeTensorARM %uint %uint_1 %uint_arr1_2
+       %ts_uint_r2 = OpTypeTensorARM %uint %uint_2 %uint_arr2_2_2
+       %ts_uint_r3 = OpTypeTensorARM %uint %uint_3 %uint_arr3_2_2_2
+       %ts_uint_r4 = OpTypeTensorARM %uint %uint_4 %uint_arr4_2_2_2_2
+           %cst_r1 = OpConstantComposite %ts_uint_r1 %uint_42 %uint_42
+           %cst_r2 = OpConstantComposite %ts_uint_r2 %cst_r1 %cst_r1
+           %cst_r3 = OpConstantComposite %ts_uint_r3 %cst_r2 %cst_r2
+           %cst_r4 = OpConstantComposite %ts_uint_r4 %cst_r3 %cst_r3
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, ValidTensorConstantRank2SpecConstantConstituent) {
+  const std::string src = R"(
+      %uint_arr1_2 = OpSpecConstantComposite %uint_arr1 %uint_0_spec
+    %uint_arr2_2_2 = OpSpecConstantComposite %uint_arr2 %uint_0_spec %uint_0_spec
+       %ts_uint_r1 = OpTypeTensorARM %uint %uint_0_spec %uint_arr1_2
+       %ts_uint_r2 = OpTypeTensorARM %uint %uint_2 %uint_arr2_2_2
+           %cst_r1 = OpSpecConstantComposite %ts_uint_r1 %uint_0_spec %uint_0_spec
+           %cst_r2 = OpSpecConstantComposite %ts_uint_r2 %cst_r1 %cst_r1
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, InvalidTensorConstantRank2ConstituentNotATensor) {
+  const std::string src = R"(
+      %uint_arr1_2 = OpConstantComposite %uint_arr1 %uint_2
+    %uint_arr2_2_2 = OpConstantComposite %uint_arr2 %uint_2 %uint_2
+       %ts_uint_r1 = OpTypeTensorARM %uint %uint_1 %uint_arr1_2
+       %ts_uint_r2 = OpTypeTensorARM %uint %uint_2 %uint_arr2_2_2
+           %cst_r1 = OpConstantComposite %ts_uint_r1 %uint_42 %uint_42
+           %cst_r2 = OpConstantComposite %ts_uint_r2 %uint_1 %cst_r1
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex("OpConstantComposite Constituent <id> "
+                            "'.*' must be an OpTypeTensorARM.*"));
+}
+
+TEST_F(ValidateTensor,
+       InvalidTensorConstantRank2ConstituentTensorWrongElementType) {
+  const std::string src = R"(
+      %uint_arr1_2 = OpConstantComposite %uint_arr1 %uint_2
+    %uint_arr2_2_2 = OpConstantComposite %uint_arr2 %uint_2 %uint_2
+       %ts_float_r1 = OpTypeTensorARM %float %uint_1 %uint_arr1_2
+       %ts_uint_r2 = OpTypeTensorARM %uint %uint_2 %uint_arr2_2_2
+           %cst_r1 = OpConstantComposite %ts_float_r1 %float_1 %float_1
+           %cst_r2 = OpConstantComposite %ts_uint_r2 %cst_r1 %cst_r1
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex("OpConstantComposite Constituent <id> "
+                            "'.*' must have the same Element Type "
+                            "as Result Type <id> .*"));
+}
+
+TEST_F(ValidateTensor, InvalidTensorConstantRank2ConstituentTensorWrongRank) {
+  const std::string src = R"(
+      %uint_arr1_2 = OpConstantComposite %uint_arr1 %uint_2
+    %uint_arr2_2_2 = OpConstantComposite %uint_arr2 %uint_2 %uint_2
+       %ts_uint_r1 = OpTypeTensorARM %uint %uint_1 %uint_arr1_2
+       %ts_uint_r2 = OpTypeTensorARM %uint %uint_2 %uint_arr2_2_2
+           %cst_r1 = OpConstantComposite %ts_uint_r1 %uint_42 %uint_42
+           %cst_r2 = OpConstantComposite %ts_uint_r2 %cst_r1 %cst_r1
+              %cst = OpConstantComposite %ts_uint_r2 %cst_r1 %cst_r2
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              ContainsRegex("OpConstantComposite Constituent <id> "
+                            "'.*' must have a Rank that is "
+                            "1 less than the Rank of Result Type <id> "
+                            "'.*', expected 1 but got 2.*"));
+}
+
+TEST_F(ValidateTensor, InvalidTensorConstantRank2ConstituentTensorWrongShape) {
+  const std::string src = R"(
+      %uint_arr1_3 = OpConstantComposite %uint_arr1 %uint_3
+    %uint_arr2_2_2 = OpConstantComposite %uint_arr2 %uint_2 %uint_2
+       %ts_uint_r1 = OpTypeTensorARM %uint %uint_1 %uint_arr1_3
+       %ts_uint_r2 = OpTypeTensorARM %uint %uint_2 %uint_arr2_2_2
+           %cst_r1 = OpConstantComposite %ts_uint_r1 %uint_42 %uint_42 %uint_42
+           %cst_r2 = OpConstantComposite %ts_uint_r2 %cst_r1 %cst_r1
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      ContainsRegex(
+          "OpConstantComposite Constituent <id> "
+          "'.*' must have a Shape that matches "
+          "that of Result Type <id> '.*' along "
+          "all inner dimensions of Result Type, expected 2 for dimension 0 of "
+          "Constituent but got 3.*"));
+}
+
+//
+// Read tests
+//
+
+TEST_F(ValidateTensor, ValidTensorReadScalar) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+            %val = OpTensorReadARM %uint %tensor %uint_arr4_1_1_1_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, ValidTensorReadArray) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+            %val = OpTensorReadARM %uint_arr2 %tensor %uint_arr4_1_1_1_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, ValidTensorReadSpecConstantRank) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_spec %tensor_var_spec_rank
+            %val = OpTensorReadARM %uint %tensor %uint_arr4_1_1_1_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, InvalidTensorReadResultTypeVoid) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+            %val = OpTensorReadARM %void %tensor %uint_arr4_1_1_1_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected Result Type to be a scalar type or array of scalar type"));
+}
+
+TEST_F(ValidateTensor, InvalidTensorReadResultTypeVector) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+            %val = OpTensorReadARM %uint_vec4 %tensor %uint_arr4_1_1_1_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected Result Type to be a scalar type or array of scalar type"));
+}
+
+TEST_F(ValidateTensor,
+       InvalidTensorReadResultTypeDoesNotMatchTensorElementType) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+            %val = OpTensorReadARM %float %tensor %uint_arr4_1_1_1_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected Result Type to be the same as the Element Type of Tensor"));
+}
+
+TEST_F(ValidateTensor, InvalidTensorReadTensorNotRanked) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_float %tensor_var_float_unranked
+            %val = OpTensorReadARM %float %tensor %uint_arr4_1_1_1_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected Tensor to be an OpTypeTensorARM whose Rank is specified"));
+}
+
+TEST_F(ValidateTensor, InvalidTensorReadCoordinatesNotArray) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+            %val = OpTensorReadARM %uint %tensor %uint_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected Coordinates to be an array whose Element Type is an "
+          "integer type and whose Length is equal to the Rank of Tensor."));
+}
+
+TEST_F(ValidateTensor, InvalidTensorReadCoordinatesArrayNotInteger) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+            %val = OpTensorReadARM %uint %tensor %float_arr4_1_1_1_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected Coordinates to be an array whose Element Type is an "
+          "integer type and whose Length is equal to the Rank of Tensor."));
+}
+
+TEST_F(ValidateTensor, InvalidTensorReadCoordinatesArrayIntegerWrongLength) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+            %val = OpTensorReadARM %uint %tensor %uint_arr2_1_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected Coordinates to be an array whose Element Type is an "
+          "integer type and whose Length is equal to the Rank of Tensor."));
+}
+
+TEST_F(ValidateTensor, ValidTensorReadScalarWithNoneTensorOperands) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+            %val = OpTensorReadARM %uint %tensor %uint_arr4_1_1_1_1 NoneARM
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, ValidTensorReadScalarWithNontemporalTensorOperands) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+            %val = OpTensorReadARM %uint %tensor %uint_arr4_1_1_1_1 NontemporalARM
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, ValidTensorReadScalarOutOfBoundsValueTensorOperands) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+            %val = OpTensorReadARM %uint %tensor %uint_arr4_1_1_1_1 OutOfBoundsValueARM %uint_42
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor,
+       InvalidTensorReadScalarOutOfBoundsValueTensorOperandsValueWrongType) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+            %val = OpTensorReadARM %uint %tensor %uint_arr4_1_1_1_1 OutOfBoundsValueARM %float_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected the type of the OutOfBoundsValueARM value to be the same "
+          "as the Element Type of Tensor."));
+}
+
+TEST_F(ValidateTensor,
+       InvalidTensorReadScalarMakeElementAvailableTensorOperands) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+            %val = OpTensorReadARM %uint %tensor %uint_arr4_1_1_1_1 MakeElementAvailableARM %uint_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "MakeElementAvailableARM cannot be used with OpTensorReadARM."));
+}
+
+TEST_F(ValidateTensor,
+       ValidTensorReadScalarWithMakeElementVisibleTensorOperands) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+            %val = OpTensorReadARM %uint %tensor %uint_arr4_1_1_1_1 MakeElementVisibleARM|NonPrivateElementARM %uint_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(
+    ValidateTensor,
+    InvalidTensorReadScalarMakeElementVisibleTensorOperandsWithoutNonPrivateElement) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+            %val = OpTensorReadARM %uint %tensor %uint_arr4_1_1_1_1 MakeElementVisibleARM %uint_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("MakeElementAvailableARM requires NonPrivateElementARM"));
+}
+
+//
+// Write tests
+//
+
+TEST_F(ValidateTensor, ValidTensorWriteScalar) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+                   OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %uint_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, ValidTensorWriteArray) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+                   OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %uint_arr2_1_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, ValidTensorWriteSpecConstantRank) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_spec %tensor_var_spec_rank
+                   OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %uint_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, InvalidTensorWriteObjectNotScalarOrArrayOfScalar) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+                   OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %uint_vec4_1_1_1_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Object to be a scalar type or array of scalar type "
+                "that is the same as the Element Type of Tensor."));
+}
+
+TEST_F(ValidateTensor, InvalidTensorWriteObjectDoesNotMatchTensorElementType) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+                   OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %float_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Expected Object to be a scalar type or array of scalar type "
+                "that is the same as the Element Type of Tensor."));
+}
+
+TEST_F(ValidateTensor, InvalidTensorWriteTensorNotRanked) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_float %tensor_var_float_unranked
+                   OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %float_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected Tensor to be an OpTypeTensorARM whose Rank is specified"));
+}
+
+TEST_F(ValidateTensor, InvalidTensorWriteCoordinatesNotArray) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+                   OpTensorWriteARM %tensor %uint_1 %uint_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected Coordinates to be an array whose Element Type is an "
+          "integer type and whose Length is equal to the Rank of Tensor."));
+}
+
+TEST_F(ValidateTensor, InvalidTensorWriteCoordinatesArrayNotInteger) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+                   OpTensorWriteARM %tensor %float_arr4_1_1_1_1 %uint_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected Coordinates to be an array whose Element Type is an "
+          "integer type and whose Length is equal to the Rank of Tensor."));
+}
+
+TEST_F(ValidateTensor, InvalidTensorWriteCoordinatesArrayIntegerWrongLength) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+                   OpTensorWriteARM %tensor %uint_arr2_1_1 %uint_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected Coordinates to be an array whose Element Type is an "
+          "integer type and whose Length is equal to the Rank of Tensor."));
+}
+
+TEST_F(ValidateTensor, ValidTensorWriteScalarWithNoneTensorOperands) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+                   OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %uint_1 NoneARM
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, ValidTensorWriteScalarWithNontemporalTensorOperands) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+                   OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %uint_1 NontemporalARM
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor,
+       ValidTensorWriteScalarWithMakeElementAvailableTensorOperands) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+                   OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %uint_1 MakeElementAvailableARM|NonPrivateElementARM %uint_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor,
+       InvalidTensorWriteScalarWithOutOfVoundsValueTensorOperands) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+                   OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %uint_1 OutOfBoundsValueARM %uint_42
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OutOfBoundsValue Tensor Operand not allowed with "
+                        "OpTensorWriteARM."));
+}
+
+TEST_F(ValidateTensor,
+       InvalidTensorWriteScalarWithMakeElementVisibleTensorOperands) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+                   OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %uint_1 MakeElementVisibleARM %uint_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("MakeElementVisibleARM not allowed with OpTensorWriteARM."));
+}
+
+TEST_F(
+    ValidateTensor,
+    InvalidTensorWriteScalarWithMakeElementAvailableTensorOperandsWithoutNonPrivateElement) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+                   OpTensorWriteARM %tensor %uint_arr4_1_1_1_1 %uint_1 MakeElementAvailableARM %uint_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("MakeElementAvailableARM requires NonPrivateElementARM."));
+}
+
+//
+// Query Size tests
+//
+
+TEST_F(ValidateTensor, ValidTensorQuerySize) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+           %size = OpTensorQuerySizeARM %uint %tensor %uint_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, ValidTensorQuerySizeSpecConstant) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_spec %tensor_var_spec_rank
+           %size = OpTensorQuerySizeARM %uint %tensor %uint_0_spec
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPVENV));
+}
+
+TEST_F(ValidateTensor, InvalidTensorQuerySizeTensorNotRanked) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_float %tensor_var_float_unranked
+           %size = OpTensorQuerySizeARM %uint %tensor %uint_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "Expected Tensor to be an OpTypeTensorARM whose Rank is specified"));
+}
+
+TEST_F(ValidateTensor, InvalidTensorQuerySizeResultNotIntegerScalarType) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_float %tensor_var_float_unranked
+           %size = OpTensorQuerySizeARM %float %tensor %uint_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Expected Result Type to be an integer type scalar"));
+}
+
+TEST_F(ValidateTensor, InvalidTensorQuerySizeDimensionNotIntegerType) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+           %size = OpTensorQuerySizeARM %uint %tensor %float_1
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Dimension must come from a constant instruction of "
+                        "scalar integer type."));
+}
+
+TEST_F(ValidateTensor, InvalidTensorQuerySizeDimensionNotConstant) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+           %dims = OpLoad %uint_arr4 %var_uint_arr4_1_1_1_1
+            %dim = OpCompositeExtract %uint %dims 0
+           %size = OpTensorQuerySizeARM %uint %tensor %dim
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Dimension must come from a constant instruction of "
+                        "scalar integer type."));
+}
+
+TEST_F(ValidateTensor, InvalidTensorQuerySizeDimension) {
+  const std::string src = R"(
+             %fn = OpFunction %void None %fnty
+         %label1 = OpLabel
+         %tensor = OpLoad %tensor_uint_4 %tensor_var
+           %size = OpTensorQuerySizeARM %uint %tensor %uint_42
+                   OpReturn
+                   OpFunctionEnd
+)";
+  std::string spvasm = GenerateModule(src);
+  CompileSuccessfully(spvasm, SPVENV);
+  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPVENV));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Dimension (42) must be less than the Rank of Tensor (4)"));
+}
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/val_type_unique_test.cpp b/test/val/val_type_unique_test.cpp
index 00e2e7f..289e341 100644
--- a/test/val/val_type_unique_test.cpp
+++ b/test/val/val_type_unique_test.cpp
@@ -1,4 +1,6 @@
 // Copyright (c) 2017 Google Inc.
+// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
+// reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -288,6 +290,28 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
 }
 
+TEST_F(ValidateTypeUnique, DuplicateNodePayloadArrayType) {
+  std::string str = R"(
+OpCapability Shader
+OpCapability ShaderEnqueueAMDX
+OpCapability Linkage
+OpExtension "SPV_AMDX_shader_enqueue"
+OpMemoryModel Logical GLSL450
+%floatt = OpTypeFloat 32
+%struct = OpTypeStruct %floatt
+%npat1 = OpTypeNodePayloadArrayAMDX %struct
+%npat2 = OpTypeNodePayloadArrayAMDX %struct
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+  CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_4);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index a6736ca..b1fad95 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -24,12 +24,21 @@
 #     LIBS   lib_target1 lib_target2
 #   )
 function(add_spvtools_tool)
+  if (SPIRV_TOOLS_USE_MIMALLOC)
+    list(APPEND SRCS mimalloc.cpp)
+  endif()
+
   set(one_value_args TARGET)
   set(multi_value_args SRCS LIBS)
   cmake_parse_arguments(
     ARG "" "${one_value_args}" "${multi_value_args}" ${ARGN})
 
   add_executable(${ARG_TARGET} ${ARG_SRCS})
+
+  if (SPIRV_TOOLS_USE_MIMALLOC)
+    target_link_libraries(${ARG_TARGET} PRIVATE mimalloc-static)
+  endif()
+
   spvtools_default_compile_options(${ARG_TARGET})
   target_link_libraries(${ARG_TARGET} PRIVATE ${ARG_LIBS})
   target_include_directories(${ARG_TARGET} PRIVATE
@@ -68,7 +77,7 @@
   target_include_directories(spirv-cfg PRIVATE ${spirv-tools_SOURCE_DIR}
                                                ${SPIRV_HEADER_INCLUDE_DIR})
   set(SPIRV_INSTALL_TARGETS spirv-as spirv-dis spirv-val spirv-opt
-                            spirv-cfg spirv-link spirv-lint)
+                            spirv-cfg spirv-link spirv-lint spirv-diff)
 
   if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL "Android"))
     add_spvtools_tool(TARGET spirv-objdump
diff --git a/tools/as/as.cpp b/tools/as/as.cpp
index 8e82170..833e929 100644
--- a/tools/as/as.cpp
+++ b/tools/as/as.cpp
@@ -28,8 +28,10 @@
 
 Usage: %s [options] [<filename>]
 
-The SPIR-V assembly text is read from <filename>.  If no file is specified,
-or if the filename is "-", then the assembly text is read from standard input.
+The SPIR-V assembly text is read from <filename>.
+Use "-" as the filename to read from standard input.
+Use "./-" to read from the file named "-".
+
 The SPIR-V binary module is written to file "out.spv", unless the -o option
 is used.
 
@@ -84,11 +86,13 @@
     options |= SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS;
   }
 
-  if (flags::positional_arguments.size() != 1) {
-    fprintf(stderr, "error: exactly one input file must be specified.\n");
+  if (flags::positional_arguments.size() > 1) {
+    fprintf(stderr, "error: one input file at most must be specified.\n");
     return 1;
   }
-  std::string inFile = flags::positional_arguments[0];
+  std::string inFile = flags::positional_arguments.size() == 1
+                           ? flags::positional_arguments[0]
+                           : "-";
 
   std::vector<char> contents;
   if (!ReadTextFile(inFile.c_str(), &contents)) return 1;
diff --git a/tools/cfg/bin_to_dot.cpp b/tools/cfg/bin_to_dot.cpp
index 40a7dc4..85fa1e6 100644
--- a/tools/cfg/bin_to_dot.cpp
+++ b/tools/cfg/bin_to_dot.cpp
@@ -172,7 +172,6 @@
   // diagnostics.  These are programmer errors, not user errors.
   if (!diagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC;
   const spvtools::AssemblyGrammar grammar(context);
-  if (!grammar.isValid()) return SPV_ERROR_INVALID_TABLE;
 
   spvtools::FriendlyNameMapper friendly_mapper(context, words, num_words);
   DotConverter converter(friendly_mapper.GetNameMapper(), out);
diff --git a/tools/diff/diff.cpp b/tools/diff/diff.cpp
index d60edb2..349ec09 100644
--- a/tools/diff/diff.cpp
+++ b/tools/diff/diff.cpp
@@ -87,7 +87,8 @@
     return spvtools::BuildModule(
         kDefaultEnvironment, spvtools::utils::CLIMessageConsumer,
         std::string(contents.begin(), contents.end()),
-        spvtools::SpirvTools::kDefaultAssembleOption |
+        static_cast<spv_text_to_binary_options_t>(
+            spvtools::SpirvTools::kDefaultAssembleOption) |
             SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   }
 
diff --git a/tools/dis/dis.cpp b/tools/dis/dis.cpp
index c294d03..74cd03c 100644
--- a/tools/dis/dis.cpp
+++ b/tools/dis/dis.cpp
@@ -68,6 +68,13 @@
   --offsets         Show byte offsets for each instruction.
 
   --comment         Add comments to make reading easier
+
+  --handle-unknown-opcodes
+                    Emit unknown opcodes and unknown extended instruction
+                    numbers as OpUnknown with raw integer operands instead of
+                    failing. The output can be re-assembled with spirv-as.
+                    Note: the ID bound in the reassembled module may be
+                    incorrect if any unknown instruction defines a result ID.
 )";
 
 // clang-format off
@@ -84,6 +91,7 @@
 FLAG_LONG_bool   (reorder_blocks, /* default_value= */ false, /* required= */ false);
 FLAG_LONG_bool   (offsets,        /* default_value= */ false, /* required= */ false);
 FLAG_LONG_bool   (comment,        /* default_value= */ false, /* required= */ false);
+FLAG_LONG_bool   (handle_unknown_opcodes, /* default_value= */ false, /* required= */ false);
 // clang-format on
 
 static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
@@ -141,6 +149,9 @@
 
   if (flags::comment.value()) options |= SPV_BINARY_TO_TEXT_OPTION_COMMENT;
 
+  if (flags::handle_unknown_opcodes.value())
+    options |= SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES;
+
   if (flags::o.value() == "-") {
     // Print to standard output.
     options |= SPV_BINARY_TO_TEXT_OPTION_PRINT;
diff --git a/tools/link/linker.cpp b/tools/link/linker.cpp
index 2f47dd4..9a512d4 100644
--- a/tools/link/linker.cpp
+++ b/tools/link/linker.cpp
@@ -72,6 +72,15 @@
                be invalid.
   --verify-ids
                Verify that IDs in the resulting modules are truly unique.
+  --fnvar-targets
+               CSV file specifying OpSpecConditionalTarget parameters for each
+               linked module.
+  --fnvar-architectures
+               CSV file specifying OpSpecConditionalArchitecture parameters for
+               each linked module.
+  --fnvar-capabilities
+               For each function variant, include its required capabilities
+               under OpSpecConditionalCapabilitiesINTEL.
   --version
                Display linker version information.
 )",
@@ -90,6 +99,9 @@
 FLAG_LONG_bool(   allow_pointer_mismatch, /* default_value= */ false,               /* required= */ false);
 FLAG_SHORT_string(o,                      /* default_value= */ "",                  /* required= */ false);
 FLAG_LONG_string( target_env,             /* default_value= */ kDefaultEnvironment, /* required= */ false);
+FLAG_LONG_string( fnvar_targets,          /* default_value= */ "",                  /* required= */ false);
+FLAG_LONG_string( fnvar_architectures,    /* default_value= */ "",                  /* required= */ false);
+FLAG_LONG_bool(   fnvar_capabilities,     /* default_value= */ false,               /* required= */ false);
 FLAG_LONG_bool(   use_highest_version,    /* default_value= */ false,               /* required= */ false);
 // clang-format on
 
@@ -140,6 +152,33 @@
     fprintf(stderr, "error: No input file specified\n");
     return 1;
   }
+  options.SetInFiles(inFiles);
+
+  if (!flags::fnvar_targets.value().empty()) {
+    std::vector<char> fn_variants_csv_chars;
+    const bool ret = ReadTextFile(flags::fnvar_targets.value().c_str(),
+                                  &fn_variants_csv_chars);
+    if (!ret) {
+      return 1;
+    }
+    const std::string csv_text(fn_variants_csv_chars.begin(),
+                               fn_variants_csv_chars.end());
+    options.SetFnVarTargetsCsv(csv_text);
+  }
+
+  if (!flags::fnvar_architectures.value().empty()) {
+    std::vector<char> fn_variants_csv_chars;
+    const bool ret = ReadTextFile(flags::fnvar_architectures.value().c_str(),
+                                  &fn_variants_csv_chars);
+    if (!ret) {
+      return 1;
+    }
+    const std::string csv_text(fn_variants_csv_chars.begin(),
+                               fn_variants_csv_chars.end());
+    options.SetFnVarArchitecturesCsv(csv_text);
+  }
+
+  options.SetHasFnVarCapabilities(flags::fnvar_capabilities.value());
 
   std::vector<std::vector<uint32_t>> contents(inFiles.size());
   for (size_t i = 0u; i < inFiles.size(); ++i) {
diff --git a/tools/objdump/extract_source.cpp b/tools/objdump/extract_source.cpp
index 0295952..d6d49d9 100644
--- a/tools/objdump/extract_source.cpp
+++ b/tools/objdump/extract_source.cpp
@@ -19,9 +19,9 @@
 #include <unordered_map>
 #include <vector>
 
+#include "source/latest_version_spirv_header.h"
 #include "source/opt/log.h"
 #include "spirv-tools/libspirv.hpp"
-#include "spirv/unified1/spirv.hpp"
 #include "tools/util/cli_consumer.h"
 
 namespace {
@@ -66,7 +66,7 @@
                              const spv_parsed_instruction_t& instruction,
                              std::string* output) {
   assert(output != nullptr);
-  assert(instruction.opcode == spv::Op::OpString);
+  assert(instruction.opcode == static_cast<unsigned>(spv::Op::OpString));
   if (instruction.num_operands != 2) {
     spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc,
                     "Missing operands for OpString.");
@@ -85,7 +85,8 @@
     const spv_position_t& loc, const spv_parsed_instruction_t& instruction,
     std::string* output) {
   assert(output != nullptr);
-  assert(instruction.opcode == spv::Op::OpSourceContinued);
+  assert(instruction.opcode ==
+         static_cast<unsigned>(spv::Op::OpSourceContinued));
   if (instruction.num_operands != 1) {
     spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc,
                     "Missing operands for OpSourceContinued.");
@@ -104,7 +105,7 @@
                              const spv_parsed_instruction_t& instruction,
                              spv::Id* filename, std::string* code) {
   assert(filename != nullptr && code != nullptr);
-  assert(instruction.opcode == spv::Op::OpSource);
+  assert(instruction.opcode == static_cast<unsigned>(spv::Op::OpSource));
   // OpCode [ Source Language | Version | File (optional) | Source (optional) ]
   if (instruction.num_words < 3) {
     spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc,
@@ -146,7 +147,7 @@
 
   std::unordered_map<uint32_t, std::string> stringMap;
   std::vector<std::pair<spv::Id, std::string>> sources;
-  spv::Op lastOpcode = spv::Op::OpMax;
+  spv::Op lastOpcode = spv::Op::Max;
   size_t instructionIndex = 0;
 
   spvtools::InstructionParser instructionParser =
@@ -155,20 +156,22 @@
         const spv_position_t loc = {0, 0, instructionIndex + 1};
         spv_result_t result = SPV_SUCCESS;
 
-        if (instruction.opcode == spv::Op::OpString) {
+        if (instruction.opcode == static_cast<unsigned>(spv::Op::OpString)) {
           std::string content;
           result = extractOpString(loc, instruction, &content);
           if (result == SPV_SUCCESS) {
             stringMap.emplace(instruction.result_id, std::move(content));
           }
-        } else if (instruction.opcode == spv::Op::OpSource) {
+        } else if (instruction.opcode ==
+                   static_cast<unsigned>(spv::Op::OpSource)) {
           spv::Id filenameId;
           std::string code;
           result = extractOpSource(loc, instruction, &filenameId, &code);
           if (result == SPV_SUCCESS) {
             sources.emplace_back(std::make_pair(filenameId, std::move(code)));
           }
-        } else if (instruction.opcode == spv::Op::OpSourceContinued) {
+        } else if (instruction.opcode ==
+                   static_cast<unsigned>(spv::Op::OpSourceContinued)) {
           if (lastOpcode != spv::Op::OpSource) {
             spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc,
                             "OpSourceContinued MUST follow an OpSource.");
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp
index 07a5725..19852e6 100644
--- a/tools/opt/opt.cpp
+++ b/tools/opt/opt.cpp
@@ -279,6 +279,10 @@
                option --relax-logical-pointer to the validator.)",
          GetLegalizationPasses().c_str());
   printf(R"(
+  --legalize-multidim-array
+               Replace multidimensional arrays of resources with single-dimensional
+               arrays. Run combine-access-chains before this pass.)");
+  printf(R"(
   --local-redundancy-elimination
                Looks for instructions in the same basic block that compute the
                same value, and deletes the redundant ones.)");
@@ -446,6 +450,15 @@
                Forwards this option to the validator.  See the validator help
                for details.)");
   printf(R"(
+  --canonicalize-ids
+               Canonicalize IDs to improve compression of SPIR-V binary files. The resulting
+               modules have an increased ID range (IDs are not as tightly packed
+               around zero), but will compress better when multiple modules are
+               compressed together, since the compressor's dictionary can find better
+               cross module commonality. This pass should be run after most optimization
+               passes except for --strip-debug because this pass will use OpName to
+               canonicalize IDs. i.e. Run --strip-debug after this pass.)");
+  printf(R"(
   --relax-struct-store
                Forwards this option to the validator.  See the validator help
                for details.)");
@@ -455,9 +468,9 @@
                instructions.)");
   printf(R"(
   --remove-unused-interface-variables
-               Removes variables referenced on the |OpEntryPoint| instruction 
-               that are not referenced in the entry point function or any function 
-               in its call tree.  Note that this could cause the shader interface 
+               Removes variables referenced on the |OpEntryPoint| instruction
+               that are not referenced in the entry point function or any function
+               in its call tree.  Note that this could cause the shader interface
                to no longer match other shader stages.)");
   printf(R"(
   --replace-invalid-opcode
@@ -465,6 +478,12 @@
                but not for the current shader stage.  To have an effect, all
                entry points must have the same execution model.)");
   printf(R"(
+  --resolve-binding-conflicts
+               Renumber bindings to avoid conflicts.
+               When an image and sampler share the same desriptor set and binding,
+               increment the binding number of the sampler. Recursively ripple
+               to higher-numbered bindings until all conflicts resolved resolved.)");
+  printf(R"(
   --ssa-rewrite
                Replace loads and stores to function local variables with
                operations on SSA IDs.)");
@@ -501,6 +520,11 @@
                is invalid, the optimizer may fail or generate incorrect code.
                This options should be used rarely, and with caution.)");
   printf(R"(
+  --split-combined-image-sampler
+               Replace combined image sampler variables and parameters into
+               pairs of images and samplers.  New variables have the same
+               bindings as the original variable.)");
+  printf(R"(
   --strength-reduction
                Replaces instructions with equivalent and less expensive ones.)");
   printf(R"(
@@ -728,7 +752,7 @@
                      spvtools::ValidatorOptions* validator_options,
                      spvtools::OptimizerOptions* optimizer_options) {
   std::vector<std::string> pass_flags;
-  bool preserve_interface = true;
+  bool preserve_interface = false;
   for (int argi = 1; argi < argc; ++argi) {
     const char* cur_arg = argv[argi];
     if ('-' == cur_arg[0]) {
diff --git a/tools/sva/.eslintrc.json b/tools/sva/.eslintrc.json
deleted file mode 100644
index 2f07726..0000000
--- a/tools/sva/.eslintrc.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
-    "env": {
-        "browser": true,
-        "es6": true,
-        "node": true,
-        "mocha": true
-    },
-    "extends": "eslint:recommended",
-    "parserOptions": {
-        "ecmaVersion": 2018,
-        "sourceType": "module"
-    },
-    "rules": {
-        "block-scoped-var": "error",
-        "consistent-return": "error",
-        "eqeqeq": ["error", "always"],
-        "indent": [ "error", 2 ],
-        "linebreak-style": [ "error", "unix" ],
-        "no-eval": "error",
-        "no-shadow": "error",
-        "no-shadow-restricted-names": "error",
-        "quotes": [ "error", "double" ],
-        "semi": [ "error", "always" ]
-    }
-}
diff --git a/tools/sva/.gitignore b/tools/sva/.gitignore
deleted file mode 100644
index 88e64c3..0000000
--- a/tools/sva/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-.DS_Store
-node_modules
-third_party/spirv-headers
-o.sva
-build
-yarn-error.log
diff --git a/tools/sva/README.md b/tools/sva/README.md
deleted file mode 100644
index cd3d13c..0000000
--- a/tools/sva/README.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# SVA
-
-SPIR-V Assembler for WebGPU. The SPIR-V Assembler is a JavaScript library to
-convert SPIR-V assembly (as produced by spirv-dis in SPIR-V Tools) into a
-SPIR-V binary. The assembler assumes it is generating WebGPU SPIR-V and thus has
-the following limitations.
-
- * Only 32 bit integers and floats supported
- * Only GLSL accepted as an extended instruction set
- * Doesn't support ! syntax for integers
- * Doesn't support hex encoding for float
-
-```shell
-yarn install
-yarn test
-```
-
-You can also use `yarn watch` to watch all of the files and re-run tests as
-needed.
-
-## Webserver
-Using `yarn serve` will start a webserver on localhost:5000. If you load the
-`tests/index.html` file this will load the SVA files into browser.
-
-## Command Line
-There is a simple assembler binary with can be executed from the command line.
-
-```shell
-yarn sva tests/simple.spv_asm
-```
-
-The above will generate a `o.sva` file in the current directory.
-
-## Update spirv.data.json
-
-If there is a new spirv-headers release update the externals folder checkout
-and then:
-
-```shell
-./tools/process_grammar.rb > src/spirv.data.json
-```
diff --git a/tools/sva/bin/sva.js b/tools/sva/bin/sva.js
deleted file mode 100755
index e2448d6..0000000
--- a/tools/sva/bin/sva.js
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env node
-//
-// Copyright 2019 Google LLC
-//
-// 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.
-
-"use strict";
-
-const fs = require("fs");
-
-import SVA from "../src/sva.js";
-
-let input = fs.readFileSync(process.argv[2], "utf-8");
-let u = SVA.assemble(input);
-
-if (typeof u === "string") {
-  console.log(u);
-} else {
-  fs.writeFileSync("o.sva", new Buffer(u.buffer), (err) => {
-    console.log(["ERROR", err]);
-  });
-}
diff --git a/tools/sva/mocha.opts b/tools/sva/mocha.opts
deleted file mode 100644
index 4a52320..0000000
--- a/tools/sva/mocha.opts
+++ /dev/null
@@ -1 +0,0 @@
---recursive
diff --git a/tools/sva/package.json b/tools/sva/package.json
deleted file mode 100644
index cd6ea67..0000000
--- a/tools/sva/package.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
-  "name": "sva",
-  "version": "0.1.0",
-  "description": "SPIR-V Assembler",
-  "main": "index.js",
-  "author": "dan sinclair <dsinclair@google.com>",
-  "license": "Apache-2.0",
-  "private": true,
-  "scripts": {
-    "sva": "node -r esm bin/sva.js",
-    "lint": "eslint --fix --ext .js .",
-    "test": "mocha --require esm src/**/*_test.js",
-    "watch": "mocha --require esm --watch --watch-extension js \"src/**/*_test.js\"",
-    "serve": "serve",
-    "bundle": "rollup -c"
-  },
-  "devDependencies": {
-    "chai": "^4.3.7",
-    "eslint": "^8.41.0",
-    "esm": "^3.2.25",
-    "mocha": "^10.2.0",
-    "rollup": "^3.29.5",
-    "serve": "^14.2.0"
-  }
-}
diff --git a/tools/sva/rollup.config.js b/tools/sva/rollup.config.js
deleted file mode 100644
index 2056e16..0000000
--- a/tools/sva/rollup.config.js
+++ /dev/null
@@ -1,7 +0,0 @@
-export default {
-  input: 'src/sva.js',
-  output: {
-    file: 'build/sva.js',
-    format: 'esm',
-  }
-}
diff --git a/tools/sva/src/assembler.js b/tools/sva/src/assembler.js
deleted file mode 100644
index 7bc208e..0000000
--- a/tools/sva/src/assembler.js
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2019 Google LLC
-//
-// 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.
-
-export default class Assembler {
-  static get GENERATOR_ID() { return 0; }
-
-  /**
-   * @param {AST} the AST to build the SPIR-V from
-   */
-  constructor(ast) {
-    this.ast_ = ast;
-  }
-
-  /**
-   * Assembles the AST into binary SPIR-V.
-   * @return {Uint32Array} The SPIR-V binary data.
-   */
-  assemble() {
-    let total_size = 5;
-    for (const inst of this.ast_.instructions()) {
-      total_size += 1;
-      for (const op of inst.operands()) {
-        total_size += op.length();
-      }
-    }
-
-    let u = new Uint32Array(total_size);
-    u[0] = 0x07230203;  // Magic
-    u[1] = 0x00010500;  // Version 1.5
-    u[2] = Assembler.GENERATOR_ID;  // Generator magic number
-    u[3] = this.ast_.getIdBounds();  // ID bounds
-    u[4] = 0;  // Reserved
-
-    let idx = 5;
-    for (const inst of this.ast_.instructions()) {
-      let op_size = 1;
-      for (const op of inst.operands()) {
-        op_size += op.length();
-      }
-
-      u[idx++] = op_size << 16 | inst.opcode();
-      for (const op of inst.operands()) {
-        idx = this.processOp(u, idx, op);
-      }
-    }
-
-    return u;
-  }
-
-  processOp(u, idx, op) {
-    if (op.type() === "string") {
-      let len = 0;
-      let v = 0;
-      for (const ch of op.value()) {
-        v = v | (ch.charCodeAt(0) << (len * 8));
-        len += 1;
-
-        if (len === 4) {
-          u[idx++] = v;
-          len = 0;
-          v = 0;
-        }
-      }
-      // Make sure either the terminating 0 byte is written or the last
-      // partial word is written.
-      u[idx++] = v;
-
-    } else if (op.type() === "float") {
-      // TODO(dsinclair): Handle 64 bit floats ...
-      let b = new ArrayBuffer(4);
-      let f = new Float32Array(b);
-      f[0] = op.value();
-
-      let u2 = new Uint32Array(b);
-
-      u[idx++] = u2[0];
-    } else {
-      u[idx++] = op.value();
-    }
-
-    for (const param of op.params()) {
-      idx = this.processOp(u, idx, param);
-    }
-
-    return idx;
-  }
-}
diff --git a/tools/sva/src/assembler_test.js b/tools/sva/src/assembler_test.js
deleted file mode 100644
index a23d211..0000000
--- a/tools/sva/src/assembler_test.js
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright 2019 Google LLC
-//
-// 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.
-
-import { assert } from "chai";
-import Lexer from "./lexer";
-import Parser from "./parser";
-import grammar from "./spirv.data.js";
-import Assembler from "./assembler";
-
-describe("assembler", () => {
-  it("generates SPIR-V magic number", () => {
-    let input = `; SPIR-V
-; Version: 1.0
-; Generator: Khronos Glslang Reference Front End; 7
-; Bound: 6
-; Schema: 0
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %main "main"
-               OpExecutionMode %main OriginUpperLeft
-               OpSource GLSL 440
-               OpName %main "main"
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd`;
-    let l = new Lexer(input);
-    let p = new Parser(grammar, l);
-
-    let ast = p.parse();
-    assert.exists(ast, p.error);
-
-    let a = new Assembler(ast);
-    let res = a.assemble();
-    assert.equal(res[0], 0x07230203);
-  });
-
-  it("assembles enumerant params", () => {
-    let input = "OpExecutionMode %main LocalSize 2 3 4";
-
-    let l = new Lexer(input);
-    let p = new Parser(grammar, l);
-
-    let ast = p.parse();
-    assert.exists(ast, p.error);
-
-    let a = new Assembler(ast);
-    let res = a.assemble();
-
-    assert.lengthOf(res, 11);
-    assert.equal(res[5], (6 /* word count */ << 16) | 16 /* opcode */);
-    assert.equal(res[6], 1 /* %main */);
-    assert.equal(res[7], 17 /* LocalSize */);
-    assert.equal(res[8], 2);
-    assert.equal(res[9], 3);
-    assert.equal(res[10], 4);
-  });
-
-  it("assembles float 32 values", () => {
-    let input = `%float = OpTypeFloat 32
-                 %float1 = OpConstant %float 0.400000006`;
-    let l = new Lexer(input);
-    let p = new Parser(grammar, l);
-
-    let ast = p.parse();
-    assert.exists(ast, p.error);
-
-    let a = new Assembler(ast);
-    let res = a.assemble();
-
-    assert.lengthOf(res, 12);
-    assert.equal(res[8], (4 /* word count */ << 16) | 43 /* opcode */);
-    assert.equal(res[9], 1 /* %float */);
-    assert.equal(res[10], 2 /* %float */);
-    assert.equal(res[11], 0x3ecccccd /* 0.400000006 */);
-  });
-
-  describe("strings", () => {
-    it("assembles 'abcd'", () => {
-      let input = `OpName %mains "abcd"`;
-      let l = new Lexer(input);
-      let p = new Parser(grammar, l);
-
-      let ast = p.parse();
-      assert.exists(ast, p.error);
-
-      let a = new Assembler(ast);
-      let res = a.assemble();
-
-      assert.lengthOf(res, 9);
-      assert.equal(res[5], (4 /* word count */ << 16) | 5 /* opcode */);
-      assert.equal(res[6], 1 /* %mains */);
-      assert.equal(res[7], 0x64636261 /* food */);
-      assert.equal(res[8], 0x00000000 /* null byte */);
-    });
-
-    it("assembles 'abcde'", () => {
-      let input = `OpName %mains "abcde"`;
-      let l = new Lexer(input);
-      let p = new Parser(grammar, l);
-
-      let ast = p.parse();
-      assert.exists(ast, p.error);
-
-      let a = new Assembler(ast);
-      let res = a.assemble();
-
-      assert.lengthOf(res, 9);
-      assert.equal(res[5], (4 /* word count */ << 16) | 5 /* opcode */);
-      assert.equal(res[6], 1 /* %mains */);
-      assert.equal(res[7], 0x64636261 /* abcd */);
-      assert.equal(res[8], 0x00000065 /* e */);
-    });
-
-    it("assembles 'abcdef'", () => {
-      let input = `OpName %mains "abcdef"`;
-      let l = new Lexer(input);
-      let p = new Parser(grammar, l);
-
-      let ast = p.parse();
-      assert.exists(ast, p.error);
-
-      let a = new Assembler(ast);
-      let res = a.assemble();
-
-      assert.lengthOf(res, 9);
-      assert.equal(res[5], (4 /* word count */ << 16) | 5 /* opcode */);
-      assert.equal(res[6], 1 /* %mains */);
-      assert.equal(res[7], 0x64636261 /* abcd */);
-      assert.equal(res[8], 0x00006665 /* ef */);
-    });
-
-    it("assembles 'abcdefg'", () => {
-      let input = `OpName %mains "abcdefg"`;
-      let l = new Lexer(input);
-      let p = new Parser(grammar, l);
-
-      let ast = p.parse();
-      assert.exists(ast, p.error);
-
-      let a = new Assembler(ast);
-      let res = a.assemble();
-
-      assert.lengthOf(res, 9);
-      assert.equal(res[5], (4 /* word count */ << 16) | 5 /* opcode */);
-      assert.equal(res[6], 1 /* %mains */);
-      assert.equal(res[7], 0x64636261 /* abcd */);
-      assert.equal(res[8], 0x00676665 /* efg */);
-    });
-  });
-});
diff --git a/tools/sva/src/ast.js b/tools/sva/src/ast.js
deleted file mode 100644
index d396d2f..0000000
--- a/tools/sva/src/ast.js
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright 2019 Google LLC
-//
-// 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.
-
-class Module {
-  constructor() {
-    this.instructions_ = [];
-    this.next_id_ = 1;
-
-    /**
-     * Maps {string, hash} where the string is the type name and the hash is:
-     *   type- 'float' or 'int'
-     *   width- number of bits needed to store number
-     *   signed- the sign of the number
-     */
-    this.types_ = {};
-
-    /**
-     * Maps {string, number} where the string is the type name and the number is
-     * the id value.
-     */
-    this.assigned_ids_ = {};
-  }
-
-  instructions() { return this.instructions_; }
-
-  instruction(val) { return this.instructions_[val]; }
-
-  addInstruction(inst) {
-    this.instructions_.push(inst);
-
-    // Record type information
-    if (inst.name() === "OpTypeInt" || inst.name() === "OpTypeFloat") {
-      let is_int = inst.name() === "OpTypeInt";
-
-      this.types_[inst.operand(0).name()] = {
-        type: is_int ? "int" : "float",
-        width: inst.operand(1).value(),
-        signed: is_int ? inst.operand(2).value() : 1
-      };
-    }
-
-    // Record operand result id's
-    inst.operands().forEach((op) => {
-      if (op.rawValue() !== undefined && op.type() === "result_id") {
-        this.next_id_ = Math.max(this.next_id_, op.rawValue() + 1);
-      }
-    });
-  }
-
-  getType(name) { return this.types_[name]; }
-
-  getId(name) {
-    if (this.assigned_ids_[name] !== undefined) {
-      return this.assigned_ids_[name];
-    }
-
-    let next = this.next_id_;
-    this.assigned_ids_[name] = next;
-
-    this.next_id_ += 1;
-    return next;
-  }
-
-  getIdBounds() { return this.next_id_; }
-}
-
-class Instruction {
-  constructor(name, opcode, operands) {
-    this.name_ = name;
-    this.opcode_ = opcode;
-    this.operands_ = operands;
-  }
-
-  name() { return this.name_; }
-
-  opcode() { return this.opcode_; }
-
-  operands() { return this.operands_; }
-
-  operand(val) { return this.operands_[val]; }
-}
-
-class Operand {
-  constructor(mod, name, type, value, params) {
-    this.module_ = mod;
-    this.name_ = name;
-    this.type_ = type;
-    this.value_ = value;
-    this.params_ = params;
-  }
-
-  name() { return this.name_; }
-
-  length() {
-    // Get the value just to force it to be filled.
-    this.value();
-
-    if (this.type_ === "string") {
-      return Math.ceil((this.value_.length + 1) / 4);
-    }
-
-    let size = 1;
-    for (const param of this.params_) {
-      size += param.length();
-    }
-    return size;
-  }
-
-  type() { return this.type_; }
-
-  rawValue() { return this.value_; }
-
-  // This method should only be called on ResultId's after the full parse is
-  // complete. This is because the AST will only have the maximum seen numeric
-  // ResultId when the parse is done.
-  value() {
-    if (this.value_ === undefined) {
-      this.value_ = this.module_.getId(this.name_);
-    }
-    return this.value_;
-  }
-
-  params() { return this.params_; }
-}
-
-export {
-  Module,
-  Instruction,
-  Operand
-};
diff --git a/tools/sva/src/lexer.js b/tools/sva/src/lexer.js
deleted file mode 100644
index b39f93a..0000000
--- a/tools/sva/src/lexer.js
+++ /dev/null
@@ -1,363 +0,0 @@
-// Copyright 2019 Google LLC
-//
-// 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.
-
-import { Token, TokenType } from "./token.js";
-
-export default class Lexer {
-  /**
-   * @param {String} input The input string to tokenize.
-   */
-  constructor(input) {
-    this.input_ = input;
-    this.len_ = input.length;
-    this.cur_pos_ = 0;
-    this.cur_line_ = 1;
-
-    this.num_regex_ = /^[0-9]+$/;
-    this.alpha_regex_ = /^[a-zA-Z_]+$/;
-    this.op_regex_ = /^Op[A-Z][^\s]*$/;
-    this.hex_regex_ = /^[0-9a-fA-F]$/;
-  }
-
-  /**
-   * Parses the next token from the input stream.
-   * @return {Token} the next token.
-   */
-  next() {
-    this.skipWhitespace();
-    this.skipComments();
-
-    if (this.cur_pos_ >= this.len_)
-      return new Token(TokenType.kEOF, this.cur_line_);
-
-    let n = this.tryHexInteger();
-    if (n !== undefined)
-      return n;
-
-    n = this.tryFloat();
-    if (n !== undefined)
-      return n;
-
-    n = this.tryInteger();
-    if (n !== undefined)
-      return n;
-
-    n = this.tryString();
-    if (n !== undefined)
-      return n;
-
-    n = this.tryOp();
-    if (n !== undefined)
-      return n;
-
-    n = this.tryPunctuation();
-    if (n !== undefined)
-      return n;
-
-    n = this.tryResultId();
-    if (n !== undefined)
-      return n;
-
-    n = this.tryIdent();
-    if (n !== undefined)
-      return n;
-
-    return new Token(TokenType.kError, this.cur_line_, "Failed to match token");
-  }
-
-  is(str) {
-    if (this.len_ <= this.cur_pos_ + (str.length - 1))
-      return false;
-
-    for (let i = 0; i < str.length; ++i) {
-      if (this.input_[this.cur_pos_ + i] !== str[i])
-        return false;
-    }
-
-    return true;
-  }
-
-  isNum(ch) {
-    return ch.match(this.num_regex_);
-  }
-
-  isAlpha(ch) {
-    return ch.match(this.alpha_regex_);
-  }
-
-  isAlphaNum(ch) {
-    return this.isNum(ch) || this.isAlpha(ch);
-  }
-
-  isHex(char) {
-    return char.match(this.hex_regex_);
-  }
-
-  isCurWhitespace() {
-    return this.is(" ") || this.is("\t") || this.is("\r") || this.is("\n");
-  }
-
-  skipWhitespace() {
-    for(;;) {
-      let cur_pos = this.cur_pos_;
-      while (this.cur_pos_ < this.len_ &&
-          this.isCurWhitespace()) {
-        if (this.is("\n"))
-          this.cur_line_ += 1;
-
-        this.cur_pos_ += 1;
-      }
-
-      this.skipComments();
-
-      // Cursor didn't move so no whitespace matched.
-      if (cur_pos === this.cur_pos_)
-        break;
-    }
-  }
-
-  skipComments() {
-    if (!this.is(";"))
-      return;
-
-    while (this.cur_pos_ < this.len_ && !this.is("\n"))
-      this.cur_pos_ += 1;
-  }
-
-  /**
-   * Attempt to parse the next part of the input as a float.
-   * @return {Token|undefined} returns a Token if a float is matched,
-   *                           undefined otherwise.
-   */
-  tryFloat() {
-    let start = this.cur_pos_;
-    let end = start;
-
-    if (this.cur_pos_ >= this.len_)
-      return undefined;
-    if (this.input_[end] === "-")
-      end += 1;
-
-    while (end < this.len_ && this.isNum(this.input_[end]))
-      end += 1;
-
-    // Must have a "." in a float
-    if (end >= this.len_ || this.input_[end] !== ".")
-      return undefined;
-
-    end += 1;
-    while (end < this.len_ && this.isNum(this.input_[end]))
-      end += 1;
-
-    let substr = this.input_.substr(start, end - start);
-    if (substr === "." || substr === "-.")
-      return undefined;
-
-    this.cur_pos_ = end;
-
-    return new Token(TokenType.kFloatLiteral, this.cur_line_, parseFloat(substr));
-  }
-
-  /**
-   * Attempt to parse a hex encoded integer.
-   * @return {Token|undefined} returns a Token if a Hex number is matched,
-   *                           undefined otherwise.
-   */
-  tryHexInteger() {
-    let start = this.cur_pos_;
-    let end = start;
-
-    if (this.cur_pos_ >= this.len_)
-      return undefined;
-    if (end + 2 >= this.len_ || this.input_[end] !== "0" ||
-        this.input_[end + 1] !== "x") {
-      return undefined;
-    }
-
-    end += 2;
-
-    while (end < this.len_ && this.isHex(this.input_[end]))
-      end += 1;
-
-    this.cur_pos_ = end;
-
-    let val = parseInt(this.input_.substr(start, end - start), 16);
-    return new Token(TokenType.kIntegerLiteral, this.cur_line_, val);
-  }
-
-  /**
-   * Attempt to parse an encoded integer.
-   * @return {Token|undefined} returns a Token if a number is matched,
-   *                           undefined otherwise.
-   */
-  tryInteger() {
-    let start = this.cur_pos_;
-    let end = start;
-
-    if (this.cur_pos_ >= this.len_)
-      return undefined;
-    if (this.input_[end] === "-")
-      end += 1;
-
-    if (end >= this.len_ || !this.isNum(this.input_[end]))
-      return undefined;
-
-    while (end < this.len_ && this.isNum(this.input_[end]))
-      end += 1;
-
-    this.cur_pos_ = end;
-
-    let val = parseInt(this.input_.substr(start, end - start), 10);
-    return new Token(TokenType.kIntegerLiteral, this.cur_line_, val);
-  }
-
-  /**
-   * Attempt to parse a result id.
-   * @return {Token|undefined} returns a Token if a result id is matched,
-   *                           undefined otherwise.
-   */
-  tryResultId() {
-    let start = this.cur_pos_;
-    if (start >= this.len_)
-      return undefined;
-    if (!this.is("%"))
-      return undefined;
-
-    start += 1;
-    this.cur_pos_ += 1;
-    while (this.cur_pos_ < this.len_ &&
-        (this.isAlphaNum(this.input_[this.cur_pos_]) || this.is("_"))) {
-      this.cur_pos_ += 1;
-    }
-
-    let ident = this.input_.substr(start, this.cur_pos_ - start);
-    let value = undefined;
-    if (ident.match(this.num_regex_))
-      value = parseInt(ident, 10);
-
-    return new Token(TokenType.kResultId, this.cur_line_, {
-      name: ident,
-      val: value
-    });
-  }
-
-  /**
-   * Attempt to parse an identifier.
-   * @return {Token|undefined} returns a Token if an identifier is matched,
-   *                           undefined otherwise.
-   */
-  tryIdent() {
-    let start = this.cur_pos_;
-    if (start >= this.len_)
-      return undefined;
-
-    while (this.cur_pos_ < this.len_ &&
-        (this.isAlphaNum(this.input_[this.cur_pos_]) || this.is("_"))) {
-      this.cur_pos_ += 1;
-    }
-
-    let ident = this.input_.substr(start, this.cur_pos_ - start);
-    return new Token(TokenType.kIdentifier, this.cur_line_, ident);
-  }
-
-  /**
-   * Attempt to parse an Op command.
-   * @return {Token|undefined} returns a Token if an Op command is matched,
-   *                           undefined otherwise.
-   */
-  tryOp() {
-    let start = this.cur_pos_;
-    if (this.cur_pos_ >= this.len_ || (this.cur_pos_ + 1 >= this.len_))
-      return undefined;
-
-    if (this.input_[this.cur_pos_] !== "O" ||
-        this.input_[this.cur_pos_ + 1] !== "p") {
-      return undefined;
-    }
-
-    while (this.cur_pos_ < this.len_ &&
-        !this.isCurWhitespace()) {
-      this.cur_pos_ += 1;
-    }
-
-    return new Token(TokenType.kOp, this.cur_line_, {
-      name: this.input_.substr(start, this.cur_pos_ - start)
-    });
-  }
-
-  /**
-   * Attempts to match punctuation strings against the input
-   * @return {Token|undefined} Returns the Token for the punctuation or
-   *                           undefined if no matches found.
-   */
-  tryPunctuation() {
-    let type = undefined;
-    if (this.is("="))
-      type = TokenType.kEqual;
-    else if (this.is("|"))
-      type = TokenType.kPipe;
-
-    if (type === undefined)
-      return undefined;
-
-    this.cur_pos_ += type.length;
-    return new Token(type, this.cur_line_, type);
-  }
-
-  /**
-   * Attempts to match strings against the input
-   * @return {Token|undefined} Returns the Token for the string or undefined
-   *                           if no match found.
-   */
-  tryString() {
-    let start = this.cur_pos_;
-
-    // Must have at least 2 chars for a string.
-    if (this.cur_pos_ >= this.len_ || (this.cur_pos_ + 1 >= this.len_))
-      return undefined;
-    if (!this.is("\""))
-      return undefined;
-
-    this.cur_pos_ += 1;
-    let str = "";
-    while (this.cur_pos_ <= this.len_) {
-      if (this.is("\""))
-        break;
-
-      if (this.is("\\")) {
-        this.cur_pos_ += 1;
-        if (this.cur_pos_ >= this.len_)
-          return undefined;
-
-        if (this.is("\\")) {
-          str += "\\";
-        } else if (this.is("\"")) {
-          str += '"';
-        } else {
-          str += this.input_[this.cur_pos_];
-        }
-      } else {
-        str += this.input_[this.cur_pos_];
-      }
-      this.cur_pos_ += 1;
-    }
-
-    if (this.cur_pos_ >= this.len_)
-      return undefined;
-
-    this.cur_pos_ += 1;
-
-    return new Token(TokenType.kStringLiteral, this.cur_line_, str);
-  }
-}
diff --git a/tools/sva/src/lexer_test.js b/tools/sva/src/lexer_test.js
deleted file mode 100644
index 32b24c7..0000000
--- a/tools/sva/src/lexer_test.js
+++ /dev/null
@@ -1,191 +0,0 @@
-// Copyright 2019 Google LLC
-//
-// 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.
-
-import { assert } from "chai";
-import Lexer from "./lexer";
-import { TokenType } from "./token";
-
-describe("lexer", () => {
-  describe("skipped content", () => {
-    it("skips whitespace", () => {
-      let input = " \t\r\n\t  \tOpKill\t\n\t  \r  ";
-      let l = new Lexer(input);
-
-      let t = l.next();
-      assert.equal(t.type, TokenType.kOp);
-      assert.equal(t.line, 2);
-      assert.equal(t.data.name, "OpKill");
-
-      t = l.next();
-      assert.equal(t.type, TokenType.kEOF);
-      assert.equal(t.line, 3);
-    });
-
-    it("skips ; comments", () => {
-      let input = `; start with comment
-OpKill ; end of line comment
-; another comment
-%1`;
-
-      let l = new Lexer(input);
-      let t = l.next();
-      assert.equal(t.type, TokenType.kOp);
-      assert.equal(t.data.name, "OpKill");
-      assert.equal(t.line, 2);
-
-      t = l.next();
-      assert.equal(t.type, TokenType.kResultId);
-      assert.equal(t.data.name, "1");
-      assert.equal(t.data.val, 1);
-      assert.equal(t.line, 4);
-    });
-  });
-
-  describe("numerics", () => {
-    it("parses floats", () => {
-      let input = ["0.0", "0.", ".0", "5.7", "5.", ".7", "-0.0", "-.0",
-        "-0.", "-5.7", "-5.", "-.7"];
-
-      let results = [0.0, 0.0, 0.0, 5.7, 5.0, 0.7, 0.0, 0.0, 0.0, -5.7, -5.0,
-        -0.7];
-      input.forEach((val, idx) => {
-        let l = new Lexer(val);
-        let t = l.next();
-
-        assert.equal(t.type, TokenType.kFloatLiteral,
-          `expected ${val} to be a float got ${t.type}`);
-        assert.equal(t.data, results[idx],
-          `expected ${results[idx]} === ${t.data}`);
-
-        t = l.next();
-        assert.equal(t.type, TokenType.kEOF);
-        assert.equal(t.data, undefined);
-      });
-    });
-
-    it("handles invalid floats", () => {
-      let input = [".", "-."];
-      input.forEach((val) => {
-        let l = new Lexer(val);
-        let t = l.next();
-
-        assert.notEqual(t.type, TokenType.kFloatLiteral,
-          `expect ${val} to not match type float`);
-      });
-    });
-
-    it("parses integers", () => {
-      let input = ["0", "-0", "123", "-123", "2147483647", "-2147483648",
-        "4294967295", "0x00", "0x24"];
-      let results = [0, 0, 123, -123,2147483647, -2147483648, 4294967295,
-        0x0, 0x24];
-
-      input.forEach((val, idx) => {
-        let l = new Lexer(val);
-        let t = l.next();
-
-        assert.equal(t.type, TokenType.kIntegerLiteral,
-          `expected ${val} to be an integer got ${t.type}`);
-        assert.equal(t.data, results[idx],
-          `expected ${results[idx]} === ${t.data}`);
-
-        t = l.next();
-        assert.equal(t.type, TokenType.kEOF);
-        assert.equal(t.data, undefined);
-      });
-    });
-  });
-
-  it("matches result_ids", () => {
-    let input = `%123
-%001
-%main
-%_a_b_c`;
-
-    let result = [
-      {name: "123", val: 123},
-      {name: "001", val: 1},
-      {name: "main", val: undefined},
-      {name: "_a_b_c", val: undefined}
-    ];
-
-    let l = new Lexer(input);
-    for (let i = 0; i < result.length; ++i) {
-      let t = l.next();
-      assert.equal(t.type, TokenType.kResultId);
-      assert.equal(t.data.name, result[i].name);
-      assert.equal(t.data.val, result[i].val);
-    }
-  });
-
-  it("matches punctuation", () => {
-    let input = "=";
-    let results = [TokenType.kEqual];
-
-    let l = new Lexer(input);
-    for (let i = 0; i < results.length; ++i) {
-      let t = l.next();
-      assert.equal(t.type, results[i]);
-      assert.equal(t.line, i + 1);
-    }
-
-    let t = l.next();
-    assert.equal(t.type, TokenType.kEOF);
-  });
-
-  describe("strings", () => {
-    it("matches strings", () => {
-      let input = "\"GLSL.std.450\"";
-
-      let l = new Lexer(input);
-      let t = l.next();
-      assert.equal(t.type, TokenType.kStringLiteral);
-      assert.equal(t.data, "GLSL.std.450");
-    });
-
-    it("handles unfinished strings", () => {
-      let input = "\"GLSL.std.450";
-
-      let l = new Lexer(input);
-      let t = l.next();
-      assert.equal(t.type, TokenType.kError);
-    });
-
-    it("handles escapes", () => {
-      let input = `"embedded\\"quote"
-"embedded\\\\slash"
-"embedded\\nchar"`;
-      let results = [`embedded\"quote`, `embedded\\slash`, `embeddednchar`];
-
-      let l = new Lexer(input);
-      for (let i = 0; i < results.length; ++i) {
-        let t = l.next();
-        assert.equal(t.type, TokenType.kStringLiteral, results[i]);
-        assert.equal(t.data, results[i]);
-      }
-    });
-  });
-
-  it("matches keywords", () => {
-    let input = "GLSL Function";
-    let results = ["GLSL", "Function"];
-
-    let l = new Lexer(input);
-    for (let i = 0; i < results.length; ++i) {
-      let t = l.next();
-      assert.equal(t.type, TokenType.kIdentifier, results[i]);
-      assert.equal(t.data, results[i]);
-    }
-  });
-});
diff --git a/tools/sva/src/parser.js b/tools/sva/src/parser.js
deleted file mode 100644
index ccf872a..0000000
--- a/tools/sva/src/parser.js
+++ /dev/null
@@ -1,283 +0,0 @@
-// Copyright 2019 Google LLC
-//
-// 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.
-
-import { TokenType } from "./token.js";
-import * as AST from "./ast.js";
-
-export default class Parser {
-  /**
-   * @param {Hash} The SPIR-V grammar
-   * @param {Lexer} The lexer
-   * @return {AST} Attempts to build an AST from the tokens returned by the
-   *               given lexer
-   */
-  constructor(grammar, lexer) {
-    this.grammar_ = grammar;
-    this.lexer_ = lexer;
-
-    this.peek_ = [];
-    this.error_ = "";
-  }
-
-  get error() { return this.error_; }
-
-  next() {
-    return this.peek_.shift() || this.lexer_.next();
-  }
-
-  peek(idx) {
-    while (this.peek_.length <= idx) {
-      this.peek_.push(this.lexer_.next());
-    }
-    return this.peek_[idx];
-  }
-
-  /**
-   * Executes the parser.
-   *
-   * @return {AST|undefined} returns a parsed AST on success or undefined
-   *                         on error. The error message can be retrieved by
-   *                         calling error().
-   */
-  parse() {
-    let ast = new AST.Module();
-    for(;;) {
-      let token = this.next();
-      if (token === TokenType.kError) {
-        this.error_ = token.line() + ": " + token.data();
-        return undefined;
-      }
-      if (token.type === TokenType.kEOF)
-        break;
-
-      let result_id = undefined;
-      if (token.type === TokenType.kResultId) {
-        result_id = token;
-
-        token = this.next();
-        if (token.type !== TokenType.kEqual) {
-          this.error_ = token.line + ": expected = after result id";
-          return undefined;
-        }
-
-        token = this.next();
-      }
-
-      if (token.type !== TokenType.kOp) {
-        this.error_ = token.line + ": expected Op got " + token.type;
-        return undefined;
-      }
-
-      let name = token.data.name;
-      let data = this.getInstructionData(name);
-      let operands = [];
-      let result_type = undefined;
-
-      for (let operand of data.operands) {
-        if (operand.kind === "IdResult") {
-          if (result_id === undefined) {
-            this.error_ = token.line + ": expected result id";
-            return undefined;
-          }
-          let o = new AST.Operand(ast, result_id.data.name, "result_id",
-            result_id.data.val, []);
-          if (o === undefined) {
-            return undefined;
-          }
-          operands.push(o);
-        } else {
-          if (operand.quantifier === "?") {
-            if (this.nextIsNewInstr()) {
-              break;
-            }
-          } else if (operand.quantifier === "*") {
-            while (!this.nextIsNewInstr()) {
-              let o = this.extractOperand(ast, result_type, operand);
-              if (o === undefined) {
-                return undefined;
-              }
-              operands.push(o);
-            }
-            break;
-          }
-
-          let o = this.extractOperand(ast, result_type, operand);
-          if (o === undefined) {
-            return undefined;
-          }
-
-          // Store the result type away so we can use it for context dependent
-          // numbers if needed.
-          if (operand.kind === "IdResultType") {
-            result_type = ast.getType(o.name());
-          }
-
-          operands.push(o);
-        }
-      }
-
-      // Verify only GLSL extended instructions are used
-      if (name === "OpExtInstImport" && operands[1].value() !== "GLSL.std.450") {
-        this.error_ = token.line + ": Only GLSL.std.450 external instructions supported";
-        return undefined;
-      }
-
-      let inst = new AST.Instruction(name, data.opcode, operands);
-
-      ast.addInstruction(inst);
-    }
-    return ast;
-  }
-
-  getInstructionData(name) {
-    return this.grammar_["instructions"][name];
-  }
-
-  nextIsNewInstr() {
-    let n0 = this.peek(0);
-    if (n0.type === TokenType.kOp || n0.type === TokenType.kEOF) {
-      return true;
-    }
-
-    let n1 = this.peek(1);
-    if (n1.type === TokenType.kEOF) {
-      return false;
-    }
-    if (n0.type === TokenType.kResultId && n1.type === TokenType.kEqual)
-      return true;
-
-    return false;
-  }
-
-  extractOperand(ast, result_type, data) {
-    let t = this.next();
-
-    let name = undefined;
-    let kind = undefined;
-    let value = undefined;
-    let params = [];
-
-    // TODO(dsinclair): There are a bunch of missing types here. See
-    // https://github.com/KhronosGroup/SPIRV-Tools/blob/master/source/text.cpp#L210
-    //
-    // LiteralSpecConstantOpInteger
-    // PairLiteralIntegerIdRef
-    // PairIdRefLiteralInteger
-    // PairIdRefIdRef
-    if (data.kind === "IdResult" || data.kind === "IdRef"
-        || data.kind === "IdResultType" || data.kind === "IdScope"
-        || data.kind === "IdMemorySemantics") {
-      if (t.type !== TokenType.kResultId) {
-        this.error_ = t.line + ": expected result id";
-        return undefined;
-      }
-
-      name = t.data.name;
-      kind = "result_id";
-      value = t.data.val;
-    } else if (data.kind === "LiteralString") {
-      if (t.type !== TokenType.kStringLiteral) {
-        this.error_ = t.line + ": expected string not found";
-        return undefined;
-      }
-
-      name = t.data;
-      kind = "string";
-      value = t.data;
-    } else if (data.kind === "LiteralInteger") {
-      if (t.type !== TokenType.kIntegerLiteral) {
-        this.error_ = t.line + ": expected integer not found";
-        return undefined;
-      }
-
-      name = "" + t.data;
-      kind = t.type;
-      value = t.data;
-    } else if (data.kind === "LiteralContextDependentNumber") {
-      if (result_type === undefined) {
-        this.error_ = t.line +
-            ": missing result type for context dependent number";
-        return undefined;
-      }
-      if (t.type !== TokenType.kIntegerLiteral
-          && t.type !== TokenType.kFloatLiteral) {
-        this.error_ = t.line + ": expected number not found";
-        return undefined;
-      }
-
-      name = "" + t.data;
-      kind = result_type.type;
-      value = t.data;
-
-    } else if (data.kind === "LiteralExtInstInteger") {
-      if (t.type !== TokenType.kIdentifier) {
-        this.error_ = t.line + ": expected instruction identifier";
-        return undefined;
-      }
-
-      if (this.grammar_.ext[t.data] === undefined) {
-        this.error_ = t.line + `: unable to find extended instruction (${t.data})`;
-        return undefined;
-      }
-
-      name = t.data;
-      kind = "integer";
-      value = this.grammar_.ext[t.data];
-
-    } else {
-      let d = this.grammar_.operand_kinds[data.kind];
-      if (d === undefined) {
-        this.error_ = t.line + ": expected " + data.kind + " not found";
-        return undefined;
-      }
-
-      let val = d.values[t.data]["value"];
-      let names = [t.data];
-      if (d.type === "BitEnum") {
-        for(;;) {
-          let tmp = this.peek(0);
-          if (tmp.type !== TokenType.kPipe) {
-            break;
-          }
-
-          this.next();  // skip pipe
-          tmp = this.next();
-
-          if (tmp.type !== TokenType.kIdentifier) {
-            this.error_ = tmp.line() + ": expected identifier";
-            return undefined;
-          }
-
-          val |= d.values[tmp.data]["value"];
-          names.push(tmp.data);
-        }
-      }
-
-      name = names.join("|");
-      kind = d.type;
-      value = val;
-
-      for (const op_name of names) {
-        if (d.values[op_name]['params'] === undefined) {
-          continue;
-        }
-
-        for (const param of d.values[op_name]["params"]) {
-          params.push(this.extractOperand(ast, result_type, { kind: param }));
-        }
-      }
-    }
-    return new AST.Operand(ast, name, kind, value, params);
-  }
-}
diff --git a/tools/sva/src/parser_test.js b/tools/sva/src/parser_test.js
deleted file mode 100644
index dffc0b3..0000000
--- a/tools/sva/src/parser_test.js
+++ /dev/null
@@ -1,489 +0,0 @@
-// Copyright 2019 Google LLC
-//
-// 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.
-
-import { assert } from "chai";
-import Lexer from "./lexer";
-import Parser from "./parser";
-import grammar from "./spirv.data.js";
-
-describe("parser", () => {
-  it("parses an opcode", () => {
-    let input = "OpKill";
-    let l = new Lexer(input);
-    let p = new Parser(grammar, l);
-
-    let ast = p.parse();
-    assert.exists(ast);
-    assert.lengthOf(ast.instructions(), 1);
-
-    let inst = ast.instruction(0);
-    assert.equal(inst.name(), "OpKill");
-    assert.equal(inst.opcode(), 252);
-    assert.lengthOf(inst.operands, 0);
-  });
-
-  it("parses an opcode with an identifier", () => {
-    let input = "OpCapability Shader";
-    let l = new Lexer(input);
-    let p = new Parser(grammar, l);
-
-    let ast = p.parse();
-    assert.exists(ast, p.error);
-    assert.lengthOf(ast.instructions(), 1);
-
-    let inst = ast.instruction(0);
-    assert.equal(inst.name(), "OpCapability");
-    assert.equal(inst.opcode(), 17);
-    assert.lengthOf(inst.operands(), 1);
-
-    let op = inst.operand(0);
-    assert.equal(op.name(), "Shader");
-    assert.equal(op.type(), "ValueEnum");
-    assert.equal(op.value(), 1);
-  });
-
-  it("parses an opcode with a result", () => {
-    let input = "%void = OpTypeVoid";
-    let l = new Lexer(input);
-    let p = new Parser(grammar, l);
-
-    let ast = p.parse();
-    assert.exists(ast);
-    assert.lengthOf(ast.instructions(), 1);
-
-    let inst = ast.instruction(0);
-    assert.equal(inst.name(), "OpTypeVoid");
-    assert.equal(inst.opcode(), 19);
-    assert.lengthOf(inst.operands(), 1);
-
-    let op = inst.operand(0);
-    assert.equal(op.name(), "void");
-    assert.equal(op.value(), 1);
-  });
-
-  it("sets module bounds based on numeric result", () => {
-    let input = "%3 = OpTypeVoid";
-
-    let l = new Lexer(input);
-    let p = new Parser(grammar, l);
-
-    let ast = p.parse();
-    assert.exists(ast);
-    assert.equal(ast.getId("next"), 4);
-  });
-
-  it("returns the same value for a named result_id", () => {
-    let input = "%3 = OpTypeFunction %int %int";
-
-    let l = new Lexer(input);
-    let p = new Parser(grammar, l);
-
-    let ast = p.parse();
-    assert.exists(ast);
-    assert.lengthOf(ast.instructions(), 1);
-
-    let inst = ast.instruction(0);
-    let op1 = inst.operand(1);
-    assert.equal(op1.name(), "int");
-    assert.equal(op1.value(), 4);
-
-    let op2 = inst.operand(2);
-    assert.equal(op2.name(), "int");
-    assert.equal(op2.value(), 4);
-  });
-
-  it("parses an opcode with a string", () => {
-    let input = "OpEntryPoint Fragment %main \"main\"";
-
-    let l = new Lexer(input);
-    let p = new Parser(grammar, l);
-
-    let ast = p.parse();
-    assert.exists(ast);
-    assert.lengthOf(ast.instructions(), 1);
-
-    let inst = ast.instruction(0);
-    let op = inst.operand(2);
-    assert.equal(op.name(), "main");
-    assert.equal(op.value(), "main");
-  });
-
-  describe("numerics", () => {
-    describe("integers", () => {
-      it("parses an opcode with an integer", () => {
-        let input = "OpSource GLSL 440";
-
-        let l = new Lexer(input);
-        let p = new Parser(grammar, l);
-
-        let ast = p.parse();
-        assert.exists(ast);
-        assert.lengthOf(ast.instructions(), 1);
-
-        let inst = ast.instruction(0);
-        let op0 = inst.operand(0);
-        assert.equal(op0.name(), "GLSL");
-        assert.equal(op0.type(), "ValueEnum");
-        assert.equal(op0.value(), 2);
-
-        let op1 = inst.operand(1);
-        assert.equal(op1.name(), "440");
-        assert.equal(op1.value(), 440);
-      });
-
-      it("parses an opcode with a hex integer", () => {
-        let input = "OpSource GLSL 0x440";
-
-        let l = new Lexer(input);
-        let p = new Parser(grammar, l);
-
-        let ast = p.parse();
-        assert.exists(ast);
-        assert.lengthOf(ast.instructions(), 1);
-
-        let inst = ast.instruction(0);
-        let op0 = inst.operand(0);
-        assert.equal(op0.name(), "GLSL");
-        assert.equal(op0.type(), "ValueEnum");
-        assert.equal(op0.value(), 2);
-
-        let op1 = inst.operand(1);
-        assert.equal(op1.name(), "1088");
-        assert.equal(op1.value(), 0x440);
-      });
-
-      it.skip("parses immediate integers", () => {
-        // TODO(dsinclair): Support or skip?
-      });
-    });
-
-    describe("floats", () => {
-      it("parses floats", () => {
-        let input = `%float = OpTypeFloat 32
-                     %float1 = OpConstant %float 0.400000006`;
-
-        let l = new Lexer(input);
-        let p = new Parser(grammar, l);
-
-        let ast = p.parse();
-        assert.exists(ast, p.error);
-        assert.lengthOf(ast.instructions(), 2);
-
-        let inst = ast.instruction(1);
-        let op2 = inst.operand(2);
-        assert.equal(op2.value(), 0.400000006);
-      });
-
-      // TODO(dsinclair): Make hex encoded floats parse ...
-      it.skip("parses hex floats", () => {
-        let input = `%float = OpTypeFloat 32
-                     %nfloat = OpConstant %float -0.4p+2
-                     %pfloat = OpConstant %float 0.4p-2
-                     %inf = OpConstant %float32 0x1p+128
-                     %neginf = OpConstant %float32 -0x1p+128
-                     %aNaN = OpConstant %float32 0x1.8p+128
-                     %moreNaN = OpConstant %float32 -0x1.0002p+128`;
-
-        let results = [-40.0, .004, 0x00000, 0x00000, 0x7fc00000, 0xff800100];
-        let l = new Lexer(input);
-        let p = new Parser(grammar, l);
-
-        let ast = p.parse();
-        assert.exists(ast, p.error);
-        assert.lengthOf(ast.instructions(), 7);
-
-        for (const idx in results) {
-          let inst = ast.instruction(idx);
-          let op2 = inst.operand(2);
-          assert.equal(op2.value(), results[idx]);
-        }
-      });
-
-      it("parses a float that looks like an int", () => {
-        let input = `%float = OpTypeFloat 32
-                     %float1 = OpConstant %float 1`;
-
-        let l = new Lexer(input);
-        let p = new Parser(grammar, l);
-
-        let ast = p.parse();
-        assert.exists(ast, p.error);
-        assert.lengthOf(ast.instructions(), 2);
-
-        let inst = ast.instruction(1);
-        let op2 = inst.operand(2);
-        assert.equal(op2.value(), 1);
-        assert.equal(op2.type(), "float");
-      });
-    });
-  });
-
-  describe("enums", () => {
-    it("parses enum values", () => {
-      let input = `%1 = OpTypeFloat 32
-  %30 = OpImageSampleExplicitLod %1 %20 %18 Grad|ConstOffset %22 %24 %29`;
-
-      let vals = [{val: 1, name: "1"},
-        {val: 30, name: "30"},
-        {val: 20, name: "20"},
-        {val: 18, name: "18"},
-        {val: 12, name: "Grad|ConstOffset"}];
-
-      let l = new Lexer(input);
-      let p = new Parser(grammar, l);
-
-      let ast = p.parse();
-      assert.exists(ast, p.error);
-      assert.lengthOf(ast.instructions(), 2);
-
-      let inst = ast.instruction(1);
-      for (let idx in vals) {
-        let op = inst.operand(idx);
-        assert.equal(op.name(), vals[idx].name);
-        assert.equal(op.value(), vals[idx].val);
-      }
-
-      // BitEnum
-      let params = inst.operand(4).params();
-      assert.lengthOf(params, 3);
-      assert.equal(params[0].name(), "22");
-      assert.equal(params[0].value(), 22);
-      assert.equal(params[1].name(), "24");
-      assert.equal(params[1].value(), 24);
-      assert.equal(params[2].name(), "29");
-      assert.equal(params[2].value(), 29);
-    });
-
-    it("parses enumerants with parameters", () => {
-      let input ="OpExecutionMode %main LocalSize 2 3 4";
-
-      let l = new Lexer(input);
-      let p = new Parser(grammar, l);
-
-      let ast = p.parse();
-      assert.exists(ast, p.error);
-      assert.lengthOf(ast.instructions(), 1);
-
-      let inst = ast.instruction(0);
-      assert.equal(inst.name(), "OpExecutionMode");
-      assert.lengthOf(inst.operands(), 2);
-      assert.equal(inst.operand(0).name(), "main");
-      assert.equal(inst.operand(1).name(), "LocalSize");
-
-      let params = inst.operand(1).params();
-      assert.lengthOf(params, 3);
-      assert.equal(params[0].name(), "2");
-      assert.equal(params[1].name(), "3");
-      assert.equal(params[2].name(), "4");
-    });
-  });
-
-  it("parses result into second operand if needed", () => {
-    let input = `%int = OpTypeInt 32 1
-                 %int_3 = OpConstant %int 3`;
-    let l = new Lexer(input);
-    let p = new Parser(grammar, l);
-
-    let ast = p.parse();
-    assert.exists(ast);
-    assert.lengthOf(ast.instructions(), 2);
-
-    let inst = ast.instruction(1);
-    assert.equal(inst.name(), "OpConstant");
-    assert.equal(inst.opcode(), 43);
-    assert.lengthOf(inst.operands(), 3);
-
-    let op0 = inst.operand(0);
-    assert.equal(op0.name(), "int");
-    assert.equal(op0.value(), 1);
-
-    let op1 = inst.operand(1);
-    assert.equal(op1.name(), "int_3");
-    assert.equal(op1.value(), 2);
-
-    let op2 = inst.operand(2);
-    assert.equal(op2.name(), "3");
-    assert.equal(op2.value(), 3);
-  });
-
-  describe("quantifiers", () => {
-    describe("?", () => {
-      it("skips if missing", () => {
-        let input = `OpImageWrite %1 %2 %3
-OpKill`;
-        let l = new Lexer(input);
-        let p = new Parser(grammar, l);
-
-        let ast = p.parse();
-        assert.exists(ast);
-        assert.lengthOf(ast.instructions(), 2);
-
-        let inst = ast.instruction(0);
-        assert.equal(inst.name(), "OpImageWrite");
-        assert.lengthOf(inst.operands(), 3);
-      });
-
-      it("skips if missing at EOF", () => {
-        let input = "OpImageWrite %1 %2 %3";
-        let l = new Lexer(input);
-        let p = new Parser(grammar, l);
-
-        let ast = p.parse();
-        assert.exists(ast);
-        assert.lengthOf(ast.instructions(), 1);
-
-        let inst = ast.instruction(0);
-        assert.equal(inst.name(), "OpImageWrite");
-        assert.lengthOf(inst.operands(), 3);
-      });
-
-      it("extracts if available", () => {
-        let input = `OpImageWrite %1 %2 %3 ConstOffset %2
-OpKill`;
-        let l = new Lexer(input);
-        let p = new Parser(grammar, l);
-
-        let ast = p.parse();
-        assert.exists(ast);
-        assert.lengthOf(ast.instructions(), 2);
-
-        let inst = ast.instruction(0);
-        assert.equal(inst.name(), "OpImageWrite");
-        assert.lengthOf(inst.operands(), 4);
-        assert.equal(inst.operand(3).name(), "ConstOffset");
-      });
-    });
-
-    describe("*", () => {
-      it("skips if missing", () => {
-        let input = `OpEntryPoint Fragment %main "main"
-OpKill`;
-
-        let l = new Lexer(input);
-        let p = new Parser(grammar, l);
-
-        let ast = p.parse();
-        assert.exists(ast);
-        assert.lengthOf(ast.instructions(), 2);
-
-        let inst = ast.instruction(0);
-        assert.equal(inst.name(), "OpEntryPoint");
-        assert.lengthOf(inst.operands(), 3);
-        assert.equal(inst.operand(2).name(), "main");
-      });
-
-      it("extracts one if available", () => {
-        let input = `OpEntryPoint Fragment %main "main" %2
-OpKill`;
-
-        let l = new Lexer(input);
-        let p = new Parser(grammar, l);
-
-        let ast = p.parse();
-        assert.exists(ast);
-        assert.lengthOf(ast.instructions(), 2);
-
-        let inst = ast.instruction(0);
-        assert.equal(inst.name(), "OpEntryPoint");
-        assert.lengthOf(inst.operands(), 4);
-        assert.equal(inst.operand(3).name(), "2");
-      });
-
-      it("extracts multiple if available", () => {
-        let input = `OpEntryPoint Fragment %main "main" %2 %3 %4 %5
-OpKill`;
-
-        let l = new Lexer(input);
-        let p = new Parser(grammar, l);
-
-        let ast = p.parse();
-        assert.exists(ast);
-        assert.lengthOf(ast.instructions(), 2);
-
-        let inst = ast.instruction(0);
-        assert.equal(inst.name(), "OpEntryPoint");
-        assert.lengthOf(inst.operands(), 7);
-        assert.equal(inst.operand(3).name(), "2");
-        assert.equal(inst.operand(4).name(), "3");
-        assert.equal(inst.operand(5).name(), "4");
-        assert.equal(inst.operand(6).name(), "5");
-      });
-    });
-  });
-
-  describe("extended instructions", () => {
-    it("errors on non-glsl extensions", () => {
-      let input = "%1 = OpExtInstImport \"OpenCL.std.100\"";
-
-      let l = new Lexer(input);
-      let p = new Parser(grammar, l);
-
-      assert.isUndefined(p.parse());
-    });
-
-    it("handles extended instructions", () => {
-      let input = `%1 = OpExtInstImport "GLSL.std.450"
-  %44 = OpExtInst %7 %1 Sqrt %43`;
-
-      let l = new Lexer(input);
-      let p = new Parser(grammar, l);
-
-      let ast = p.parse();
-      assert.exists(ast, p.error);
-      assert.lengthOf(ast.instructions(), 2);
-
-      let inst = ast.instruction(1);
-      assert.lengthOf(inst.operands(), 5);
-      assert.equal(inst.operand(3).value(), 31);
-      assert.equal(inst.operand(3).name(), "Sqrt");
-      assert.equal(inst.operand(4).value(), 43);
-      assert.equal(inst.operand(4).name(), "43");
-    });
-  });
-
-  it.skip("handles spec constant ops", () => {
-    // let input = "%sum = OpSpecConstantOp %i32 IAdd %a %b";
-  });
-
-  it("handles OpCopyMemory", () => {
-    let input = "OpCopyMemory %1 %2 " +
-                "Volatile|Nontemporal|MakePointerVisible %3 " +
-                "Aligned|MakePointerAvailable|NonPrivatePointer 16 %4";
-
-    let l = new Lexer(input);
-    let p = new Parser(grammar, l);
-
-    let ast = p.parse();
-    assert.exists(ast, p.error);
-    assert.lengthOf(ast.instructions(), 1);
-
-    let inst = ast.instruction(0);
-    assert.lengthOf(inst.operands(), 4);
-    assert.equal(inst.operand(0).value(), 1);
-    assert.equal(inst.operand(1).value(), 2);
-
-    assert.equal(inst.operand(2).name(),
-        "Volatile|Nontemporal|MakePointerVisible");
-    assert.equal(inst.operand(2).value(), 21);
-    assert.lengthOf(inst.operand(2).params(), 1);
-    assert.equal(inst.operand(2).params()[0].value(), 3);
-
-    assert.equal(inst.operand(3).name(),
-        "Aligned|MakePointerAvailable|NonPrivatePointer");
-    assert.equal(inst.operand(3).value(), 42);
-    assert.lengthOf(inst.operand(3).params(), 2);
-    assert.equal(inst.operand(3).params()[0].value(), 16);
-    assert.equal(inst.operand(3).params()[1].value(), 4);
-  });
-});
diff --git a/tools/sva/src/spirv.data.js b/tools/sva/src/spirv.data.js
deleted file mode 100644
index 67c0966..0000000
--- a/tools/sva/src/spirv.data.js
+++ /dev/null
@@ -1,4570 +0,0 @@
-/*Copyright (c) 2014-2016 The Khronos Group Inc.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and/or associated documentation files (the "Materials"),
-to deal in the Materials without restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Materials, and to permit persons to whom the
-Materials are furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Materials.
-
-MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
-STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
-HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ 
-
-THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
-IN THE MATERIALS.*/
-
-// THIS FILE IS GENERATED WITH tools/process_grammar.rb
-
-export default {
-  "magic": "0x07230203",
-  "version": [
-    1,
-    5
-  ],
-  "instructions": {
-    "OpNop": {
-      "opcode": 0,
-      "operands": [
-
-      ]
-    },
-    "OpUndef": {
-      "opcode": 1,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        }
-      ]
-    },
-    "OpSourceContinued": {
-      "opcode": 2,
-      "operands": [
-        {
-          "kind": "LiteralString"
-        }
-      ]
-    },
-    "OpSource": {
-      "opcode": 3,
-      "operands": [
-        {
-          "kind": "SourceLanguage"
-        },
-        {
-          "kind": "LiteralInteger"
-        },
-        {
-          "kind": "IdRef",
-          "quantifier": "?"
-        },
-        {
-          "kind": "LiteralString",
-          "quantifier": "?"
-        }
-      ]
-    },
-    "OpSourceExtension": {
-      "opcode": 4,
-      "operands": [
-        {
-          "kind": "LiteralString"
-        }
-      ]
-    },
-    "OpName": {
-      "opcode": 5,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "LiteralString"
-        }
-      ]
-    },
-    "OpMemberName": {
-      "opcode": 6,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "LiteralInteger"
-        },
-        {
-          "kind": "LiteralString"
-        }
-      ]
-    },
-    "OpString": {
-      "opcode": 7,
-      "operands": [
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "LiteralString"
-        }
-      ]
-    },
-    "OpLine": {
-      "opcode": 8,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "LiteralInteger"
-        },
-        {
-          "kind": "LiteralInteger"
-        }
-      ]
-    },
-    "OpExtension": {
-      "opcode": 10,
-      "operands": [
-        {
-          "kind": "LiteralString"
-        }
-      ]
-    },
-    "OpExtInstImport": {
-      "opcode": 11,
-      "operands": [
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "LiteralString"
-        }
-      ]
-    },
-    "OpExtInst": {
-      "opcode": 12,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "LiteralExtInstInteger"
-        },
-        {
-          "kind": "IdRef",
-          "quantifier": "*"
-        }
-      ]
-    },
-    "OpMemoryModel": {
-      "opcode": 14,
-      "operands": [
-        {
-          "kind": "AddressingModel"
-        },
-        {
-          "kind": "MemoryModel"
-        }
-      ]
-    },
-    "OpEntryPoint": {
-      "opcode": 15,
-      "operands": [
-        {
-          "kind": "ExecutionModel"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "LiteralString"
-        },
-        {
-          "kind": "IdRef",
-          "quantifier": "*"
-        }
-      ]
-    },
-    "OpExecutionMode": {
-      "opcode": 16,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "ExecutionMode"
-        }
-      ]
-    },
-    "OpCapability": {
-      "opcode": 17,
-      "operands": [
-        {
-          "kind": "Capability"
-        }
-      ]
-    },
-    "OpTypeVoid": {
-      "opcode": 19,
-      "operands": [
-        {
-          "kind": "IdResult"
-        }
-      ]
-    },
-    "OpTypeBool": {
-      "opcode": 20,
-      "operands": [
-        {
-          "kind": "IdResult"
-        }
-      ]
-    },
-    "OpTypeInt": {
-      "opcode": 21,
-      "operands": [
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "LiteralInteger"
-        },
-        {
-          "kind": "LiteralInteger"
-        }
-      ]
-    },
-    "OpTypeFloat": {
-      "opcode": 22,
-      "operands": [
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "LiteralInteger"
-        }
-      ]
-    },
-    "OpTypeVector": {
-      "opcode": 23,
-      "operands": [
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "LiteralInteger"
-        }
-      ]
-    },
-    "OpTypeMatrix": {
-      "opcode": 24,
-      "operands": [
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "LiteralInteger"
-        }
-      ]
-    },
-    "OpTypeImage": {
-      "opcode": 25,
-      "operands": [
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "Dim"
-        },
-        {
-          "kind": "LiteralInteger"
-        },
-        {
-          "kind": "LiteralInteger"
-        },
-        {
-          "kind": "LiteralInteger"
-        },
-        {
-          "kind": "LiteralInteger"
-        },
-        {
-          "kind": "ImageFormat"
-        },
-        {
-          "kind": "AccessQualifier",
-          "quantifier": "?"
-        }
-      ]
-    },
-    "OpTypeSampler": {
-      "opcode": 26,
-      "operands": [
-        {
-          "kind": "IdResult"
-        }
-      ]
-    },
-    "OpTypeSampledImage": {
-      "opcode": 27,
-      "operands": [
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpTypeArray": {
-      "opcode": 28,
-      "operands": [
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpTypeRuntimeArray": {
-      "opcode": 29,
-      "operands": [
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpTypeStruct": {
-      "opcode": 30,
-      "operands": [
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef",
-          "quantifier": "*"
-        }
-      ]
-    },
-    "OpTypePointer": {
-      "opcode": 32,
-      "operands": [
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "StorageClass"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpTypeFunction": {
-      "opcode": 33,
-      "operands": [
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef",
-          "quantifier": "*"
-        }
-      ]
-    },
-    "OpConstantTrue": {
-      "opcode": 41,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        }
-      ]
-    },
-    "OpConstantFalse": {
-      "opcode": 42,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        }
-      ]
-    },
-    "OpConstant": {
-      "opcode": 43,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "LiteralContextDependentNumber"
-        }
-      ]
-    },
-    "OpConstantComposite": {
-      "opcode": 44,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef",
-          "quantifier": "*"
-        }
-      ]
-    },
-    "OpConstantNull": {
-      "opcode": 46,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        }
-      ]
-    },
-    "OpSpecConstantTrue": {
-      "opcode": 48,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        }
-      ]
-    },
-    "OpSpecConstantFalse": {
-      "opcode": 49,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        }
-      ]
-    },
-    "OpSpecConstant": {
-      "opcode": 50,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "LiteralContextDependentNumber"
-        }
-      ]
-    },
-    "OpSpecConstantComposite": {
-      "opcode": 51,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef",
-          "quantifier": "*"
-        }
-      ]
-    },
-    "OpSpecConstantOp": {
-      "opcode": 52,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "LiteralSpecConstantOpInteger"
-        }
-      ]
-    },
-    "OpFunction": {
-      "opcode": 54,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "FunctionControl"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFunctionParameter": {
-      "opcode": 55,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        }
-      ]
-    },
-    "OpFunctionEnd": {
-      "opcode": 56,
-      "operands": [
-
-      ]
-    },
-    "OpFunctionCall": {
-      "opcode": 57,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef",
-          "quantifier": "*"
-        }
-      ]
-    },
-    "OpVariable": {
-      "opcode": 59,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "StorageClass"
-        },
-        {
-          "kind": "IdRef",
-          "quantifier": "?"
-        }
-      ]
-    },
-    "OpImageTexelPointer": {
-      "opcode": 60,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpLoad": {
-      "opcode": 61,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "MemoryAccess",
-          "quantifier": "?"
-        }
-      ]
-    },
-    "OpStore": {
-      "opcode": 62,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "MemoryAccess",
-          "quantifier": "?"
-        }
-      ]
-    },
-    "OpCopyMemory": {
-      "opcode": 63,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "MemoryAccess",
-          "quantifier": "?"
-        },
-        {
-          "kind": "MemoryAccess",
-          "quantifier": "?"
-        }
-      ]
-    },
-    "OpAccessChain": {
-      "opcode": 65,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef",
-          "quantifier": "*"
-        }
-      ]
-    },
-    "OpInBoundsAccessChain": {
-      "opcode": 66,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef",
-          "quantifier": "*"
-        }
-      ]
-    },
-    "OpArrayLength": {
-      "opcode": 68,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "LiteralInteger"
-        }
-      ]
-    },
-    "OpDecorate": {
-      "opcode": 71,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "Decoration"
-        }
-      ]
-    },
-    "OpMemberDecorate": {
-      "opcode": 72,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "LiteralInteger"
-        },
-        {
-          "kind": "Decoration"
-        }
-      ]
-    },
-    "OpDecorationGroup": {
-      "opcode": 73,
-      "operands": [
-        {
-          "kind": "IdResult"
-        }
-      ]
-    },
-    "OpGroupDecorate": {
-      "opcode": 74,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef",
-          "quantifier": "*"
-        }
-      ]
-    },
-    "OpGroupMemberDecorate": {
-      "opcode": 75,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "PairIdRefLiteralInteger",
-          "quantifier": "*"
-        }
-      ]
-    },
-    "OpVectorExtractDynamic": {
-      "opcode": 77,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpVectorInsertDynamic": {
-      "opcode": 78,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpVectorShuffle": {
-      "opcode": 79,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "LiteralInteger",
-          "quantifier": "*"
-        }
-      ]
-    },
-    "OpCompositeConstruct": {
-      "opcode": 80,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef",
-          "quantifier": "*"
-        }
-      ]
-    },
-    "OpCompositeExtract": {
-      "opcode": 81,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "LiteralInteger",
-          "quantifier": "*"
-        }
-      ]
-    },
-    "OpCompositeInsert": {
-      "opcode": 82,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "LiteralInteger",
-          "quantifier": "*"
-        }
-      ]
-    },
-    "OpCopyObject": {
-      "opcode": 83,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpTranspose": {
-      "opcode": 84,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpSampledImage": {
-      "opcode": 86,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpImageSampleImplicitLod": {
-      "opcode": 87,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "ImageOperands",
-          "quantifier": "?"
-        }
-      ]
-    },
-    "OpImageSampleExplicitLod": {
-      "opcode": 88,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "ImageOperands"
-        }
-      ]
-    },
-    "OpImageSampleDrefImplicitLod": {
-      "opcode": 89,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "ImageOperands",
-          "quantifier": "?"
-        }
-      ]
-    },
-    "OpImageSampleDrefExplicitLod": {
-      "opcode": 90,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "ImageOperands"
-        }
-      ]
-    },
-    "OpImageSampleProjImplicitLod": {
-      "opcode": 91,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "ImageOperands",
-          "quantifier": "?"
-        }
-      ]
-    },
-    "OpImageSampleProjExplicitLod": {
-      "opcode": 92,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "ImageOperands"
-        }
-      ]
-    },
-    "OpImageSampleProjDrefImplicitLod": {
-      "opcode": 93,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "ImageOperands",
-          "quantifier": "?"
-        }
-      ]
-    },
-    "OpImageSampleProjDrefExplicitLod": {
-      "opcode": 94,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "ImageOperands"
-        }
-      ]
-    },
-    "OpImageFetch": {
-      "opcode": 95,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "ImageOperands",
-          "quantifier": "?"
-        }
-      ]
-    },
-    "OpImageGather": {
-      "opcode": 96,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "ImageOperands",
-          "quantifier": "?"
-        }
-      ]
-    },
-    "OpImageDrefGather": {
-      "opcode": 97,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "ImageOperands",
-          "quantifier": "?"
-        }
-      ]
-    },
-    "OpImageRead": {
-      "opcode": 98,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "ImageOperands",
-          "quantifier": "?"
-        }
-      ]
-    },
-    "OpImageWrite": {
-      "opcode": 99,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "ImageOperands",
-          "quantifier": "?"
-        }
-      ]
-    },
-    "OpImage": {
-      "opcode": 100,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpImageQuerySizeLod": {
-      "opcode": 103,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpImageQuerySize": {
-      "opcode": 104,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpImageQueryLod": {
-      "opcode": 105,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpImageQueryLevels": {
-      "opcode": 106,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpImageQuerySamples": {
-      "opcode": 107,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpConvertFToU": {
-      "opcode": 109,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpConvertFToS": {
-      "opcode": 110,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpConvertSToF": {
-      "opcode": 111,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpConvertUToF": {
-      "opcode": 112,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpUConvert": {
-      "opcode": 113,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpSConvert": {
-      "opcode": 114,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFConvert": {
-      "opcode": 115,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpQuantizeToF16": {
-      "opcode": 116,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpBitcast": {
-      "opcode": 124,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpSNegate": {
-      "opcode": 126,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFNegate": {
-      "opcode": 127,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpIAdd": {
-      "opcode": 128,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFAdd": {
-      "opcode": 129,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpISub": {
-      "opcode": 130,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFSub": {
-      "opcode": 131,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpIMul": {
-      "opcode": 132,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFMul": {
-      "opcode": 133,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpUDiv": {
-      "opcode": 134,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpSDiv": {
-      "opcode": 135,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFDiv": {
-      "opcode": 136,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpUMod": {
-      "opcode": 137,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpSRem": {
-      "opcode": 138,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpSMod": {
-      "opcode": 139,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFRem": {
-      "opcode": 140,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFMod": {
-      "opcode": 141,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpVectorTimesScalar": {
-      "opcode": 142,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpMatrixTimesScalar": {
-      "opcode": 143,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpVectorTimesMatrix": {
-      "opcode": 144,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpMatrixTimesVector": {
-      "opcode": 145,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpMatrixTimesMatrix": {
-      "opcode": 146,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpOuterProduct": {
-      "opcode": 147,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpDot": {
-      "opcode": 148,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpIAddCarry": {
-      "opcode": 149,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpISubBorrow": {
-      "opcode": 150,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpUMulExtended": {
-      "opcode": 151,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpSMulExtended": {
-      "opcode": 152,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpAny": {
-      "opcode": 154,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpAll": {
-      "opcode": 155,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpIsNan": {
-      "opcode": 156,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpIsInf": {
-      "opcode": 157,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpLogicalEqual": {
-      "opcode": 164,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpLogicalNotEqual": {
-      "opcode": 165,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpLogicalOr": {
-      "opcode": 166,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpLogicalAnd": {
-      "opcode": 167,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpLogicalNot": {
-      "opcode": 168,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpSelect": {
-      "opcode": 169,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpIEqual": {
-      "opcode": 170,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpINotEqual": {
-      "opcode": 171,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpUGreaterThan": {
-      "opcode": 172,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpSGreaterThan": {
-      "opcode": 173,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpUGreaterThanEqual": {
-      "opcode": 174,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpSGreaterThanEqual": {
-      "opcode": 175,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpULessThan": {
-      "opcode": 176,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpSLessThan": {
-      "opcode": 177,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpULessThanEqual": {
-      "opcode": 178,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpSLessThanEqual": {
-      "opcode": 179,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFOrdEqual": {
-      "opcode": 180,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFUnordEqual": {
-      "opcode": 181,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFOrdNotEqual": {
-      "opcode": 182,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFUnordNotEqual": {
-      "opcode": 183,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFOrdLessThan": {
-      "opcode": 184,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFUnordLessThan": {
-      "opcode": 185,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFOrdGreaterThan": {
-      "opcode": 186,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFUnordGreaterThan": {
-      "opcode": 187,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFOrdLessThanEqual": {
-      "opcode": 188,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFUnordLessThanEqual": {
-      "opcode": 189,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFOrdGreaterThanEqual": {
-      "opcode": 190,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFUnordGreaterThanEqual": {
-      "opcode": 191,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpShiftRightLogical": {
-      "opcode": 194,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpShiftRightArithmetic": {
-      "opcode": 195,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpShiftLeftLogical": {
-      "opcode": 196,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpBitwiseOr": {
-      "opcode": 197,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpBitwiseXor": {
-      "opcode": 198,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpBitwiseAnd": {
-      "opcode": 199,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpNot": {
-      "opcode": 200,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpBitFieldInsert": {
-      "opcode": 201,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpBitFieldSExtract": {
-      "opcode": 202,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpBitFieldUExtract": {
-      "opcode": 203,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpBitReverse": {
-      "opcode": 204,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpBitCount": {
-      "opcode": 205,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpDPdx": {
-      "opcode": 207,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpDPdy": {
-      "opcode": 208,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFwidth": {
-      "opcode": 209,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpDPdxFine": {
-      "opcode": 210,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpDPdyFine": {
-      "opcode": 211,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFwidthFine": {
-      "opcode": 212,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpDPdxCoarse": {
-      "opcode": 213,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpDPdyCoarse": {
-      "opcode": 214,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpFwidthCoarse": {
-      "opcode": 215,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpControlBarrier": {
-      "opcode": 224,
-      "operands": [
-        {
-          "kind": "IdScope"
-        },
-        {
-          "kind": "IdScope"
-        },
-        {
-          "kind": "IdMemorySemantics"
-        }
-      ]
-    },
-    "OpMemoryBarrier": {
-      "opcode": 225,
-      "operands": [
-        {
-          "kind": "IdScope"
-        },
-        {
-          "kind": "IdMemorySemantics"
-        }
-      ]
-    },
-    "OpAtomicLoad": {
-      "opcode": 227,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdScope"
-        },
-        {
-          "kind": "IdMemorySemantics"
-        }
-      ]
-    },
-    "OpAtomicStore": {
-      "opcode": 228,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdScope"
-        },
-        {
-          "kind": "IdMemorySemantics"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpAtomicExchange": {
-      "opcode": 229,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdScope"
-        },
-        {
-          "kind": "IdMemorySemantics"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpAtomicCompareExchange": {
-      "opcode": 230,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdScope"
-        },
-        {
-          "kind": "IdMemorySemantics"
-        },
-        {
-          "kind": "IdMemorySemantics"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpAtomicIIncrement": {
-      "opcode": 232,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdScope"
-        },
-        {
-          "kind": "IdMemorySemantics"
-        }
-      ]
-    },
-    "OpAtomicIDecrement": {
-      "opcode": 233,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdScope"
-        },
-        {
-          "kind": "IdMemorySemantics"
-        }
-      ]
-    },
-    "OpAtomicIAdd": {
-      "opcode": 234,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdScope"
-        },
-        {
-          "kind": "IdMemorySemantics"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpAtomicISub": {
-      "opcode": 235,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdScope"
-        },
-        {
-          "kind": "IdMemorySemantics"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpAtomicSMin": {
-      "opcode": 236,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdScope"
-        },
-        {
-          "kind": "IdMemorySemantics"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpAtomicUMin": {
-      "opcode": 237,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdScope"
-        },
-        {
-          "kind": "IdMemorySemantics"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpAtomicSMax": {
-      "opcode": 238,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdScope"
-        },
-        {
-          "kind": "IdMemorySemantics"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpAtomicUMax": {
-      "opcode": 239,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdScope"
-        },
-        {
-          "kind": "IdMemorySemantics"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpAtomicAnd": {
-      "opcode": 240,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdScope"
-        },
-        {
-          "kind": "IdMemorySemantics"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpAtomicOr": {
-      "opcode": 241,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdScope"
-        },
-        {
-          "kind": "IdMemorySemantics"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpAtomicXor": {
-      "opcode": 242,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdScope"
-        },
-        {
-          "kind": "IdMemorySemantics"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpPhi": {
-      "opcode": 245,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "PairIdRefIdRef",
-          "quantifier": "*"
-        }
-      ]
-    },
-    "OpLoopMerge": {
-      "opcode": 246,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "LoopControl"
-        }
-      ]
-    },
-    "OpSelectionMerge": {
-      "opcode": 247,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "SelectionControl"
-        }
-      ]
-    },
-    "OpLabel": {
-      "opcode": 248,
-      "operands": [
-        {
-          "kind": "IdResult"
-        }
-      ]
-    },
-    "OpBranch": {
-      "opcode": 249,
-      "operands": [
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpBranchConditional": {
-      "opcode": 250,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "LiteralInteger",
-          "quantifier": "*"
-        }
-      ]
-    },
-    "OpSwitch": {
-      "opcode": 251,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "PairLiteralIntegerIdRef",
-          "quantifier": "*"
-        }
-      ]
-    },
-    "OpKill": {
-      "opcode": 252,
-      "operands": [
-
-      ]
-    },
-    "OpReturn": {
-      "opcode": 253,
-      "operands": [
-
-      ]
-    },
-    "OpReturnValue": {
-      "opcode": 254,
-      "operands": [
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpUnreachable": {
-      "opcode": 255,
-      "operands": [
-
-      ]
-    },
-    "OpNoLine": {
-      "opcode": 317,
-      "operands": [
-
-      ]
-    },
-    "OpModuleProcessed": {
-      "opcode": 330,
-      "operands": [
-        {
-          "kind": "LiteralString"
-        }
-      ]
-    },
-    "OpExecutionModeId": {
-      "opcode": 331,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "ExecutionMode"
-        }
-      ]
-    },
-    "OpDecorateId": {
-      "opcode": 332,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "Decoration"
-        }
-      ]
-    },
-    "OpCopyLogical": {
-      "opcode": 400,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpPtrEqual": {
-      "opcode": 401,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpPtrNotEqual": {
-      "opcode": 402,
-      "operands": [
-        {
-          "kind": "IdResultType"
-        },
-        {
-          "kind": "IdResult"
-        },
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "IdRef"
-        }
-      ]
-    },
-    "OpDecorateString": {
-      "opcode": 5632,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "Decoration"
-        }
-      ]
-    },
-    "OpDecorateStringGOOGLE": {
-      "opcode": 5632,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "Decoration"
-        }
-      ]
-    },
-    "OpMemberDecorateString": {
-      "opcode": 5633,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "LiteralInteger"
-        },
-        {
-          "kind": "Decoration"
-        }
-      ]
-    },
-    "OpMemberDecorateStringGOOGLE": {
-      "opcode": 5633,
-      "operands": [
-        {
-          "kind": "IdRef"
-        },
-        {
-          "kind": "LiteralInteger"
-        },
-        {
-          "kind": "Decoration"
-        }
-      ]
-    }
-  },
-  "operand_kinds": {
-    "ImageOperands": {
-      "type": "BitEnum",
-      "values": {
-        "None": {
-          "value": 0
-        },
-        "Bias": {
-          "value": 1,
-          "params": [
-            "IdRef"
-          ]
-        },
-        "Lod": {
-          "value": 2,
-          "params": [
-            "IdRef"
-          ]
-        },
-        "Grad": {
-          "value": 4,
-          "params": [
-            "IdRef",
-            "IdRef"
-          ]
-        },
-        "ConstOffset": {
-          "value": 8,
-          "params": [
-            "IdRef"
-          ]
-        },
-        "Sample": {
-          "value": 64,
-          "params": [
-            "IdRef"
-          ]
-        },
-        "MakeTexelAvailable": {
-          "value": 256,
-          "params": [
-            "IdScope"
-          ]
-        },
-        "MakeTexelAvailableKHR": {
-          "value": 256,
-          "params": [
-            "IdScope"
-          ]
-        },
-        "MakeTexelVisible": {
-          "value": 512,
-          "params": [
-            "IdScope"
-          ]
-        },
-        "MakeTexelVisibleKHR": {
-          "value": 512,
-          "params": [
-            "IdScope"
-          ]
-        },
-        "NonPrivateTexel": {
-          "value": 1024
-        },
-        "NonPrivateTexelKHR": {
-          "value": 1024
-        },
-        "VolatileTexel": {
-          "value": 2048
-        },
-        "VolatileTexelKHR": {
-          "value": 2048
-        },
-        "SignExtend": {
-          "value": 4096
-        },
-        "ZeroExtend": {
-          "value": 8192
-        }
-      }
-    },
-    "FPFastMathMode": {
-      "type": "BitEnum",
-      "values": {
-        "None": {
-          "value": 0
-        }
-      }
-    },
-    "SelectionControl": {
-      "type": "BitEnum",
-      "values": {
-        "None": {
-          "value": 0
-        },
-        "Flatten": {
-          "value": 1
-        },
-        "DontFlatten": {
-          "value": 2
-        }
-      }
-    },
-    "LoopControl": {
-      "type": "BitEnum",
-      "values": {
-        "None": {
-          "value": 0
-        },
-        "Unroll": {
-          "value": 1
-        },
-        "DontUnroll": {
-          "value": 2
-        },
-        "DependencyInfinite": {
-          "value": 4
-        },
-        "DependencyLength": {
-          "value": 8,
-          "params": [
-            "LiteralInteger"
-          ]
-        },
-        "MinIterations": {
-          "value": 16,
-          "params": [
-            "LiteralInteger"
-          ]
-        },
-        "MaxIterations": {
-          "value": 32,
-          "params": [
-            "LiteralInteger"
-          ]
-        },
-        "IterationMultiple": {
-          "value": 64,
-          "params": [
-            "LiteralInteger"
-          ]
-        },
-        "PeelCount": {
-          "value": 128,
-          "params": [
-            "LiteralInteger"
-          ]
-        },
-        "PartialCount": {
-          "value": 256,
-          "params": [
-            "LiteralInteger"
-          ]
-        }
-      }
-    },
-    "FunctionControl": {
-      "type": "BitEnum",
-      "values": {
-        "None": {
-          "value": 0
-        },
-        "Inline": {
-          "value": 1
-        },
-        "DontInline": {
-          "value": 2
-        },
-        "Pure": {
-          "value": 4
-        },
-        "Const": {
-          "value": 8
-        }
-      }
-    },
-    "MemorySemantics": {
-      "type": "BitEnum",
-      "values": {
-        "Relaxed": {
-          "value": 0
-        },
-        "None": {
-          "value": 0
-        },
-        "Acquire": {
-          "value": 2
-        },
-        "Release": {
-          "value": 4
-        },
-        "AcquireRelease": {
-          "value": 8
-        },
-        "SequentiallyConsistent": {
-          "value": 16
-        },
-        "UniformMemory": {
-          "value": 64
-        },
-        "SubgroupMemory": {
-          "value": 128
-        },
-        "WorkgroupMemory": {
-          "value": 256
-        },
-        "CrossWorkgroupMemory": {
-          "value": 512
-        },
-        "ImageMemory": {
-          "value": 2048
-        },
-        "OutputMemory": {
-          "value": 4096
-        },
-        "OutputMemoryKHR": {
-          "value": 4096
-        },
-        "MakeAvailable": {
-          "value": 8192
-        },
-        "MakeAvailableKHR": {
-          "value": 8192
-        },
-        "MakeVisible": {
-          "value": 16384
-        },
-        "MakeVisibleKHR": {
-          "value": 16384
-        },
-        "Volatile": {
-          "value": 32768
-        }
-      }
-    },
-    "MemoryAccess": {
-      "type": "BitEnum",
-      "values": {
-        "None": {
-          "value": 0
-        },
-        "Volatile": {
-          "value": 1
-        },
-        "Aligned": {
-          "value": 2,
-          "params": [
-            "LiteralInteger"
-          ]
-        },
-        "Nontemporal": {
-          "value": 4
-        },
-        "MakePointerAvailable": {
-          "value": 8,
-          "params": [
-            "IdScope"
-          ]
-        },
-        "MakePointerAvailableKHR": {
-          "value": 8,
-          "params": [
-            "IdScope"
-          ]
-        },
-        "MakePointerVisible": {
-          "value": 16,
-          "params": [
-            "IdScope"
-          ]
-        },
-        "MakePointerVisibleKHR": {
-          "value": 16,
-          "params": [
-            "IdScope"
-          ]
-        },
-        "NonPrivatePointer": {
-          "value": 32
-        },
-        "NonPrivatePointerKHR": {
-          "value": 32
-        }
-      }
-    },
-    "KernelProfilingInfo": {
-      "type": "BitEnum",
-      "values": {
-        "None": {
-          "value": 0
-        }
-      }
-    },
-    "SourceLanguage": {
-      "type": "ValueEnum",
-      "values": {
-        "Unknown": {
-          "value": 0
-        },
-        "ESSL": {
-          "value": 1
-        },
-        "GLSL": {
-          "value": 2
-        },
-        "OpenCL_C": {
-          "value": 3
-        },
-        "OpenCL_CPP": {
-          "value": 4
-        },
-        "HLSL": {
-          "value": 5
-        }
-      }
-    },
-    "ExecutionModel": {
-      "type": "ValueEnum",
-      "values": {
-        "Vertex": {
-          "value": 0
-        },
-        "Fragment": {
-          "value": 4
-        },
-        "GLCompute": {
-          "value": 5
-        }
-      }
-    },
-    "AddressingModel": {
-      "type": "ValueEnum",
-      "values": {
-        "Logical": {
-          "value": 0
-        }
-      }
-    },
-    "MemoryModel": {
-      "type": "ValueEnum",
-      "values": {
-        "Simple": {
-          "value": 0
-        },
-        "GLSL450": {
-          "value": 1
-        },
-        "Vulkan": {
-          "value": 3
-        },
-        "VulkanKHR": {
-          "value": 3
-        }
-      }
-    },
-    "ExecutionMode": {
-      "type": "ValueEnum",
-      "values": {
-        "PixelCenterInteger": {
-          "value": 6
-        },
-        "OriginUpperLeft": {
-          "value": 7
-        },
-        "OriginLowerLeft": {
-          "value": 8
-        },
-        "EarlyFragmentTests": {
-          "value": 9
-        },
-        "DepthReplacing": {
-          "value": 12
-        },
-        "DepthGreater": {
-          "value": 14
-        },
-        "DepthLess": {
-          "value": 15
-        },
-        "DepthUnchanged": {
-          "value": 16
-        },
-        "LocalSize": {
-          "value": 17,
-          "params": [
-            "LiteralInteger",
-            "LiteralInteger",
-            "LiteralInteger"
-          ]
-        },
-        "LocalSizeId": {
-          "value": 38,
-          "params": [
-            "IdRef",
-            "IdRef",
-            "IdRef"
-          ]
-        }
-      }
-    },
-    "StorageClass": {
-      "type": "ValueEnum",
-      "values": {
-        "UniformConstant": {
-          "value": 0
-        },
-        "Input": {
-          "value": 1
-        },
-        "Uniform": {
-          "value": 2
-        },
-        "Output": {
-          "value": 3
-        },
-        "Workgroup": {
-          "value": 4
-        },
-        "CrossWorkgroup": {
-          "value": 5
-        },
-        "Private": {
-          "value": 6
-        },
-        "Function": {
-          "value": 7
-        },
-        "PushConstant": {
-          "value": 9
-        },
-        "Image": {
-          "value": 11
-        },
-        "StorageBuffer": {
-          "value": 12
-        }
-      }
-    },
-    "Dim": {
-      "type": "ValueEnum",
-      "values": {
-        "1D": {
-          "value": 0
-        },
-        "2D": {
-          "value": 1
-        },
-        "3D": {
-          "value": 2
-        },
-        "Cube": {
-          "value": 3
-        }
-      }
-    },
-    "ImageFormat": {
-      "type": "ValueEnum",
-      "values": {
-        "Unknown": {
-          "value": 0
-        },
-        "Rgba32f": {
-          "value": 1
-        },
-        "Rgba16f": {
-          "value": 2
-        },
-        "R32f": {
-          "value": 3
-        },
-        "Rgba8": {
-          "value": 4
-        },
-        "Rgba8Snorm": {
-          "value": 5
-        },
-        "Rgba32i": {
-          "value": 21
-        },
-        "Rgba16i": {
-          "value": 22
-        },
-        "Rgba8i": {
-          "value": 23
-        },
-        "R32i": {
-          "value": 24
-        },
-        "Rgba32ui": {
-          "value": 30
-        },
-        "Rgba16ui": {
-          "value": 31
-        },
-        "Rgba8ui": {
-          "value": 32
-        },
-        "R32ui": {
-          "value": 33
-        }
-      }
-    },
-    "FPRoundingMode": {
-      "type": "ValueEnum",
-      "values": {
-        "RTE": {
-          "value": 0
-        },
-        "RTZ": {
-          "value": 1
-        },
-        "RTP": {
-          "value": 2
-        },
-        "RTN": {
-          "value": 3
-        }
-      }
-    },
-    "Decoration": {
-      "type": "ValueEnum",
-      "values": {
-        "RelaxedPrecision": {
-          "value": 0
-        },
-        "SpecId": {
-          "value": 1,
-          "params": [
-            "LiteralInteger"
-          ]
-        },
-        "Block": {
-          "value": 2
-        },
-        "BufferBlock": {
-          "value": 3
-        },
-        "RowMajor": {
-          "value": 4
-        },
-        "ColMajor": {
-          "value": 5
-        },
-        "ArrayStride": {
-          "value": 6,
-          "params": [
-            "LiteralInteger"
-          ]
-        },
-        "MatrixStride": {
-          "value": 7,
-          "params": [
-            "LiteralInteger"
-          ]
-        },
-        "GLSLShared": {
-          "value": 8
-        },
-        "GLSLPacked": {
-          "value": 9
-        },
-        "BuiltIn": {
-          "value": 11,
-          "params": [
-            "BuiltIn"
-          ]
-        },
-        "NoPerspective": {
-          "value": 13
-        },
-        "Flat": {
-          "value": 14
-        },
-        "Centroid": {
-          "value": 16
-        },
-        "Invariant": {
-          "value": 18
-        },
-        "Restrict": {
-          "value": 19
-        },
-        "Aliased": {
-          "value": 20
-        },
-        "Volatile": {
-          "value": 21
-        },
-        "Coherent": {
-          "value": 23
-        },
-        "NonWritable": {
-          "value": 24
-        },
-        "NonReadable": {
-          "value": 25
-        },
-        "Uniform": {
-          "value": 26
-        },
-        "UniformId": {
-          "value": 27,
-          "params": [
-            "IdScope"
-          ]
-        },
-        "Location": {
-          "value": 30,
-          "params": [
-            "LiteralInteger"
-          ]
-        },
-        "Component": {
-          "value": 31,
-          "params": [
-            "LiteralInteger"
-          ]
-        },
-        "Index": {
-          "value": 32,
-          "params": [
-            "LiteralInteger"
-          ]
-        },
-        "Binding": {
-          "value": 33,
-          "params": [
-            "LiteralInteger"
-          ]
-        },
-        "DescriptorSet": {
-          "value": 34,
-          "params": [
-            "LiteralInteger"
-          ]
-        },
-        "Offset": {
-          "value": 35,
-          "params": [
-            "LiteralInteger"
-          ]
-        },
-        "FPRoundingMode": {
-          "value": 39,
-          "params": [
-            "FPRoundingMode"
-          ]
-        },
-        "NoContraction": {
-          "value": 42
-        },
-        "NoSignedWrap": {
-          "value": 4469
-        },
-        "NoUnsignedWrap": {
-          "value": 4470
-        },
-        "ExplicitInterpAMD": {
-          "value": 4999
-        },
-        "CounterBuffer": {
-          "value": 5634,
-          "params": [
-            "IdRef"
-          ]
-        },
-        "HlslCounterBufferGOOGLE": {
-          "value": 5634,
-          "params": [
-            "IdRef"
-          ]
-        },
-        "UserSemantic": {
-          "value": 5635,
-          "params": [
-            "LiteralString"
-          ]
-        },
-        "HlslSemanticGOOGLE": {
-          "value": 5635,
-          "params": [
-            "LiteralString"
-          ]
-        },
-        "UserTypeGOOGLE": {
-          "value": 5636,
-          "params": [
-            "LiteralString"
-          ]
-        }
-      }
-    },
-    "BuiltIn": {
-      "type": "ValueEnum",
-      "values": {
-        "Position": {
-          "value": 0
-        },
-        "PointSize": {
-          "value": 1
-        },
-        "VertexId": {
-          "value": 5
-        },
-        "InstanceId": {
-          "value": 6
-        },
-        "FragCoord": {
-          "value": 15
-        },
-        "PointCoord": {
-          "value": 16
-        },
-        "FrontFacing": {
-          "value": 17
-        },
-        "SampleMask": {
-          "value": 20
-        },
-        "FragDepth": {
-          "value": 22
-        },
-        "HelperInvocation": {
-          "value": 23
-        },
-        "NumWorkgroups": {
-          "value": 24
-        },
-        "WorkgroupSize": {
-          "value": 25
-        },
-        "WorkgroupId": {
-          "value": 26
-        },
-        "LocalInvocationId": {
-          "value": 27
-        },
-        "GlobalInvocationId": {
-          "value": 28
-        },
-        "LocalInvocationIndex": {
-          "value": 29
-        },
-        "VertexIndex": {
-          "value": 42
-        },
-        "InstanceIndex": {
-          "value": 43
-        },
-        "BaryCoordNoPerspAMD": {
-          "value": 4992
-        },
-        "BaryCoordNoPerspCentroidAMD": {
-          "value": 4993
-        },
-        "BaryCoordNoPerspSampleAMD": {
-          "value": 4994
-        },
-        "BaryCoordSmoothAMD": {
-          "value": 4995
-        },
-        "BaryCoordSmoothCentroidAMD": {
-          "value": 4996
-        },
-        "BaryCoordSmoothSampleAMD": {
-          "value": 4997
-        },
-        "BaryCoordPullModelAMD": {
-          "value": 4998
-        }
-      }
-    },
-    "Scope": {
-      "type": "ValueEnum",
-      "values": {
-        "CrossDevice": {
-          "value": 0
-        },
-        "Device": {
-          "value": 1
-        },
-        "Workgroup": {
-          "value": 2
-        },
-        "Subgroup": {
-          "value": 3
-        },
-        "Invocation": {
-          "value": 4
-        },
-        "QueueFamily": {
-          "value": 5
-        },
-        "QueueFamilyKHR": {
-          "value": 5
-        }
-      }
-    },
-    "Capability": {
-      "type": "ValueEnum",
-      "values": {
-        "Matrix": {
-          "value": 0
-        },
-        "Shader": {
-          "value": 1
-        },
-        "Geometry": {
-          "value": 2
-        },
-        "Tessellation": {
-          "value": 3
-        },
-        "Addresses": {
-          "value": 4
-        },
-        "Linkage": {
-          "value": 5
-        },
-        "Kernel": {
-          "value": 6
-        },
-        "Float16": {
-          "value": 9
-        },
-        "Float64": {
-          "value": 10
-        },
-        "Int64": {
-          "value": 11
-        },
-        "Groups": {
-          "value": 18
-        },
-        "AtomicStorage": {
-          "value": 21
-        },
-        "Int16": {
-          "value": 22
-        },
-        "ImageGatherExtended": {
-          "value": 25
-        },
-        "StorageImageMultisample": {
-          "value": 27
-        },
-        "UniformBufferArrayDynamicIndexing": {
-          "value": 28
-        },
-        "SampledImageArrayDynamicIndexing": {
-          "value": 29
-        },
-        "StorageBufferArrayDynamicIndexing": {
-          "value": 30
-        },
-        "StorageImageArrayDynamicIndexing": {
-          "value": 31
-        },
-        "ClipDistance": {
-          "value": 32
-        },
-        "CullDistance": {
-          "value": 33
-        },
-        "SampleRateShading": {
-          "value": 35
-        },
-        "SampledRect": {
-          "value": 37
-        },
-        "Int8": {
-          "value": 39
-        },
-        "InputAttachment": {
-          "value": 40
-        },
-        "SparseResidency": {
-          "value": 41
-        },
-        "MinLod": {
-          "value": 42
-        },
-        "Sampled1D": {
-          "value": 43
-        },
-        "Image1D": {
-          "value": 44
-        },
-        "SampledCubeArray": {
-          "value": 45
-        },
-        "SampledBuffer": {
-          "value": 46
-        },
-        "ImageMSArray": {
-          "value": 48
-        },
-        "StorageImageExtendedFormats": {
-          "value": 49
-        },
-        "ImageQuery": {
-          "value": 50
-        },
-        "DerivativeControl": {
-          "value": 51
-        },
-        "InterpolationFunction": {
-          "value": 52
-        },
-        "TransformFeedback": {
-          "value": 53
-        },
-        "StorageImageReadWithoutFormat": {
-          "value": 55
-        },
-        "StorageImageWriteWithoutFormat": {
-          "value": 56
-        },
-        "GroupNonUniform": {
-          "value": 61
-        },
-        "ShaderLayer": {
-          "value": 69
-        },
-        "ShaderViewportIndex": {
-          "value": 70
-        },
-        "SubgroupBallotKHR": {
-          "value": 4423
-        },
-        "DrawParameters": {
-          "value": 4427
-        },
-        "SubgroupVoteKHR": {
-          "value": 4431
-        },
-        "StorageBuffer16BitAccess": {
-          "value": 4433
-        },
-        "StorageUniformBufferBlock16": {
-          "value": 4433
-        },
-        "StoragePushConstant16": {
-          "value": 4435
-        },
-        "StorageInputOutput16": {
-          "value": 4436
-        },
-        "DeviceGroup": {
-          "value": 4437
-        },
-        "MultiView": {
-          "value": 4439
-        },
-        "VariablePointersStorageBuffer": {
-          "value": 4441
-        },
-        "AtomicStorageOps": {
-          "value": 4445
-        },
-        "SampleMaskPostDepthCoverage": {
-          "value": 4447
-        },
-        "StorageBuffer8BitAccess": {
-          "value": 4448
-        },
-        "StoragePushConstant8": {
-          "value": 4450
-        },
-        "DenormPreserve": {
-          "value": 4464
-        },
-        "DenormFlushToZero": {
-          "value": 4465
-        },
-        "SignedZeroInfNanPreserve": {
-          "value": 4466
-        },
-        "RoundingModeRTE": {
-          "value": 4467
-        },
-        "RoundingModeRTZ": {
-          "value": 4468
-        },
-        "Float16ImageAMD": {
-          "value": 5008
-        },
-        "ImageGatherBiasLodAMD": {
-          "value": 5009
-        },
-        "FragmentMaskAMD": {
-          "value": 5010
-        },
-        "StencilExportEXT": {
-          "value": 5013
-        },
-        "ImageReadWriteLodAMD": {
-          "value": 5015
-        },
-        "ShaderClockKHR": {
-          "value": 5055
-        },
-        "QuadControlKHR": {
-          "value": 5087
-        },
-        "FragmentFullyCoveredEXT": {
-          "value": 5265
-        },
-        "MeshShadingNV": {
-          "value": 5266
-        },
-        "ImageFootprintNV": {
-          "value": 5282
-        },
-        "FragmentBarycentricNV": {
-          "value": 5284
-        },
-        "ComputeDerivativeGroupQuadsNV": {
-          "value": 5288
-        },
-        "FragmentDensityEXT": {
-          "value": 5291
-        },
-        "ShadingRateNV": {
-          "value": 5291
-        },
-        "GroupNonUniformPartitionedNV": {
-          "value": 5297
-        },
-        "ShaderNonUniform": {
-          "value": 5301
-        },
-        "ShaderNonUniformEXT": {
-          "value": 5301
-        },
-        "RuntimeDescriptorArray": {
-          "value": 5302
-        },
-        "RuntimeDescriptorArrayEXT": {
-          "value": 5302
-        },
-        "RayTracingNV": {
-          "value": 5340
-        },
-        "VulkanMemoryModel": {
-          "value": 5345
-        },
-        "VulkanMemoryModelKHR": {
-          "value": 5345
-        },
-        "VulkanMemoryModelDeviceScope": {
-          "value": 5346
-        },
-        "VulkanMemoryModelDeviceScopeKHR": {
-          "value": 5346
-        },
-        "PhysicalStorageBufferAddresses": {
-          "value": 5347
-        },
-        "PhysicalStorageBufferAddressesEXT": {
-          "value": 5347
-        },
-        "ComputeDerivativeGroupLinearNV": {
-          "value": 5350
-        },
-        "CooperativeMatrixNV": {
-          "value": 5357
-        },
-        "FragmentShaderSampleInterlockEXT": {
-          "value": 5363
-        },
-        "FragmentShaderShadingRateInterlockEXT": {
-          "value": 5372
-        },
-        "ShaderSMBuiltinsNV": {
-          "value": 5373
-        },
-        "FragmentShaderPixelInterlockEXT": {
-          "value": 5378
-        },
-        "DemoteToHelperInvocationEXT": {
-          "value": 5379
-        },
-        "SubgroupShuffleINTEL": {
-          "value": 5568
-        },
-        "SubgroupBufferBlockIOINTEL": {
-          "value": 5569
-        },
-        "SubgroupImageBlockIOINTEL": {
-          "value": 5570
-        },
-        "SubgroupImageMediaBlockIOINTEL": {
-          "value": 5579
-        },
-        "IntegerFunctions2INTEL": {
-          "value": 5584
-        },
-        "SubgroupAvcMotionEstimationINTEL": {
-          "value": 5696
-        },
-        "SubgroupAvcMotionEstimationIntraINTEL": {
-          "value": 5697
-        },
-        "SubgroupAvcMotionEstimationChromaINTEL": {
-          "value": 5698
-        }
-      }
-    }
-  },
-  "ext": {
-    "Round": 1,
-    "RoundEven": 2,
-    "Trunc": 3,
-    "FAbs": 4,
-    "SAbs": 5,
-    "FSign": 6,
-    "SSign": 7,
-    "Floor": 8,
-    "Ceil": 9,
-    "Fract": 10,
-    "Radians": 11,
-    "Degrees": 12,
-    "Sin": 13,
-    "Cos": 14,
-    "Tan": 15,
-    "Asin": 16,
-    "Acos": 17,
-    "Atan": 18,
-    "Sinh": 19,
-    "Cosh": 20,
-    "Tanh": 21,
-    "Asinh": 22,
-    "Acosh": 23,
-    "Atanh": 24,
-    "Atan2": 25,
-    "Pow": 26,
-    "Exp": 27,
-    "Log": 28,
-    "Exp2": 29,
-    "Log2": 30,
-    "Sqrt": 31,
-    "InverseSqrt": 32,
-    "Determinant": 33,
-    "MatrixInverse": 34,
-    "Modf": 35,
-    "ModfStruct": 36,
-    "FMin": 37,
-    "UMin": 38,
-    "SMin": 39,
-    "FMax": 40,
-    "UMax": 41,
-    "SMax": 42,
-    "FClamp": 43,
-    "UClamp": 44,
-    "SClamp": 45,
-    "FMix": 46,
-    "IMix": 47,
-    "Step": 48,
-    "SmoothStep": 49,
-    "Fma": 50,
-    "Frexp": 51,
-    "FrexpStruct": 52,
-    "Ldexp": 53,
-    "PackSnorm4x8": 54,
-    "PackUnorm4x8": 55,
-    "PackSnorm2x16": 56,
-    "PackUnorm2x16": 57,
-    "PackHalf2x16": 58,
-    "PackDouble2x32": 59,
-    "UnpackSnorm2x16": 60,
-    "UnpackUnorm2x16": 61,
-    "UnpackHalf2x16": 62,
-    "UnpackSnorm4x8": 63,
-    "UnpackUnorm4x8": 64,
-    "UnpackDouble2x32": 65,
-    "Length": 66,
-    "Distance": 67,
-    "Cross": 68,
-    "Normalize": 69,
-    "FaceForward": 70,
-    "Reflect": 71,
-    "Refract": 72,
-    "FindILsb": 73,
-    "FindSMsb": 74,
-    "FindUMsb": 75,
-    "InterpolateAtCentroid": 76,
-    "InterpolateAtSample": 77,
-    "InterpolateAtOffset": 78,
-    "NMin": 79,
-    "NMax": 80,
-    "NClamp": 81
-  }
-}
diff --git a/tools/sva/src/sva.js b/tools/sva/src/sva.js
deleted file mode 100644
index c76ed29..0000000
--- a/tools/sva/src/sva.js
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2019 Google LLC
-//
-// 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.
-
-import Parser from "./parser.js";
-import Lexer from "./lexer.js";
-import Assembler from "./assembler.js";
-
-import grammar from "./spirv.data.js";
-
-export default class SVA {
-  /**
-   * Attempts to convert |input| SPIR-V assembly into SPIR-V binary.
-   *
-   * @param {String} the input string containing the assembly
-   * @return {Uint32Array|string} returns a Uint32Array containing the binary
-   *                             SPIR-V or a string on error.
-   */
-  static assemble(input) {
-    let l = new Lexer(input);
-    let p = new Parser(grammar, l);
-
-    let ast = p.parse();
-    if (ast === undefined)
-      return p.error;
-
-    let a = new Assembler(ast);
-    return a.assemble();
-  }
-}
diff --git a/tools/sva/src/token.js b/tools/sva/src/token.js
deleted file mode 100644
index 3813191..0000000
--- a/tools/sva/src/token.js
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2019 Google LLC
-//
-// 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.
-
-const TokenType = {
-  kEOF: "end of file",
-  kError: "error",
-
-  kIdentifier: "identifier",
-
-  kIntegerLiteral: "integer_literal",
-  kFloatLiteral: "float_literal",
-  kStringLiteral: "string_literal",
-  kResultId: "result_id",
-
-  kOp: "Op",
-  kEqual: "=",
-  kPipe: "|",
-};
-
-class Token {
-  /**
-   * @param {TokenType} type The type of token
-   * @param {Integer} line The line number this token was on
-   * @param {Any} data Data attached to the token
-   * @param {Integer} bits If the type is a float or integer the bit width
-   */
-  constructor(type, line, data) {
-    this.type_ = type;
-    this.line_ = line;
-    this.data_ = data;
-    this.bits_ = 0;
-  }
-
-  get type() { return this.type_; }
-  get line() { return this.line_; }
-
-  get data() { return this.data_; }
-  set data(val) { this.data_ = val; }
-
-  get bits() { return this.bits_; }
-  set bits(val) { this.bits_ = val; }
-}
-
-export {Token, TokenType};
diff --git a/tools/sva/tests/empty_main.spv_asm b/tools/sva/tests/empty_main.spv_asm
deleted file mode 100644
index ad6e64b..0000000
--- a/tools/sva/tests/empty_main.spv_asm
+++ /dev/null
@@ -1,18 +0,0 @@
-; SPIR-V
-; Version: 1.0
-; Generator: Khronos Glslang Reference Front End; 7
-; Bound: 6
-; Schema: 0
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %main "main"
-               OpExecutionMode %main OriginUpperLeft
-               OpSource GLSL 440
-               OpName %main "main"
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
diff --git a/tools/sva/tests/index.html b/tools/sva/tests/index.html
deleted file mode 100644
index dd02847..0000000
--- a/tools/sva/tests/index.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!doctype html>
-<html>
-<head>
-  <meta charset='utf-8'>
-</head>
-<body>
-  <pre id='code'><code></code></pre>
-
-  <script type="module">
-    let c = document.getElementById('code');
-
-    import SVA from "/build/sva.js";
-
-    let assembly = SVA.assemble("OpCapability Shader");
-    if (typeof assembly === "string") {
-      c.innerText = assembly;
-    } else {
-      c.innerText = Array.from(assembly)
-        .map(b => b.toString(16).padStart(8, "0")).join(" ");
-    }
-  </script>
-</body>
-</html>
diff --git a/tools/sva/tests/simple.spv_asm b/tools/sva/tests/simple.spv_asm
deleted file mode 100644
index b4b3f67..0000000
--- a/tools/sva/tests/simple.spv_asm
+++ /dev/null
@@ -1,30 +0,0 @@
-; SPIR-V
-; Version: 1.0
-; Generator: Khronos Glslang Reference Front End; 7
-; Bound: 14
-; Schema: 0
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %main "main" %gl_FragColor
-               OpExecutionMode %main OriginUpperLeft
-               OpSource GLSL 330
-               OpName %main "main"
-               OpName %gl_FragColor "gl_FragColor"
-               OpDecorate %gl_FragColor Location 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-%gl_FragColor = OpVariable %_ptr_Output_v4float Output
-%float_0_400000006 = OpConstant %float 0.400000006
-%float_0_800000012 = OpConstant %float 0.800000012
-    %float_1 = OpConstant %float 1
-         %13 = OpConstantComposite %v4float %float_0_400000006 %float_0_400000006 %float_0_800000012 %float_1
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpStore %gl_FragColor %13
-               OpReturn
-               OpFunctionEnd
-
diff --git a/tools/sva/tools/process_grammar.rb b/tools/sva/tools/process_grammar.rb
deleted file mode 100755
index 1bbff68..0000000
--- a/tools/sva/tools/process_grammar.rb
+++ /dev/null
@@ -1,119 +0,0 @@
-#!/usr/bin/env ruby
-
-# Copyright 2019 Google LLC
-#
-# 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.
-
-require 'json'
-
-GRAMMAR = "../../external/spirv-headers/include/spirv/unified1/spirv.core.grammar.json"
-GLSL = "../../external/spirv-headers/include/spirv/unified1/extinst.glsl.std.450.grammar.json"
-
-CAPABILITIES = %w(
-      Matrix
-      Shader
-      Sampled1D
-      Image1D
-      DerivativeControl
-      ImageQuery
-      VulkanMemoryModel
-)
-
-g = JSON.parse(File.open(GRAMMAR).read)
-magic = g['magic_number']
-vers = [g['major_version'], g['minor_version']]
-instructions = {}
-
-g['instructions'].each do |inst|
-  if (inst.has_key?('capabilities'))
-    skip = true
-    inst['capabilities'].each do |cap|
-      if CAPABILITIES.include?(cap)
-        skip = false
-        break
-      end
-    end
-    next if skip
-  end
-
-  op = {
-    opcode: inst['opcode'],
-    operands: []
-  }
-
-  if !inst['operands'].nil?
-    inst['operands'].each do |operand|
-      operand.delete('name')
-      op[:operands] << operand
-    end
-  end
-
-  instructions[inst['opname']] = op
-end
-
-operand_kinds = {}
-g['operand_kinds'].each do |op_kind|
-  next if op_kind['category'] !~ /Enum/
-
-  kind = {
-    type: op_kind['category'],
-    values: {}
-  }
-
-  op_kind['enumerants'].each do |enum|
-    if (enum.has_key?('capabilities'))
-      skip = true
-      enum['capabilities'].each do |cap|
-        if CAPABILITIES.include?(cap)
-          skip = false
-          break
-        end
-      end
-      next if skip
-    end
-
-    v = if op_kind['category'] == 'BitEnum'
-      enum['value'].to_i(16)
-    else
-      enum['value'].to_i
-    end
-    params = []
-    if enum.has_key?('parameters')
-      enum['parameters'].each do |param|
-        params << param['kind']
-      end
-    end
-    kind[:values][enum['enumerant']] = {value: v}
-    kind[:values][enum['enumerant']][:params] = params unless params.empty?
-  end
-
-  next if kind[:values].empty?
-  operand_kinds[op_kind['kind']] = kind
-end
-
-# We only support GLSL extensions at the moment.
-ext = {}
-glsl = JSON.parse(File.open(GLSL).read)
-glsl['instructions'].each do |inst|
-  ext[inst['opname']] = inst['opcode']
-end
-
-puts "/*#{g['copyright'].join("\n")}*/"
-puts "\n// THIS FILE IS GENERATED WITH tools/process_grammar.rb\n\n"
-puts "export default " + JSON.pretty_generate({
-  magic: magic,
-  version: vers,
-  instructions: instructions,
-  operand_kinds: operand_kinds,
-  ext: ext
-})
diff --git a/tools/sva/yarn.lock b/tools/sva/yarn.lock
deleted file mode 100644
index 38788e2..0000000
--- a/tools/sva/yarn.lock
+++ /dev/null
@@ -1,1474 +0,0 @@
-# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
-# yarn lockfile v1
-
-
-"@eslint-community/eslint-utils@^4.2.0":
-  version "4.4.0"
-  resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
-  integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
-  dependencies:
-    eslint-visitor-keys "^3.3.0"
-
-"@eslint-community/regexpp@^4.4.0":
-  version "4.5.1"
-  resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.1.tgz#cdd35dce4fa1a89a4fd42b1599eb35b3af408884"
-  integrity sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==
-
-"@eslint/eslintrc@^2.0.3":
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.3.tgz#4910db5505f4d503f27774bf356e3704818a0331"
-  integrity sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==
-  dependencies:
-    ajv "^6.12.4"
-    debug "^4.3.2"
-    espree "^9.5.2"
-    globals "^13.19.0"
-    ignore "^5.2.0"
-    import-fresh "^3.2.1"
-    js-yaml "^4.1.0"
-    minimatch "^3.1.2"
-    strip-json-comments "^3.1.1"
-
-"@eslint/js@8.41.0":
-  version "8.41.0"
-  resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.41.0.tgz#080321c3b68253522f7646b55b577dd99d2950b3"
-  integrity sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==
-
-"@humanwhocodes/config-array@^0.11.8":
-  version "0.11.8"
-  resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9"
-  integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==
-  dependencies:
-    "@humanwhocodes/object-schema" "^1.2.1"
-    debug "^4.1.1"
-    minimatch "^3.0.5"
-
-"@humanwhocodes/module-importer@^1.0.1":
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
-  integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
-
-"@humanwhocodes/object-schema@^1.2.1":
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
-  integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
-
-"@nodelib/fs.scandir@2.1.5":
-  version "2.1.5"
-  resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
-  integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
-  dependencies:
-    "@nodelib/fs.stat" "2.0.5"
-    run-parallel "^1.1.9"
-
-"@nodelib/fs.stat@2.0.5":
-  version "2.0.5"
-  resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
-  integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
-
-"@nodelib/fs.walk@^1.2.8":
-  version "1.2.8"
-  resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
-  integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
-  dependencies:
-    "@nodelib/fs.scandir" "2.1.5"
-    fastq "^1.6.0"
-
-"@zeit/schemas@2.29.0":
-  version "2.29.0"
-  resolved "https://registry.yarnpkg.com/@zeit/schemas/-/schemas-2.29.0.tgz#a59ae6ebfdf4ddc66a876872dd736baa58b6696c"
-  integrity sha512-g5QiLIfbg3pLuYUJPlisNKY+epQJTcMDsOnVNkscrDP1oi7vmJnzOANYJI/1pZcVJ6umUkBv3aFtlg1UvUHGzA==
-
-accepts@~1.3.5:
-  version "1.3.8"
-  resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
-  integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
-  dependencies:
-    mime-types "~2.1.34"
-    negotiator "0.6.3"
-
-acorn-jsx@^5.3.2:
-  version "5.3.2"
-  resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
-  integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
-
-acorn@^8.8.0:
-  version "8.8.2"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
-  integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
-
-ajv@8.11.0:
-  version "8.11.0"
-  resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f"
-  integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==
-  dependencies:
-    fast-deep-equal "^3.1.1"
-    json-schema-traverse "^1.0.0"
-    require-from-string "^2.0.2"
-    uri-js "^4.2.2"
-
-ajv@^6.10.0, ajv@^6.12.4:
-  version "6.12.6"
-  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
-  integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
-  dependencies:
-    fast-deep-equal "^3.1.1"
-    fast-json-stable-stringify "^2.0.0"
-    json-schema-traverse "^0.4.1"
-    uri-js "^4.2.2"
-
-ansi-align@^3.0.1:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59"
-  integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==
-  dependencies:
-    string-width "^4.1.0"
-
-ansi-colors@4.1.1:
-  version "4.1.1"
-  resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
-  integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
-
-ansi-regex@^5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
-  integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
-
-ansi-regex@^6.0.1:
-  version "6.0.1"
-  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a"
-  integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==
-
-ansi-styles@^4.0.0, ansi-styles@^4.1.0:
-  version "4.3.0"
-  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
-  integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
-  dependencies:
-    color-convert "^2.0.1"
-
-ansi-styles@^6.1.0:
-  version "6.2.1"
-  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
-  integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
-
-anymatch@~3.1.2:
-  version "3.1.3"
-  resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
-  integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
-  dependencies:
-    normalize-path "^3.0.0"
-    picomatch "^2.0.4"
-
-arch@^2.2.0:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11"
-  integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==
-
-arg@5.0.2:
-  version "5.0.2"
-  resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c"
-  integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==
-
-argparse@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
-  integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
-
-assertion-error@^1.1.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
-  integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==
-
-balanced-match@^1.0.0:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
-  integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
-
-binary-extensions@^2.0.0:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
-  integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
-
-boxen@7.0.0:
-  version "7.0.0"
-  resolved "https://registry.yarnpkg.com/boxen/-/boxen-7.0.0.tgz#9e5f8c26e716793fc96edcf7cf754cdf5e3fbf32"
-  integrity sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==
-  dependencies:
-    ansi-align "^3.0.1"
-    camelcase "^7.0.0"
-    chalk "^5.0.1"
-    cli-boxes "^3.0.0"
-    string-width "^5.1.2"
-    type-fest "^2.13.0"
-    widest-line "^4.0.1"
-    wrap-ansi "^8.0.1"
-
-brace-expansion@^1.1.7:
-  version "1.1.11"
-  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
-  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
-  dependencies:
-    balanced-match "^1.0.0"
-    concat-map "0.0.1"
-
-brace-expansion@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
-  integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
-  dependencies:
-    balanced-match "^1.0.0"
-
-braces@~3.0.2:
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
-  integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
-  dependencies:
-    fill-range "^7.1.1"
-
-browser-stdout@1.3.1:
-  version "1.3.1"
-  resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
-  integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
-
-bytes@3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
-  integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==
-
-callsites@^3.0.0:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
-  integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
-
-camelcase@^6.0.0:
-  version "6.3.0"
-  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
-  integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
-
-camelcase@^7.0.0:
-  version "7.0.1"
-  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-7.0.1.tgz#f02e50af9fd7782bc8b88a3558c32fd3a388f048"
-  integrity sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==
-
-chai@^4.3.7:
-  version "4.3.7"
-  resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51"
-  integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==
-  dependencies:
-    assertion-error "^1.1.0"
-    check-error "^1.0.2"
-    deep-eql "^4.1.2"
-    get-func-name "^2.0.0"
-    loupe "^2.3.1"
-    pathval "^1.1.1"
-    type-detect "^4.0.5"
-
-chalk-template@0.4.0:
-  version "0.4.0"
-  resolved "https://registry.yarnpkg.com/chalk-template/-/chalk-template-0.4.0.tgz#692c034d0ed62436b9062c1707fadcd0f753204b"
-  integrity sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==
-  dependencies:
-    chalk "^4.1.2"
-
-chalk@5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.0.1.tgz#ca57d71e82bb534a296df63bbacc4a1c22b2a4b6"
-  integrity sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==
-
-chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2:
-  version "4.1.2"
-  resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
-  integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
-  dependencies:
-    ansi-styles "^4.1.0"
-    supports-color "^7.1.0"
-
-chalk@^5.0.1:
-  version "5.2.0"
-  resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.2.0.tgz#249623b7d66869c673699fb66d65723e54dfcfb3"
-  integrity sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==
-
-check-error@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
-  integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==
-
-chokidar@3.5.3:
-  version "3.5.3"
-  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
-  integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
-  dependencies:
-    anymatch "~3.1.2"
-    braces "~3.0.2"
-    glob-parent "~5.1.2"
-    is-binary-path "~2.1.0"
-    is-glob "~4.0.1"
-    normalize-path "~3.0.0"
-    readdirp "~3.6.0"
-  optionalDependencies:
-    fsevents "~2.3.2"
-
-cli-boxes@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-3.0.0.tgz#71a10c716feeba005e4504f36329ef0b17cf3145"
-  integrity sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==
-
-clipboardy@3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-3.0.0.tgz#f3876247404d334c9ed01b6f269c11d09a5e3092"
-  integrity sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==
-  dependencies:
-    arch "^2.2.0"
-    execa "^5.1.1"
-    is-wsl "^2.2.0"
-
-cliui@^7.0.2:
-  version "7.0.4"
-  resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
-  integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==
-  dependencies:
-    string-width "^4.2.0"
-    strip-ansi "^6.0.0"
-    wrap-ansi "^7.0.0"
-
-color-convert@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
-  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
-  dependencies:
-    color-name "~1.1.4"
-
-color-name@~1.1.4:
-  version "1.1.4"
-  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
-  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
-
-compressible@~2.0.16:
-  version "2.0.18"
-  resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
-  integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==
-  dependencies:
-    mime-db ">= 1.43.0 < 2"
-
-compression@1.7.4:
-  version "1.7.4"
-  resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f"
-  integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==
-  dependencies:
-    accepts "~1.3.5"
-    bytes "3.0.0"
-    compressible "~2.0.16"
-    debug "2.6.9"
-    on-headers "~1.0.2"
-    safe-buffer "5.1.2"
-    vary "~1.1.2"
-
-concat-map@0.0.1:
-  version "0.0.1"
-  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
-  integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
-
-content-disposition@0.5.2:
-  version "0.5.2"
-  resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"
-  integrity sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==
-
-cross-spawn@^7.0.2, cross-spawn@^7.0.3:
-  version "7.0.3"
-  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
-  integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
-  dependencies:
-    path-key "^3.1.0"
-    shebang-command "^2.0.0"
-    which "^2.0.1"
-
-debug@2.6.9:
-  version "2.6.9"
-  resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
-  integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
-  dependencies:
-    ms "2.0.0"
-
-debug@4.3.4, debug@^4.1.1, debug@^4.3.2:
-  version "4.3.4"
-  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
-  integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
-  dependencies:
-    ms "2.1.2"
-
-decamelize@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837"
-  integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==
-
-deep-eql@^4.1.2:
-  version "4.1.3"
-  resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d"
-  integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==
-  dependencies:
-    type-detect "^4.0.0"
-
-deep-extend@^0.6.0:
-  version "0.6.0"
-  resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
-  integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
-
-deep-is@^0.1.3:
-  version "0.1.4"
-  resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
-  integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
-
-diff@5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
-  integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
-
-doctrine@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
-  integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
-  dependencies:
-    esutils "^2.0.2"
-
-eastasianwidth@^0.2.0:
-  version "0.2.0"
-  resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
-  integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
-
-emoji-regex@^8.0.0:
-  version "8.0.0"
-  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
-  integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
-
-emoji-regex@^9.2.2:
-  version "9.2.2"
-  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
-  integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
-
-escalade@^3.1.1:
-  version "3.1.1"
-  resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
-  integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
-
-escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
-  integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
-
-eslint-scope@^7.2.0:
-  version "7.2.0"
-  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b"
-  integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==
-  dependencies:
-    esrecurse "^4.3.0"
-    estraverse "^5.2.0"
-
-eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1:
-  version "3.4.1"
-  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994"
-  integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==
-
-eslint@^8.41.0:
-  version "8.41.0"
-  resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.41.0.tgz#3062ca73363b4714b16dbc1e60f035e6134b6f1c"
-  integrity sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==
-  dependencies:
-    "@eslint-community/eslint-utils" "^4.2.0"
-    "@eslint-community/regexpp" "^4.4.0"
-    "@eslint/eslintrc" "^2.0.3"
-    "@eslint/js" "8.41.0"
-    "@humanwhocodes/config-array" "^0.11.8"
-    "@humanwhocodes/module-importer" "^1.0.1"
-    "@nodelib/fs.walk" "^1.2.8"
-    ajv "^6.10.0"
-    chalk "^4.0.0"
-    cross-spawn "^7.0.2"
-    debug "^4.3.2"
-    doctrine "^3.0.0"
-    escape-string-regexp "^4.0.0"
-    eslint-scope "^7.2.0"
-    eslint-visitor-keys "^3.4.1"
-    espree "^9.5.2"
-    esquery "^1.4.2"
-    esutils "^2.0.2"
-    fast-deep-equal "^3.1.3"
-    file-entry-cache "^6.0.1"
-    find-up "^5.0.0"
-    glob-parent "^6.0.2"
-    globals "^13.19.0"
-    graphemer "^1.4.0"
-    ignore "^5.2.0"
-    import-fresh "^3.0.0"
-    imurmurhash "^0.1.4"
-    is-glob "^4.0.0"
-    is-path-inside "^3.0.3"
-    js-yaml "^4.1.0"
-    json-stable-stringify-without-jsonify "^1.0.1"
-    levn "^0.4.1"
-    lodash.merge "^4.6.2"
-    minimatch "^3.1.2"
-    natural-compare "^1.4.0"
-    optionator "^0.9.1"
-    strip-ansi "^6.0.1"
-    strip-json-comments "^3.1.0"
-    text-table "^0.2.0"
-
-esm@^3.2.25:
-  version "3.2.25"
-  resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10"
-  integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==
-
-espree@^9.5.2:
-  version "9.5.2"
-  resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.2.tgz#e994e7dc33a082a7a82dceaf12883a829353215b"
-  integrity sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==
-  dependencies:
-    acorn "^8.8.0"
-    acorn-jsx "^5.3.2"
-    eslint-visitor-keys "^3.4.1"
-
-esquery@^1.4.2:
-  version "1.5.0"
-  resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b"
-  integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==
-  dependencies:
-    estraverse "^5.1.0"
-
-esrecurse@^4.3.0:
-  version "4.3.0"
-  resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
-  integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
-  dependencies:
-    estraverse "^5.2.0"
-
-estraverse@^5.1.0, estraverse@^5.2.0:
-  version "5.3.0"
-  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
-  integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
-
-esutils@^2.0.2:
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
-  integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
-
-execa@^5.1.1:
-  version "5.1.1"
-  resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
-  integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
-  dependencies:
-    cross-spawn "^7.0.3"
-    get-stream "^6.0.0"
-    human-signals "^2.1.0"
-    is-stream "^2.0.0"
-    merge-stream "^2.0.0"
-    npm-run-path "^4.0.1"
-    onetime "^5.1.2"
-    signal-exit "^3.0.3"
-    strip-final-newline "^2.0.0"
-
-fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
-  version "3.1.3"
-  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
-  integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
-
-fast-json-stable-stringify@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
-  integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
-
-fast-levenshtein@^2.0.6:
-  version "2.0.6"
-  resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
-  integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
-
-fast-url-parser@1.1.3:
-  version "1.1.3"
-  resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d"
-  integrity sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==
-  dependencies:
-    punycode "^1.3.2"
-
-fastq@^1.6.0:
-  version "1.15.0"
-  resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a"
-  integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==
-  dependencies:
-    reusify "^1.0.4"
-
-file-entry-cache@^6.0.1:
-  version "6.0.1"
-  resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
-  integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
-  dependencies:
-    flat-cache "^3.0.4"
-
-fill-range@^7.1.1:
-  version "7.1.1"
-  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
-  integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
-  dependencies:
-    to-regex-range "^5.0.1"
-
-find-up@5.0.0, find-up@^5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
-  integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
-  dependencies:
-    locate-path "^6.0.0"
-    path-exists "^4.0.0"
-
-flat-cache@^3.0.4:
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
-  integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==
-  dependencies:
-    flatted "^3.1.0"
-    rimraf "^3.0.2"
-
-flat@^5.0.2:
-  version "5.0.2"
-  resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
-  integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
-
-flatted@^3.1.0:
-  version "3.2.7"
-  resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
-  integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
-
-fs.realpath@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
-  integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
-
-fsevents@~2.3.2:
-  version "2.3.2"
-  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
-  integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
-
-get-caller-file@^2.0.5:
-  version "2.0.5"
-  resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
-  integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
-
-get-func-name@^2.0.0:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41"
-  integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==
-
-get-stream@^6.0.0:
-  version "6.0.1"
-  resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
-  integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
-
-glob-parent@^6.0.2:
-  version "6.0.2"
-  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
-  integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
-  dependencies:
-    is-glob "^4.0.3"
-
-glob-parent@~5.1.2:
-  version "5.1.2"
-  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
-  integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
-  dependencies:
-    is-glob "^4.0.1"
-
-glob@7.2.0:
-  version "7.2.0"
-  resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
-  integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
-  dependencies:
-    fs.realpath "^1.0.0"
-    inflight "^1.0.4"
-    inherits "2"
-    minimatch "^3.0.4"
-    once "^1.3.0"
-    path-is-absolute "^1.0.0"
-
-glob@^7.1.3:
-  version "7.2.3"
-  resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
-  integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
-  dependencies:
-    fs.realpath "^1.0.0"
-    inflight "^1.0.4"
-    inherits "2"
-    minimatch "^3.1.1"
-    once "^1.3.0"
-    path-is-absolute "^1.0.0"
-
-globals@^13.19.0:
-  version "13.20.0"
-  resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82"
-  integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==
-  dependencies:
-    type-fest "^0.20.2"
-
-graphemer@^1.4.0:
-  version "1.4.0"
-  resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
-  integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
-
-has-flag@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
-  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
-
-he@1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
-  integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
-
-human-signals@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
-  integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
-
-ignore@^5.2.0:
-  version "5.2.4"
-  resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324"
-  integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==
-
-import-fresh@^3.0.0, import-fresh@^3.2.1:
-  version "3.3.0"
-  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
-  integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
-  dependencies:
-    parent-module "^1.0.0"
-    resolve-from "^4.0.0"
-
-imurmurhash@^0.1.4:
-  version "0.1.4"
-  resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
-  integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
-
-inflight@^1.0.4:
-  version "1.0.6"
-  resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
-  integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
-  dependencies:
-    once "^1.3.0"
-    wrappy "1"
-
-inherits@2:
-  version "2.0.4"
-  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
-  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
-
-ini@~1.3.0:
-  version "1.3.8"
-  resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
-  integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
-
-is-binary-path@~2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
-  integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
-  dependencies:
-    binary-extensions "^2.0.0"
-
-is-docker@^2.0.0:
-  version "2.2.1"
-  resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
-  integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==
-
-is-extglob@^2.1.1:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
-  integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
-
-is-fullwidth-code-point@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
-  integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
-
-is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
-  version "4.0.3"
-  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
-  integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
-  dependencies:
-    is-extglob "^2.1.1"
-
-is-number@^7.0.0:
-  version "7.0.0"
-  resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
-  integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
-
-is-path-inside@^3.0.3:
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
-  integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
-
-is-plain-obj@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
-  integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
-
-is-port-reachable@4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/is-port-reachable/-/is-port-reachable-4.0.0.tgz#dac044091ef15319c8ab2f34604d8794181f8c2d"
-  integrity sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==
-
-is-stream@^2.0.0:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
-  integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
-
-is-unicode-supported@^0.1.0:
-  version "0.1.0"
-  resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
-  integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
-
-is-wsl@^2.2.0:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271"
-  integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==
-  dependencies:
-    is-docker "^2.0.0"
-
-isexe@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
-  integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
-
-js-yaml@4.1.0, js-yaml@^4.1.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
-  integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
-  dependencies:
-    argparse "^2.0.1"
-
-json-schema-traverse@^0.4.1:
-  version "0.4.1"
-  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
-  integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
-
-json-schema-traverse@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
-  integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
-
-json-stable-stringify-without-jsonify@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
-  integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
-
-levn@^0.4.1:
-  version "0.4.1"
-  resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
-  integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
-  dependencies:
-    prelude-ls "^1.2.1"
-    type-check "~0.4.0"
-
-locate-path@^6.0.0:
-  version "6.0.0"
-  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
-  integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
-  dependencies:
-    p-locate "^5.0.0"
-
-lodash.merge@^4.6.2:
-  version "4.6.2"
-  resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
-  integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
-
-log-symbols@4.1.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
-  integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==
-  dependencies:
-    chalk "^4.1.0"
-    is-unicode-supported "^0.1.0"
-
-loupe@^2.3.1:
-  version "2.3.6"
-  resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53"
-  integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==
-  dependencies:
-    get-func-name "^2.0.0"
-
-merge-stream@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
-  integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
-
-mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
-  version "1.52.0"
-  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
-  integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
-
-mime-db@~1.33.0:
-  version "1.33.0"
-  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db"
-  integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==
-
-mime-types@2.1.18:
-  version "2.1.18"
-  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8"
-  integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==
-  dependencies:
-    mime-db "~1.33.0"
-
-mime-types@~2.1.34:
-  version "2.1.35"
-  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
-  integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
-  dependencies:
-    mime-db "1.52.0"
-
-mimic-fn@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
-  integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
-
-minimatch@3.1.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
-  version "3.1.2"
-  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
-  integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
-  dependencies:
-    brace-expansion "^1.1.7"
-
-minimatch@5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b"
-  integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==
-  dependencies:
-    brace-expansion "^2.0.1"
-
-minimist@^1.2.0:
-  version "1.2.8"
-  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
-  integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
-
-mocha@^10.2.0:
-  version "10.2.0"
-  resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8"
-  integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==
-  dependencies:
-    ansi-colors "4.1.1"
-    browser-stdout "1.3.1"
-    chokidar "3.5.3"
-    debug "4.3.4"
-    diff "5.0.0"
-    escape-string-regexp "4.0.0"
-    find-up "5.0.0"
-    glob "7.2.0"
-    he "1.2.0"
-    js-yaml "4.1.0"
-    log-symbols "4.1.0"
-    minimatch "5.0.1"
-    ms "2.1.3"
-    nanoid "3.3.3"
-    serialize-javascript "6.0.0"
-    strip-json-comments "3.1.1"
-    supports-color "8.1.1"
-    workerpool "6.2.1"
-    yargs "16.2.0"
-    yargs-parser "20.2.4"
-    yargs-unparser "2.0.0"
-
-ms@2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
-  integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
-
-ms@2.1.2:
-  version "2.1.2"
-  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
-  integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
-
-ms@2.1.3:
-  version "2.1.3"
-  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
-  integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
-
-nanoid@3.3.3:
-  version "3.3.3"
-  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25"
-  integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==
-
-natural-compare@^1.4.0:
-  version "1.4.0"
-  resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
-  integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
-
-negotiator@0.6.3:
-  version "0.6.3"
-  resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
-  integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
-
-normalize-path@^3.0.0, normalize-path@~3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
-  integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
-
-npm-run-path@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
-  integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
-  dependencies:
-    path-key "^3.0.0"
-
-on-headers@~1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
-  integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
-
-once@^1.3.0:
-  version "1.4.0"
-  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
-  integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
-  dependencies:
-    wrappy "1"
-
-onetime@^5.1.2:
-  version "5.1.2"
-  resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
-  integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
-  dependencies:
-    mimic-fn "^2.1.0"
-
-optionator@^0.9.1:
-  version "0.9.1"
-  resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
-  integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==
-  dependencies:
-    deep-is "^0.1.3"
-    fast-levenshtein "^2.0.6"
-    levn "^0.4.1"
-    prelude-ls "^1.2.1"
-    type-check "^0.4.0"
-    word-wrap "^1.2.3"
-
-p-limit@^3.0.2:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
-  integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
-  dependencies:
-    yocto-queue "^0.1.0"
-
-p-locate@^5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
-  integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
-  dependencies:
-    p-limit "^3.0.2"
-
-parent-module@^1.0.0:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
-  integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
-  dependencies:
-    callsites "^3.0.0"
-
-path-exists@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
-  integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
-
-path-is-absolute@^1.0.0:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
-  integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
-
-path-is-inside@1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
-  integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==
-
-path-key@^3.0.0, path-key@^3.1.0:
-  version "3.1.1"
-  resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
-  integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
-
-path-to-regexp@3.3.0:
-  version "3.3.0"
-  resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-3.3.0.tgz#f7f31d32e8518c2660862b644414b6d5c63a611b"
-  integrity sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==
-
-pathval@^1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d"
-  integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==
-
-picomatch@^2.0.4, picomatch@^2.2.1:
-  version "2.3.1"
-  resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
-  integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
-
-prelude-ls@^1.2.1:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
-  integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
-
-punycode@^1.3.2:
-  version "1.4.1"
-  resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
-  integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==
-
-punycode@^2.1.0:
-  version "2.3.0"
-  resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
-  integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
-
-queue-microtask@^1.2.2:
-  version "1.2.3"
-  resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
-  integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
-
-randombytes@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
-  integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
-  dependencies:
-    safe-buffer "^5.1.0"
-
-range-parser@1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
-  integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==
-
-rc@^1.0.1, rc@^1.1.6:
-  version "1.2.8"
-  resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
-  integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
-  dependencies:
-    deep-extend "^0.6.0"
-    ini "~1.3.0"
-    minimist "^1.2.0"
-    strip-json-comments "~2.0.1"
-
-readdirp@~3.6.0:
-  version "3.6.0"
-  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
-  integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
-  dependencies:
-    picomatch "^2.2.1"
-
-registry-auth-token@3.3.2:
-  version "3.3.2"
-  resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20"
-  integrity sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==
-  dependencies:
-    rc "^1.1.6"
-    safe-buffer "^5.0.1"
-
-registry-url@3.1.0:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942"
-  integrity sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==
-  dependencies:
-    rc "^1.0.1"
-
-require-directory@^2.1.1:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
-  integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
-
-require-from-string@^2.0.2:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
-  integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
-
-resolve-from@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
-  integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
-
-reusify@^1.0.4:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
-  integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
-
-rimraf@^3.0.2:
-  version "3.0.2"
-  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
-  integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
-  dependencies:
-    glob "^7.1.3"
-
-rollup@^3.29.5:
-  version "3.29.5"
-  resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.5.tgz#8a2e477a758b520fb78daf04bca4c522c1da8a54"
-  integrity sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==
-  optionalDependencies:
-    fsevents "~2.3.2"
-
-run-parallel@^1.1.9:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
-  integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
-  dependencies:
-    queue-microtask "^1.2.2"
-
-safe-buffer@5.1.2:
-  version "5.1.2"
-  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
-  integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
-
-safe-buffer@^5.0.1, safe-buffer@^5.1.0:
-  version "5.2.1"
-  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
-  integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
-
-serialize-javascript@6.0.0:
-  version "6.0.0"
-  resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8"
-  integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==
-  dependencies:
-    randombytes "^2.1.0"
-
-serve-handler@6.1.5:
-  version "6.1.5"
-  resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.5.tgz#a4a0964f5c55c7e37a02a633232b6f0d6f068375"
-  integrity sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==
-  dependencies:
-    bytes "3.0.0"
-    content-disposition "0.5.2"
-    fast-url-parser "1.1.3"
-    mime-types "2.1.18"
-    minimatch "3.1.2"
-    path-is-inside "1.0.2"
-    path-to-regexp "3.3.0"
-    range-parser "1.2.0"
-
-serve@^14.2.0:
-  version "14.2.0"
-  resolved "https://registry.yarnpkg.com/serve/-/serve-14.2.0.tgz#3d768e88fa13ad8644f2393599189707176e66b8"
-  integrity sha512-+HOw/XK1bW8tw5iBilBz/mJLWRzM8XM6MPxL4J/dKzdxq1vfdEWSwhaR7/yS8EJp5wzvP92p1qirysJvnEtjXg==
-  dependencies:
-    "@zeit/schemas" "2.29.0"
-    ajv "8.11.0"
-    arg "5.0.2"
-    boxen "7.0.0"
-    chalk "5.0.1"
-    chalk-template "0.4.0"
-    clipboardy "3.0.0"
-    compression "1.7.4"
-    is-port-reachable "4.0.0"
-    serve-handler "6.1.5"
-    update-check "1.5.4"
-
-shebang-command@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
-  integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
-  dependencies:
-    shebang-regex "^3.0.0"
-
-shebang-regex@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
-  integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
-
-signal-exit@^3.0.3:
-  version "3.0.7"
-  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
-  integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
-
-string-width@^4.1.0, string-width@^4.2.0:
-  version "4.2.3"
-  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
-  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
-  dependencies:
-    emoji-regex "^8.0.0"
-    is-fullwidth-code-point "^3.0.0"
-    strip-ansi "^6.0.1"
-
-string-width@^5.0.1, string-width@^5.1.2:
-  version "5.1.2"
-  resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
-  integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==
-  dependencies:
-    eastasianwidth "^0.2.0"
-    emoji-regex "^9.2.2"
-    strip-ansi "^7.0.1"
-
-strip-ansi@^6.0.0, strip-ansi@^6.0.1:
-  version "6.0.1"
-  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
-  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
-  dependencies:
-    ansi-regex "^5.0.1"
-
-strip-ansi@^7.0.1:
-  version "7.1.0"
-  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
-  integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==
-  dependencies:
-    ansi-regex "^6.0.1"
-
-strip-final-newline@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
-  integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
-
-strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
-  version "3.1.1"
-  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
-  integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
-
-strip-json-comments@~2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
-  integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
-
-supports-color@8.1.1:
-  version "8.1.1"
-  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
-  integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
-  dependencies:
-    has-flag "^4.0.0"
-
-supports-color@^7.1.0:
-  version "7.2.0"
-  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
-  integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
-  dependencies:
-    has-flag "^4.0.0"
-
-text-table@^0.2.0:
-  version "0.2.0"
-  resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
-  integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
-
-to-regex-range@^5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
-  integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
-  dependencies:
-    is-number "^7.0.0"
-
-type-check@^0.4.0, type-check@~0.4.0:
-  version "0.4.0"
-  resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
-  integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
-  dependencies:
-    prelude-ls "^1.2.1"
-
-type-detect@^4.0.0, type-detect@^4.0.5:
-  version "4.0.8"
-  resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
-  integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
-
-type-fest@^0.20.2:
-  version "0.20.2"
-  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
-  integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
-
-type-fest@^2.13.0:
-  version "2.19.0"
-  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b"
-  integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==
-
-update-check@1.5.4:
-  version "1.5.4"
-  resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.5.4.tgz#5b508e259558f1ad7dbc8b4b0457d4c9d28c8743"
-  integrity sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==
-  dependencies:
-    registry-auth-token "3.3.2"
-    registry-url "3.1.0"
-
-uri-js@^4.2.2:
-  version "4.4.1"
-  resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
-  integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
-  dependencies:
-    punycode "^2.1.0"
-
-vary@~1.1.2:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
-  integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
-
-which@^2.0.1:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
-  integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
-  dependencies:
-    isexe "^2.0.0"
-
-widest-line@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-4.0.1.tgz#a0fc673aaba1ea6f0a0d35b3c2795c9a9cc2ebf2"
-  integrity sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==
-  dependencies:
-    string-width "^5.0.1"
-
-word-wrap@^1.2.3:
-  version "1.2.4"
-  resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f"
-  integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==
-
-workerpool@6.2.1:
-  version "6.2.1"
-  resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
-  integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==
-
-wrap-ansi@^7.0.0:
-  version "7.0.0"
-  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
-  integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
-  dependencies:
-    ansi-styles "^4.0.0"
-    string-width "^4.1.0"
-    strip-ansi "^6.0.0"
-
-wrap-ansi@^8.0.1:
-  version "8.1.0"
-  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
-  integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==
-  dependencies:
-    ansi-styles "^6.1.0"
-    string-width "^5.0.1"
-    strip-ansi "^7.0.1"
-
-wrappy@1:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
-  integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
-
-y18n@^5.0.5:
-  version "5.0.8"
-  resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
-  integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
-
-yargs-parser@20.2.4:
-  version "20.2.4"
-  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"
-  integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==
-
-yargs-parser@^20.2.2:
-  version "20.2.9"
-  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
-  integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
-
-yargs-unparser@2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb"
-  integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==
-  dependencies:
-    camelcase "^6.0.0"
-    decamelize "^4.0.0"
-    flat "^5.0.2"
-    is-plain-obj "^2.1.0"
-
-yargs@16.2.0:
-  version "16.2.0"
-  resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
-  integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
-  dependencies:
-    cliui "^7.0.2"
-    escalade "^3.1.1"
-    get-caller-file "^2.0.5"
-    require-directory "^2.1.1"
-    string-width "^4.2.0"
-    y18n "^5.0.5"
-    yargs-parser "^20.2.2"
-
-yocto-queue@^0.1.0:
-  version "0.1.0"
-  resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
-  integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
diff --git a/tools/util/flags.cpp b/tools/util/flags.cpp
index 11b8967..9c99677 100644
--- a/tools/util/flags.cpp
+++ b/tools/util/flags.cpp
@@ -14,6 +14,8 @@
 
 #include "flags.h"
 
+#include <algorithm>
+#include <cerrno>
 #include <cstdlib>
 #include <cstring>
 #include <iostream>
diff --git a/tools/val/val.cpp b/tools/val/val.cpp
index c75c617..377fd0b 100644
--- a/tools/val/val.cpp
+++ b/tools/val/val.cpp
@@ -15,6 +15,7 @@
 #include <cassert>
 #include <cstdio>
 #include <cstring>
+#include <filesystem>
 #include <iostream>
 #include <vector>
 
@@ -27,12 +28,15 @@
 void print_usage(char* argv0) {
   std::string target_env_list = spvTargetEnvList(36, 105);
   printf(
-      R"(%s - Validate a SPIR-V binary file.
+      R"(%s - Validate a SPIR-V binary file(s).
 
-USAGE: %s [options] [<filename>]
+USAGE: %s [options] [<path>]
 
-The SPIR-V binary is read from <filename>. If no file is specified,
-or if the filename is "-", then the binary is read from standard input.
+The SPIR-V binary is read from <path>. If no path is specified,
+or if the path is "-", then the binary is read from standard input.
+The <path> parameter may also specify a directory; in this case,
+the tool will recursively process all regular files with the .spv
+extension within that directory.
 
 NOTE: The validator is a work in progress.
 
@@ -66,6 +70,10 @@
                                    members.
   --allow-localsizeid              Allow use of the LocalSizeId decoration where it would otherwise not
                                    be allowed by the target environment.
+  --allow-offset-texture-operand   Allow use of the Offset texture operands where it would otherwise not
+                                   be allowed by the target environment.
+  --allow-vulkan-32-bit-bitwise    Allow use of non-32 bit for the Base operand where it would otherwise
+                                   not be allowed by the target environment.
   --before-hlsl-legalization       Allows code patterns that are intended to be
                                    fixed by spirv-opt's legalization passes.
   --version                        Display validator version information.
@@ -75,6 +83,53 @@
       argv0, argv0, target_env_list.c_str());
 }
 
+bool process_single_file(const char* filename, spv_target_env& target_env,
+                         spvtools::ValidatorOptions& options,
+                         bool use_default_msg_consumer) {
+  std::vector<uint32_t> contents;
+  if (!ReadBinaryFile(filename, &contents)) return false;
+
+  spvtools::SpirvTools tools(target_env);
+
+  // Use a lambda expression here so filename can be captured. Messages use a
+  // fairly standard notation of `filename:line`.
+  auto CLIMessageConsumerWithFilename =
+      [filename](spv_message_level_t level, const char*,
+                 const spv_position_t& position, const char* message) {
+        const char* pretty_filename = filename;
+        if (!filename || 0 == strcmp(filename, "-")) {
+          pretty_filename = "stdin";
+        }
+
+        switch (level) {
+          case SPV_MSG_FATAL:
+          case SPV_MSG_INTERNAL_ERROR:
+          case SPV_MSG_ERROR:
+            std::cerr << "error: " << pretty_filename << ":" << position.index
+                      << ": " << message << std::endl;
+            break;
+          case SPV_MSG_WARNING:
+            std::cout << "warning: " << pretty_filename << ":" << position.index
+                      << ": " << message << std::endl;
+            break;
+          case SPV_MSG_INFO:
+            std::cout << "info: " << pretty_filename << ":" << position.index
+                      << ": " << message << std::endl;
+            break;
+          default:
+            break;
+        }
+      };
+
+  if (use_default_msg_consumer) {
+    tools.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
+  } else {
+    tools.SetMessageConsumer(CLIMessageConsumerWithFilename);
+  }
+
+  return tools.Validate(contents.data(), contents.size(), options);
+}
+
 int main(int argc, char** argv) {
   const char* inFile = nullptr;
   spv_target_env target_env = SPV_ENV_UNIVERSAL_1_6;
@@ -111,7 +166,7 @@
         printf("%s\n", spvSoftwareVersionDetailsString());
         printf(
             "Targets:\n  %s\n  %s\n  %s\n  %s\n  %s\n  %s\n  %s\n  %s\n  %s\n  "
-            "%s\n  %s\n  %s\n  %s\n",
+            "%s\n  %s\n  %s\n  %s %s\n",
             spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_0),
             spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1),
             spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_2),
@@ -124,7 +179,8 @@
             spvTargetEnvDescription(SPV_ENV_VULKAN_1_1),
             spvTargetEnvDescription(SPV_ENV_VULKAN_1_1_SPIRV_1_4),
             spvTargetEnvDescription(SPV_ENV_VULKAN_1_2),
-            spvTargetEnvDescription(SPV_ENV_VULKAN_1_3));
+            spvTargetEnvDescription(SPV_ENV_VULKAN_1_3),
+            spvTargetEnvDescription(SPV_ENV_VULKAN_1_4));
         continue_processing = false;
         return_code = 0;
       } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) {
@@ -160,6 +216,10 @@
         options.SetSkipBlockLayout(true);
       } else if (0 == strcmp(cur_arg, "--allow-localsizeid")) {
         options.SetAllowLocalSizeId(true);
+      } else if (0 == strcmp(cur_arg, "--allow-offset-texture-operand")) {
+        options.SetAllowOffsetTextureOperand(true);
+      } else if (0 == strcmp(cur_arg, "--allow-vulkan-32-bit-bitwise")) {
+        options.SetAllowVulkan32BitBitwise(true);
       } else if (0 == strcmp(cur_arg, "--relax-struct-store")) {
         options.SetRelaxStructStore(true);
       } else if (0 == cur_arg[1]) {
@@ -192,13 +252,35 @@
     return return_code;
   }
 
-  std::vector<uint32_t> contents;
-  if (!ReadBinaryFile(inFile, &contents)) return 1;
+  if (inFile &&
+      std::filesystem::is_directory(std::filesystem::status(inFile))) {
+    const std::filesystem::path dir(inFile);
+    bool succeed = true;
+    for (auto const& entry :
+         std::filesystem::recursive_directory_iterator(dir)) {
+      if (!entry.is_regular_file()) {
+        continue;
+      }
 
-  spvtools::SpirvTools tools(target_env);
-  tools.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
+      std::filesystem::path filepath = entry.path();
 
-  bool succeed = tools.Validate(contents.data(), contents.size(), options);
+      if (filepath.extension() != ".spv") continue;
 
-  return !succeed;
+      // Copy the string, because in C++20 the result type of
+      // std::filesystem::path::u8string changes type from std::string to
+      // std::u8string, and the pointer type ends up incompatible. Normalize
+      // to std::string first via copying.
+      const auto filepath_u8str = filepath.u8string();
+      const std::string filepath_str(filepath_u8str.begin(),
+                                     filepath_u8str.end());
+      if (!process_single_file(filepath_str.c_str(), target_env, options,
+                               false)) {
+        succeed = false;
+      }
+    }
+
+    return !succeed;
+  }
+
+  return !process_single_file(inFile, target_env, options, true);
 }
diff --git a/utils/Table/Context.py b/utils/Table/Context.py
new file mode 100755
index 0000000..086ab8d
--- /dev/null
+++ b/utils/Table/Context.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python3
+# Copyright 2025 Google LLC
+
+# 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.
+
+#from typing import *
+from enum import IntEnum
+from typing import Dict, List
+from . IndexRange import *
+from . StringList import *
+
+
+class Context():
+    """
+    Contains global tables for strings, and lists-of-strings.
+    It contains:
+
+        - string_buffer: A list of null-terminated strings. The list is
+          partitioned into contiguous segments dedicated to a specific
+          kind of string, e.g.:
+            - all instruction opcodes
+            - all extension names
+            - all aliases for a given enum
+            - all enum names for a first operand type
+            - all enum names for a second operand type, etc.
+          Strings are sorted within each segment.
+
+        - string_total_len: The sum of lengths of strings in string_buffer.
+
+        - strings: Maps a string to an IndexRange indicating where the string
+          can be found in the (future) concatenation of all strings in the
+          string_buffer.
+
+        - range_buffer: A dictionary mapping a string kind to a list IndexRange
+              objects R. Each R is one of:
+
+            - An index range referencing one string as it appears in the
+              (future) concatenation of all strings in the string_buffer.
+              In this case R represents a single string.
+
+            - An index range referencing earlier elements in range_buffer[kind]
+              itself.  In this case R represents a list of strings.
+
+        - ranges:  A dictionary mapping a string kind and lists-of-strings
+            to its encoding in the range_buffer array.
+
+            It is a two-level mapping of Python type:
+
+                Dict[str,dict[StringList,IndexRange]]
+
+            where
+
+                ranges[kind][list of strings] = an IndexRange
+
+            The 'kind' string encodes a purpose including:
+                - opcodes: the list of instruction opcode strings.
+                - the list of aliases for an opcode, or an enum
+                - an operand type such as 'SPV_OPERAND_TYPE_DIMENSIONALITY':
+                  the list of operand type names, e.g. '2D', '3D', 'Cube',
+                  'Rect', etc. in the case of SPV_OPERAND_TYPE_DIMENSIONALITY.
+            By convention, the 'kind' string should be a singular noun for
+            the type of object named by each member of the list.
+
+            The IndexRange leaf value encodes a list of strings as in the
+            second case described for 'range_buffer'.
+
+    """
+    def __init__(self) -> None:
+        self.string_total_len: int = 0  # Sum of  lengths of all strings in string_buffer
+        self.string_buffer: List[str] = []
+        self.strings: Dict[str, IndexRange] = {}
+        self.ir_to_string: Dict[IndexRange, str] = {} # Inverse of self.strings
+
+        self.range_buffer: Dict[str,List[IndexRange]] = {}
+        # We need StringList here because it's hashable, and so it
+        # can be used as the key for a dict.
+        self.ranges: Dict[str,Dict[StringList,IndexRange]] = {}
+
+    def GetString(self, ir: IndexRange) -> str:
+        if ir in self.ir_to_string:
+            return self.ir_to_string[ir]
+        raise Exception("unregistered index range {}".format(str(ir)))
+
+    def AddString(self, s: str) -> IndexRange:
+        """
+        Adds or finds a string in the string_buffer.
+        Returns its IndexRange.
+        """
+        if s in self.strings:
+            return self.strings[s]
+        # Allocate space, including for the terminating null.
+        s_space: int = len(s) + 1
+        ir = IndexRange(self.string_total_len, s_space)
+        self.strings[s] = ir
+        self.ir_to_string[ir] = s
+        self.string_total_len += s_space
+        self.string_buffer.append(s)
+        return ir
+
+    def AddStringList(self, kind: str, words: List[str]) -> IndexRange:
+        """
+        Ensures a list of strings is recorded in range_buffer[kind], and
+        returns its location in the range_buffer[kind].
+        As a side effect, also ensures each string in the list is in
+        the string_buffer.
+        """
+        l = StringList(words)
+
+        entry: Dict[StringList, IndexRange] = self.ranges.get(kind, {})
+        if kind not in self.ranges:
+            self.ranges[kind] = entry
+            self.range_buffer[kind] = []
+
+        if l in entry:
+            return entry[l]
+        new_ranges = [self.AddString(s) for s in l]
+        ir = IndexRange(len(self.range_buffer[kind]), len(new_ranges))
+        self.range_buffer[kind].extend(new_ranges)
+        entry[l] = ir
+        return ir
+
+    def dump(self) -> None:
+        print("string_total_len: {}".format(self.string_total_len))
+
+        sbi = 0
+        print("string_buffer:")
+        for sb in self.string_buffer:
+            print("  {}: '{}'".format(sbi, sb))
+            sbi += len(sb) + 1
+        print("")
+
+        s = []
+        for k,v in self.strings.items():
+            s.append("'{}': {}".format(k,str(v)))
+        print("strings:\n  {}\n".format('\n  '.join(s)))
+
+        for rbk, rbv in self.range_buffer.items():
+            print("range_buffer[{}]:".format(rbk))
+            i: int = 0
+            for r in rbv:
+                print("  {} {}: {}".format(rbk, i, str(r)))
+                i += 1
+            print("")
+
+        for rk, rv in self.ranges.items():
+            for key,val in rv.items():
+                print("ranges[{}][{}]: {}".format(str(rk),str(key), str(val)))
+        print("")
+
+
diff --git a/utils/Table/Context_test.py b/utils/Table/Context_test.py
new file mode 100755
index 0000000..a168bf7
--- /dev/null
+++ b/utils/Table/Context_test.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python3
+# Copyright 2025 Google LLC
+
+# 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.
+
+import unittest
+from . Context import Context
+from . IndexRange import IndexRange
+from . StringList import StringList
+
+class TestCreate(unittest.TestCase):
+  def test_creation(self) -> None:
+    x = Context()
+    self.assertIsInstance(x.string_total_len, int)
+    self.assertIsInstance(x.string_buffer, list)
+    self.assertIsInstance(x.strings, dict)
+    self.assertEqual(x.string_total_len, 0)
+    self.assertEqual(x.string_buffer, [])
+    self.assertEqual(x.strings, {})
+
+class TestString(unittest.TestCase):
+  def test_AddString_new(self) -> None:
+    x = Context()
+    abc_ir = x.AddString("abc")
+    self.assertEqual(abc_ir, IndexRange(0,4))
+    self.assertEqual(x.string_total_len, 4)
+    self.assertEqual(x.string_buffer, ["abc"])
+    self.assertEqual(x.strings, {"abc": IndexRange(0,4)})
+
+    qz_ir = x.AddString("qz")
+    self.assertEqual(qz_ir, IndexRange(4,3))
+    self.assertEqual(x.string_total_len, 7)
+    self.assertEqual(x.string_buffer, ["abc", "qz"])
+    self.assertEqual(x.strings, {"abc": IndexRange(0,4), "qz": IndexRange(4,3)})
+
+    empty_ir = x.AddString("")
+    self.assertEqual(empty_ir, IndexRange(7,1))
+    self.assertEqual(x.string_total_len, 8)
+    self.assertEqual(x.string_buffer, ["abc", "qz", ""])
+    self.assertEqual(x.strings, {"abc": IndexRange(0,4), "qz": IndexRange(4,3), "": IndexRange(7,1)})
+
+  def test_AddString_idempotent(self) -> None:
+    x = Context()
+    abc_ir = x.AddString("abc")
+    self.assertEqual(abc_ir, IndexRange(0,4))
+    self.assertEqual(x.string_total_len, 4)
+    self.assertEqual(x.string_buffer, ["abc"])
+    self.assertEqual(x.strings, {"abc": IndexRange(0,4)})
+
+    abc_ir = x.AddString("abc")
+    self.assertEqual(abc_ir, IndexRange(0,4))
+    self.assertEqual(x.string_total_len, 4)
+    self.assertEqual(x.string_buffer, ["abc"])
+    self.assertEqual(x.strings, {"abc": IndexRange(0,4)})
+
+class TestStringList(unittest.TestCase):
+  def test_AddStringList_empty(self) -> None:
+    x = Context()
+    x_ir = x.AddStringList('x', [])
+    self.assertEqual(x_ir, IndexRange(0,0))
+    self.assertEqual(x.string_buffer, [])
+    self.assertEqual(x.range_buffer, { 'x': [] })
+    self.assertEqual(x.ranges, {'x': {StringList([]): IndexRange(0,0)}})
+
+  def test_AddgStringList_nonempty(self) -> None:
+    x = Context()
+    x_ir = x.AddStringList('x', ["abc", "def"])
+
+    self.assertEqual(x_ir, IndexRange(0,2))
+    self.assertEqual(x.range_buffer, {'x': [IndexRange(0,4), IndexRange(4,4)]})
+    self.assertEqual(x.ranges, {'x': {StringList(['abc','def']): IndexRange(0,2)}})
+
+  def test_AddgStringList_nonempty_idempotent(self) -> None:
+    x = Context()
+    x_ir = x.AddStringList('x', ["abc", "def"])
+    y_ir = x.AddStringList('x', ["abc", "def"])
+
+    self.assertEqual(x_ir, IndexRange(0,2))
+    self.assertEqual(y_ir, IndexRange(0,2))
+    self.assertEqual(x.range_buffer, {'x': [IndexRange(0,4), IndexRange(4,4)]})
+    self.assertEqual(x.ranges, {'x': {StringList(['abc','def']): IndexRange(0,2)}})
+
+  def test_AddgStringList_nonempty_does_not_sort(self) -> None:
+    x = Context()
+    x_ir = x.AddStringList('x', ["abc", "def"])
+    y_ir = x.AddStringList('x', ["def", "abc"])
+
+    self.assertEqual(x_ir, IndexRange(0,2))
+    self.assertEqual(y_ir, IndexRange(2,2))
+    self.assertEqual(x.range_buffer, {'x': [IndexRange(0,4),
+                                            IndexRange(4,4),
+                                            IndexRange(4,4),
+                                            IndexRange(0,4)]})
+    self.assertEqual(x.ranges, {'x':{StringList(['abc','def']): IndexRange(0,2),
+                                     StringList(['def','abc']): IndexRange(2,2)}})
+
+  def test_AddgStringList_separate_by_kind(self) -> None:
+    x = Context()
+    x_ir = x.AddStringList('x', ["abc", "def"])
+    y_ir = x.AddStringList('y', ["ghi", "abc"])
+
+    self.assertEqual(x_ir, IndexRange(0,2))
+    self.assertEqual(y_ir, IndexRange(0,2))
+    self.assertEqual(x.range_buffer,
+                     {'x': [IndexRange(0,4), IndexRange(4,4)],
+                      'y': [IndexRange(8,4), IndexRange(0,4)]})
+    self.assertEqual(x.ranges, {'x': {StringList(['abc','def']): IndexRange(0,2)},
+                                'y': {StringList(['ghi','abc']): IndexRange(0,2)}})
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/utils/Table/IndexRange.py b/utils/Table/IndexRange.py
new file mode 100755
index 0000000..ba29ee2
--- /dev/null
+++ b/utils/Table/IndexRange.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python3
+# Copyright 2025 Google LLC
+
+# 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.
+
+from typing import Any
+
+class IndexRange():
+  def __init__(self, first: int, count: int) -> None:
+    self.first = first
+    self.count = count
+    if first < 0:
+      raise Exception("invalid arg: first {} must be non-negative".format(first))
+    if count < 0:
+      raise Exception("invalid arg: count {} must be non-negative".format(count))
+
+  def __eq__(self, other: Any) -> bool:
+    return isinstance(other, IndexRange) and self.first == other.first and self.count == other.count
+
+  def __hash__(self) -> int:
+    return hash("{} {}".format(self.first, self.count))
+
+  def __str__(self) -> str:
+    return "IR({}, {})".format(self.first, self.count)
diff --git a/utils/Table/IndexRange_test.py b/utils/Table/IndexRange_test.py
new file mode 100755
index 0000000..16f38e4
--- /dev/null
+++ b/utils/Table/IndexRange_test.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+# Copyright 2025 Google LLC
+
+# 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.
+
+import unittest
+from . IndexRange import IndexRange
+
+class TestIndexRange(unittest.TestCase):
+  def test_creation(self) -> None:
+    x: IndexRange = IndexRange(4,5);
+    self.assertEqual(x.first, 4)
+    self.assertEqual(x.count, 5)
+
+  def test_creation_bad_first(self) -> None:
+    self.assertRaises(Exception, IndexRange, -1, 5)
+
+  def test_creation_bad_count(self) -> None:
+    self.assertRaises(Exception, IndexRange, 1, -5)
+
+  def test_distinct(self) -> None:
+    x = IndexRange(4, 5);
+    y = IndexRange(6, 7);
+    self.assertNotEqual(x.first, y.first)
+    self.assertNotEqual(x.count, y.count)
+
+  def test_equality(self) -> None:
+    self.assertEqual(IndexRange(4,5), IndexRange(4,5))
+    self.assertNotEqual(IndexRange(4,5), IndexRange(4,7))
+    self.assertNotEqual(IndexRange(7,5), IndexRange(4,5))
+
+  def test_hash_heuristic(self) -> None:
+    x = hash(IndexRange(4,5))
+    y = hash(IndexRange(4,5))
+    z = hash(IndexRange(6,7))
+    self.assertEqual(x, y)
+    self.assertNotEqual(x, z)
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/utils/Table/Operand.py b/utils/Table/Operand.py
new file mode 100755
index 0000000..8f5fc49
--- /dev/null
+++ b/utils/Table/Operand.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+# Copyright 2025 Google LLC
+
+# 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.
+
+from typing import List
+
+class Operand():
+    def __init__(self, json: dict) -> None:
+        val = json.get('value',None)
+
+        self._obj = json
+
+    @property
+    def enumerant(self) -> str:
+        result = self._obj.get('enumerant', None)
+        if result is None:
+            raise Exception("operand needs an enumerant string")
+        return result
+
+    @property
+    def value(self) -> int:
+        val: str|int = self._obj['value']
+        if isinstance(val, int):
+            return val
+        elif isinstance(val,str):
+            if val.startswith("0x"):
+                return int(val, 16)
+            else:
+                return int(val, 10)
+        else:
+            raise Exception("operand needs a value integer or string")
+
+    @property
+    def capabilities(self) -> List[str]:
+        return self._obj.get('capabilities',[])
+
+    @property
+    def extensions(self) -> List[str]:
+        return self._obj.get('extensions',[])
+
+    @property
+    def aliases(self) -> List[str]:
+        return self._obj.get('aliases',[])
+
+    @property
+    def parameters(self) -> List[dict]:
+        return self._obj.get('parameters',[])
+
+    @property
+    def version(self): #   -> str | None  # Invald in Python 3.8
+        return self._obj.get('version',None)
+
+    @property
+    def lastVersion(self): #  -> str | None # Invalid in Python 3.8
+        return self._obj.get('lastVersion',None)
diff --git a/utils/Table/Operand_test.py b/utils/Table/Operand_test.py
new file mode 100755
index 0000000..30a197c
--- /dev/null
+++ b/utils/Table/Operand_test.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+# Copyright 2025 Google LLC
+
+# 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.
+
+import unittest
+from . Operand import *
+
+class TestOperand(unittest.TestCase):
+    def test_enumerant(self) -> None:
+        x = Operand({'enumerant': 'abc'});
+        self.assertEqual(x.enumerant, 'abc');
+
+    def test_value_decimal(self) -> None:
+        x = Operand({'value': 123});
+        self.assertEqual(x.value, 123);
+
+    def test_value_str_hex(self) -> None:
+        x = Operand({'value': "0x0101"});
+        self.assertEqual(x.value, 257);
+
+    def test_value_str_dec(self) -> None:
+        x = Operand({'value': "0101"});
+        self.assertEqual(x.value, 101);
+
+    def test_value_str_invalid_dec(self) -> None:
+        x = Operand({'value': "01ab"});
+        self.assertRaises(Exception, lambda y: x.value, 0);
+
+    def test_value_str_invalid_hex(self) -> None:
+        x = Operand({'value': "0x010j"});
+        self.assertRaises(Exception, lambda y: x.value, 0);
+
+    def test_capabilities_absent(self) -> None:
+        x = Operand({});
+        self.assertEqual(x.capabilities, []);
+
+    def test_capabilities_present(self) -> None:
+        x = Operand({'capabilities': ['abc', 'def']});
+        self.assertEqual(x.capabilities, ['abc', 'def']);
+
+    def test_extensions_absent(self) -> None:
+        x = Operand({});
+        self.assertEqual(x.extensions, []);
+
+    def test_extensions_present(self) -> None:
+        x = Operand({'extensions': ['abc', 'def']});
+        self.assertEqual(x.extensions, ['abc', 'def']);
+
+    def test_aliases_absent(self) -> None:
+        x = Operand({});
+        self.assertEqual(x.aliases, []);
+
+    def test_aliases_present(self) -> None:
+        x = Operand({'aliases': ['abc', 'def']});
+        self.assertEqual(x.aliases, ['abc', 'def']);
+
+    def test_parameters_absent(self) -> None:
+        x = Operand({});
+        self.assertEqual(x.parameters, []);
+
+    def test_parameters_present(self) -> None:
+        x = Operand({'parameters': ['abc', 'def']});
+        self.assertEqual(x.parameters, ['abc', 'def']);
+
+    def test_version_absent(self) -> None:
+        x = Operand({});
+        self.assertEqual(x.version, None);
+
+    def test_version_present(self) -> None:
+        x = Operand({'version': '1.0'});
+        self.assertEqual(x.version, '1.0');
+
+    def test_lastVersion_absent(self) -> None:
+        x = Operand({});
+        self.assertEqual(x.lastVersion, None);
+
+    def test_lastVersion_present(self) -> None:
+        x = Operand({'lastVersion': '1.3'});
+        self.assertEqual(x.lastVersion, '1.3');
+
+    def test_all_propertites(self) -> None:
+        x = Operand({'enumerant': 'Foobar',
+                         'value': 12,
+                         'capabilities': ["yes"],
+                         'extensions': ["SPV_FOOBAR_baz_bat"],
+                         'version': "1.0",
+                         'lastVersion': "1.3",
+                         });
+        self.assertEqual(x.enumerant, 'Foobar');
+        self.assertEqual(x.value, 12);
+        self.assertEqual(x.capabilities, ["yes"]);
+        self.assertEqual(x.extensions, ["SPV_FOOBAR_baz_bat"]);
+        self.assertEqual(x.version, '1.0');
+        self.assertEqual(x.lastVersion, '1.3');
diff --git a/utils/Table/StringList.py b/utils/Table/StringList.py
new file mode 100755
index 0000000..0ba166b
--- /dev/null
+++ b/utils/Table/StringList.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+# Copyright 2025 Google LLC
+
+# 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.
+
+import functools
+from typing import List
+
+class StringList(list):
+    """
+    A hashable ordered list of strings.
+    This can be used as the key for a dictionary.
+    """
+    def __init__(self, strs: List[str]) -> None:
+        super().__init__(strs)
+
+    def __hash__(self) -> int: # type: ignore[override]
+        return functools.reduce(lambda h, ir: hash((h, hash(ir))), self, 0)
diff --git a/utils/Table/StringList_test.py b/utils/Table/StringList_test.py
new file mode 100755
index 0000000..d3cd1ed
--- /dev/null
+++ b/utils/Table/StringList_test.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python3
+# Copyright 2025 Google LLC
+
+# 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.
+
+import unittest
+from . StringList import *
+
+class TestStringList(unittest.TestCase):
+  def test_creation_empty(self) -> None:
+    x = StringList([])
+    self.assertEqual(len(x), 0)
+    self.assertEqual(x, [])
+
+  def test_creation_nonempty(self) -> None:
+    x = StringList(["abc", "def"])
+    self.assertEqual(len(x), 2)
+    self.assertEqual(x, ["abc", "def"])
+
+  def test_creation_does_not_sort(self) -> None:
+    x = StringList(["abc", "def"])
+    self.assertEqual(x, ["abc", "def"])
+    self.assertNotEqual(x, ["def", "abc"])
+
+  def test_equality(self) -> None:
+    x = StringList(["abc", "def"])
+    y = StringList(["abc", "def"])
+    z = StringList(["abc", "ef"])
+    self.assertEqual(x, x)
+    self.assertEqual(x, y)
+    self.assertNotEqual(x, z)
+
+  def test_hash_heuristic(self) -> None:
+    x = StringList(["abc", "def"])
+    y = StringList(["abc", "def"])
+    z = StringList(["abc", "df"])
+    self.assertEqual(hash(x), hash(x))
+    self.assertEqual(hash(x), hash(y))
+    self.assertNotEqual(hash(x), hash(z))
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/kokoro/check-format/presubmit_check_format.cfg b/utils/Table/__init__.py
similarity index 73%
copy from kokoro/check-format/presubmit_check_format.cfg
copy to utils/Table/__init__.py
index 1993289..2210b4e 100644
--- a/kokoro/check-format/presubmit_check_format.cfg
+++ b/utils/Table/__init__.py
@@ -1,16 +1,18 @@
-# Copyright (c) 2018 Google LLC.
-#
+#!/usr/bin/env python3
+# Copyright 2025 Google LLC
+
 # 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
+#     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.
-
-# Presubmit build configuration.
-build_file: "SPIRV-Tools/kokoro/check-format/build.sh"
+__all__ = [
+    "Context",
+    "IndexRange",
+]
diff --git a/utils/check_code_format.sh b/utils/check_code_format.sh
index da5e019..96db03f 100755
--- a/utils/check_code_format.sh
+++ b/utils/check_code_format.sh
@@ -12,13 +12,33 @@
 # 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.
-#
-# Script to determine if source code in Pull Request is properly formatted.
-# Exits with non 0 exit code if formatting is needed.
-#
-# This script assumes to be invoked at the project root directory.
 
-BASE_BRANCH=${1:-main}
+# This script determines if the source code in a Pull Request is properly formatted.
+# Exits with non 0 exit code if formatting is needed.
+# Assumptions:
+#    - git and python3 are on the path
+#    - Runs from the project root diretory.
+#    - 'clang-format' is on the path, or env var CLANG_FORMAT points to it.
+#    - 'clang-format-diff.py' is in the utils directory, or env var
+#       points to it.CLANG_FORMAT_DIFF
+
+BASE_BRANCH=$(git merge-base main HEAD)
+
+CLANG_FORMAT=${CLANG_FORMAT:-clang-format}
+if [ ! -f "$CLANG_FORMAT" ]; then
+  echo missing clang-format: set CLANG_FORMAT or put clang-format in the PATH
+  exit 1
+fi
+
+# Find clang-format-diff.py from an environment variable, or use a default
+CLANG_FORMAT_DIFF=${CLANG_FORMAT_DIFF:-./utils/clang-format-diff.py}
+if [ ! -f "$CLANG_FORMAT_DIFF" ]; then
+  echo missing clang-format-diffy.py: set CLANG_FORMAT_DIFF or put it in ./utils/clang-format-diff.py
+  exit 1
+fi
+
+echo "Comparing "$(git rev-parse HEAD)" against $BASE_BRANCH"
+echo Using $("$CLANG_FORMAT" --version)
 
 FILES_TO_CHECK=$(git diff --name-only ${BASE_BRANCH} | grep -E ".*\.(cpp|cc|c\+\+|cxx|c|h|hpp)$")
 
@@ -26,8 +46,11 @@
   echo "No source code to check for formatting."
   exit 0
 fi
+echo "Checking Formatting for the following files"
+echo "${FILES_TO_CHECK}"
+echo
 
-FORMAT_DIFF=$(git diff -U0 ${BASE_BRANCH} -- ${FILES_TO_CHECK} | python ./utils/clang-format-diff.py -p1 -style=file)
+FORMAT_DIFF=$(git diff -U0 ${BASE_BRANCH} -- ${FILES_TO_CHECK} | python3 "${CLANG_FORMAT_DIFF}" -p1 -style=file -binary "$CLANG_FORMAT")
 
 if [ -z "${FORMAT_DIFF}" ]; then
   echo "All source code in PR properly formatted."
diff --git a/utils/check_copyright.py b/utils/check_copyright.py
index c477ece..ca4a218 100755
--- a/utils/check_copyright.py
+++ b/utils/check_copyright.py
@@ -37,6 +37,7 @@
            'André Perez Maselco',
            'Vasyl Teliman',
            'Advanced Micro Devices, Inc.',
+           'Arm Ltd.',
            'Stefano Milizia',
            'Alastair F. Donaldson',
            'Mostafa Ashraf',
@@ -45,7 +46,7 @@
            'Nintendo',
            'Epic Games, Inc.',
            'NVIDIA Corporation']
-CURRENT_YEAR = 2023
+CURRENT_YEAR = 2025
 
 FIRST_YEAR = 2014
 FINAL_YEAR = CURRENT_YEAR + 5
@@ -65,7 +66,7 @@
 YEAR_OR_RANGE_REGEX = '(' + YEAR_REGEX + '|' + YEAR_RANGE_REGEX + ')'
 
 # The final regular expression to match a valid copyright line.
-COPYRIGHT_RE = re.compile('Copyright \(c\) {} ({})'.format(
+COPYRIGHT_RE = re.compile('Copyright( \\(c\\))? {} ({})'.format(
     YEAR_OR_RANGE_REGEX, '|'.join(AUTHORS)))
 
 MIT_BEGIN_RE = re.compile('Permission is hereby granted, '
@@ -73,7 +74,7 @@
 MIT_END_RE = re.compile('MATERIALS OR THE USE OR OTHER DEALINGS IN '
                         'THE MATERIALS.')
 APACHE2_BEGIN_RE = re.compile('Licensed under the Apache License, '
-                              'Version 2.0 \(the "License"\);')
+                              'Version 2.0 \\(the "License"\\);')
 APACHE2_END_RE = re.compile('limitations under the License.')
 
 LICENSED = """Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/utils/generate_grammar_tables.py b/utils/generate_grammar_tables.py
deleted file mode 100755
index 2b695b7..0000000
--- a/utils/generate_grammar_tables.py
+++ /dev/null
@@ -1,963 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2016 Google 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.
-"""Generates various info tables from SPIR-V JSON grammar."""
-
-import errno
-import json
-import os.path
-import re
-
-# Prefix for all C variables generated by this script.
-PYGEN_VARIABLE_PREFIX = 'pygen_variable'
-
-# Extensions to recognize, but which don't necessarily come from the SPIR-V
-# core or KHR grammar files.  Get this list from the SPIR-V registry web page.
-# NOTE: Only put things on this list if it is not in those grammar files.
-EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS = """
-SPV_AMD_gcn_shader
-SPV_AMD_gpu_shader_half_float
-SPV_AMD_gpu_shader_int16
-SPV_AMD_shader_trinary_minmax
-SPV_KHR_non_semantic_info
-SPV_EXT_relaxed_printf_string_address_space
-"""
-
-OUTPUT_LANGUAGE = 'c'
-
-def make_path_to_file(f):
-    """Makes all ancestor directories to the given file, if they don't yet
-    exist.
-
-    Arguments:
-        f: The file whose ancestor directories are to be created.
-    """
-    dir = os.path.dirname(os.path.abspath(f))
-    try:
-        os.makedirs(dir)
-    except OSError as e:
-        if e.errno == errno.EEXIST and os.path.isdir(dir):
-            pass
-        else:
-            raise
-
-
-def convert_min_required_version(version):
-    """Converts the minimal required SPIR-V version encoded in the grammar to
-    the symbol in SPIRV-Tools."""
-    if version is None:
-        return 'SPV_SPIRV_VERSION_WORD(1, 0)'
-    if version == 'None':
-        return '0xffffffffu'
-    return 'SPV_SPIRV_VERSION_WORD({})'.format(version.replace('.', ','))
-
-
-def convert_max_required_version(version):
-    """Converts the maximum required SPIR-V version encoded in the grammar to
-    the symbol in SPIRV-Tools."""
-    if version is None:
-        return '0xffffffffu'
-    return 'SPV_SPIRV_VERSION_WORD({})'.format(version.replace('.', ','))
-
-def get_alias_array_name(aliases):
-    """Returns the name of the array containing all the given aliases.
-
-    Arguments:
-      - aliases: a sequence of alias names
-    """
-    if not aliases:
-        return 'nullptr';
-    return '{}_aliases_{}'.format(PYGEN_VARIABLE_PREFIX, ''.join(aliases))
-
-def compose_alias_list(aliases):
-    """Returns a string containing a braced list of aliases.
-
-    Arguments:
-      - aliases: a sequence of alias names
-
-    Returns:
-      a string containing the braced list of char* named by aliases.
-    """
-    return '{' + ', '.join([('"{}"').format(a) for a in aliases]) + '}'
-
-def generate_aliases_arrays(aliases):
-    """Returns the arrays of aliases
-
-    Arguments:
-      - aliases: a sequence of sequence of alias names
-    """
-    aliases = sorted(set([tuple(a) for a in aliases if a]))
-    arrays = [
-        'static const char* {}[] = {};'.format(
-            get_alias_array_name(a), compose_alias_list(a))
-        for a in aliases]
-    return '\n'.join(arrays)
-
-def compose_capability_list(caps):
-    """Returns a string containing a braced list of capabilities as enums.
-
-    Arguments:
-      - caps: a sequence of capability names
-
-    Returns:
-      a string containing the braced list of SpvCapability* or spv::Capability:: enums named by caps.
-    """
-    base_string = 'SpvCapability'
-    global OUTPUT_LANGUAGE
-    if OUTPUT_LANGUAGE == 'c++':
-        base_string = 'spv::Capability::'
-
-    return '{' + ', '.join([(base_string + '{}').format(c) for c in caps]) + '}'
-
-
-def get_capability_array_name(caps):
-    """Returns the name of the array containing all the given capabilities.
-
-    Args:
-      - caps: a sequence of capability names
-    """
-    if not caps:
-        return 'nullptr'
-    return '{}_caps_{}'.format(PYGEN_VARIABLE_PREFIX, ''.join(caps))
-
-
-def generate_capability_arrays(caps):
-    """Returns the arrays of capabilities.
-
-    Arguments:
-      - caps: a sequence of sequence of capability names
-    """
-    caps = sorted(set([tuple(c) for c in caps if c]))
-    cap_str = 'SpvCapability'
-    global OUTPUT_LANGUAGE
-    if OUTPUT_LANGUAGE == 'c++':
-        cap_str = 'spv::Capability'
-    arrays = [
-        'static const ' + cap_str + ' {}[] = {};'.format(
-            get_capability_array_name(c), compose_capability_list(c))
-        for c in caps]
-    return '\n'.join(arrays)
-
-
-def compose_extension_list(exts):
-    """Returns a string containing a braced list of extensions as enums.
-
-    Arguments:
-      - exts: a sequence of extension names
-
-    Returns:
-      a string containing the braced list of extensions named by exts.
-    """
-    return '{' + ', '.join(
-        ['spvtools::Extension::k{}'.format(e) for e in exts]) + '}'
-
-
-def get_extension_array_name(extensions):
-    """Returns the name of the array containing all the given extensions.
-
-    Args:
-      - extensions: a sequence of extension names
-    """
-    if not extensions:
-        return 'nullptr'
-    else:
-        return '{}_exts_{}'.format(
-            PYGEN_VARIABLE_PREFIX, ''.join(extensions))
-
-
-def generate_extension_arrays(extensions):
-    """Returns the arrays of extensions.
-
-    Arguments:
-      - caps: a sequence of sequence of extension names
-    """
-    extensions = sorted(set([tuple(e) for e in extensions if e]))
-    arrays = [
-        'static const spvtools::Extension {}[] = {};'.format(
-            get_extension_array_name(e), compose_extension_list(e))
-        for e in extensions]
-    return '\n'.join(arrays)
-
-
-def convert_operand_kind(operand_tuple):
-    """Returns the corresponding operand type used in spirv-tools for the given
-    operand kind and quantifier used in the JSON grammar.
-
-    Arguments:
-      - operand_tuple: a tuple of two elements:
-          - operand kind: used in the JSON grammar
-          - quantifier: '', '?', or '*'
-
-    Returns:
-      a string of the enumerant name in spv_operand_type_t
-    """
-    kind, quantifier = operand_tuple
-    # The following cases are where we differ between the JSON grammar and
-    # spirv-tools.
-    if kind == 'IdResultType':
-        kind = 'TypeId'
-    elif kind == 'IdResult':
-        kind = 'ResultId'
-    elif kind == 'IdMemorySemantics' or kind == 'MemorySemantics':
-        kind = 'MemorySemanticsId'
-    elif kind == 'IdScope' or kind == 'Scope':
-        kind = 'ScopeId'
-    elif kind == 'IdRef':
-        kind = 'Id'
-
-    elif kind == 'ImageOperands':
-        kind = 'Image'
-    elif kind == 'Dim':
-        kind = 'Dimensionality'
-    elif kind == 'ImageFormat':
-        kind = 'SamplerImageFormat'
-    elif kind == 'KernelEnqueueFlags':
-        kind = 'KernelEnqFlags'
-
-    elif kind == 'LiteralExtInstInteger':
-        kind = 'ExtensionInstructionNumber'
-    elif kind == 'LiteralSpecConstantOpInteger':
-        kind = 'SpecConstantOpNumber'
-    elif kind == 'LiteralContextDependentNumber':
-        kind = 'TypedLiteralNumber'
-
-    elif kind == 'PairLiteralIntegerIdRef':
-        kind = 'LiteralIntegerId'
-    elif kind == 'PairIdRefLiteralInteger':
-        kind = 'IdLiteralInteger'
-    elif kind == 'PairIdRefIdRef':  # Used by OpPhi in the grammar
-        kind = 'Id'
-
-    if kind == 'FPRoundingMode':
-        kind = 'FpRoundingMode'
-    elif kind == 'FPFastMathMode':
-        kind = 'FpFastMathMode'
-
-    if quantifier == '?':
-        kind = 'Optional{}'.format(kind)
-    elif quantifier == '*':
-        kind = 'Variable{}'.format(kind)
-
-    return 'SPV_OPERAND_TYPE_{}'.format(
-        re.sub(r'([a-z])([A-Z])', r'\1_\2', kind).upper())
-
-
-class InstInitializer(object):
-    """Instances holds a SPIR-V instruction suitable for printing as the
-    initializer for spv_opcode_desc_t."""
-
-    def __init__(self, opname, aliases, caps, exts, operands, version, lastVersion):
-        """Initialization.
-
-        Arguments:
-          - opname: opcode name (with the 'Op' prefix)
-          - aliases: a sequence of aliases for the name of this opcode
-          - caps: a sequence of capability names required by this opcode
-          - exts: a sequence of names of extensions enabling this enumerant
-          - operands: a sequence of (operand-kind, operand-quantifier) tuples
-          - version: minimal SPIR-V version required for this opcode
-          - lastVersion: last version of SPIR-V that includes this opcode
-        """
-
-        assert opname.startswith('Op')
-        self.opname = opname[2:]  # Remove the "Op" prefix.
-        self.num_aliases = len(aliases);
-        self.aliases_mask = get_alias_array_name(aliases)
-        self.num_caps = len(caps)
-        self.caps_mask = get_capability_array_name(caps)
-        self.num_exts = len(exts)
-        self.exts = get_extension_array_name(exts)
-        self.operands = [convert_operand_kind(o) for o in operands]
-
-        self.fix_syntax()
-
-        operands = [o[0] for o in operands]
-        self.ref_type_id = 'IdResultType' in operands
-        self.def_result_id = 'IdResult' in operands
-
-        self.version = convert_min_required_version(version)
-        self.lastVersion = convert_max_required_version(lastVersion)
-
-    def fix_syntax(self):
-        """Fix an instruction's syntax, adjusting for differences between the
-        officially released grammar and how SPIRV-Tools uses the grammar.
-
-        Fixes:
-            - ExtInst should not end with SPV_OPERAND_VARIABLE_ID.
-            https://github.com/KhronosGroup/SPIRV-Tools/issues/233
-        """
-        if (self.opname == 'ExtInst'
-                and self.operands[-1] == 'SPV_OPERAND_TYPE_VARIABLE_ID'):
-            self.operands.pop()
-
-    def __str__(self):
-        global OUTPUT_LANGUAGE
-        base_str = 'SpvOp'
-        if OUTPUT_LANGUAGE == 'c++':
-            base_str = 'spv::Op::Op'
-
-        template = ['{{"{opname}"', base_str + '{opname}',
-                    '{num_aliases}', '{aliases_mask}',
-                    '{num_caps}', '{caps_mask}',
-                    '{num_operands}', '{{{operands}}}',
-                    '{def_result_id}', '{ref_type_id}',
-                    '{num_exts}', '{exts}',
-                    '{min_version}', '{max_version}}}']
-        return ', '.join(template).format(
-            opname=self.opname,
-            num_aliases=self.num_aliases,
-            aliases_mask=self.aliases_mask,
-            num_caps=self.num_caps,
-            caps_mask=self.caps_mask,
-            num_operands=len(self.operands),
-            operands=', '.join(self.operands),
-            def_result_id=(1 if self.def_result_id else 0),
-            ref_type_id=(1 if self.ref_type_id else 0),
-            num_exts=self.num_exts,
-            exts=self.exts,
-            min_version=self.version,
-            max_version=self.lastVersion)
-
-
-class ExtInstInitializer(object):
-    """Instances holds a SPIR-V extended instruction suitable for printing as
-    the initializer for spv_ext_inst_desc_t."""
-
-    def __init__(self, opname, opcode, caps, operands):
-        """Initialization.
-
-        Arguments:
-          - opname: opcode name
-          - opcode: enumerant value for this opcode
-          - caps: a sequence of capability names required by this opcode
-          - operands: a sequence of (operand-kind, operand-quantifier) tuples
-        """
-        self.opname = opname
-        self.opcode = opcode
-        self.num_caps = len(caps)
-        self.caps_mask = get_capability_array_name(caps)
-        self.operands = [convert_operand_kind(o) for o in operands]
-        self.operands.append('SPV_OPERAND_TYPE_NONE')
-
-    def __str__(self):
-        template = ['{{"{opname}"', '{opcode}', '{num_caps}', '{caps_mask}',
-                    '{{{operands}}}}}']
-        return ', '.join(template).format(
-            opname=self.opname,
-            opcode=self.opcode,
-            num_caps=self.num_caps,
-            caps_mask=self.caps_mask,
-            operands=', '.join(self.operands))
-
-
-def generate_instruction(inst, is_ext_inst):
-    """Returns the C initializer for the given SPIR-V instruction.
-
-    Arguments:
-      - inst: a dict containing information about a SPIR-V instruction
-      - is_ext_inst: a bool indicating whether |inst| is an extended
-                     instruction.
-
-    Returns:
-      a string containing the C initializer for spv_opcode_desc_t or
-      spv_ext_inst_desc_t
-    """
-    opname = inst.get('opname')
-    opcode = inst.get('opcode')
-    aliases = inst.get('aliases', [])
-    caps = inst.get('capabilities', [])
-    exts = inst.get('extensions', [])
-    operands = inst.get('operands', {})
-    operands = [(o['kind'], o.get('quantifier', '')) for o in operands]
-    min_version = inst.get('version', None)
-    max_version = inst.get('lastVersion', None)
-
-    assert opname is not None
-
-    if is_ext_inst:
-        return str(ExtInstInitializer(opname, opcode, caps, operands))
-    else:
-        return str(InstInitializer(opname, aliases, caps, exts, operands, min_version, max_version))
-
-
-def generate_instruction_table(inst_table):
-    """Returns the info table containing all SPIR-V instructions, sorted by
-    opcode, and prefixed by capability arrays.
-
-    Note:
-      - the built-in sorted() function is guaranteed to be stable.
-        https://docs.python.org/3/library/functions.html#sorted
-
-    Arguments:
-      - inst_table: a list containing all SPIR-V instructions.
-    """
-    inst_table = sorted(inst_table, key=lambda k: (k['opcode'], k['opname']))
-
-    aliases_arrays = generate_aliases_arrays(
-        [inst.get('aliases', []) for inst in inst_table])
-    caps_arrays = generate_capability_arrays(
-        [inst.get('capabilities', []) for inst in inst_table])
-    exts_arrays = generate_extension_arrays(
-        [inst.get('extensions', []) for inst in inst_table])
-
-    insts = [generate_instruction(inst, False) for inst in inst_table]
-    insts = ['static const spv_opcode_desc_t kOpcodeTableEntries[] = {{\n'
-             '  {}\n}};'.format(',\n  '.join(insts))]
-
-    return '{}\n\n{}\n\n{}\n\n{}'.format(aliases_arrays, caps_arrays, exts_arrays, '\n'.join(insts))
-
-
-def generate_extended_instruction_table(json_grammar, set_name, operand_kind_prefix=""):
-    """Returns the info table containing all SPIR-V extended instructions,
-    sorted by opcode, and prefixed by capability arrays.
-
-    Arguments:
-      - inst_table: a list containing all SPIR-V instructions.
-      - set_name: the name of the extended instruction set.
-      - operand_kind_prefix: the prefix, if any, to add to the front
-        of operand kind names.
-    """
-    if operand_kind_prefix:
-        prefix_operand_kind_names(operand_kind_prefix, json_grammar)
-
-    inst_table = json_grammar["instructions"]
-    set_name = set_name.replace(".", "_")
-
-    inst_table = sorted(inst_table, key=lambda k: k['opcode'])
-    caps = [inst.get('capabilities', []) for inst in inst_table]
-    caps_arrays = generate_capability_arrays(caps)
-    insts = [generate_instruction(inst, True) for inst in inst_table]
-    insts = ['static const spv_ext_inst_desc_t {}_entries[] = {{\n'
-             '  {}\n}};'.format(set_name, ',\n  '.join(insts))]
-
-    return '{}\n\n{}'.format(caps_arrays, '\n'.join(insts))
-
-
-class EnumerantInitializer(object):
-    """Prints an enumerant as the initializer for spv_operand_desc_t."""
-
-    def __init__(self, enumerant, value, aliases, caps, exts, parameters, version, lastVersion):
-        """Initialization.
-
-        Arguments:
-          - enumerant: enumerant name
-          - value: enumerant value
-          - aliases: a sequence of aliased capability names
-          - caps: a sequence of capability names required by this enumerant
-          - exts: a sequence of names of extensions enabling this enumerant
-          - parameters: a sequence of (operand-kind, operand-quantifier) tuples
-          - version: minimal SPIR-V version required for this opcode
-          - lastVersion: last SPIR-V version this opode appears
-        """
-        self.enumerant = enumerant
-        self.value = value
-        self.num_aliases = len(aliases)
-        self.aliases = get_alias_array_name(aliases)
-        self.num_caps = len(caps)
-        self.caps = get_capability_array_name(caps)
-        self.num_exts = len(exts)
-        self.exts = get_extension_array_name(exts)
-        self.parameters = [convert_operand_kind(p) for p in parameters]
-        self.version = convert_min_required_version(version)
-        self.lastVersion = convert_max_required_version(lastVersion)
-
-    def __str__(self):
-        template = ['{{"{enumerant}"', '{value}',
-                    '{num_aliases}', '{aliases}',
-                    '{num_caps}', '{caps}',
-                    '{num_exts}', '{exts}',
-                    '{{{parameters}}}', '{min_version}',
-                    '{max_version}}}']
-        return ', '.join(template).format(
-            enumerant=self.enumerant,
-            value=self.value,
-            num_aliases=self.num_aliases,
-            aliases=self.aliases,
-            num_caps=self.num_caps,
-            caps=self.caps,
-            num_exts=self.num_exts,
-            exts=self.exts,
-            parameters=', '.join(self.parameters),
-            min_version=self.version,
-            max_version=self.lastVersion)
-
-
-def generate_enum_operand_kind_entry(entry, extension_map):
-    """Returns the C initializer for the given operand enum entry.
-
-    Arguments:
-      - entry: a dict containing information about an enum entry
-      - extension_map: a dict mapping enum value to list of extensions
-
-    Returns:
-      a string containing the C initializer for spv_operand_desc_t
-    """
-    enumerant = entry.get('enumerant')
-    value = entry.get('value')
-    aliases = entry.get('aliases', [])
-    caps = entry.get('capabilities', [])
-    if value in extension_map:
-        exts = extension_map[value]
-    else:
-        exts = []
-    params = entry.get('parameters', [])
-    params = [p.get('kind') for p in params]
-    params = zip(params, [''] * len(params))
-    version = entry.get('version', None)
-    max_version = entry.get('lastVersion', None)
-
-    assert enumerant is not None
-    assert value is not None
-
-    return str(EnumerantInitializer(
-        enumerant, value, aliases, caps, exts, params, version, max_version))
-
-
-def generate_enum_operand_kind(enum, synthetic_exts_list):
-    """Returns the C definition for the given operand kind.
-    It's a static const named array of spv_operand_desc_t.
-
-    Also appends to |synthetic_exts_list| a list of extension lists
-    used.
-    """
-    kind = enum.get('kind')
-    assert kind is not None
-
-    # Sort all enumerants according to their values, but otherwise
-    # preserve their order so the first name listed in the grammar
-    # as the preferred name for disassembly.
-    if enum.get('category') == 'ValueEnum':
-        def functor(k): return (k['value'])
-    else:
-        def functor(k): return (int(k['value'], 16))
-    entries = sorted(enum.get('enumerants', []), key=functor)
-
-    # SubgroupEqMask and SubgroupEqMaskKHR are the same number with
-    # same semantics, but one has no extension list while the other
-    # does.  Both should have the extension list.
-    # So create a mapping from enum value to the union of the extensions
-    # across all those grammar entries.  Preserve order.
-    extension_map = {}
-    for e in entries:
-        value = e.get('value')
-        extension_map[value] = []
-    for e in entries:
-        value = e.get('value')
-        exts = e.get('extensions', [])
-        for ext in exts:
-            if ext not in extension_map[value]:
-                extension_map[value].append(ext)
-    synthetic_exts_list.extend(extension_map.values())
-
-    name = '{}_{}Entries'.format(PYGEN_VARIABLE_PREFIX, kind)
-    entries = ['  {}'.format(generate_enum_operand_kind_entry(e, extension_map))
-               for e in entries]
-    if len(entries) == 0:
-        # Insert a dummy entry. Otherwise the array is empty and compilation
-        # will fail in MSVC.
-        entries = ['  {"place holder", 0, 0, nullptr, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(999,0), 0}']
-
-    template = ['static const spv_operand_desc_t {name}[] = {{',
-                '{entries}', '}};']
-    entries = '\n'.join(template).format(
-        name=name,
-        entries=',\n'.join(entries))
-
-    return kind, name, entries
-
-
-def generate_operand_kind_table(enums):
-    """Returns the info table containing all SPIR-V operand kinds."""
-    # We only need to output info tables for those operand kinds that are enums.
-    enums = [e for e in enums if e.get('category') in ['ValueEnum', 'BitEnum']]
-
-    aliases = [entry.get('aliases', [])
-               for enum in enums
-               for entry in enum.get('enumerants', [])]
-    aliases_arrays = generate_aliases_arrays(aliases)
-
-    caps = [entry.get('capabilities', [])
-            for enum in enums
-            for entry in enum.get('enumerants', [])]
-    caps_arrays = generate_capability_arrays(caps)
-
-    exts = [entry.get('extensions', [])
-            for enum in enums
-            for entry in enum.get('enumerants', [])]
-    enums = [generate_enum_operand_kind(e, exts) for e in enums]
-    exts_arrays = generate_extension_arrays(exts)
-
-    # We have a few operand kinds that require their optional counterpart to
-    # exist in the operand info table.
-    optional_enums = ['ImageOperands', 'AccessQualifier', 'MemoryAccess', 'PackedVectorFormat', 'CooperativeMatrixOperands', 'RawAccessChainOperands', 'FPEncoding']
-    optional_enums = [e for e in enums if e[0] in optional_enums]
-    enums.extend(optional_enums)
-
-    enum_kinds, enum_names, enum_entries = zip(*enums)
-    # Mark the last few as optional ones.
-    enum_quantifiers = [''] * (len(enums) - len(optional_enums)) + ['?'] * len(optional_enums)
-    # And we don't want redefinition of them.
-    enum_entries = enum_entries[:-len(optional_enums)]
-    enum_kinds = [convert_operand_kind(e)
-                  for e in zip(enum_kinds, enum_quantifiers)]
-    table_entries = zip(enum_kinds, enum_names, enum_names)
-    table_entries = ['  {{{}, ARRAY_SIZE({}), {}}}'.format(*e)
-                     for e in table_entries]
-
-    template = [
-        'static const spv_operand_desc_group_t {p}_OperandInfoTable[] = {{',
-        '{enums}', '}};']
-    table = '\n'.join(template).format(
-        p=PYGEN_VARIABLE_PREFIX, enums=',\n'.join(table_entries))
-
-    return '\n\n'.join((aliases_arrays,) + (caps_arrays,) + (exts_arrays,) + enum_entries + (table,))
-
-
-def get_extension_list(instructions, operand_kinds):
-    """Returns extensions as an alphabetically sorted list of strings."""
-
-    things_with_an_extensions_field = [item for item in instructions]
-
-    enumerants = sum([item.get('enumerants', [])
-                      for item in operand_kinds], [])
-
-    things_with_an_extensions_field.extend(enumerants)
-
-    extensions = sum([item.get('extensions', [])
-                      for item in things_with_an_extensions_field
-                      if item.get('extensions')], [])
-
-    for item in EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS.split():
-            # If it's already listed in a grammar, then don't put it in the
-            # special exceptions list.
-        assert item not in extensions, 'Extension %s is already in a grammar file' % item
-
-    extensions.extend(
-        EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS.split())
-
-    # Validator would ignore type declaration unique check. Should only be used
-    # for legacy autogenerated test files containing multiple instances of the
-    # same type declaration, if fixing the test by other methods is too
-    # difficult. Shouldn't be used for any other reasons.
-    extensions.append('SPV_VALIDATOR_ignore_type_decl_unique')
-
-    return sorted(set(extensions))
-
-
-def get_capabilities(operand_kinds):
-    """Returns capabilities as a list of JSON objects, in order of
-    appearance."""
-    enumerants = sum([item.get('enumerants', []) for item in operand_kinds
-                      if item.get('kind') in ['Capability']], [])
-    return enumerants
-
-
-def generate_extension_enum(extensions):
-    """Returns enumeration containing extensions declared in the grammar."""
-    return ',\n'.join(['k' + extension for extension in extensions])
-
-
-def generate_extension_to_string_mapping(extensions):
-    """Returns mapping function from extensions to corresponding strings."""
-    function = 'const char* ExtensionToString(Extension extension) {\n'
-    function += '  switch (extension) {\n'
-    template = '    case Extension::k{extension}:\n' \
-        '      return "{extension}";\n'
-    function += ''.join([template.format(extension=extension)
-                         for extension in extensions])
-    function += '  }\n\n  return "";\n}'
-    return function
-
-
-def generate_string_to_extension_mapping(extensions):
-    """Returns mapping function from strings to corresponding extensions."""
-
-    function = '''
-    bool GetExtensionFromString(const char* str, Extension* extension) {{
-        static const char* known_ext_strs[] = {{ {strs} }};
-        static const Extension known_ext_ids[] = {{ {ids} }};
-        const auto b = std::begin(known_ext_strs);
-        const auto e = std::end(known_ext_strs);
-        const auto found = std::equal_range(
-            b, e, str, [](const char* str1, const char* str2) {{
-                return std::strcmp(str1, str2) < 0;
-            }});
-        if (found.first == e || found.first == found.second) return false;
-
-        *extension = known_ext_ids[found.first - b];
-        return true;
-    }}
-    '''.format(strs=', '.join(['"{}"'.format(e) for e in extensions]),
-               ids=', '.join(['Extension::k{}'.format(e) for e in extensions]))
-
-    return function
-
-
-def generate_capability_to_string_mapping(operand_kinds):
-    """Returns mapping function from capabilities to corresponding strings.
-
-    We take care to avoid emitting duplicate values.
-    """
-    cap_str = 'SpvCapability'
-    cap_join = ''
-    global OUTPUT_LANGUAGE
-    if OUTPUT_LANGUAGE == 'c++':
-        cap_str = 'spv::Capability'
-        cap_join = '::'
-
-    function = 'const char* CapabilityToString(' + cap_str + ' capability) {\n'
-    function += '  switch (capability) {\n'
-    template = '    case ' + cap_str + cap_join + '{capability}:\n' \
-        '      return "{capability}";\n'
-    emitted = set()  # The values of capabilities we already have emitted
-    for capability in get_capabilities(operand_kinds):
-        value = capability.get('value')
-        if value not in emitted:
-            emitted.add(value)
-            function += template.format(capability=capability.get('enumerant'))
-    function += '    case ' + cap_str + cap_join + 'Max:\n' \
-        '      assert(0 && "Attempting to convert ' + cap_str + cap_join + 'Max to string");\n' \
-        '      return "";\n'
-    function += '  }\n\n  return "";\n}'
-    return function
-
-
-def generate_all_string_enum_mappings(extensions, operand_kinds):
-    """Returns all string-to-enum / enum-to-string mapping tables."""
-    tables = []
-    tables.append(generate_extension_to_string_mapping(extensions))
-    tables.append(generate_string_to_extension_mapping(extensions))
-    tables.append(generate_capability_to_string_mapping(operand_kinds))
-    return '\n\n'.join(tables)
-
-
-def precondition_operand_kinds(operand_kinds):
-    """For operand kinds that have the same number, make sure they all have the
-    same extension list."""
-
-    # Map operand kind and value to list of the union of extensions
-    # for same-valued enumerants.
-    exts = {}
-    for kind_entry in operand_kinds:
-        kind = kind_entry.get('kind')
-        for enum_entry in kind_entry.get('enumerants', []):
-            value = enum_entry.get('value')
-            key = kind + '.' + str(value)
-            if key in exts:
-                exts[key].extend(enum_entry.get('extensions', []))
-            else:
-                exts[key] = enum_entry.get('extensions', [])
-            exts[key] = sorted(set(exts[key]))
-
-    # Now make each entry the same list.
-    for kind_entry in operand_kinds:
-        kind = kind_entry.get('kind')
-        for enum_entry in kind_entry.get('enumerants', []):
-            value = enum_entry.get('value')
-            key = kind + '.' + str(value)
-            if len(exts[key]) > 0:
-                enum_entry['extensions'] = exts[key]
-
-    return operand_kinds
-
-
-def prefix_operand_kind_names(prefix, json_dict):
-    """Modifies json_dict, by prefixing all the operand kind names
-    with the given prefix.  Also modifies their uses in the instructions
-    to match.
-    """
-
-    old_to_new = {}
-    for operand_kind in json_dict["operand_kinds"]:
-        old_name = operand_kind["kind"]
-        new_name = prefix + old_name
-        operand_kind["kind"] = new_name
-        old_to_new[old_name] = new_name
-
-    for instruction in json_dict["instructions"]:
-        for operand in instruction.get("operands", []):
-            replacement = old_to_new.get(operand["kind"])
-            if replacement is not None:
-                operand["kind"] = replacement
-
-
-def main():
-    import argparse
-    parser = argparse.ArgumentParser(description='Generate SPIR-V info tables')
-
-    parser.add_argument('--spirv-core-grammar', metavar='<path>',
-                        type=str, required=False,
-                        help='input JSON grammar file for core SPIR-V '
-                        'instructions')
-    parser.add_argument('--extinst-debuginfo-grammar', metavar='<path>',
-                        type=str, required=False, default=None,
-                        help='input JSON grammar file for DebugInfo extended '
-                        'instruction set')
-    parser.add_argument('--extinst-cldebuginfo100-grammar', metavar='<path>',
-                        type=str, required=False, default=None,
-                        help='input JSON grammar file for OpenCL.DebugInfo.100 '
-                        'extended instruction set')
-    parser.add_argument('--extinst-glsl-grammar', metavar='<path>',
-                        type=str, required=False, default=None,
-                        help='input JSON grammar file for GLSL extended '
-                        'instruction set')
-    parser.add_argument('--extinst-opencl-grammar', metavar='<path>',
-                        type=str, required=False, default=None,
-                        help='input JSON grammar file for OpenCL extended '
-                        'instruction set')
-    parser.add_argument('--output-language',
-                        type=str, required=False, default='c',
-                        choices=['c','c++'],
-                        help='specify output language type')
-
-    parser.add_argument('--core-insts-output', metavar='<path>',
-                        type=str, required=False, default=None,
-                        help='output file for core SPIR-V instructions')
-    parser.add_argument('--glsl-insts-output', metavar='<path>',
-                        type=str, required=False, default=None,
-                        help='output file for GLSL extended instruction set')
-    parser.add_argument('--opencl-insts-output', metavar='<path>',
-                        type=str, required=False, default=None,
-                        help='output file for OpenCL extended instruction set')
-    parser.add_argument('--operand-kinds-output', metavar='<path>',
-                        type=str, required=False, default=None,
-                        help='output file for operand kinds')
-    parser.add_argument('--extension-enum-output', metavar='<path>',
-                        type=str, required=False, default=None,
-                        help='output file for extension enumeration')
-    parser.add_argument('--enum-string-mapping-output', metavar='<path>',
-                        type=str, required=False, default=None,
-                        help='output file for enum-string mappings')
-    parser.add_argument('--extinst-vendor-grammar', metavar='<path>',
-                        type=str, required=False, default=None,
-                        help='input JSON grammar file for vendor extended '
-                        'instruction set'),
-    parser.add_argument('--vendor-insts-output', metavar='<path>',
-                        type=str, required=False, default=None,
-                        help='output file for vendor extended instruction set')
-    parser.add_argument('--vendor-operand-kind-prefix', metavar='<string>',
-                        type=str, required=False, default=None,
-                        help='prefix for operand kinds (to disambiguate operand type enums)')
-    args = parser.parse_args()
-
-    global OUTPUT_LANGUAGE
-    OUTPUT_LANGUAGE = args.output_language
-
-    # The GN build system needs this because it doesn't handle quoting
-    # empty string arguments well.
-    if args.vendor_operand_kind_prefix == "...nil...":
-        args.vendor_operand_kind_prefix = ""
-
-    if (args.core_insts_output is None) != \
-            (args.operand_kinds_output is None):
-        print('error: --core-insts-output and --operand-kinds-output '
-              'should be specified together.')
-        exit(1)
-    if args.operand_kinds_output and not (args.spirv_core_grammar and
-         args.extinst_debuginfo_grammar and
-         args.extinst_cldebuginfo100_grammar):
-        print('error: --operand-kinds-output requires --spirv-core-grammar '
-              'and --extinst-debuginfo-grammar '
-              'and --extinst-cldebuginfo100-grammar')
-        exit(1)
-    if (args.glsl_insts_output is None) != \
-            (args.extinst_glsl_grammar is None):
-        print('error: --glsl-insts-output and --extinst-glsl-grammar '
-              'should be specified together.')
-        exit(1)
-    if (args.opencl_insts_output is None) != \
-            (args.extinst_opencl_grammar is None):
-        print('error: --opencl-insts-output and --extinst-opencl-grammar '
-              'should be specified together.')
-        exit(1)
-    if (args.vendor_insts_output is None) != \
-            (args.extinst_vendor_grammar is None):
-        print('error: --vendor-insts-output and '
-              '--extinst-vendor-grammar should be specified together.')
-        exit(1)
-    if all([args.core_insts_output is None,
-            args.glsl_insts_output is None,
-            args.opencl_insts_output is None,
-            args.vendor_insts_output is None,
-            args.extension_enum_output is None,
-            args.enum_string_mapping_output is None]):
-        print('error: at least one output should be specified.')
-        exit(1)
-
-    if args.spirv_core_grammar is not None:
-        with open(args.spirv_core_grammar) as json_file:
-            core_grammar = json.loads(json_file.read())
-            with open(args.extinst_debuginfo_grammar) as debuginfo_json_file:
-                debuginfo_grammar = json.loads(debuginfo_json_file.read())
-                with open(args.extinst_cldebuginfo100_grammar) as cldebuginfo100_json_file:
-                    cldebuginfo100_grammar = json.loads(cldebuginfo100_json_file.read())
-                    prefix_operand_kind_names("CLDEBUG100_", cldebuginfo100_grammar)
-                    instructions = []
-                    instructions.extend(core_grammar['instructions'])
-                    instructions.extend(debuginfo_grammar['instructions'])
-                    instructions.extend(cldebuginfo100_grammar['instructions'])
-                    operand_kinds = []
-                    operand_kinds.extend(core_grammar['operand_kinds'])
-                    operand_kinds.extend(debuginfo_grammar['operand_kinds'])
-                    operand_kinds.extend(cldebuginfo100_grammar['operand_kinds'])
-                    extensions = get_extension_list(instructions, operand_kinds)
-                    operand_kinds = precondition_operand_kinds(operand_kinds)
-        if args.core_insts_output is not None:
-            make_path_to_file(args.core_insts_output)
-            make_path_to_file(args.operand_kinds_output)
-            with open(args.core_insts_output, 'w') as f:
-                f.write(generate_instruction_table(
-                    core_grammar['instructions']))
-            with open(args.operand_kinds_output, 'w') as f:
-                f.write(generate_operand_kind_table(operand_kinds))
-        if args.extension_enum_output is not None:
-            make_path_to_file(args.extension_enum_output)
-            with open(args.extension_enum_output, 'w') as f:
-                f.write(generate_extension_enum(extensions))
-        if args.enum_string_mapping_output is not None:
-            make_path_to_file(args.enum_string_mapping_output)
-            with open(args.enum_string_mapping_output, 'w') as f:
-                f.write(generate_all_string_enum_mappings(
-                    extensions, operand_kinds))
-
-    if args.extinst_glsl_grammar is not None:
-        with open(args.extinst_glsl_grammar) as json_file:
-            grammar = json.loads(json_file.read())
-            make_path_to_file(args.glsl_insts_output)
-            with open(args.glsl_insts_output, 'w') as f:
-                f.write(generate_extended_instruction_table(
-                    grammar, 'glsl'))
-
-    if args.extinst_opencl_grammar is not None:
-        with open(args.extinst_opencl_grammar) as json_file:
-            grammar = json.loads(json_file.read())
-            make_path_to_file(args.opencl_insts_output)
-            with open(args.opencl_insts_output, 'w') as f:
-                f.write(generate_extended_instruction_table(
-                    grammar, 'opencl'))
-
-    if args.extinst_vendor_grammar is not None:
-        with open(args.extinst_vendor_grammar) as json_file:
-            grammar = json.loads(json_file.read())
-            make_path_to_file(args.vendor_insts_output)
-            name = args.extinst_vendor_grammar
-            start = name.find('extinst.') + len('extinst.')
-            name = name[start:-len('.grammar.json')].replace('-', '_')
-            with open(args.vendor_insts_output, 'w') as f:
-                f.write(generate_extended_instruction_table(
-                    grammar, name, args.vendor_operand_kind_prefix))
-
-
-if __name__ == '__main__':
-    main()
diff --git a/utils/ggt.py b/utils/ggt.py
new file mode 100755
index 0000000..cd32f31
--- /dev/null
+++ b/utils/ggt.py
@@ -0,0 +1,977 @@
+#!/usr/bin/env python3
+# Copyright (c) 2016 Google 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.
+"""Generates compressed grammar tables from SPIR-V JSON grammar."""
+
+import errno
+import json
+import os.path
+import re
+import sys
+from typing import Dict, List, Tuple, Any
+
+# Find modules relative to the directory containing this script.
+# This is needed for hermetic Bazel builds, where the Table files are bundled
+# together with this script, while keeping their relative locations.
+sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+
+from Table.Context import Context
+from Table.IndexRange import IndexRange
+from Table.Operand import Operand
+
+class GrammarError(Exception):
+    pass
+
+# Extensions to recognize, but which don't necessarily come from the SPIR-V
+# core or KHR grammar files.  Get this list from the SPIR-V registry web page.
+# NOTE: Only put things on this list if it is not in those grammar files.
+EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS = """
+SPV_AMD_gpu_shader_half_float
+SPV_AMD_gpu_shader_int16
+SPV_KHR_non_semantic_info
+SPV_EXT_relaxed_printf_string_address_space
+"""
+
+class ExtInst():
+    """
+    An extended instruction set.
+
+    Properties:
+       prefix: the string prefix for operand enums. Often an empty string.
+       file: the location of the JSON grammar file
+       name: the name, can be used as an identifier
+       enum_name: the enum name, e.g. SPV_EXT_INST_OPENCL_STD
+       grammar: the JSON object for the grammar, loaded from the file.
+    """
+    def __init__(self,spec: str):
+        matches = re.fullmatch('^([^,]*),(.*)',spec)
+        if matches is None:
+            raise Exception("Invalid prefix and path: {}".format(spec))
+        self.prefix = matches[1]
+        if self.prefix is None:
+            self.prefix = ""
+        self.file = matches[2]
+        matches = re.match('.*extinst\\.(.*)\\.grammar.json', self.file)
+        if matches is None:
+            raise Exception("Invalid grammar file name: {}".format(self.file))
+        self.name = matches[1].replace('-','_').replace('.','_')
+
+        self.enum_name = 'SPV_EXT_INST_TYPE_{}'.format(self.name).upper()
+        if self.enum_name == "SPV_EXT_INST_TYPE_OPENCL_STD_100":
+            # Live with an old decision, by adjusting this name.
+            self.enum_name = "SPV_EXT_INST_TYPE_OPENCL_STD"
+        if self.enum_name == "SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO":
+            # The version-agnostic grammar file name omits "_100", but the
+            # public enum in libspirv.h retains the suffix for compatibility.
+            self.enum_name = "SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100"
+
+        self.load()
+
+    def load(self):
+        """
+        Populates self.grammar from the file.
+        Applies the self.prefix to operand enums
+        """
+        with open(self.file) as json_file:
+            self.grammar = json.loads(json_file.read())
+            if len(self.prefix) > 0:
+                prefix_operand_kind_names(self.prefix, self.grammar)
+
+
+def convert_min_required_version(version): # (version: str | None) -> str
+    """Converts the minimal required SPIR-V version encoded in the grammar to
+    the symbol in SPIRV-Tools."""
+    if version is None:
+        return 'SPV_SPIRV_VERSION_WORD(1, 0)'
+    if version == 'None':
+        return '0xffffffffu'
+    return 'SPV_SPIRV_VERSION_WORD({})'.format(version.replace('.', ','))
+
+
+def convert_max_required_version(version): # (version: str | None) -> str
+    """Converts the maximum required SPIR-V version encoded in the grammar to
+    the symbol in SPIRV-Tools."""
+    if version is None:
+        return '0xffffffffu'
+    return 'SPV_SPIRV_VERSION_WORD({})'.format(version.replace('.', ','))
+
+
+def c_bool(b: bool) -> str:
+    return 'true' if b else 'false'
+
+
+def ctype(kind: str, quantifier: str) -> str:
+    """Returns the corresponding operand type used in spirv-tools for the given
+    operand kind and quantifier used in the JSON grammar.
+
+    Arguments:
+      - kind, e.g. 'IdRef'
+      - quantifier, e.g. '', '?', '*'
+
+    Returns:
+      a string of the enumerant name in spv_operand_type_t
+    """
+    if kind == '':
+        raise Error("operand JSON object missing a 'kind' field")
+    # The following cases are where we differ between the JSON grammar and
+    # spirv-tools.
+    if kind == 'IdResultType':
+        kind = 'TypeId'
+    elif kind == 'IdResult':
+        kind = 'ResultId'
+    elif kind == 'IdMemorySemantics' or kind == 'MemorySemantics':
+        kind = 'MemorySemanticsId'
+    elif kind == 'IdScope' or kind == 'Scope':
+        kind = 'ScopeId'
+    elif kind == 'IdRef':
+        kind = 'Id'
+
+    elif kind == 'ImageOperands':
+        kind = 'Image'
+    elif kind == 'Dim':
+        kind = 'Dimensionality'
+    elif kind == 'ImageFormat':
+        kind = 'SamplerImageFormat'
+    elif kind == 'KernelEnqueueFlags':
+        kind = 'KernelEnqFlags'
+
+    elif kind == 'LiteralExtInstInteger':
+        kind = 'ExtensionInstructionNumber'
+    elif kind == 'LiteralSpecConstantOpInteger':
+        kind = 'SpecConstantOpNumber'
+    elif kind == 'LiteralContextDependentNumber':
+        kind = 'TypedLiteralNumber'
+
+    elif kind == 'PairLiteralIntegerIdRef':
+        kind = 'LiteralIntegerId'
+    elif kind == 'PairIdRefLiteralInteger':
+        kind = 'IdLiteralInteger'
+    elif kind == 'PairIdRefIdRef':  # Used by OpPhi in the grammar
+        kind = 'Id'
+
+    if kind == 'FPRoundingMode':
+        kind = 'FpRoundingMode'
+    elif kind == 'FPFastMathMode':
+        kind = 'FpFastMathMode'
+
+    if quantifier == '?':
+        kind = 'Optional{}'.format(kind)
+    elif quantifier == '*':
+        kind = 'Variable{}'.format(kind)
+
+    return 'SPV_OPERAND_TYPE_{}'.format(
+        re.sub(r'([a-z])([A-Z])', r'\1_\2', kind).upper())
+
+
+def convert_operand_kind(obj: Dict[str, str]) -> str:
+    """Returns the corresponding operand type used in spirv-tools for the given
+    operand kind and quantifier used in the JSON grammar.
+
+    Arguments:
+      - obj: an instruction operand, having keys:
+          - 'kind', e.g. 'IdRef'
+          - optionally, a quantifier: '?' or '*'
+
+    Returns:
+      a string of the enumerant name in spv_operand_type_t
+    """
+    kind = obj.get('kind', '')
+    quantifier = obj.get('quantifier', '')
+    return ctype(kind, quantifier)
+
+
+def to_safe_identifier(s: str) -> str:
+    """
+    Returns a new string with all non-letters converted to underscores,
+    and prepending 'k'.
+    The result should be safe to use as a C identifier.
+    """
+    return 'k' + re.sub(r'[^a-zA-Z0-9]', '_', s)
+
+
+class Grammar():
+    """
+    Accumulates string and enum tables.
+    The extensions and operand kinds lists are fixed at creation time.
+    Prints tables for instructions, operand kinds, and underlying string
+    and enum tables.
+    Assumes an index range is emitted by printing an IndexRange object.
+    """
+    def __init__(self, extensions: List[str], operand_kinds:List[dict], printing_classes: List[str]) -> None:
+        self.context = Context()
+        self.extensions = sorted(extensions)
+        self.operand_kinds = sorted(operand_kinds, key = lambda ok: convert_operand_kind(ok))
+        self.printing_classes = sorted([to_safe_identifier(x) for x in printing_classes])
+
+        # The self.header_ignore_decls are only used to debug the flow.
+        # They are copied into the C++ source code where they are more likely
+        # to be seen by humans.
+        self.header_ignore_decls: List[str] = [self.IndexRangeDecls()]
+
+        # The self.header_decls content goes into core_tables_header.inc to be
+        # included in a .h file.
+        self.header_decls: List[str] = []
+        # The self.body_decls content goes into core_tables_body.inc to be included
+        # in a .cpp file.  It includes definitions of static variables and
+        # hidden functions.
+        self.body_decls: List[str] = []
+
+        if len(self.operand_kinds) == 0:
+            raise Exception("operand_kinds should be a non-empty list")
+        if len(self.extensions) == 0:
+            raise Exception("extensions should be a non-empty list")
+
+        self.ComputePrintingClassDecls()
+        self.ComputeExtensionDecls()
+
+        # These operand kinds need to have their optional counterpart to also
+        # be represented in the lookup tables, with the same content.
+        self.operand_kinds_needing_optional_variant = [
+                'ImageOperands',
+                'AccessQualifier',
+                'MemoryAccess',
+                'PackedVectorFormat',
+                'CooperativeMatrixOperands',
+                'MatrixMultiplyAccumulateOperands',
+                'RawAccessChainOperands',
+                'FPEncoding',
+                'TensorOperands',
+                'Capability']
+
+    def dump(self) -> None:
+        self.context.dump()
+
+    def IndexRangeDecls(self) -> str:
+        return """
+struct IndexRange {
+  uint32_t first = 0; // index of the first element in the range
+  uint32_t count = 0; // number of elements in the range
+};
+constexpr inline IndexRange IR(uint32_t first, uint32_t count) {
+  return {first, count};
+}
+"""
+
+    def ComputePrintingClassDecls(self) -> str:
+        parts: List[str] = []
+        parts.append("enum class PrintingClass : uint32_t {");
+        parts.extend(["  {},".format(x) for x in self.printing_classes])
+        parts.append("};\n")
+        self.header_decls.extend(parts)
+
+    def ComputeExtensionDecls(self) -> None:
+        parts: List[str] = []
+        parts.append("enum Extension : uint32_t {");
+        parts.extend(["  {},".format(to_safe_identifier(x)) for x in self.extensions])
+        parts.append("};\n")
+        self.header_decls.extend(parts)
+
+        parts = []
+        parts.append("// Returns the name of an extension, as an index into kStrings")
+        parts.append("IndexRange ExtensionToIndexRange(Extension extension) {\n  switch(extension) {")
+        for e in self.extensions:
+            parts.append('    case Extension::k{}: return {};'.format(e,self.context.AddString(e)))
+        parts.append("    default: break;");
+        parts.append('  }\n  return {};\n}\n');
+        self.body_decls.extend(parts)
+
+        parts = []
+        parts.append("""// Extension names and values, ordered by name
+// The fields in order are:
+//   name, indexing into kStrings
+//   enum value""")
+        parts.append("static const std::array<NameValue,{}> kExtensionNames{{{{".format(len(self.extensions)))
+        for e in self.extensions:
+            parts.append('    {{{}, static_cast<uint32_t>({})}},'.format(self.context.AddString(e), to_safe_identifier(e)))
+        parts.append("}};\n")
+        self.body_decls.extend(parts)
+
+    def ComputeOperandTables(self) -> None:
+        """
+        Returns the string for the C definitions of the operand kind tables.
+
+        An operand kind such as ImageOperands also has an associated
+        operand kind that is an 'optional' variant.
+        These are represented as two distinct operand kinds in spv_operand_type_t.
+        For example, ImageOperands maps to both SPV_OPERAND_TYPE_IMAGE, and also
+        to SPV_OPERAND_TYPE_OPTIONAL_IMAGE.
+
+        The definitions are:
+         - kOperandsByValue: a 1-dimensional array of all operand descriptions
+           sorted first by operand kind, then by operand value.
+           Only non-optional operand kinds are represented here.
+
+         - kOperandsByValueRangeByKind: a function mapping from operand kind to
+           the index range into kOperandByValue.
+           This has mappings for both concrete and corresponding optional operand kinds.
+
+         - kOperandNames: a 1-dimensional array of all operand NameIndex
+           entries, sorted first by operand kinds, then by operand name.
+           The name part is represented by an index range into the string table.
+           The index part is the index of this name's entry into the by-value array.
+           This can have more entries than the by-value array, because names
+           can have string aliases. For example,the MemorySemantics value 0
+           is named both "Relaxed" and "None".
+           Only non-optional operand kinds are represented here.
+
+         - kOperandNamesRangeByKind: a mapping from operand kind to the index
+           range into kOperandNames.
+           This has mappings for both concrete and corresponding optional operand kinds.
+        """
+
+        self.header_ignore_decls.append(
+"""
+struct NameIndex {
+  // Location of the null-terminated name in the global string table.
+  IndexRange name;
+  // Index of this name's entry in in the associated by-value table.
+  uint32_t index;
+};
+struct NameValue {
+  // Location of the null-terminated name in the global string table.
+  IndexRange name;
+  // Enum value in the binary format.
+  uint32_t value;
+};
+// Describes a SPIR-V operand.
+struct OperandDesc {
+  uint32_t value;
+  IndexRange operands_range;      // Indexes kOperandSpans
+  IndexRange name_range;          // Indexes kStrings
+  IndexRange aliases_range;       // Indexes kAliasSpans
+  IndexRange capabilities_range;  // Indexes kCapabilitySpans
+  // A set of extensions that enable this feature. If empty then this operand
+  // value is in core and its availability is subject to minVersion. The
+  // assembler, binary parser, and disassembler ignore this rule, so you can
+  // freely process invalid modules.
+  IndexRange extensions_range;  // Indexes kExtensionSpans
+  // Minimal core SPIR-V version required for this feature, if without
+  // extensions. ~0u means reserved for future use. ~0u and non-empty
+  // extension lists means only available in extensions.
+  uint32_t minVersion;
+  uint32_t lastVersion;
+  utils::Span<spv_operand_type_t> operands() const;
+  utils::Span<char> name() const;
+  utils::Span<IndexRange> aliases() const;
+  utils::Span<spv::Capability> capabilities() const;
+  utils::Span<spvtools::Extension> extensions() const;
+  OperandDesc(const OperandDesc&) = delete;
+  OperandDesc(OperandDesc&&) = delete;
+};
+""")
+
+        def ShouldEmit(operand_kind_json: Dict[str,any]):
+            """ Returns true if we should emit a table for the given
+            operand kind.
+            """
+            category = operand_kind_json.get('category')
+            return category in ['ValueEnum', 'BitEnum']
+
+        # Populate kOperandsByValue
+        operands_by_value: List[str] = []
+        operands_by_value_by_kind: Dict[str,IndexRange] = {}
+        # Maps the operand kind and value to the index into kOperandsByValue
+        index_by_kind_and_value: Dict[Tuple(str,int),int] = {}
+        index = 0
+        for operand_kind_json in self.operand_kinds:
+            kind_key: str = convert_operand_kind(operand_kind_json)
+            if ShouldEmit(operand_kind_json):
+                operands = [Operand(o) for o in operand_kind_json['enumerants']]
+                operand_descs: List[str] = []
+                for o in sorted(operands, key = lambda o: o.value):
+                    suboperands = [convert_operand_kind(p) for p in o.parameters]
+                    desc = [
+                        o.value,
+                        self.context.AddStringList('operand', suboperands),
+                        str(self.context.AddString(o.enumerant)) + '/* {} */'.format(o.enumerant),
+                        self.context.AddStringList('alias', o.aliases),
+                        self.context.AddStringList('capability', o.capabilities),
+                        self.context.AddStringList('extension', o.extensions),
+                        convert_min_required_version(o.version),
+                        convert_max_required_version(o.lastVersion),
+                    ]
+                    operand_descs.append('{' + ','.join([str(d) for d in desc]) + '}}, // {}'.format(kind_key))
+                    index_by_kind_and_value[(kind_key,o.value)] = index
+                    index += 1
+                operands_by_value_by_kind[kind_key] = IndexRange(len(operands_by_value), len(operand_descs))
+                operands_by_value.extend(operand_descs)
+            else:
+                pass
+
+        parts = []
+        parts.append("""// Operand descriptions, ordered by (operand kind, operand enum value).
+// The fields in order are:
+//   enum value
+//   operands, an IndexRange into kOperandSpans
+//   name, a character-counting IndexRange into kStrings
+//   aliases, an IndexRange into kAliasSpans
+//   capabilities, an IndexRange into kCapabilitySpans
+//   extensions, as an IndexRange into kExtensionSpans
+//   version, first version of SPIR-V that has it
+//   lastVersion, last version of SPIR-V that has it""")
+        parts.append("static const std::array<OperandDesc, {}> kOperandsByValue{{{{".format(len(operands_by_value)))
+        parts.extend(['  ' + str(x) for x in operands_by_value])
+        parts.append("}};\n")
+        self.body_decls.extend(parts)
+
+        parts = []
+        parts.append("""// Maps an operand kind to possible operands for that kind.
+// The result is an IndexRange into kOperandsByValue, and the operands
+// are sorted by value within that span.
+// An optional variant of a kind maps to the details for the corresponding
+// concrete operand kind.""")
+        parts.append("IndexRange OperandByValueRangeForKind(spv_operand_type_t type) {\n  switch(type) {")
+        for kind_key, ir in operands_by_value_by_kind.items():
+            parts.append("    case {}: return {};".format(
+                kind_key,
+                str(operands_by_value_by_kind[kind_key])))
+        for kind in self.operand_kinds_needing_optional_variant:
+            non_optional_kind = ctype(kind,'')
+            if non_optional_kind in operands_by_value_by_kind:
+                parts.append("    case {}: return {};".format(
+                    ctype(kind, '?'),
+                    str(operands_by_value_by_kind[ctype(kind,'')])))
+            else:
+                raise GrammarError(
+                        "error: unknown operand type {}, from JSON grammar operand '{}':".format(non_optional_kind, kind) +
+                        " consider updating spv_operand_type_t in spirv-tools/libspirv.h")
+            
+        parts.append("    default: break;");
+        parts.append("  }\n  return IR(0,0);\n}\n")
+        self.body_decls.extend(parts)
+
+        # Populate kOperandNames
+        operand_names: List[Tuple[IndexRange,int]] = []
+        name_range_for_kind: Dict[str,IndexRange] = {}
+        for operand_kind_json in self.operand_kinds:
+            kind_key: str = convert_operand_kind(operand_kind_json)
+            if ShouldEmit(operand_kind_json):
+                operands = [Operand(o) for o in operand_kind_json['enumerants']]
+                tuples: List[Tuple[str,int,str]] = []
+                for o in operands:
+                    tuples.append((o.enumerant, o.value, kind_key))
+                    for a in o.aliases:
+                        tuples.append((a, o.value, kind_key))
+                tuples = sorted(tuples, key = lambda t: t[0])
+                ir_tuples = [(self.context.AddString(t[0]),t[1],t[2]) for t in tuples]
+                name_range_for_kind[kind_key] = IndexRange(len(operand_names), len(ir_tuples))
+                operand_names.extend(ir_tuples)
+            else:
+                pass
+        operand_name_strings: List[str] = []
+        for i in range(0, len(operand_names)):
+            ir, value, kind_key = operand_names[i]
+            index = index_by_kind_and_value[(kind_key,value)]
+            operand_name_strings.append('{{{}, {}}}, // {} {} in {}'.format(
+                str(ir),index,i,self.context.GetString(ir),kind_key))
+
+        parts: List[str] = []
+        parts.append("""// Operand names and index into kOperandsByValue, ordered by (operand kind, name)
+// The fields in order are:
+//   name, either the primary name or an alias, indexing into kStrings
+//   index into the kOperandsByValue array""")
+        parts.append("static const std::array<NameIndex, {}> kOperandNames{{{{".format(len(operand_name_strings)))
+        parts.extend(['  ' + str(x) for x in operand_name_strings])
+        parts.append("}};\n")
+        self.body_decls.extend(parts)
+
+        parts.append("""// Maps an operand kind to possible names for operands of that kind.
+// The result is an IndexRange into kOperandNames, and the names
+// are sorted by name within that span.
+// An optional variant of a kind maps to the details for the corresponding
+// concrete operand kind.""")
+        parts = ["IndexRange OperandNameRangeForKind(spv_operand_type_t type) {\n  switch(type) {"]
+        for kind_key, ir in name_range_for_kind.items():
+            parts.append("    case {}: return {};".format(
+                kind_key,
+                str(name_range_for_kind[kind_key])))
+        for kind in self.operand_kinds_needing_optional_variant:
+            parts.append("    case {}: return {};".format(
+                ctype(kind, '?'),
+                str(name_range_for_kind[ctype(kind,'')])))
+        parts.append("    default: break;");
+        parts.append("  }\n  return IR(0,0);\n}\n")
+        self.body_decls.extend(parts)
+
+
+    def ComputeInstructionTables(self, insts) -> None:
+        """
+        Creates declarations for instruction tables.
+        Populates self.header_ignore_decls, self.body_decls.
+
+        Params:
+            insts: an array of instructions objects using the JSON schema
+        """
+        self.header_ignore_decls.append("""
+// Describes an Instruction
+struct InstructionDesc {
+  const spv::Op value;
+  const bool hasResult;
+  const bool hasType;
+  const IndexRange operands_range;      // Indexes kOperandSpans
+  const IndexRange name_range;          // Indexes kStrings
+  const IndexRange aliases_range;       // Indexes kAliasSpans
+  const IndexRange capabilities_range;  // Indexes kCapbilitySpans
+  // A set of extensions that enable this feature. If empty then this operand
+  // value is in core and its availability is subject to minVersion. The
+  // assembler, binary parser, and disassembler ignore this rule, so you can
+  // freely process invalid modules.
+  const IndexRange extensions_range;    // Indexes kExtensionSpans
+  // Minimal core SPIR-V version required for this feature, if without
+  // extensions. ~0u means reserved for future use. ~0u and non-empty
+  // extension lists means only available in extensions.
+  uint32_t minVersion;
+  uint32_t lastVersion;
+  PrintingClass printingClass; // Section of SPIR-V spec. e.g. kComposite, kImage
+  utils::Span<spv_operand_type_t> operands() const;
+  utils::Span<char> name() const;
+  utils::Span<IndexRange> aliases() const;
+  utils::Span<spv::Capability> capabilities() const;
+  utils::Span<spvtools::Extension> extensions() const;
+  OperandDesc(const OperandDesc&) = delete;
+  OperandDesc(OperandDesc&&) = delete;
+};
+""")
+
+        # Create the array of InstructionDesc
+        lines: List[str] = []
+        # Maps the opcode name (without "Op" prefix) to its index in the table.
+        index_by_opcode: Dict[int,int] = {}
+        # Sort by opcode, so lookup can use binary search
+        for inst in sorted(insts, key = lambda inst: int(inst['opcode'])):
+            parts: List[str] = []
+
+            opname: str = inst['opname']
+
+            operand_kinds = [convert_operand_kind(o) for o in inst.get('operands',[])]
+            if opname == 'OpExtInst' and operand_kinds[-1] == 'SPV_OPERAND_TYPE_VARIABLE_ID':
+                # The published grammar uses 'sequence of ID' at the
+                # end of the ExtInst operands. But SPIRV-Tools uses
+                # a specific pattern based on the particular opcode.
+                # Drop it here.
+                # See https://github.com/KhronosGroup/SPIRV-Tools/issues/233
+                operand_kinds.pop()
+
+            hasResult = 'SPV_OPERAND_TYPE_RESULT_ID' in operand_kinds
+            hasType = 'SPV_OPERAND_TYPE_TYPE_ID' in operand_kinds
+
+            # Remove the "Op" prefix from opcode alias names
+            aliases = [name[2:] for name in inst.get('aliases',[])]
+
+            parts.extend([
+                'spv::Op::' + opname,
+                c_bool(hasResult),
+                c_bool(hasType),
+                self.context.AddStringList('operand', operand_kinds),
+                self.context.AddString(opname[2:]),
+                self.context.AddStringList('alias', aliases),
+                self.context.AddStringList('capability', inst.get('capabilities',[])),
+                self.context.AddStringList('extension', inst.get('extensions',[])),
+                convert_min_required_version(inst.get('version', None)),
+                convert_max_required_version(inst.get('lastVersion', None)),
+                'PrintingClass::' + to_safe_identifier(inst.get('class','@exclude'))
+            ])
+
+            index_by_opcode[int(inst['opcode'])] = len(lines)
+            lines.append('{{{}}},'.format(', '.join([str(x) for x in parts])))
+        parts = []
+        parts.append("""// Instruction descriptions, ordered by opcode.
+// The fields in order are:
+//   opcode
+//   a boolean indicating if the instruction produces a result ID
+//   a boolean indicating if the instruction result ID has a type
+//   operands, an IndexRange into kOperandSpans
+//   opcode name (without the 'Op' prefix), a character-counting IndexRange into kStrings
+//   aliases, an IndexRange into kAliasSpans
+//   capabilities, an IndexRange into kCapabilitySpans
+//   extensions, as an IndexRange into kExtensionSpans
+//   version, first version of SPIR-V that has it
+//   lastVersion, last version of SPIR-V that has it""")
+        parts.append("static const std::array<InstructionDesc, {}> kInstructionDesc{{{{".format(len(lines)));
+        parts.extend(['  ' + l for l in lines])
+        parts.append("}};\n");
+        self.body_decls.extend(parts)
+
+        # Create kInstructionNames.
+        opcode_name_entries: List[str] = []
+        name_value_pairs: List[Tuple[str,int]] = []
+        for i in insts:
+            name_value_pairs.append((i['opname'][2:], i['opcode']))
+            for a in i.get('aliases',[]):
+                name_value_pairs.append((a[2:], i['opcode']))
+        name_value_pairs = sorted(name_value_pairs)
+        inst_name_strings: List[str] = []
+        for i in range(0, len(name_value_pairs)):
+            name, value = name_value_pairs[i]
+            ir = self.context.AddString(name)
+            index = index_by_opcode[value]
+            inst_name_strings.append('{{{}, {}}}, // {} {}'.format(str(ir),index,i,name))
+        parts: List[str] = []
+        parts.append("""// Opcode strings (without the 'Op' prefix) and opcode values, ordered by name.
+// The fields in order are:
+//   name, either the primary name or an alias, indexing into kStrings
+//   index into kInstructionDesc""")
+        parts.append("static const std::array<NameIndex, {}> kInstructionNames{{{{".format(len(inst_name_strings)))
+        parts.extend(['  ' + str(x) for x in inst_name_strings])
+        parts.append("}};\n")
+        self.body_decls.extend(parts)
+
+
+    def ComputeExtendedInstructions(self, extinsts) -> None:
+        """
+        Generates tables for extended instructions
+
+        Args:
+            self
+            extinsts: a list of extinst objects
+        """
+
+        """
+            ExtInstDesc {
+             value:         uint32_t
+             name:          IndexRange
+             operands:      IndexRange
+             capabilities:  IndexRange
+            }
+
+        The definitions are:
+         - kExtInstByValue: a 1-dimensional array of all operand descriptions
+           sorted first by extended instruction enum, then by operand value.
+
+         - ExtInstByValueRangeForKind: a function mapping from extinst enum to
+           the index range into kExtInstByValue.
+
+         - kExtInstNames: a 1-dimensional array of all extinst name-index pairs,
+           sorted first by extinst enum, then by operand name.
+           The name part is represented by an index range into the string table.
+           The index part is the index of this name's entry in the kExtInstByValue
+           array.
+
+         - kExtInstNamesRangeByKind: a mapping from operand kind to the index
+           range into kOperandNames.
+           This has mappings for both concrete and corresponding optional operand kinds.
+        """
+
+        # Create kExtInstByValue
+        by_value: List[List[Any]] = []
+        by_value_by_kind: Dict[str,IndexRange] = {}
+        index_by_kind_and_opcode: Dict[Tuple[str,int],int] = {}
+        index = 0
+        for e in extinsts:
+            insts_in_set = []
+            for inst in sorted(e.grammar['instructions'], key = lambda inst: inst['opcode']):
+                operands = [convert_operand_kind(o) for o in inst.get('operands',[])]
+                inst_parts = [
+                    inst['opcode'],
+                    self.context.AddStringList('operand', operands),
+                    self.context.AddString(inst['opname']),
+                    self.context.AddStringList('capability', inst.get('capabilities',[])),
+                ]
+                inst_parts = [str(x) for x in inst_parts]
+                insts_in_set.append('    {{{}}}, // {} in {}'.format(
+                        ','.join(inst_parts), inst['opname'], e.name))
+                index_by_kind_and_opcode[(e.enum_name,int(inst['opcode']))] = index
+                index += 1
+            by_value_by_kind[e.enum_name] = IndexRange(len(by_value), len(insts_in_set))
+            by_value.extend(insts_in_set)
+
+        parts: List[str] = []
+        parts.append("""// Extended instruction descriptions, ordered by (extinst enum, opcode value).
+// The fields in order are:
+//   enum value
+//   operands, an IndexRange into kOperandSpans
+//   name, a character-counting IndexRange into kStrings
+//   capabilities, an IndexRange into kCapabilitySpans""")
+        parts.append("static const std::array<ExtInstDesc, {}> kExtInstByValue{{{{".format(len(by_value)))
+        parts.extend(by_value)
+        parts.append('}};\n')
+        self.body_decls.extend(parts)
+
+        # Create kExtInstByValueRangeForKind
+        parts = []
+        parts.append("""// Maps an extended instruction enum to possible names for operands of that kind.
+// The result is an IndexRange into kOperandNames, and the names
+// are sorted by name within that span.
+// An optional variant of a kind maps to the details for the corresponding
+// concrete operand kind.""")
+        parts = ["IndexRange ExtInstByValueRangeForKind(spv_ext_inst_type_t type) {\n  switch(type) {"]
+        for name, ir in by_value_by_kind.items():
+            parts.append("    case {}: return {};".format(name, ir))
+        parts.append("    default: break;");
+        parts.append("  }\n  return IR(0,0);\n}\n")
+        self.body_decls.extend(parts)
+
+        # Create kExtInstNames
+        parts = []
+        by_name: List[List[Any]] = []
+        by_name_by_kind: Dict[str,IndexRange] = {}
+        for e in extinsts:
+            # Sort by name within a set
+            insts_by_name = sorted(e.grammar['instructions'], key = lambda i: i['opname'])
+            insts_in_set = []
+            for inst in insts_by_name:
+                index = index_by_kind_and_opcode[(e.enum_name,int(inst['opcode']))]
+                insts_in_set.append(
+                        '    {{{}, {}}}, // {} in {}'.format(
+                                str(self.context.AddString(inst['opname'])),
+                                index,
+                                inst['opname'],
+                                e.name))
+            by_name_by_kind[e.enum_name] = IndexRange(len(by_name), len(insts_in_set))
+            by_name.extend(insts_in_set)
+        parts.append("""// Extended instruction opcode names sorted by extended instruction kind, then opcode name.
+// The fields in order are:
+//   name
+//   index into kExtInstByValue""")
+        parts.append("static const std::array<NameIndex, {}> kExtInstNames{{{{".format(len(by_name)))
+        parts.extend(by_name)
+        parts.append('}};\n')
+        self.body_decls.extend(parts)
+
+        # Create kExtInstNameRangeByKind
+        parts = []
+        parts.append("""// Maps an extended instruction kind to possible names for instructions of that kind.
+// The result is an IndexRange into kExtInstNames, and the names
+// are sorted by name within that span.""")
+        parts = ["IndexRange ExtInstNameRangeForKind(spv_ext_inst_type_t type) {\n  switch(type) {"]
+        for name, ir in by_name_by_kind.items():
+            parts.append("    case {}: return {};".format(name, str(ir)))
+        parts.append("    default: break;");
+        parts.append("  }\n  return IR(0,0);\n}\n")
+        self.body_decls.extend(parts)
+
+    def ComputeLeafTables(self) -> None:
+        """
+        Generates the tables that the instruction and operand tables point to.
+        The tables are:
+            - the string table
+            - the table of sequences of:
+               - capabilities
+               - extensions
+               - operands
+
+        This method must be called after computing instruction and operand tables.
+        """
+
+        def c_str(s: str):
+            """
+            Returns the source for a C string literal or the given string, including
+            the explicit null at the end
+            """
+            return '"{}\\0"'.format(json.dumps(s).strip('"'))
+
+        parts: List[str] = []
+        parts.append("// Array of characters, referenced by IndexRanges elsewhere.")
+        parts.append("// Each IndexRange denotes a string.")
+        parts.append('static const char kStrings[] =');
+        parts.extend(['  {} // {}'.format(c_str(s), str(self.context.strings[s])) for s in self.context.string_buffer])
+        parts.append(';\n');
+        self.body_decls.extend(parts);
+
+        parts: List[str] = []
+        parts.append("""// Array of IndexRanges, where each represents a string by referencing
+// the kStrings table.
+// This array contains all sequences of alias strings used in the grammar.
+// This table is referenced by an IndexRange elsewhere, i.e. by the 'aliases'
+// field of an instruction or operand description.""")
+        parts.append('static const IndexRange kAliasSpans[] = {');
+        ranges = self.context.range_buffer['alias']
+        for i in range(0, len(ranges)):
+            ir = ranges[i]
+            parts.append('  {}, // {} {}'.format(str(ir), i, self.context.GetString(ir)))
+        parts.append('};\n');
+        self.body_decls.extend(parts);
+
+        parts = []
+        parts.append("// Array of capabilities, referenced by IndexRanges elsewhere.")
+        parts.append("// Contains all sequences of capabilities used in the grammar.")
+        parts.append('static const spv::Capability kCapabilitySpans[] = {');
+        capability_ranges = self.context.range_buffer['capability']
+        for i in range(0, len(capability_ranges)):
+            ir = capability_ranges[i]
+            cap = self.context.GetString(ir)
+            parts.append('  spv::Capability::{}, // {}'.format(cap, i))
+        parts.append('};\n');
+        self.body_decls.extend(parts);
+
+        parts = []
+        parts.append("// Array of extensions, referenced by IndexRanges elsewhere.")
+        parts.append("// Contains all sequences of extensions used in the grammar.")
+        parts.append('static const spvtools::Extension kExtensionSpans[] = {');
+        ranges = self.context.range_buffer['extension']
+        for i in range(0, len(ranges)):
+            ir = ranges[i]
+            name = self.context.GetString(ir)
+            parts.append('  spvtools::Extension::k{}, // {}'.format(name, i))
+        parts.append('};\n');
+        self.body_decls.extend(parts);
+
+        parts = []
+        parts.append("// Array of operand types, referenced by IndexRanges elsewhere.")
+        parts.append("// Contains all sequences of operand types used in the grammar.")
+        parts.append('static const spv_operand_type_t kOperandSpans[] = {');
+        ranges = self.context.range_buffer['operand']
+        for i in range(0, len(ranges)):
+            ir = ranges[i]
+            name = self.context.GetString(ir)
+            parts.append('  {}, // {}'.format(name, i))
+        parts.append('};\n');
+        self.body_decls.extend(parts)
+
+
+def make_path_to_file(f: str) -> None:
+    """Makes all ancestor directories to the given file, if they don't yet
+    exist.
+
+    Arguments:
+        f: The file whose ancestor directories are to be created.
+    """
+    dir = os.path.dirname(os.path.abspath(f))
+    try:
+        os.makedirs(dir)
+    except OSError as e:
+        if e.errno == errno.EEXIST and os.path.isdir(dir):
+            pass
+        else:
+            raise
+
+
+def get_extension_list(instructions, operand_kinds):
+    """Returns extensions as an alphabetically sorted list of strings.
+
+    Args:
+      instructions: list of instruction objects, using the JSON grammar file schema
+      operand_kinds: list of operand_kind objects, using the JSON grammar file schema
+    """
+
+    things_with_an_extensions_field = [item for item in instructions]
+
+    enumerants = sum([item.get('enumerants', [])
+                      for item in operand_kinds], [])
+
+    things_with_an_extensions_field.extend(enumerants)
+
+    extensions = sum([item.get('extensions', [])
+                      for item in things_with_an_extensions_field
+                      if item.get('extensions')], [])
+
+    for item in EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS.split():
+            # If it's already listed in a grammar, then don't put it in the
+            # special exceptions list.
+        assert item not in extensions, 'Extension %s is already in a grammar file' % item
+
+    extensions.extend(
+        EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS.split())
+
+    # Validator would ignore type declaration unique check. Should only be used
+    # for legacy autogenerated test files containing multiple instances of the
+    # same type declaration, if fixing the test by other methods is too
+    # difficult. Shouldn't be used for any other reasons.
+    extensions.append('SPV_VALIDATOR_ignore_type_decl_unique')
+
+    return sorted(set(extensions))
+
+
+def prefix_operand_kind_names(prefix, json_dict):
+    """Modifies json_dict, by prefixing all the operand kind names
+    with the given prefix.  Also modifies their uses in the instructions
+    to match.
+    """
+
+    old_to_new = {}
+    for operand_kind in json_dict["operand_kinds"]:
+        old_name = operand_kind["kind"]
+        new_name = prefix + old_name
+        operand_kind["kind"] = new_name
+        old_to_new[old_name] = new_name
+
+    for instruction in json_dict["instructions"]:
+        for operand in instruction.get("operands", []):
+            replacement = old_to_new.get(operand["kind"])
+            if replacement is not None:
+                operand["kind"] = replacement
+
+
+def main():
+    import argparse
+    parser = argparse.ArgumentParser(description='Generate SPIR-V info tables')
+
+    parser.add_argument('--spirv-core-grammar', metavar='<path>',
+                        type=str, required=False,
+                        help='input JSON grammar file for core SPIR-V '
+                        'instructions')
+    parser.add_argument('--extinst', metavar='<path>',
+                        type=str, action='append', required=False, default=None,
+                        help='extended instruction info: an enum prefix, then a comma, then'
+                        ' the file location of the JSON grammar')
+
+    parser.add_argument('--core-tables-body-output', metavar='<path>',
+                        type=str, required=False, default=None,
+                        help='output file for core SPIR-V grammar tables to be included in .cpp')
+    parser.add_argument('--core-tables-header-output', metavar='<path>',
+                        type=str, required=False, default=None,
+                        help='output file for core SPIR-V grammar tables to be included in .h')
+
+    args = parser.parse_args()
+
+    if args.spirv_core_grammar is None:
+        print('error: missing --spirv-core-grammar ')
+        sys.exit(1)
+    if (args.core_tables_body_output is None) and (args.core_tables_header_output is None):
+        print('error: need at least one of --core-tables-body-output --core-tables-header-output ')
+        sys.exit(1)
+    if len(args.extinst) < 1:
+        print('error: missing --extinst ')
+        sys.exit(1)
+
+    # Load the JSON grammar files.
+    extinsts = sorted([ExtInst(e) for e in args.extinst], key = lambda e: e.name)
+    with open(args.spirv_core_grammar) as json_file:
+        core_grammar = json.loads(json_file.read())
+        printing_class: List[str] = [e['tag'] for e in core_grammar['instruction_printing_class']]
+
+    # Collect all operand kinds and instructions, so we can generate
+    # extension lists, capability lists, and alias lists.
+    # Make a copy to avoid polluting the instruction list.
+    instructions = [x for x in core_grammar['instructions']]
+    operand_kinds = [x for x in core_grammar['operand_kinds']]
+    for e in extinsts:
+        instructions.extend(e.grammar.get('instructions',[]))
+        operand_kinds.extend(e.grammar.get('operand_kinds',[]))
+
+    extensions = get_extension_list(instructions, operand_kinds)
+
+    g = Grammar(extensions, operand_kinds, printing_class)
+    g.ComputeOperandTables()
+    g.ComputeInstructionTables(core_grammar['instructions'])
+    g.ComputeExtendedInstructions(extinsts)
+    g.ComputeLeafTables()
+
+    if args.core_tables_body_output is not None:
+        make_path_to_file(args.core_tables_body_output)
+        with open(args.core_tables_body_output, 'w') as f:
+            f.write('\n'.join(g.body_decls))
+    if args.core_tables_header_output is not None:
+        make_path_to_file(args.core_tables_header_output)
+        with open(args.core_tables_header_output, 'w') as f:
+            f.write('\n'.join(g.header_decls))
+    sys.exit(0)
+
+
+if __name__ == '__main__':
+  try:
+    main()
+  except GrammarError as ge:
+    print(ge)
+    sys.exit(1)
diff --git a/utils/roll_deps.sh b/utils/roll_deps.sh
index a62ebe9..d8c1da3 100755
--- a/utils/roll_deps.sh
+++ b/utils/roll_deps.sh
@@ -35,6 +35,7 @@
 dependency_to_branch_map["external/googletest/"]="origin/main"
 dependency_to_branch_map["external/re2/"]="origin/main"
 dependency_to_branch_map["external/spirv-headers/"]="origin/main"
+dependency_to_branch_map["external/mimalloc/"]="origin/main"
 
 # This script assumes it's parent directory is the repo root.
 repo_path=$(dirname "$0")/..
diff --git a/utils/vim/README.md b/utils/vim/README.md
new file mode 100644
index 0000000..a73977e
--- /dev/null
+++ b/utils/vim/README.md
@@ -0,0 +1,87 @@
+# Neovim configuration guide for SPIR-V disassembly files
+
+This directory holds instructions to configure Neovim for SPIR-V assembly files (`.spvasm`)
+
+At the end, Neovim should support:
+* Syntax highlighting
+* Jump to definition
+* Find all references
+* Symbol renaming
+* Operand hover information
+* Formatting
+* Completion suggestions for all Opcodes and Ids
+
+While the instructions here are specifically for Neovim, they should translate easily to vim.
+
+## Dependencies
+
+In order to build and install the Visual Studio Code language server extension, you will need to install and have on your `PATH` the following dependencies:
+* [`golang 1.16+`](https://golang.org/)
+
+## File type detection
+
+Neovim's default config location is typically `~/.config/nvim` so the rest of the instructions assume that but it will need to be changed if your system is different.
+
+Tell neovim that `*.spvasm` files should be treated as `spvasm` filetype
+```bash
+echo "au BufRead,BufNewFile *.spvasm                set filetype=spvasm" > ~/.config/nvim/ftdetect/spvasm.vim
+```
+
+## Syntax Highlighting
+
+### Generate the syntax highlighting file
+```bash
+cd <spirv-tools dir>
+mkdir -p build && cd build
+# Any platform is fine, ninja is used an as example
+cmake -G Ninja ..
+ninja spirv-tools-vimsyntax
+```
+
+### Copy the syntax file 
+```bash
+cp spvasm.vim ~/.config/nvim/syntax/spvasm.vim
+```
+
+## Language Server
+
+### Building the LSP (masOS / Linux)
+
+Run `build_lsp.sh`
+Copy `spirvls` and `spirv.json` to a location in `$PATH`
+
+```bash
+cd <spirv-tools dir>/utils/vscode
+./build_lsp.sh
+sudo cp spirvls/* /usr/local/bin/
+```
+
+### Building the LSP (Windows)
+
+TODO
+
+### Configuring Neovim
+
+Configuration will depend a lot on your installed plugins but assuming you are using [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) the following should be sufficient.
+
+```lua
+local lspconfig = require 'lspconfig'
+local configs = require 'lspconfig.configs'
+
+if not configs.spvasm then
+  configs.spvasm = {
+    default_config = {
+      cmd = { 'spirvls' },
+      filetypes = { 'spvasm' },
+      root_dir = function(fname)
+        return '.'
+      end,
+      settings = {},
+    },
+  }
+end
+
+lspconfig.spvasm.setup {
+  capabilities = require('cmp_nvim_lsp').default_capabilities(vim.lsp.protocol.make_client_capabilities()),
+}
+```
diff --git a/utils/generate_vim_syntax.py b/utils/vim/generate_syntax.py
similarity index 100%
rename from utils/generate_vim_syntax.py
rename to utils/vim/generate_syntax.py
diff --git a/utils/vscode/README.md b/utils/vscode/README.md
index d7aa2b4..9cf82b1 100644
--- a/utils/vscode/README.md
+++ b/utils/vscode/README.md
@@ -9,6 +9,7 @@
 * Symbol renaming
 * Operand hover information
 * Formatting
+* Completion suggestions for all Opcodes and Ids
 
 ## Dependencies
 
@@ -18,8 +19,8 @@
 
 ## Installing (macOS / Linux)
 
-Run `install.sh`
+Run `install_vscode.sh`
 
 ## Installing (Windows)
 
-Run `install.bat`
+Run `install_vscode.bat`
diff --git a/utils/vscode/build_lsp.sh b/utils/vscode/build_lsp.sh
new file mode 100755
index 0000000..dc92cc8
--- /dev/null
+++ b/utils/vscode/build_lsp.sh
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+# Copyright (c) 2019 Google 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.
+
+set -e # Fail on any error.
+
+ROOT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+
+pushd ${ROOT_PATH}
+    go run ./src/tools/gen-grammar.go --cache ./cache --template ./spirv.json.tmpl --out ./spirv.json
+    go run ./src/tools/gen-grammar.go --cache ./cache --template ./src/schema/schema.go.tmpl --out ./src/schema/schema.go
+
+    mkdir -p ./spirvls
+    cp ./spirv.json ./spirvls
+
+    go build -o ./spirvls/spirvls ./src/langsvr.go
+popd
diff --git a/utils/vscode/install.bat b/utils/vscode/install_vscode.bat
similarity index 100%
rename from utils/vscode/install.bat
rename to utils/vscode/install_vscode.bat
diff --git a/utils/vscode/install.sh b/utils/vscode/install_vscode.sh
similarity index 100%
rename from utils/vscode/install.sh
rename to utils/vscode/install_vscode.sh
diff --git a/utils/vscode/spirv.json b/utils/vscode/spirv.json
index 2e88296..4ab2529 100644
--- a/utils/vscode/spirv.json
+++ b/utils/vscode/spirv.json
@@ -12,6 +12,8 @@
 		{ "include": "#BitEnum_MemoryAccess" },
 		{ "include": "#BitEnum_KernelProfilingInfo" },
 		{ "include": "#BitEnum_RayFlags" },
+		{ "include": "#BitEnum_FragmentShadingRate" },
+		{ "include": "#BitEnum_RawAccessChainOperands" },
 		{ "include": "#ValueEnum_SourceLanguage" },
 		{ "include": "#ValueEnum_ExecutionModel" },
 		{ "include": "#ValueEnum_AddressingModel" },
@@ -25,8 +27,13 @@
 		{ "include": "#ValueEnum_ImageChannelOrder" },
 		{ "include": "#ValueEnum_ImageChannelDataType" },
 		{ "include": "#ValueEnum_FPRoundingMode" },
+		{ "include": "#ValueEnum_FPDenormMode" },
+		{ "include": "#ValueEnum_QuantizationModes" },
+		{ "include": "#ValueEnum_FPOperationMode" },
+		{ "include": "#ValueEnum_OverflowModes" },
 		{ "include": "#ValueEnum_LinkageType" },
 		{ "include": "#ValueEnum_AccessQualifier" },
+		{ "include": "#ValueEnum_HostAccessQualifier" },
 		{ "include": "#ValueEnum_FunctionParameterAttribute" },
 		{ "include": "#ValueEnum_Decoration" },
 		{ "include": "#ValueEnum_BuiltIn" },
@@ -37,6 +44,17 @@
 		{ "include": "#ValueEnum_RayQueryIntersection" },
 		{ "include": "#ValueEnum_RayQueryCommittedIntersectionType" },
 		{ "include": "#ValueEnum_RayQueryCandidateIntersectionType" },
+		{ "include": "#ValueEnum_PackedVectorFormat" },
+		{ "include": "#BitEnum_CooperativeMatrixOperands" },
+		{ "include": "#ValueEnum_CooperativeMatrixLayout" },
+		{ "include": "#ValueEnum_CooperativeMatrixUse" },
+		{ "include": "#BitEnum_CooperativeMatrixReduce" },
+		{ "include": "#ValueEnum_TensorClampMode" },
+		{ "include": "#BitEnum_TensorAddressingOperands" },
+		{ "include": "#ValueEnum_InitializationModeQualifier" },
+		{ "include": "#ValueEnum_LoadCacheControl" },
+		{ "include": "#ValueEnum_StoreCacheControl" },
+		{ "include": "#ValueEnum_NamedMaximumNumberOfRegisters" },
 		{ "include": "#BitEnum_DebugInfoFlags" },
 		{ "include": "#ValueEnum_DebugBaseTypeAttributeEncoding" },
 		{ "include": "#ValueEnum_DebugCompositeType" },
@@ -53,11 +71,11 @@
 	],
 	"repository": { 
 		"BitEnum_ImageOperands": {
-			"match": "\\b(None|Bias|Lod|Grad|ConstOffset|Offset|ConstOffsets|Sample|MinLod|MakeTexelAvailable|MakeTexelAvailableKHR|MakeTexelVisible|MakeTexelVisibleKHR|NonPrivateTexel|NonPrivateTexelKHR|VolatileTexel|VolatileTexelKHR|SignExtend|ZeroExtend)\\b",
+			"match": "\\b(None|Bias|Lod|Grad|ConstOffset|Offset|ConstOffsets|Sample|MinLod|MakeTexelAvailable|MakeTexelVisible|NonPrivateTexel|VolatileTexel|SignExtend|ZeroExtend|Nontemporal|Offsets)\\b",
 			"name": "keyword.spirv"
 		},
 		"BitEnum_FPFastMathMode": {
-			"match": "\\b(None|NotNaN|NotInf|NSZ|AllowRecip|Fast)\\b",
+			"match": "\\b(None|NotNaN|NotInf|NSZ|AllowRecip|Fast|AllowContract|AllowReassoc|AllowTransform)\\b",
 			"name": "keyword.spirv"
 		},
 		"BitEnum_SelectionControl": {
@@ -65,19 +83,19 @@
 			"name": "keyword.spirv"
 		},
 		"BitEnum_LoopControl": {
-			"match": "\\b(None|Unroll|DontUnroll|DependencyInfinite|DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount)\\b",
+			"match": "\\b(None|Unroll|DontUnroll|DependencyInfinite|DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount|InitiationIntervalINTEL|MaxConcurrencyINTEL|DependencyArrayINTEL|PipelineEnableINTEL|LoopCoalesceINTEL|MaxInterleavingINTEL|SpeculatedIterationsINTEL|NoFusionINTEL|LoopCountINTEL|MaxReinvocationDelayINTEL)\\b",
 			"name": "keyword.spirv"
 		},
 		"BitEnum_FunctionControl": {
-			"match": "\\b(None|Inline|DontInline|Pure|Const)\\b",
+			"match": "\\b(None|Inline|DontInline|Pure|Const|OptNoneEXT)\\b",
 			"name": "keyword.spirv"
 		},
 		"BitEnum_MemorySemantics": {
-			"match": "\\b(Relaxed|None|Acquire|Release|AcquireRelease|SequentiallyConsistent|UniformMemory|SubgroupMemory|WorkgroupMemory|CrossWorkgroupMemory|AtomicCounterMemory|ImageMemory|OutputMemory|OutputMemoryKHR|MakeAvailable|MakeAvailableKHR|MakeVisible|MakeVisibleKHR|Volatile)\\b",
+			"match": "\\b(Relaxed|Acquire|Release|AcquireRelease|SequentiallyConsistent|UniformMemory|SubgroupMemory|WorkgroupMemory|CrossWorkgroupMemory|AtomicCounterMemory|ImageMemory|OutputMemory|MakeAvailable|MakeVisible|Volatile)\\b",
 			"name": "keyword.spirv"
 		},
 		"BitEnum_MemoryAccess": {
-			"match": "\\b(None|Volatile|Aligned|Nontemporal|MakePointerAvailable|MakePointerAvailableKHR|MakePointerVisible|MakePointerVisibleKHR|NonPrivatePointer|NonPrivatePointerKHR)\\b",
+			"match": "\\b(None|Volatile|Aligned|Nontemporal|MakePointerAvailable|MakePointerVisible|NonPrivatePointer|AliasScopeINTELMask|NoAliasINTELMask)\\b",
 			"name": "keyword.spirv"
 		},
 		"BitEnum_KernelProfilingInfo": {
@@ -85,35 +103,43 @@
 			"name": "keyword.spirv"
 		},
 		"BitEnum_RayFlags": {
-			"match": "\\b(NoneKHR|OpaqueKHR|NoOpaqueKHR|TerminateOnFirstHitKHR|SkipClosestHitShaderKHR|CullBackFacingTrianglesKHR|CullFrontFacingTrianglesKHR|CullOpaqueKHR|CullNoOpaqueKHR|SkipTrianglesKHR|SkipAABBsKHR)\\b",
+			"match": "\\b(NoneKHR|OpaqueKHR|NoOpaqueKHR|TerminateOnFirstHitKHR|SkipClosestHitShaderKHR|CullBackFacingTrianglesKHR|CullFrontFacingTrianglesKHR|CullOpaqueKHR|CullNoOpaqueKHR|SkipTrianglesKHR|SkipAABBsKHR|ForceOpacityMicromap2StateEXT)\\b",
+			"name": "keyword.spirv"
+		},
+		"BitEnum_FragmentShadingRate": {
+			"match": "\\b(Vertical2Pixels|Vertical4Pixels|Horizontal2Pixels|Horizontal4Pixels)\\b",
+			"name": "keyword.spirv"
+		},
+		"BitEnum_RawAccessChainOperands": {
+			"match": "\\b(None|RobustnessPerComponentNV|RobustnessPerElementNV)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_SourceLanguage": {
-			"match": "\\b(Unknown|ESSL|GLSL|OpenCL_C|OpenCL_CPP|HLSL)\\b",
+			"match": "\\b(Unknown|ESSL|GLSL|OpenCL_C|OpenCL_CPP|HLSL|CPP_for_OpenCL|SYCL|HERO_C|NZSL|WGSL|Slang|Zig)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_ExecutionModel": {
-			"match": "\\b(Vertex|TessellationControl|TessellationEvaluation|Geometry|Fragment|GLCompute|Kernel|TaskNV|MeshNV|RayGenerationNV|RayGenerationKHR|IntersectionNV|IntersectionKHR|AnyHitNV|AnyHitKHR|ClosestHitNV|ClosestHitKHR|MissNV|MissKHR|CallableNV|CallableKHR)\\b",
+			"match": "\\b(Vertex|TessellationControl|TessellationEvaluation|Geometry|Fragment|GLCompute|Kernel|TaskNV|MeshNV|RayGenerationKHR|IntersectionKHR|AnyHitKHR|ClosestHitKHR|MissKHR|CallableKHR|TaskEXT|MeshEXT)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_AddressingModel": {
-			"match": "\\b(Logical|Physical32|Physical64|PhysicalStorageBuffer64|PhysicalStorageBuffer64EXT)\\b",
+			"match": "\\b(Logical|Physical32|Physical64|PhysicalStorageBuffer64)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_MemoryModel": {
-			"match": "\\b(Simple|GLSL450|OpenCL|Vulkan|VulkanKHR)\\b",
+			"match": "\\b(Simple|GLSL450|OpenCL|Vulkan)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_ExecutionMode": {
-			"match": "\\b(Invocations|SpacingEqual|SpacingFractionalEven|SpacingFractionalOdd|VertexOrderCw|VertexOrderCcw|PixelCenterInteger|OriginUpperLeft|OriginLowerLeft|EarlyFragmentTests|PointMode|Xfb|DepthReplacing|DepthGreater|DepthLess|DepthUnchanged|LocalSize|LocalSizeHint|InputPoints|InputLines|InputLinesAdjacency|Triangles|InputTrianglesAdjacency|Quads|Isolines|OutputVertices|OutputPoints|OutputLineStrip|OutputTriangleStrip|VecTypeHint|ContractionOff|Initializer|Finalizer|SubgroupSize|SubgroupsPerWorkgroup|SubgroupsPerWorkgroupId|LocalSizeId|LocalSizeHintId|PostDepthCoverage|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|StencilRefReplacingEXT|OutputLinesNV|OutputPrimitivesNV|DerivativeGroupQuadsNV|DerivativeGroupLinearNV|OutputTrianglesNV|PixelInterlockOrderedEXT|PixelInterlockUnorderedEXT|SampleInterlockOrderedEXT|SampleInterlockUnorderedEXT|ShadingRateInterlockOrderedEXT|ShadingRateInterlockUnorderedEXT)\\b",
+			"match": "\\b(Invocations|SpacingEqual|SpacingFractionalEven|SpacingFractionalOdd|VertexOrderCw|VertexOrderCcw|PixelCenterInteger|OriginUpperLeft|OriginLowerLeft|EarlyFragmentTests|PointMode|Xfb|DepthReplacing|DepthGreater|DepthLess|DepthUnchanged|LocalSize|LocalSizeHint|InputPoints|InputLines|InputLinesAdjacency|Triangles|InputTrianglesAdjacency|Quads|Isolines|OutputVertices|OutputPoints|OutputLineStrip|OutputTriangleStrip|VecTypeHint|ContractionOff|Initializer|Finalizer|SubgroupSize|SubgroupsPerWorkgroup|SubgroupsPerWorkgroupId|LocalSizeId|LocalSizeHintId|NonCoherentColorAttachmentReadEXT|NonCoherentDepthAttachmentReadEXT|NonCoherentStencilAttachmentReadEXT|SubgroupUniformControlFlowKHR|PostDepthCoverage|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|EarlyAndLateFragmentTestsAMD|StencilRefReplacingEXT|CoalescingAMDX|IsApiEntryAMDX|MaxNodeRecursionAMDX|StaticNumWorkgroupsAMDX|ShaderIndexAMDX|MaxNumWorkgroupsAMDX|StencilRefUnchangedFrontAMD|StencilRefGreaterFrontAMD|StencilRefLessFrontAMD|StencilRefUnchangedBackAMD|StencilRefGreaterBackAMD|StencilRefLessBackAMD|QuadDerivativesKHR|RequireFullQuadsKHR|SharesInputWithAMDX|OutputLinesEXT|OutputPrimitivesEXT|DerivativeGroupQuadsKHR|DerivativeGroupLinearKHR|OutputTrianglesEXT|PixelInterlockOrderedEXT|PixelInterlockUnorderedEXT|SampleInterlockOrderedEXT|SampleInterlockUnorderedEXT|ShadingRateInterlockOrderedEXT|ShadingRateInterlockUnorderedEXT|SharedLocalMemorySizeINTEL|RoundingModeRTPINTEL|RoundingModeRTNINTEL|FloatingPointModeALTINTEL|FloatingPointModeIEEEINTEL|MaxWorkgroupSizeINTEL|MaxWorkDimINTEL|NoGlobalOffsetINTEL|NumSIMDWorkitemsINTEL|SchedulerTargetFmaxMhzINTEL|MaximallyReconvergesKHR|FPFastMathDefault|StreamingInterfaceINTEL|RegisterMapInterfaceINTEL|NamedBarrierCountINTEL|MaximumRegistersINTEL|MaximumRegistersIdINTEL|NamedMaximumRegistersINTEL)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_StorageClass": {
-			"match": "\\b(UniformConstant|Input|Uniform|Output|Workgroup|CrossWorkgroup|Private|Function|Generic|PushConstant|AtomicCounter|Image|StorageBuffer|CallableDataNV|CallableDataKHR|IncomingCallableDataNV|IncomingCallableDataKHR|RayPayloadNV|RayPayloadKHR|HitAttributeNV|HitAttributeKHR|IncomingRayPayloadNV|IncomingRayPayloadKHR|ShaderRecordBufferNV|ShaderRecordBufferKHR|PhysicalStorageBuffer|PhysicalStorageBufferEXT)\\b",
+			"match": "\\b(UniformConstant|Input|Uniform|Output|Workgroup|CrossWorkgroup|Private|Function|Generic|PushConstant|AtomicCounter|Image|StorageBuffer|TileImageEXT|NodePayloadAMDX|CallableDataKHR|IncomingCallableDataKHR|RayPayloadKHR|HitAttributeKHR|IncomingRayPayloadKHR|ShaderRecordBufferKHR|PhysicalStorageBuffer|HitObjectAttributeNV|TaskPayloadWorkgroupEXT|CodeSectionINTEL|DeviceOnlyINTEL|HostOnlyINTEL)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_Dim": {
-			"match": "\\b(1D|2D|3D|Cube|Rect|Buffer|SubpassData)\\b",
+			"match": "\\b(1D|2D|3D|Cube|Rect|Buffer|SubpassData|TileImageDataEXT)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_SamplerAddressingMode": {
@@ -125,7 +151,7 @@
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_ImageFormat": {
-			"match": "\\b(Unknown|Rgba32f|Rgba16f|R32f|Rgba8|Rgba8Snorm|Rg32f|Rg16f|R11fG11fB10f|R16f|Rgba16|Rgb10A2|Rg16|Rg8|R16|R8|Rgba16Snorm|Rg16Snorm|Rg8Snorm|R16Snorm|R8Snorm|Rgba32i|Rgba16i|Rgba8i|R32i|Rg32i|Rg16i|Rg8i|R16i|R8i|Rgba32ui|Rgba16ui|Rgba8ui|R32ui|Rgb10a2ui|Rg32ui|Rg16ui|Rg8ui|R16ui|R8ui)\\b",
+			"match": "\\b(Unknown|Rgba32f|Rgba16f|R32f|Rgba8|Rgba8Snorm|Rg32f|Rg16f|R11fG11fB10f|R16f|Rgba16|Rgb10A2|Rg16|Rg8|R16|R8|Rgba16Snorm|Rg16Snorm|Rg8Snorm|R16Snorm|R8Snorm|Rgba32i|Rgba16i|Rgba8i|R32i|Rg32i|Rg16i|Rg8i|R16i|R8i|Rgba32ui|Rgba16ui|Rgba8ui|R32ui|Rgb10a2ui|Rg32ui|Rg16ui|Rg8ui|R16ui|R8ui|R64ui|R64i)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_ImageChannelOrder": {
@@ -133,35 +159,55 @@
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_ImageChannelDataType": {
-			"match": "\\b(SnormInt8|SnormInt16|UnormInt8|UnormInt16|UnormShort565|UnormShort555|UnormInt101010|SignedInt8|SignedInt16|SignedInt32|UnsignedInt8|UnsignedInt16|UnsignedInt32|HalfFloat|Float|UnormInt24|UnormInt101010_2)\\b",
+			"match": "\\b(SnormInt8|SnormInt16|UnormInt8|UnormInt16|UnormShort565|UnormShort555|UnormInt101010|SignedInt8|SignedInt16|SignedInt32|UnsignedInt8|UnsignedInt16|UnsignedInt32|HalfFloat|Float|UnormInt24|UnormInt101010_2|UnsignedIntRaw10EXT|UnsignedIntRaw12EXT|UnormInt2_101010EXT)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_FPRoundingMode": {
 			"match": "\\b(RTE|RTZ|RTP|RTN)\\b",
 			"name": "keyword.spirv"
 		},
+		"ValueEnum_FPDenormMode": {
+			"match": "\\b(Preserve|FlushToZero)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_QuantizationModes": {
+			"match": "\\b(TRN|TRN_ZERO|RND|RND_ZERO|RND_INF|RND_MIN_INF|RND_CONV|RND_CONV_ODD)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_FPOperationMode": {
+			"match": "\\b(IEEE|ALT)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_OverflowModes": {
+			"match": "\\b(WRAP|SAT|SAT_ZERO|SAT_SYM)\\b",
+			"name": "keyword.spirv"
+		},
 		"ValueEnum_LinkageType": {
-			"match": "\\b(Export|Import)\\b",
+			"match": "\\b(Export|Import|LinkOnceODR)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_AccessQualifier": {
 			"match": "\\b(ReadOnly|WriteOnly|ReadWrite)\\b",
 			"name": "keyword.spirv"
 		},
+		"ValueEnum_HostAccessQualifier": {
+			"match": "\\b(NoneINTEL|ReadINTEL|WriteINTEL|ReadWriteINTEL)\\b",
+			"name": "keyword.spirv"
+		},
 		"ValueEnum_FunctionParameterAttribute": {
-			"match": "\\b(Zext|Sext|ByVal|Sret|NoAlias|NoCapture|NoWrite|NoReadWrite)\\b",
+			"match": "\\b(Zext|Sext|ByVal|Sret|NoAlias|NoCapture|NoWrite|NoReadWrite|RuntimeAlignedINTEL)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_Decoration": {
-			"match": "\\b(RelaxedPrecision|SpecId|Block|BufferBlock|RowMajor|ColMajor|ArrayStride|MatrixStride|GLSLShared|GLSLPacked|CPacked|BuiltIn|NoPerspective|Flat|Patch|Centroid|Sample|Invariant|Restrict|Aliased|Volatile|Constant|Coherent|NonWritable|NonReadable|Uniform|UniformId|SaturatedConversion|Stream|Location|Component|Index|Binding|DescriptorSet|Offset|XfbBuffer|XfbStride|FuncParamAttr|FPRoundingMode|FPFastMathMode|LinkageAttributes|NoContraction|InputAttachmentIndex|Alignment|MaxByteOffset|AlignmentId|MaxByteOffsetId|NoSignedWrap|NoUnsignedWrap|ExplicitInterpAMD|OverrideCoverageNV|PassthroughNV|ViewportRelativeNV|SecondaryViewportRelativeNV|PerPrimitiveNV|PerViewNV|PerTaskNV|PerVertexNV|NonUniform|NonUniformEXT|RestrictPointer|RestrictPointerEXT|AliasedPointer|AliasedPointerEXT|CounterBuffer|HlslCounterBufferGOOGLE|UserSemantic|HlslSemanticGOOGLE|UserTypeGOOGLE)\\b",
+			"match": "\\b(RelaxedPrecision|SpecId|Block|BufferBlock|RowMajor|ColMajor|ArrayStride|MatrixStride|GLSLShared|GLSLPacked|CPacked|BuiltIn|NoPerspective|Flat|Patch|Centroid|Sample|Invariant|Restrict|Aliased|Volatile|Constant|Coherent|NonWritable|NonReadable|Uniform|UniformId|SaturatedConversion|Stream|Location|Component|Index|Binding|DescriptorSet|Offset|XfbBuffer|XfbStride|FuncParamAttr|FPRoundingMode|FPFastMathMode|LinkageAttributes|NoContraction|InputAttachmentIndex|Alignment|MaxByteOffset|AlignmentId|MaxByteOffsetId|NoSignedWrap|NoUnsignedWrap|WeightTextureQCOM|BlockMatchTextureQCOM|BlockMatchSamplerQCOM|ExplicitInterpAMD|NodeSharesPayloadLimitsWithAMDX|NodeMaxPayloadsAMDX|TrackFinishWritingAMDX|PayloadNodeNameAMDX|PayloadNodeBaseIndexAMDX|PayloadNodeSparseArrayAMDX|PayloadNodeArraySizeAMDX|PayloadDispatchIndirectAMDX|OverrideCoverageNV|PassthroughNV|ViewportRelativeNV|SecondaryViewportRelativeNV|PerPrimitiveEXT|PerViewNV|PerTaskNV|PerVertexKHR|NonUniform|RestrictPointer|AliasedPointer|HitObjectShaderRecordBufferNV|BindlessSamplerNV|BindlessImageNV|BoundSamplerNV|BoundImageNV|SIMTCallINTEL|ReferencedIndirectlyINTEL|ClobberINTEL|SideEffectsINTEL|VectorComputeVariableINTEL|FuncParamIOKindINTEL|VectorComputeFunctionINTEL|StackCallINTEL|GlobalVariableOffsetINTEL|CounterBuffer|UserSemantic|UserTypeGOOGLE|FunctionRoundingModeINTEL|FunctionDenormModeINTEL|RegisterINTEL|MemoryINTEL|NumbanksINTEL|BankwidthINTEL|MaxPrivateCopiesINTEL|SinglepumpINTEL|DoublepumpINTEL|MaxReplicatesINTEL|SimpleDualPortINTEL|MergeINTEL|BankBitsINTEL|ForcePow2DepthINTEL|StridesizeINTEL|WordsizeINTEL|TrueDualPortINTEL|BurstCoalesceINTEL|CacheSizeINTEL|DontStaticallyCoalesceINTEL|PrefetchINTEL|StallEnableINTEL|FuseLoopsInFunctionINTEL|MathOpDSPModeINTEL|AliasScopeINTEL|NoAliasINTEL|InitiationIntervalINTEL|MaxConcurrencyINTEL|PipelineEnableINTEL|BufferLocationINTEL|IOPipeStorageINTEL|FunctionFloatingPointModeINTEL|SingleElementVectorINTEL|VectorComputeCallableFunctionINTEL|MediaBlockIOINTEL|StallFreeINTEL|FPMaxErrorDecorationINTEL|LatencyControlLabelINTEL|LatencyControlConstraintINTEL|ConduitKernelArgumentINTEL|RegisterMapKernelArgumentINTEL|MMHostInterfaceAddressWidthINTEL|MMHostInterfaceDataWidthINTEL|MMHostInterfaceLatencyINTEL|MMHostInterfaceReadWriteModeINTEL|MMHostInterfaceMaxBurstINTEL|MMHostInterfaceWaitRequestINTEL|StableKernelArgumentINTEL|HostAccessINTEL|InitModeINTEL|ImplementInRegisterMapINTEL|CacheControlLoadINTEL|CacheControlStoreINTEL)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_BuiltIn": {
-			"match": "\\b(Position|PointSize|ClipDistance|CullDistance|VertexId|InstanceId|PrimitiveId|InvocationId|Layer|ViewportIndex|TessLevelOuter|TessLevelInner|TessCoord|PatchVertices|FragCoord|PointCoord|FrontFacing|SampleId|SamplePosition|SampleMask|FragDepth|HelperInvocation|NumWorkgroups|WorkgroupSize|WorkgroupId|LocalInvocationId|GlobalInvocationId|LocalInvocationIndex|WorkDim|GlobalSize|EnqueuedWorkgroupSize|GlobalOffset|GlobalLinearId|SubgroupSize|SubgroupMaxSize|NumSubgroups|NumEnqueuedSubgroups|SubgroupId|SubgroupLocalInvocationId|VertexIndex|InstanceIndex|SubgroupEqMask|SubgroupGeMask|SubgroupGtMask|SubgroupLeMask|SubgroupLtMask|SubgroupEqMaskKHR|SubgroupGeMaskKHR|SubgroupGtMaskKHR|SubgroupLeMaskKHR|SubgroupLtMaskKHR|BaseVertex|BaseInstance|DrawIndex|DeviceIndex|ViewIndex|BaryCoordNoPerspAMD|BaryCoordNoPerspCentroidAMD|BaryCoordNoPerspSampleAMD|BaryCoordSmoothAMD|BaryCoordSmoothCentroidAMD|BaryCoordSmoothSampleAMD|BaryCoordPullModelAMD|FragStencilRefEXT|ViewportMaskNV|SecondaryPositionNV|SecondaryViewportMaskNV|PositionPerViewNV|ViewportMaskPerViewNV|FullyCoveredEXT|TaskCountNV|PrimitiveCountNV|PrimitiveIndicesNV|ClipDistancePerViewNV|CullDistancePerViewNV|LayerPerViewNV|MeshViewCountNV|MeshViewIndicesNV|BaryCoordNV|BaryCoordNoPerspNV|FragSizeEXT|FragmentSizeNV|FragInvocationCountEXT|InvocationsPerPixelNV|LaunchIdNV|LaunchIdKHR|LaunchSizeNV|LaunchSizeKHR|WorldRayOriginNV|WorldRayOriginKHR|WorldRayDirectionNV|WorldRayDirectionKHR|ObjectRayOriginNV|ObjectRayOriginKHR|ObjectRayDirectionNV|ObjectRayDirectionKHR|RayTminNV|RayTminKHR|RayTmaxNV|RayTmaxKHR|InstanceCustomIndexNV|InstanceCustomIndexKHR|ObjectToWorldNV|ObjectToWorldKHR|WorldToObjectNV|WorldToObjectKHR|HitTNV|HitTKHR|HitKindNV|HitKindKHR|IncomingRayFlagsNV|IncomingRayFlagsKHR|RayGeometryIndexKHR|WarpsPerSMNV|SMCountNV|WarpIDNV|SMIDNV)\\b",
+			"match": "\\b(Position|PointSize|ClipDistance|CullDistance|VertexId|InstanceId|PrimitiveId|InvocationId|Layer|ViewportIndex|TessLevelOuter|TessLevelInner|TessCoord|PatchVertices|FragCoord|PointCoord|FrontFacing|SampleId|SamplePosition|SampleMask|FragDepth|HelperInvocation|NumWorkgroups|WorkgroupSize|WorkgroupId|LocalInvocationId|GlobalInvocationId|LocalInvocationIndex|WorkDim|GlobalSize|EnqueuedWorkgroupSize|GlobalOffset|GlobalLinearId|SubgroupSize|SubgroupMaxSize|NumSubgroups|NumEnqueuedSubgroups|SubgroupId|SubgroupLocalInvocationId|VertexIndex|InstanceIndex|CoreIDARM|CoreCountARM|CoreMaxIDARM|WarpIDARM|WarpMaxIDARM|SubgroupEqMask|SubgroupGeMask|SubgroupGtMask|SubgroupLeMask|SubgroupLtMask|BaseVertex|BaseInstance|DrawIndex|PrimitiveShadingRateKHR|DeviceIndex|ViewIndex|ShadingRateKHR|BaryCoordNoPerspAMD|BaryCoordNoPerspCentroidAMD|BaryCoordNoPerspSampleAMD|BaryCoordSmoothAMD|BaryCoordSmoothCentroidAMD|BaryCoordSmoothSampleAMD|BaryCoordPullModelAMD|FragStencilRefEXT|RemainingRecursionLevelsAMDX|ShaderIndexAMDX|ViewportMaskNV|SecondaryPositionNV|SecondaryViewportMaskNV|PositionPerViewNV|ViewportMaskPerViewNV|FullyCoveredEXT|TaskCountNV|PrimitiveCountNV|PrimitiveIndicesNV|ClipDistancePerViewNV|CullDistancePerViewNV|LayerPerViewNV|MeshViewCountNV|MeshViewIndicesNV|BaryCoordKHR|BaryCoordNoPerspKHR|FragSizeEXT|FragInvocationCountEXT|PrimitivePointIndicesEXT|PrimitiveLineIndicesEXT|PrimitiveTriangleIndicesEXT|CullPrimitiveEXT|LaunchIdKHR|LaunchSizeKHR|WorldRayOriginKHR|WorldRayDirectionKHR|ObjectRayOriginKHR|ObjectRayDirectionKHR|RayTminKHR|RayTmaxKHR|InstanceCustomIndexKHR|ObjectToWorldKHR|WorldToObjectKHR|HitTNV|HitKindKHR|CurrentRayTimeNV|HitTriangleVertexPositionsKHR|HitMicroTriangleVertexPositionsNV|HitMicroTriangleVertexBarycentricsNV|IncomingRayFlagsKHR|RayGeometryIndexKHR|WarpsPerSMNV|SMCountNV|WarpIDNV|SMIDNV|HitKindFrontFacingMicroTriangleNV|HitKindBackFacingMicroTriangleNV|CullMaskKHR)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_Scope": {
-			"match": "\\b(CrossDevice|Device|Workgroup|Subgroup|Invocation|QueueFamily|QueueFamilyKHR|ShaderCallKHR)\\b",
+			"match": "\\b(CrossDevice|Device|Workgroup|Subgroup|Invocation|QueueFamily|ShaderCallKHR)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_GroupOperation": {
@@ -173,7 +219,7 @@
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_Capability": {
-			"match": "\\b(Matrix|Shader|Geometry|Tessellation|Addresses|Linkage|Kernel|Vector16|Float16Buffer|Float16|Float64|Int64|Int64Atomics|ImageBasic|ImageReadWrite|ImageMipmap|Pipes|Groups|DeviceEnqueue|LiteralSampler|AtomicStorage|Int16|TessellationPointSize|GeometryPointSize|ImageGatherExtended|StorageImageMultisample|UniformBufferArrayDynamicIndexing|SampledImageArrayDynamicIndexing|StorageBufferArrayDynamicIndexing|StorageImageArrayDynamicIndexing|ClipDistance|CullDistance|ImageCubeArray|SampleRateShading|ImageRect|SampledRect|GenericPointer|Int8|InputAttachment|SparseResidency|MinLod|Sampled1D|Image1D|SampledCubeArray|SampledBuffer|ImageBuffer|ImageMSArray|StorageImageExtendedFormats|ImageQuery|DerivativeControl|InterpolationFunction|TransformFeedback|GeometryStreams|StorageImageReadWithoutFormat|StorageImageWriteWithoutFormat|MultiViewport|SubgroupDispatch|NamedBarrier|PipeStorage|GroupNonUniform|GroupNonUniformVote|GroupNonUniformArithmetic|GroupNonUniformBallot|GroupNonUniformShuffle|GroupNonUniformShuffleRelative|GroupNonUniformClustered|GroupNonUniformQuad|ShaderLayer|ShaderViewportIndex|SubgroupBallotKHR|DrawParameters|SubgroupVoteKHR|StorageBuffer16BitAccess|StorageUniformBufferBlock16|UniformAndStorageBuffer16BitAccess|StorageUniform16|StoragePushConstant16|StorageInputOutput16|DeviceGroup|MultiView|VariablePointersStorageBuffer|VariablePointers|AtomicStorageOps|SampleMaskPostDepthCoverage|StorageBuffer8BitAccess|UniformAndStorageBuffer8BitAccess|StoragePushConstant8|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|RayQueryProvisionalKHR|RayTraversalPrimitiveCullingProvisionalKHR|Float16ImageAMD|ImageGatherBiasLodAMD|FragmentMaskAMD|StencilExportEXT|ImageReadWriteLodAMD|ShaderClockKHR|SampleMaskOverrideCoverageNV|GeometryShaderPassthroughNV|ShaderViewportIndexLayerEXT|ShaderViewportIndexLayerNV|ShaderViewportMaskNV|ShaderStereoViewNV|PerViewAttributesNV|FragmentFullyCoveredEXT|MeshShadingNV|ImageFootprintNV|FragmentBarycentricNV|ComputeDerivativeGroupQuadsNV|FragmentDensityEXT|ShadingRateNV|GroupNonUniformPartitionedNV|ShaderNonUniform|ShaderNonUniformEXT|RuntimeDescriptorArray|RuntimeDescriptorArrayEXT|InputAttachmentArrayDynamicIndexing|InputAttachmentArrayDynamicIndexingEXT|UniformTexelBufferArrayDynamicIndexing|UniformTexelBufferArrayDynamicIndexingEXT|StorageTexelBufferArrayDynamicIndexing|StorageTexelBufferArrayDynamicIndexingEXT|UniformBufferArrayNonUniformIndexing|UniformBufferArrayNonUniformIndexingEXT|SampledImageArrayNonUniformIndexing|SampledImageArrayNonUniformIndexingEXT|StorageBufferArrayNonUniformIndexing|StorageBufferArrayNonUniformIndexingEXT|StorageImageArrayNonUniformIndexing|StorageImageArrayNonUniformIndexingEXT|InputAttachmentArrayNonUniformIndexing|InputAttachmentArrayNonUniformIndexingEXT|UniformTexelBufferArrayNonUniformIndexing|UniformTexelBufferArrayNonUniformIndexingEXT|StorageTexelBufferArrayNonUniformIndexing|StorageTexelBufferArrayNonUniformIndexingEXT|RayTracingNV|VulkanMemoryModel|VulkanMemoryModelKHR|VulkanMemoryModelDeviceScope|VulkanMemoryModelDeviceScopeKHR|PhysicalStorageBufferAddresses|PhysicalStorageBufferAddressesEXT|ComputeDerivativeGroupLinearNV|RayTracingProvisionalKHR|CooperativeMatrixNV|FragmentShaderSampleInterlockEXT|FragmentShaderShadingRateInterlockEXT|ShaderSMBuiltinsNV|FragmentShaderPixelInterlockEXT|DemoteToHelperInvocationEXT|SubgroupShuffleINTEL|SubgroupBufferBlockIOINTEL|SubgroupImageBlockIOINTEL|SubgroupImageMediaBlockIOINTEL|IntegerFunctions2INTEL|SubgroupAvcMotionEstimationINTEL|SubgroupAvcMotionEstimationIntraINTEL|SubgroupAvcMotionEstimationChromaINTEL)\\b",
+			"match": "\\b(Matrix|Shader|Geometry|Tessellation|Addresses|Linkage|Kernel|Vector16|Float16Buffer|Float16|Float64|Int64|Int64Atomics|ImageBasic|ImageReadWrite|ImageMipmap|Pipes|Groups|DeviceEnqueue|LiteralSampler|AtomicStorage|Int16|TessellationPointSize|GeometryPointSize|ImageGatherExtended|StorageImageMultisample|UniformBufferArrayDynamicIndexing|SampledImageArrayDynamicIndexing|StorageBufferArrayDynamicIndexing|StorageImageArrayDynamicIndexing|ClipDistance|CullDistance|ImageCubeArray|SampleRateShading|ImageRect|SampledRect|GenericPointer|Int8|InputAttachment|SparseResidency|MinLod|Sampled1D|Image1D|SampledCubeArray|SampledBuffer|ImageBuffer|ImageMSArray|StorageImageExtendedFormats|ImageQuery|DerivativeControl|InterpolationFunction|TransformFeedback|GeometryStreams|StorageImageReadWithoutFormat|StorageImageWriteWithoutFormat|MultiViewport|SubgroupDispatch|NamedBarrier|PipeStorage|GroupNonUniform|GroupNonUniformVote|GroupNonUniformArithmetic|GroupNonUniformBallot|GroupNonUniformShuffle|GroupNonUniformShuffleRelative|GroupNonUniformClustered|GroupNonUniformQuad|ShaderLayer|ShaderViewportIndex|UniformDecoration|CoreBuiltinsARM|TileImageColorReadAccessEXT|TileImageDepthReadAccessEXT|TileImageStencilReadAccessEXT|CooperativeMatrixLayoutsARM|FragmentShadingRateKHR|SubgroupBallotKHR|DrawParameters|WorkgroupMemoryExplicitLayoutKHR|WorkgroupMemoryExplicitLayout8BitAccessKHR|WorkgroupMemoryExplicitLayout16BitAccessKHR|SubgroupVoteKHR|StorageBuffer16BitAccess|UniformAndStorageBuffer16BitAccess|StoragePushConstant16|StorageInputOutput16|DeviceGroup|MultiView|VariablePointersStorageBuffer|VariablePointers|AtomicStorageOps|SampleMaskPostDepthCoverage|StorageBuffer8BitAccess|UniformAndStorageBuffer8BitAccess|StoragePushConstant8|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|RayQueryProvisionalKHR|RayQueryKHR|UntypedPointersKHR|RayTraversalPrimitiveCullingKHR|RayTracingKHR|TextureSampleWeightedQCOM|TextureBoxFilterQCOM|TextureBlockMatchQCOM|TextureBlockMatch2QCOM|Float16ImageAMD|ImageGatherBiasLodAMD|FragmentMaskAMD|StencilExportEXT|ImageReadWriteLodAMD|Int64ImageEXT|ShaderClockKHR|ShaderEnqueueAMDX|QuadControlKHR|SampleMaskOverrideCoverageNV|GeometryShaderPassthroughNV|ShaderViewportIndexLayerEXT|ShaderViewportMaskNV|ShaderStereoViewNV|PerViewAttributesNV|FragmentFullyCoveredEXT|MeshShadingNV|ImageFootprintNV|MeshShadingEXT|FragmentBarycentricKHR|ComputeDerivativeGroupQuadsKHR|FragmentDensityEXT|GroupNonUniformPartitionedNV|ShaderNonUniform|RuntimeDescriptorArray|InputAttachmentArrayDynamicIndexing|UniformTexelBufferArrayDynamicIndexing|StorageTexelBufferArrayDynamicIndexing|UniformBufferArrayNonUniformIndexing|SampledImageArrayNonUniformIndexing|StorageBufferArrayNonUniformIndexing|StorageImageArrayNonUniformIndexing|InputAttachmentArrayNonUniformIndexing|UniformTexelBufferArrayNonUniformIndexing|StorageTexelBufferArrayNonUniformIndexing|RayTracingPositionFetchKHR|RayTracingNV|RayTracingMotionBlurNV|VulkanMemoryModel|VulkanMemoryModelDeviceScope|PhysicalStorageBufferAddresses|ComputeDerivativeGroupLinearKHR|RayTracingProvisionalKHR|CooperativeMatrixNV|FragmentShaderSampleInterlockEXT|FragmentShaderShadingRateInterlockEXT|ShaderSMBuiltinsNV|FragmentShaderPixelInterlockEXT|DemoteToHelperInvocation|DisplacementMicromapNV|RayTracingOpacityMicromapEXT|ShaderInvocationReorderNV|BindlessTextureNV|RayQueryPositionFetchKHR|AtomicFloat16VectorNV|RayTracingDisplacementMicromapNV|RawAccessChainsNV|CooperativeMatrixReductionsNV|CooperativeMatrixConversionsNV|CooperativeMatrixPerElementOperationsNV|CooperativeMatrixTensorAddressingNV|CooperativeMatrixBlockLoadsNV|TensorAddressingNV|SubgroupShuffleINTEL|SubgroupBufferBlockIOINTEL|SubgroupImageBlockIOINTEL|SubgroupImageMediaBlockIOINTEL|RoundToInfinityINTEL|FloatingPointModeINTEL|IntegerFunctions2INTEL|FunctionPointersINTEL|IndirectReferencesINTEL|AsmINTEL|AtomicFloat32MinMaxEXT|AtomicFloat64MinMaxEXT|AtomicFloat16MinMaxEXT|VectorComputeINTEL|VectorAnyINTEL|ExpectAssumeKHR|SubgroupAvcMotionEstimationINTEL|SubgroupAvcMotionEstimationIntraINTEL|SubgroupAvcMotionEstimationChromaINTEL|VariableLengthArrayINTEL|FunctionFloatControlINTEL|FPGAMemoryAttributesINTEL|FPFastMathModeINTEL|ArbitraryPrecisionIntegersINTEL|ArbitraryPrecisionFloatingPointINTEL|UnstructuredLoopControlsINTEL|FPGALoopControlsINTEL|KernelAttributesINTEL|FPGAKernelAttributesINTEL|FPGAMemoryAccessesINTEL|FPGAClusterAttributesINTEL|LoopFuseINTEL|FPGADSPControlINTEL|MemoryAccessAliasingINTEL|FPGAInvocationPipeliningAttributesINTEL|FPGABufferLocationINTEL|ArbitraryPrecisionFixedPointINTEL|USMStorageClassesINTEL|RuntimeAlignedAttributeINTEL|IOPipesINTEL|BlockingPipesINTEL|FPGARegINTEL|DotProductInputAll|DotProductInput4x8Bit|DotProductInput4x8BitPacked|DotProduct|RayCullMaskKHR|CooperativeMatrixKHR|ReplicatedCompositesEXT|BitInstructions|GroupNonUniformRotateKHR|FloatControls2|AtomicFloat32AddEXT|AtomicFloat64AddEXT|LongCompositesINTEL|OptNoneEXT|AtomicFloat16AddEXT|DebugInfoModuleINTEL|BFloat16ConversionINTEL|SplitBarrierINTEL|ArithmeticFenceEXT|FPGAClusterAttributesV2INTEL|FPGAKernelAttributesv2INTEL|FPMaxErrorINTEL|FPGALatencyControlINTEL|FPGAArgumentInterfacesINTEL|GlobalVariableHostAccessINTEL|GlobalVariableFPGADecorationsINTEL|SubgroupBufferPrefetchINTEL|GroupUniformArithmeticKHR|MaskedGatherScatterINTEL|CacheControlsINTEL|RegisterLimitsINTEL)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_RayQueryIntersection": {
@@ -188,8 +234,52 @@
 			"match": "\\b(RayQueryCandidateIntersectionTriangleKHR|RayQueryCandidateIntersectionAABBKHR)\\b",
 			"name": "keyword.spirv"
 		},
+		"ValueEnum_PackedVectorFormat": {
+			"match": "\\b(PackedVectorFormat4x8Bit)\\b",
+			"name": "keyword.spirv"
+		},
+		"BitEnum_CooperativeMatrixOperands": {
+			"match": "\\b(NoneKHR|MatrixASignedComponentsKHR|MatrixBSignedComponentsKHR|MatrixCSignedComponentsKHR|MatrixResultSignedComponentsKHR|SaturatingAccumulationKHR)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_CooperativeMatrixLayout": {
+			"match": "\\b(RowMajorKHR|ColumnMajorKHR|RowBlockedInterleavedARM|ColumnBlockedInterleavedARM)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_CooperativeMatrixUse": {
+			"match": "\\b(MatrixAKHR|MatrixBKHR|MatrixAccumulatorKHR)\\b",
+			"name": "keyword.spirv"
+		},
+		"BitEnum_CooperativeMatrixReduce": {
+			"match": "\\b(Row|Column|2x2)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_TensorClampMode": {
+			"match": "\\b(Undefined|Constant|ClampToEdge|Repeat|RepeatMirrored)\\b",
+			"name": "keyword.spirv"
+		},
+		"BitEnum_TensorAddressingOperands": {
+			"match": "\\b(None|TensorView|DecodeFunc)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_InitializationModeQualifier": {
+			"match": "\\b(InitOnDeviceReprogramINTEL|InitOnDeviceResetINTEL)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_LoadCacheControl": {
+			"match": "\\b(UncachedINTEL|CachedINTEL|StreamingINTEL|InvalidateAfterReadINTEL|ConstCachedINTEL)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_StoreCacheControl": {
+			"match": "\\b(UncachedINTEL|WriteThroughINTEL|WriteBackINTEL|StreamingINTEL)\\b",
+			"name": "keyword.spirv"
+		},
+		"ValueEnum_NamedMaximumNumberOfRegisters": {
+			"match": "\\b(AutoINTEL)\\b",
+			"name": "keyword.spirv"
+		},
 		"BitEnum_DebugInfoFlags": {
-			"match": "\\b(FlagIsProtected|FlagIsPrivate|FlagIsPublic|FlagIsLocal|FlagIsDefinition|FlagFwdDecl|FlagArtificial|FlagExplicit|FlagPrototyped|FlagObjectPointer|FlagStaticMember|FlagIndirectVariable|FlagLValueReference|FlagRValueReference|FlagIsOptimized|FlagIsEnumClass|FlagTypePassByValue|FlagTypePassByReference)\\b",
+			"match": "\\b(None|FlagIsProtected|FlagIsPrivate|FlagIsPublic|FlagIsLocal|FlagIsDefinition|FlagFwdDecl|FlagArtificial|FlagExplicit|FlagPrototyped|FlagObjectPointer|FlagStaticMember|FlagIndirectVariable|FlagLValueReference|FlagRValueReference|FlagIsOptimized|FlagIsEnumClass|FlagTypePassByValue|FlagTypePassByReference)\\b",
 			"name": "keyword.spirv"
 		},
 		"ValueEnum_DebugBaseTypeAttributeEncoding": {
@@ -217,7 +307,7 @@
 			"name": "entity.name.function.spirv"
 		},
 		"extopcode": {
-			"match": "(Round|RoundEven|Trunc|FAbs|SAbs|FSign|SSign|Floor|Ceil|Fract|Radians|Degrees|Sin|Cos|Tan|Asin|Acos|Atan|Sinh|Cosh|Tanh|Asinh|Acosh|Atanh|Atan2|Pow|Exp|Log|Exp2|Log2|Sqrt|InverseSqrt|Determinant|MatrixInverse|Modf|ModfStruct|FMin|UMin|SMin|FMax|UMax|SMax|FClamp|UClamp|SClamp|FMix|IMix|Step|SmoothStep|Fma|Frexp|FrexpStruct|Ldexp|PackSnorm4x8|PackUnorm4x8|PackSnorm2x16|PackUnorm2x16|PackHalf2x16|PackDouble2x32|UnpackSnorm2x16|UnpackUnorm2x16|UnpackHalf2x16|UnpackSnorm4x8|UnpackUnorm4x8|UnpackDouble2x32|Length|Distance|Cross|Normalize|FaceForward|Reflect|Refract|FindILsb|FindSMsb|FindUMsb|InterpolateAtCentroid|InterpolateAtSample|InterpolateAtOffset|NMin|NMax|NClamp|acos|acosh|acospi|asin|asinh|asinpi|atan|atan2|atanh|atanpi|atan2pi|cbrt|ceil|copysign|cos|cosh|cospi|erfc|erf|exp|exp2|exp10|expm1|fabs|fdim|floor|fma|fmax|fmin|fmod|fract|frexp|hypot|ilogb|ldexp|lgamma|lgamma_r|log|log2|log10|log1p|logb|mad|maxmag|minmag|modf|nan|nextafter|pow|pown|powr|remainder|remquo|rint|rootn|round|rsqrt|sin|sincos|sinh|sinpi|sqrt|tan|tanh|tanpi|tgamma|trunc|half_cos|half_divide|half_exp|half_exp2|half_exp10|half_log|half_log2|half_log10|half_powr|half_recip|half_rsqrt|half_sin|half_sqrt|half_tan|native_cos|native_divide|native_exp|native_exp2|native_exp10|native_log|native_log2|native_log10|native_powr|native_recip|native_rsqrt|native_sin|native_sqrt|native_tan|s_abs|s_abs_diff|s_add_sat|u_add_sat|s_hadd|u_hadd|s_rhadd|u_rhadd|s_clamp|u_clamp|clz|ctz|s_mad_hi|u_mad_sat|s_mad_sat|s_max|u_max|s_min|u_min|s_mul_hi|rotate|s_sub_sat|u_sub_sat|u_upsample|s_upsample|popcount|s_mad24|u_mad24|s_mul24|u_mul24|u_abs|u_abs_diff|u_mul_hi|u_mad_hi|fclamp|degrees|fmax_common|fmin_common|mix|radians|step|smoothstep|sign|cross|distance|length|normalize|fast_distance|fast_length|fast_normalize|bitselect|select|vloadn|vstoren|vload_half|vload_halfn|vstore_half|vstore_half_r|vstore_halfn|vstore_halfn_r|vloada_halfn|vstorea_halfn|vstorea_halfn_r|shuffle|shuffle2|printf|prefetch|DebugInfoNone|DebugCompilationUnit|DebugTypeBasic|DebugTypePointer|DebugTypeQualifier|DebugTypeArray|DebugTypeVector|DebugTypedef|DebugTypeFunction|DebugTypeEnum|DebugTypeComposite|DebugTypeMember|DebugTypeInheritance|DebugTypePtrToMember|DebugTypeTemplate|DebugTypeTemplateParameter|DebugTypeTemplateTemplateParameter|DebugTypeTemplateParameterPack|DebugGlobalVariable|DebugFunctionDeclaration|DebugFunction|DebugLexicalBlock|DebugLexicalBlockDiscriminator|DebugScope|DebugNoScope|DebugInlinedAt|DebugLocalVariable|DebugInlinedVariable|DebugDeclare|DebugValue|DebugOperation|DebugExpression|DebugMacroDef|DebugMacroUndef|DebugImportedEntity|DebugSource)",
+			"match": "(Round|RoundEven|Trunc|FAbs|SAbs|FSign|SSign|Floor|Ceil|Fract|Radians|Degrees|Sin|Cos|Tan|Asin|Acos|Atan|Sinh|Cosh|Tanh|Asinh|Acosh|Atanh|Atan2|Pow|Exp|Log|Exp2|Log2|Sqrt|InverseSqrt|Determinant|MatrixInverse|Modf|ModfStruct|FMin|UMin|SMin|FMax|UMax|SMax|FClamp|UClamp|SClamp|FMix|IMix|Step|SmoothStep|Fma|Frexp|FrexpStruct|Ldexp|PackSnorm4x8|PackUnorm4x8|PackSnorm2x16|PackUnorm2x16|PackHalf2x16|PackDouble2x32|UnpackSnorm2x16|UnpackUnorm2x16|UnpackHalf2x16|UnpackSnorm4x8|UnpackUnorm4x8|UnpackDouble2x32|Length|Distance|Cross|Normalize|FaceForward|Reflect|Refract|FindILsb|FindSMsb|FindUMsb|InterpolateAtCentroid|InterpolateAtSample|InterpolateAtOffset|NMin|NMax|NClamp|acos|acosh|acospi|asin|asinh|asinpi|atan|atan2|atanh|atanpi|atan2pi|cbrt|ceil|copysign|cos|cosh|cospi|erfc|erf|exp|exp2|exp10|expm1|fabs|fdim|floor|fma|fmax|fmin|fmod|fract|frexp|hypot|ilogb|ldexp|lgamma|lgamma_r|log|log2|log10|log1p|logb|mad|maxmag|minmag|modf|nan|nextafter|pow|pown|powr|remainder|remquo|rint|rootn|round|rsqrt|sin|sincos|sinh|sinpi|sqrt|tan|tanh|tanpi|tgamma|trunc|half_cos|half_divide|half_exp|half_exp2|half_exp10|half_log|half_log2|half_log10|half_powr|half_recip|half_rsqrt|half_sin|half_sqrt|half_tan|native_cos|native_divide|native_exp|native_exp2|native_exp10|native_log|native_log2|native_log10|native_powr|native_recip|native_rsqrt|native_sin|native_sqrt|native_tan|s_abs|s_abs_diff|s_add_sat|u_add_sat|s_hadd|u_hadd|s_rhadd|u_rhadd|s_clamp|u_clamp|clz|ctz|s_mad_hi|u_mad_sat|s_mad_sat|s_max|u_max|s_min|u_min|s_mul_hi|rotate|s_sub_sat|u_sub_sat|u_upsample|s_upsample|popcount|s_mad24|u_mad24|s_mul24|u_mul24|u_abs|u_abs_diff|u_mul_hi|u_mad_hi|fclamp|degrees|fmax_common|fmin_common|mix|radians|step|smoothstep|sign|cross|distance|length|normalize|fast_distance|fast_length|fast_normalize|bitselect|select|vloadn|vstoren|vload_half|vload_halfn|vstore_half|vstore_half_r|vstore_halfn|vstore_halfn_r|vloada_halfn|vstorea_halfn|vstorea_halfn_r|shuffle|shuffle2|printf|prefetch|DebugInfoNone|DebugCompilationUnit|DebugTypeBasic|DebugTypePointer|DebugTypeQualifier|DebugTypeArray|DebugTypeVector|DebugTypedef|DebugTypeFunction|DebugTypeEnum|DebugTypeComposite|DebugTypeMember|DebugTypeInheritance|DebugTypePtrToMember|DebugTypeTemplate|DebugTypeTemplateParameter|DebugTypeTemplateTemplateParameter|DebugTypeTemplateParameterPack|DebugGlobalVariable|DebugFunctionDeclaration|DebugFunction|DebugLexicalBlock|DebugLexicalBlockDiscriminator|DebugScope|DebugNoScope|DebugInlinedAt|DebugLocalVariable|DebugInlinedVariable|DebugDeclare|DebugValue|DebugOperation|DebugExpression|DebugMacroDef|DebugMacroUndef|DebugImportedEntity|DebugSource|DebugModuleINTEL)",
 			"name": "entity.name.function.ext"
 		},
 		"identifier": {
diff --git a/utils/vscode/src/langsvr.go b/utils/vscode/src/langsvr.go
index b76e35f..56b11b2 100644
--- a/utils/vscode/src/langsvr.go
+++ b/utils/vscode/src/langsvr.go
@@ -19,7 +19,6 @@
 	"context"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"log"
 	"os"
 	"path"
@@ -67,7 +66,7 @@
 
 // main entry point.
 func main() {
-	log.SetOutput(ioutil.Discard)
+	log.SetOutput(io.Discard)
 	if enableDebugLogging {
 		// create a log file in the executable's directory.
 		if logfile, err := os.Create(path.Join(path.Dir(os.Args[0]), "log.txt")); err == nil {
@@ -213,6 +212,10 @@
 			ReferencesProvider:         true,
 			RenameProvider:             true,
 			DocumentFormattingProvider: true,
+			CompletionProvider: &lsp.CompletionOptions{
+				TriggerCharacters: []string{"%"},
+				ResolveProvider:   false,
+			},
 		},
 	}
 	return &res, nil
@@ -225,9 +228,56 @@
 	log.Println("server.WillSaveWaitUntil()")
 	return nil, nil
 }
+
+func markdownOpcode(op *schema.Opcode) string {
+	sb := strings.Builder{}
+	sb.WriteString(fmt.Sprintf("**%s** (%s)\n\n", op.Opname, op.Class))
+
+	for idx, operand := range op.Operands {
+		sb.WriteString(fmt.Sprintf("Operand %d%s: ", idx, operand.Quantifier))
+		sb.WriteString(fmt.Sprintf("%s (%s)\n", operand.Name, operand.Kind.Kind))
+	}
+
+	return sb.String()
+}
+
 func (s *server) Completion(ctx context.Context, p *lsp.CompletionParams) (*lsp.CompletionList, error) {
 	log.Println("server.Completion()")
-	return nil, nil
+	f := s.getFile(p.TextDocument.URI)
+	if f == nil {
+		return nil, fmt.Errorf("Unknown file")
+	}
+
+	if p.Context.TriggerCharacter == "%" {
+		idents := []lsp.CompletionItem{}
+		for name, ident := range f.res.Identifiers {
+			idents = append(idents, lsp.CompletionItem{
+				Label:         name,
+				Kind:          6,
+				Documentation: ident.Definition.Range.Text(f.res.Lines),
+			})
+		}
+		res := &lsp.CompletionList{
+			IsIncomplete: false,
+			Items:        idents,
+		}
+
+		return res, nil
+	}
+
+	opcodes := []lsp.CompletionItem{}
+	for name, opcode := range schema.Opcodes {
+		opcodes = append(opcodes, lsp.CompletionItem{
+			Label:         name,
+			Kind:          3,
+			Documentation: markdownOpcode(opcode),
+		})
+	}
+	res := &lsp.CompletionList{
+		IsIncomplete: false,
+		Items:        opcodes,
+	}
+	return res, nil
 }
 func (s *server) Resolve(ctx context.Context, p *lsp.CompletionItem) (*lsp.CompletionItem, error) {
 	log.Println("server.Resolve()")
@@ -246,7 +296,7 @@
 		default:
 			sb.WriteString(fmt.Sprintf("<Unhandled type '%T'>", v))
 		case *parser.Instruction:
-			sb.WriteString(fmt.Sprintf("```\n%v\n```", v.Opcode.Opname))
+			sb.WriteString(markdownOpcode(v.Opcode))
 		case *parser.Identifier:
 			sb.WriteString(fmt.Sprintf("```\n%v\n```", v.Definition.Range.Text(f.res.Lines)))
 		case *parser.Operand:
diff --git a/utils/vscode/src/schema/schema.go b/utils/vscode/src/schema/schema.go
index ed02de4..b7931ae 100755
--- a/utils/vscode/src/schema/schema.go
+++ b/utils/vscode/src/schema/schema.go
@@ -447,12 +447,60 @@
 		"OpPtrEqual": OpPtrEqual,
 		"OpPtrNotEqual": OpPtrNotEqual,
 		"OpPtrDiff": OpPtrDiff,
+		"OpColorAttachmentReadEXT": OpColorAttachmentReadEXT,
+		"OpDepthAttachmentReadEXT": OpDepthAttachmentReadEXT,
+		"OpStencilAttachmentReadEXT": OpStencilAttachmentReadEXT,
+		"OpTerminateInvocation": OpTerminateInvocation,
+		"OpTypeUntypedPointerKHR": OpTypeUntypedPointerKHR,
+		"OpUntypedVariableKHR": OpUntypedVariableKHR,
+		"OpUntypedAccessChainKHR": OpUntypedAccessChainKHR,
+		"OpUntypedInBoundsAccessChainKHR": OpUntypedInBoundsAccessChainKHR,
 		"OpSubgroupBallotKHR": OpSubgroupBallotKHR,
 		"OpSubgroupFirstInvocationKHR": OpSubgroupFirstInvocationKHR,
+		"OpUntypedPtrAccessChainKHR": OpUntypedPtrAccessChainKHR,
+		"OpUntypedInBoundsPtrAccessChainKHR": OpUntypedInBoundsPtrAccessChainKHR,
+		"OpUntypedArrayLengthKHR": OpUntypedArrayLengthKHR,
+		"OpUntypedPrefetchKHR": OpUntypedPrefetchKHR,
 		"OpSubgroupAllKHR": OpSubgroupAllKHR,
 		"OpSubgroupAnyKHR": OpSubgroupAnyKHR,
 		"OpSubgroupAllEqualKHR": OpSubgroupAllEqualKHR,
+		"OpGroupNonUniformRotateKHR": OpGroupNonUniformRotateKHR,
 		"OpSubgroupReadInvocationKHR": OpSubgroupReadInvocationKHR,
+		"OpExtInstWithForwardRefsKHR": OpExtInstWithForwardRefsKHR,
+		"OpTraceRayKHR": OpTraceRayKHR,
+		"OpExecuteCallableKHR": OpExecuteCallableKHR,
+		"OpConvertUToAccelerationStructureKHR": OpConvertUToAccelerationStructureKHR,
+		"OpIgnoreIntersectionKHR": OpIgnoreIntersectionKHR,
+		"OpTerminateRayKHR": OpTerminateRayKHR,
+		"OpSDot": OpSDot,
+		"OpUDot": OpUDot,
+		"OpSUDot": OpSUDot,
+		"OpSDotAccSat": OpSDotAccSat,
+		"OpUDotAccSat": OpUDotAccSat,
+		"OpSUDotAccSat": OpSUDotAccSat,
+		"OpTypeCooperativeMatrixKHR": OpTypeCooperativeMatrixKHR,
+		"OpCooperativeMatrixLoadKHR": OpCooperativeMatrixLoadKHR,
+		"OpCooperativeMatrixStoreKHR": OpCooperativeMatrixStoreKHR,
+		"OpCooperativeMatrixMulAddKHR": OpCooperativeMatrixMulAddKHR,
+		"OpCooperativeMatrixLengthKHR": OpCooperativeMatrixLengthKHR,
+		"OpConstantCompositeReplicateEXT": OpConstantCompositeReplicateEXT,
+		"OpSpecConstantCompositeReplicateEXT": OpSpecConstantCompositeReplicateEXT,
+		"OpCompositeConstructReplicateEXT": OpCompositeConstructReplicateEXT,
+		"OpTypeRayQueryKHR": OpTypeRayQueryKHR,
+		"OpRayQueryInitializeKHR": OpRayQueryInitializeKHR,
+		"OpRayQueryTerminateKHR": OpRayQueryTerminateKHR,
+		"OpRayQueryGenerateIntersectionKHR": OpRayQueryGenerateIntersectionKHR,
+		"OpRayQueryConfirmIntersectionKHR": OpRayQueryConfirmIntersectionKHR,
+		"OpRayQueryProceedKHR": OpRayQueryProceedKHR,
+		"OpRayQueryGetIntersectionTypeKHR": OpRayQueryGetIntersectionTypeKHR,
+		"OpImageSampleWeightedQCOM": OpImageSampleWeightedQCOM,
+		"OpImageBoxFilterQCOM": OpImageBoxFilterQCOM,
+		"OpImageBlockMatchSSDQCOM": OpImageBlockMatchSSDQCOM,
+		"OpImageBlockMatchSADQCOM": OpImageBlockMatchSADQCOM,
+		"OpImageBlockMatchWindowSSDQCOM": OpImageBlockMatchWindowSSDQCOM,
+		"OpImageBlockMatchWindowSADQCOM": OpImageBlockMatchWindowSADQCOM,
+		"OpImageBlockMatchGatherSSDQCOM": OpImageBlockMatchGatherSSDQCOM,
+		"OpImageBlockMatchGatherSADQCOM": OpImageBlockMatchGatherSADQCOM,
 		"OpGroupIAddNonUniformAMD": OpGroupIAddNonUniformAMD,
 		"OpGroupFAddNonUniformAMD": OpGroupFAddNonUniformAMD,
 		"OpGroupFMinNonUniformAMD": OpGroupFMinNonUniformAMD,
@@ -464,45 +512,66 @@
 		"OpFragmentMaskFetchAMD": OpFragmentMaskFetchAMD,
 		"OpFragmentFetchAMD": OpFragmentFetchAMD,
 		"OpReadClockKHR": OpReadClockKHR,
+		"OpAllocateNodePayloadsAMDX": OpAllocateNodePayloadsAMDX,
+		"OpEnqueueNodePayloadsAMDX": OpEnqueueNodePayloadsAMDX,
+		"OpTypeNodePayloadArrayAMDX": OpTypeNodePayloadArrayAMDX,
+		"OpFinishWritingNodePayloadAMDX": OpFinishWritingNodePayloadAMDX,
+		"OpNodePayloadArrayLengthAMDX": OpNodePayloadArrayLengthAMDX,
+		"OpIsNodePayloadValidAMDX": OpIsNodePayloadValidAMDX,
+		"OpConstantStringAMDX": OpConstantStringAMDX,
+		"OpSpecConstantStringAMDX": OpSpecConstantStringAMDX,
+		"OpGroupNonUniformQuadAllKHR": OpGroupNonUniformQuadAllKHR,
+		"OpGroupNonUniformQuadAnyKHR": OpGroupNonUniformQuadAnyKHR,
+		"OpHitObjectRecordHitMotionNV": OpHitObjectRecordHitMotionNV,
+		"OpHitObjectRecordHitWithIndexMotionNV": OpHitObjectRecordHitWithIndexMotionNV,
+		"OpHitObjectRecordMissMotionNV": OpHitObjectRecordMissMotionNV,
+		"OpHitObjectGetWorldToObjectNV": OpHitObjectGetWorldToObjectNV,
+		"OpHitObjectGetObjectToWorldNV": OpHitObjectGetObjectToWorldNV,
+		"OpHitObjectGetObjectRayDirectionNV": OpHitObjectGetObjectRayDirectionNV,
+		"OpHitObjectGetObjectRayOriginNV": OpHitObjectGetObjectRayOriginNV,
+		"OpHitObjectTraceRayMotionNV": OpHitObjectTraceRayMotionNV,
+		"OpHitObjectGetShaderRecordBufferHandleNV": OpHitObjectGetShaderRecordBufferHandleNV,
+		"OpHitObjectGetShaderBindingTableRecordIndexNV": OpHitObjectGetShaderBindingTableRecordIndexNV,
+		"OpHitObjectRecordEmptyNV": OpHitObjectRecordEmptyNV,
+		"OpHitObjectTraceRayNV": OpHitObjectTraceRayNV,
+		"OpHitObjectRecordHitNV": OpHitObjectRecordHitNV,
+		"OpHitObjectRecordHitWithIndexNV": OpHitObjectRecordHitWithIndexNV,
+		"OpHitObjectRecordMissNV": OpHitObjectRecordMissNV,
+		"OpHitObjectExecuteShaderNV": OpHitObjectExecuteShaderNV,
+		"OpHitObjectGetCurrentTimeNV": OpHitObjectGetCurrentTimeNV,
+		"OpHitObjectGetAttributesNV": OpHitObjectGetAttributesNV,
+		"OpHitObjectGetHitKindNV": OpHitObjectGetHitKindNV,
+		"OpHitObjectGetPrimitiveIndexNV": OpHitObjectGetPrimitiveIndexNV,
+		"OpHitObjectGetGeometryIndexNV": OpHitObjectGetGeometryIndexNV,
+		"OpHitObjectGetInstanceIdNV": OpHitObjectGetInstanceIdNV,
+		"OpHitObjectGetInstanceCustomIndexNV": OpHitObjectGetInstanceCustomIndexNV,
+		"OpHitObjectGetWorldRayDirectionNV": OpHitObjectGetWorldRayDirectionNV,
+		"OpHitObjectGetWorldRayOriginNV": OpHitObjectGetWorldRayOriginNV,
+		"OpHitObjectGetRayTMaxNV": OpHitObjectGetRayTMaxNV,
+		"OpHitObjectGetRayTMinNV": OpHitObjectGetRayTMinNV,
+		"OpHitObjectIsEmptyNV": OpHitObjectIsEmptyNV,
+		"OpHitObjectIsHitNV": OpHitObjectIsHitNV,
+		"OpHitObjectIsMissNV": OpHitObjectIsMissNV,
+		"OpReorderThreadWithHitObjectNV": OpReorderThreadWithHitObjectNV,
+		"OpReorderThreadWithHintNV": OpReorderThreadWithHintNV,
+		"OpTypeHitObjectNV": OpTypeHitObjectNV,
 		"OpImageSampleFootprintNV": OpImageSampleFootprintNV,
+		"OpCooperativeMatrixConvertNV": OpCooperativeMatrixConvertNV,
+		"OpEmitMeshTasksEXT": OpEmitMeshTasksEXT,
+		"OpSetMeshOutputsEXT": OpSetMeshOutputsEXT,
 		"OpGroupNonUniformPartitionNV": OpGroupNonUniformPartitionNV,
 		"OpWritePackedPrimitiveIndices4x8NV": OpWritePackedPrimitiveIndices4x8NV,
-		"OpReportIntersectionNV": OpReportIntersectionNV,
+		"OpFetchMicroTriangleVertexPositionNV": OpFetchMicroTriangleVertexPositionNV,
+		"OpFetchMicroTriangleVertexBarycentricNV": OpFetchMicroTriangleVertexBarycentricNV,
 		"OpReportIntersectionKHR": OpReportIntersectionKHR,
 		"OpIgnoreIntersectionNV": OpIgnoreIntersectionNV,
-		"OpIgnoreIntersectionKHR": OpIgnoreIntersectionKHR,
 		"OpTerminateRayNV": OpTerminateRayNV,
-		"OpTerminateRayKHR": OpTerminateRayKHR,
 		"OpTraceNV": OpTraceNV,
-		"OpTraceRayKHR": OpTraceRayKHR,
-		"OpTypeAccelerationStructureNV": OpTypeAccelerationStructureNV,
+		"OpTraceMotionNV": OpTraceMotionNV,
+		"OpTraceRayMotionNV": OpTraceRayMotionNV,
+		"OpRayQueryGetIntersectionTriangleVertexPositionsKHR": OpRayQueryGetIntersectionTriangleVertexPositionsKHR,
 		"OpTypeAccelerationStructureKHR": OpTypeAccelerationStructureKHR,
-		"OpTypeRayQueryProvisionalKHR": OpTypeRayQueryProvisionalKHR,
-		"OpRayQueryInitializeKHR": OpRayQueryInitializeKHR,
-		"OpRayQueryTerminateKHR": OpRayQueryTerminateKHR,
-		"OpRayQueryGenerateIntersectionKHR": OpRayQueryGenerateIntersectionKHR,
-		"OpRayQueryConfirmIntersectionKHR": OpRayQueryConfirmIntersectionKHR,
-		"OpRayQueryProceedKHR": OpRayQueryProceedKHR,
-		"OpRayQueryGetIntersectionTypeKHR": OpRayQueryGetIntersectionTypeKHR,
-		"OpRayQueryGetRayTMinKHR": OpRayQueryGetRayTMinKHR,
-		"OpRayQueryGetRayFlagsKHR": OpRayQueryGetRayFlagsKHR,
-		"OpRayQueryGetIntersectionTKHR": OpRayQueryGetIntersectionTKHR,
-		"OpRayQueryGetIntersectionInstanceCustomIndexKHR": OpRayQueryGetIntersectionInstanceCustomIndexKHR,
-		"OpRayQueryGetIntersectionInstanceIdKHR": OpRayQueryGetIntersectionInstanceIdKHR,
-		"OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR": OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR,
-		"OpRayQueryGetIntersectionGeometryIndexKHR": OpRayQueryGetIntersectionGeometryIndexKHR,
-		"OpRayQueryGetIntersectionPrimitiveIndexKHR": OpRayQueryGetIntersectionPrimitiveIndexKHR,
-		"OpRayQueryGetIntersectionBarycentricsKHR": OpRayQueryGetIntersectionBarycentricsKHR,
-		"OpRayQueryGetIntersectionFrontFaceKHR": OpRayQueryGetIntersectionFrontFaceKHR,
-		"OpRayQueryGetIntersectionCandidateAABBOpaqueKHR": OpRayQueryGetIntersectionCandidateAABBOpaqueKHR,
-		"OpRayQueryGetIntersectionObjectRayDirectionKHR": OpRayQueryGetIntersectionObjectRayDirectionKHR,
-		"OpRayQueryGetIntersectionObjectRayOriginKHR": OpRayQueryGetIntersectionObjectRayOriginKHR,
-		"OpRayQueryGetWorldRayDirectionKHR": OpRayQueryGetWorldRayDirectionKHR,
-		"OpRayQueryGetWorldRayOriginKHR": OpRayQueryGetWorldRayOriginKHR,
-		"OpRayQueryGetIntersectionObjectToWorldKHR": OpRayQueryGetIntersectionObjectToWorldKHR,
-		"OpRayQueryGetIntersectionWorldToObjectKHR": OpRayQueryGetIntersectionWorldToObjectKHR,
 		"OpExecuteCallableNV": OpExecuteCallableNV,
-		"OpExecuteCallableKHR": OpExecuteCallableKHR,
 		"OpTypeCooperativeMatrixNV": OpTypeCooperativeMatrixNV,
 		"OpCooperativeMatrixLoadNV": OpCooperativeMatrixLoadNV,
 		"OpCooperativeMatrixStoreNV": OpCooperativeMatrixStoreNV,
@@ -510,8 +579,33 @@
 		"OpCooperativeMatrixLengthNV": OpCooperativeMatrixLengthNV,
 		"OpBeginInvocationInterlockEXT": OpBeginInvocationInterlockEXT,
 		"OpEndInvocationInterlockEXT": OpEndInvocationInterlockEXT,
-		"OpDemoteToHelperInvocationEXT": OpDemoteToHelperInvocationEXT,
+		"OpCooperativeMatrixReduceNV": OpCooperativeMatrixReduceNV,
+		"OpCooperativeMatrixLoadTensorNV": OpCooperativeMatrixLoadTensorNV,
+		"OpCooperativeMatrixStoreTensorNV": OpCooperativeMatrixStoreTensorNV,
+		"OpCooperativeMatrixPerElementOpNV": OpCooperativeMatrixPerElementOpNV,
+		"OpTypeTensorLayoutNV": OpTypeTensorLayoutNV,
+		"OpTypeTensorViewNV": OpTypeTensorViewNV,
+		"OpCreateTensorLayoutNV": OpCreateTensorLayoutNV,
+		"OpTensorLayoutSetDimensionNV": OpTensorLayoutSetDimensionNV,
+		"OpTensorLayoutSetStrideNV": OpTensorLayoutSetStrideNV,
+		"OpTensorLayoutSliceNV": OpTensorLayoutSliceNV,
+		"OpTensorLayoutSetClampValueNV": OpTensorLayoutSetClampValueNV,
+		"OpCreateTensorViewNV": OpCreateTensorViewNV,
+		"OpTensorViewSetDimensionNV": OpTensorViewSetDimensionNV,
+		"OpTensorViewSetStrideNV": OpTensorViewSetStrideNV,
+		"OpDemoteToHelperInvocation": OpDemoteToHelperInvocation,
 		"OpIsHelperInvocationEXT": OpIsHelperInvocationEXT,
+		"OpTensorViewSetClipNV": OpTensorViewSetClipNV,
+		"OpTensorLayoutSetBlockSizeNV": OpTensorLayoutSetBlockSizeNV,
+		"OpCooperativeMatrixTransposeNV": OpCooperativeMatrixTransposeNV,
+		"OpConvertUToImageNV": OpConvertUToImageNV,
+		"OpConvertUToSamplerNV": OpConvertUToSamplerNV,
+		"OpConvertImageToUNV": OpConvertImageToUNV,
+		"OpConvertSamplerToUNV": OpConvertSamplerToUNV,
+		"OpConvertUToSampledImageNV": OpConvertUToSampledImageNV,
+		"OpConvertSampledImageToUNV": OpConvertSampledImageToUNV,
+		"OpSamplerImageAddressingModeNV": OpSamplerImageAddressingModeNV,
+		"OpRawAccessChainNV": OpRawAccessChainNV,
 		"OpSubgroupShuffleINTEL": OpSubgroupShuffleINTEL,
 		"OpSubgroupShuffleDownINTEL": OpSubgroupShuffleDownINTEL,
 		"OpSubgroupShuffleUpINTEL": OpSubgroupShuffleUpINTEL,
@@ -536,10 +630,17 @@
 		"OpUSubSatINTEL": OpUSubSatINTEL,
 		"OpIMul32x16INTEL": OpIMul32x16INTEL,
 		"OpUMul32x16INTEL": OpUMul32x16INTEL,
+		"OpConstantFunctionPointerINTEL": OpConstantFunctionPointerINTEL,
+		"OpFunctionPointerCallINTEL": OpFunctionPointerCallINTEL,
+		"OpAsmTargetINTEL": OpAsmTargetINTEL,
+		"OpAsmINTEL": OpAsmINTEL,
+		"OpAsmCallINTEL": OpAsmCallINTEL,
+		"OpAtomicFMinEXT": OpAtomicFMinEXT,
+		"OpAtomicFMaxEXT": OpAtomicFMaxEXT,
+		"OpAssumeTrueKHR": OpAssumeTrueKHR,
+		"OpExpectKHR": OpExpectKHR,
 		"OpDecorateString": OpDecorateString,
-		"OpDecorateStringGOOGLE": OpDecorateStringGOOGLE,
 		"OpMemberDecorateString": OpMemberDecorateString,
-		"OpMemberDecorateStringGOOGLE": OpMemberDecorateStringGOOGLE,
 		"OpVmeImageINTEL": OpVmeImageINTEL,
 		"OpTypeVmeImageINTEL": OpTypeVmeImageINTEL,
 		"OpTypeAvcImePayloadINTEL": OpTypeAvcImePayloadINTEL,
@@ -658,6 +759,109 @@
 		"OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL": OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL,
 		"OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL": OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL,
 		"OpSubgroupAvcSicGetInterRawSadsINTEL": OpSubgroupAvcSicGetInterRawSadsINTEL,
+		"OpVariableLengthArrayINTEL": OpVariableLengthArrayINTEL,
+		"OpSaveMemoryINTEL": OpSaveMemoryINTEL,
+		"OpRestoreMemoryINTEL": OpRestoreMemoryINTEL,
+		"OpArbitraryFloatSinCosPiINTEL": OpArbitraryFloatSinCosPiINTEL,
+		"OpArbitraryFloatCastINTEL": OpArbitraryFloatCastINTEL,
+		"OpArbitraryFloatCastFromIntINTEL": OpArbitraryFloatCastFromIntINTEL,
+		"OpArbitraryFloatCastToIntINTEL": OpArbitraryFloatCastToIntINTEL,
+		"OpArbitraryFloatAddINTEL": OpArbitraryFloatAddINTEL,
+		"OpArbitraryFloatSubINTEL": OpArbitraryFloatSubINTEL,
+		"OpArbitraryFloatMulINTEL": OpArbitraryFloatMulINTEL,
+		"OpArbitraryFloatDivINTEL": OpArbitraryFloatDivINTEL,
+		"OpArbitraryFloatGTINTEL": OpArbitraryFloatGTINTEL,
+		"OpArbitraryFloatGEINTEL": OpArbitraryFloatGEINTEL,
+		"OpArbitraryFloatLTINTEL": OpArbitraryFloatLTINTEL,
+		"OpArbitraryFloatLEINTEL": OpArbitraryFloatLEINTEL,
+		"OpArbitraryFloatEQINTEL": OpArbitraryFloatEQINTEL,
+		"OpArbitraryFloatRecipINTEL": OpArbitraryFloatRecipINTEL,
+		"OpArbitraryFloatRSqrtINTEL": OpArbitraryFloatRSqrtINTEL,
+		"OpArbitraryFloatCbrtINTEL": OpArbitraryFloatCbrtINTEL,
+		"OpArbitraryFloatHypotINTEL": OpArbitraryFloatHypotINTEL,
+		"OpArbitraryFloatSqrtINTEL": OpArbitraryFloatSqrtINTEL,
+		"OpArbitraryFloatLogINTEL": OpArbitraryFloatLogINTEL,
+		"OpArbitraryFloatLog2INTEL": OpArbitraryFloatLog2INTEL,
+		"OpArbitraryFloatLog10INTEL": OpArbitraryFloatLog10INTEL,
+		"OpArbitraryFloatLog1pINTEL": OpArbitraryFloatLog1pINTEL,
+		"OpArbitraryFloatExpINTEL": OpArbitraryFloatExpINTEL,
+		"OpArbitraryFloatExp2INTEL": OpArbitraryFloatExp2INTEL,
+		"OpArbitraryFloatExp10INTEL": OpArbitraryFloatExp10INTEL,
+		"OpArbitraryFloatExpm1INTEL": OpArbitraryFloatExpm1INTEL,
+		"OpArbitraryFloatSinINTEL": OpArbitraryFloatSinINTEL,
+		"OpArbitraryFloatCosINTEL": OpArbitraryFloatCosINTEL,
+		"OpArbitraryFloatSinCosINTEL": OpArbitraryFloatSinCosINTEL,
+		"OpArbitraryFloatSinPiINTEL": OpArbitraryFloatSinPiINTEL,
+		"OpArbitraryFloatCosPiINTEL": OpArbitraryFloatCosPiINTEL,
+		"OpArbitraryFloatASinINTEL": OpArbitraryFloatASinINTEL,
+		"OpArbitraryFloatASinPiINTEL": OpArbitraryFloatASinPiINTEL,
+		"OpArbitraryFloatACosINTEL": OpArbitraryFloatACosINTEL,
+		"OpArbitraryFloatACosPiINTEL": OpArbitraryFloatACosPiINTEL,
+		"OpArbitraryFloatATanINTEL": OpArbitraryFloatATanINTEL,
+		"OpArbitraryFloatATanPiINTEL": OpArbitraryFloatATanPiINTEL,
+		"OpArbitraryFloatATan2INTEL": OpArbitraryFloatATan2INTEL,
+		"OpArbitraryFloatPowINTEL": OpArbitraryFloatPowINTEL,
+		"OpArbitraryFloatPowRINTEL": OpArbitraryFloatPowRINTEL,
+		"OpArbitraryFloatPowNINTEL": OpArbitraryFloatPowNINTEL,
+		"OpLoopControlINTEL": OpLoopControlINTEL,
+		"OpAliasDomainDeclINTEL": OpAliasDomainDeclINTEL,
+		"OpAliasScopeDeclINTEL": OpAliasScopeDeclINTEL,
+		"OpAliasScopeListDeclINTEL": OpAliasScopeListDeclINTEL,
+		"OpFixedSqrtINTEL": OpFixedSqrtINTEL,
+		"OpFixedRecipINTEL": OpFixedRecipINTEL,
+		"OpFixedRsqrtINTEL": OpFixedRsqrtINTEL,
+		"OpFixedSinINTEL": OpFixedSinINTEL,
+		"OpFixedCosINTEL": OpFixedCosINTEL,
+		"OpFixedSinCosINTEL": OpFixedSinCosINTEL,
+		"OpFixedSinPiINTEL": OpFixedSinPiINTEL,
+		"OpFixedCosPiINTEL": OpFixedCosPiINTEL,
+		"OpFixedSinCosPiINTEL": OpFixedSinCosPiINTEL,
+		"OpFixedLogINTEL": OpFixedLogINTEL,
+		"OpFixedExpINTEL": OpFixedExpINTEL,
+		"OpPtrCastToCrossWorkgroupINTEL": OpPtrCastToCrossWorkgroupINTEL,
+		"OpCrossWorkgroupCastToPtrINTEL": OpCrossWorkgroupCastToPtrINTEL,
+		"OpReadPipeBlockingINTEL": OpReadPipeBlockingINTEL,
+		"OpWritePipeBlockingINTEL": OpWritePipeBlockingINTEL,
+		"OpFPGARegINTEL": OpFPGARegINTEL,
+		"OpRayQueryGetRayTMinKHR": OpRayQueryGetRayTMinKHR,
+		"OpRayQueryGetRayFlagsKHR": OpRayQueryGetRayFlagsKHR,
+		"OpRayQueryGetIntersectionTKHR": OpRayQueryGetIntersectionTKHR,
+		"OpRayQueryGetIntersectionInstanceCustomIndexKHR": OpRayQueryGetIntersectionInstanceCustomIndexKHR,
+		"OpRayQueryGetIntersectionInstanceIdKHR": OpRayQueryGetIntersectionInstanceIdKHR,
+		"OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR": OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR,
+		"OpRayQueryGetIntersectionGeometryIndexKHR": OpRayQueryGetIntersectionGeometryIndexKHR,
+		"OpRayQueryGetIntersectionPrimitiveIndexKHR": OpRayQueryGetIntersectionPrimitiveIndexKHR,
+		"OpRayQueryGetIntersectionBarycentricsKHR": OpRayQueryGetIntersectionBarycentricsKHR,
+		"OpRayQueryGetIntersectionFrontFaceKHR": OpRayQueryGetIntersectionFrontFaceKHR,
+		"OpRayQueryGetIntersectionCandidateAABBOpaqueKHR": OpRayQueryGetIntersectionCandidateAABBOpaqueKHR,
+		"OpRayQueryGetIntersectionObjectRayDirectionKHR": OpRayQueryGetIntersectionObjectRayDirectionKHR,
+		"OpRayQueryGetIntersectionObjectRayOriginKHR": OpRayQueryGetIntersectionObjectRayOriginKHR,
+		"OpRayQueryGetWorldRayDirectionKHR": OpRayQueryGetWorldRayDirectionKHR,
+		"OpRayQueryGetWorldRayOriginKHR": OpRayQueryGetWorldRayOriginKHR,
+		"OpRayQueryGetIntersectionObjectToWorldKHR": OpRayQueryGetIntersectionObjectToWorldKHR,
+		"OpRayQueryGetIntersectionWorldToObjectKHR": OpRayQueryGetIntersectionWorldToObjectKHR,
+		"OpAtomicFAddEXT": OpAtomicFAddEXT,
+		"OpTypeBufferSurfaceINTEL": OpTypeBufferSurfaceINTEL,
+		"OpTypeStructContinuedINTEL": OpTypeStructContinuedINTEL,
+		"OpConstantCompositeContinuedINTEL": OpConstantCompositeContinuedINTEL,
+		"OpSpecConstantCompositeContinuedINTEL": OpSpecConstantCompositeContinuedINTEL,
+		"OpCompositeConstructContinuedINTEL": OpCompositeConstructContinuedINTEL,
+		"OpConvertFToBF16INTEL": OpConvertFToBF16INTEL,
+		"OpConvertBF16ToFINTEL": OpConvertBF16ToFINTEL,
+		"OpControlBarrierArriveINTEL": OpControlBarrierArriveINTEL,
+		"OpControlBarrierWaitINTEL": OpControlBarrierWaitINTEL,
+		"OpArithmeticFenceEXT": OpArithmeticFenceEXT,
+		"OpSubgroupBlockPrefetchINTEL": OpSubgroupBlockPrefetchINTEL,
+		"OpGroupIMulKHR": OpGroupIMulKHR,
+		"OpGroupFMulKHR": OpGroupFMulKHR,
+		"OpGroupBitwiseAndKHR": OpGroupBitwiseAndKHR,
+		"OpGroupBitwiseOrKHR": OpGroupBitwiseOrKHR,
+		"OpGroupBitwiseXorKHR": OpGroupBitwiseXorKHR,
+		"OpGroupLogicalAndKHR": OpGroupLogicalAndKHR,
+		"OpGroupLogicalOrKHR": OpGroupLogicalOrKHR,
+		"OpGroupLogicalXorKHR": OpGroupLogicalXorKHR,
+		"OpMaskedGatherINTEL": OpMaskedGatherINTEL,
+		"OpMaskedScatterINTEL": OpMaskedScatterINTEL,
 	}
 
 	// ExtOpcodes is a map of extension name to Opcode description list.
@@ -946,6 +1150,7 @@
 			"DebugMacroUndef": OpenCLDebugInfo100_DebugMacroUndef,
 			"DebugImportedEntity": OpenCLDebugInfo100_DebugImportedEntity,
 			"DebugSource": OpenCLDebugInfo100_DebugSource,
+			"DebugModuleINTEL": OpenCLDebugInfo100_DebugModuleINTEL,
 		},
 	}
 
@@ -1297,6 +1502,11 @@
 				Name:       "'Width'",
 				Quantifier: "",
 			}, 
+			Operand {
+				Kind:       OperandKindFPEncoding,
+				Name:       "'Floating Point Encoding'",
+				Quantifier: "?",
+			}, 
 		},
 	}
 	OpTypeVector = &Opcode {
@@ -10022,6 +10232,197 @@
 			}, 
 		},
 	}
+	OpColorAttachmentReadEXT = &Opcode {
+		Opname:   "OpColorAttachmentReadEXT",
+		Class:    "Image",
+		Opcode:   4160,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Attachment'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sample'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpDepthAttachmentReadEXT = &Opcode {
+		Opname:   "OpDepthAttachmentReadEXT",
+		Class:    "Image",
+		Opcode:   4161,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sample'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpStencilAttachmentReadEXT = &Opcode {
+		Opname:   "OpStencilAttachmentReadEXT",
+		Class:    "Image",
+		Opcode:   4162,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Sample'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpTerminateInvocation = &Opcode {
+		Opname:   "OpTerminateInvocation",
+		Class:    "Control-Flow",
+		Opcode:   4416,
+		Operands: []Operand {
+		},
+	}
+	OpTypeUntypedPointerKHR = &Opcode {
+		Opname:   "OpTypeUntypedPointerKHR",
+		Class:    "Type-Declaration",
+		Opcode:   4417,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindStorageClass,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpUntypedVariableKHR = &Opcode {
+		Opname:   "OpUntypedVariableKHR",
+		Class:    "Memory",
+		Opcode:   4418,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindStorageClass,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Data Type'",
+				Quantifier: "?",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Initializer'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpUntypedAccessChainKHR = &Opcode {
+		Opname:   "OpUntypedAccessChainKHR",
+		Class:    "Memory",
+		Opcode:   4419,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Indexes'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpUntypedInBoundsAccessChainKHR = &Opcode {
+		Opname:   "OpUntypedInBoundsAccessChainKHR",
+		Class:    "Memory",
+		Opcode:   4420,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Indexes'",
+				Quantifier: "*",
+			}, 
+		},
+	}
 	OpSubgroupBallotKHR = &Opcode {
 		Opname:   "OpSubgroupBallotKHR",
 		Class:    "Group",
@@ -10066,6 +10467,144 @@
 			}, 
 		},
 	}
+	OpUntypedPtrAccessChainKHR = &Opcode {
+		Opname:   "OpUntypedPtrAccessChainKHR",
+		Class:    "Memory",
+		Opcode:   4423,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Element'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Indexes'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpUntypedInBoundsPtrAccessChainKHR = &Opcode {
+		Opname:   "OpUntypedInBoundsPtrAccessChainKHR",
+		Class:    "Memory",
+		Opcode:   4424,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Element'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Indexes'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpUntypedArrayLengthKHR = &Opcode {
+		Opname:   "OpUntypedArrayLengthKHR",
+		Class:    "Memory",
+		Opcode:   4425,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Structure'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Array member'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpUntypedPrefetchKHR = &Opcode {
+		Opname:   "OpUntypedPrefetchKHR",
+		Class:    "Memory",
+		Opcode:   4426,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Num Bytes'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RW'",
+				Quantifier: "?",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Locality'",
+				Quantifier: "?",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Cache Type'",
+				Quantifier: "?",
+			}, 
+		},
+	}
 	OpSubgroupAllKHR = &Opcode {
 		Opname:   "OpSubgroupAllKHR",
 		Class:    "Group",
@@ -10132,6 +10671,43 @@
 			}, 
 		},
 	}
+	OpGroupNonUniformRotateKHR = &Opcode {
+		Opname:   "OpGroupNonUniformRotateKHR",
+		Class:    "Group",
+		Opcode:   4431,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Delta'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClusterSize'",
+				Quantifier: "?",
+			}, 
+		},
+	}
 	OpSubgroupReadInvocationKHR = &Opcode {
 		Opname:   "OpSubgroupReadInvocationKHR",
 		Class:    "Group",
@@ -10159,6 +10735,1056 @@
 			}, 
 		},
 	}
+	OpExtInstWithForwardRefsKHR = &Opcode {
+		Opname:   "OpExtInstWithForwardRefsKHR",
+		Class:    "Extension",
+		Opcode:   4433,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Set'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralExtInstInteger,
+				Name:       "'Instruction'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1', + 'Operand 2', + ...",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpTraceRayKHR = &Opcode {
+		Opname:   "OpTraceRayKHR",
+		Class:    "Reserved",
+		Opcode:   4445,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Accel'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ray Flags'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Cull Mask'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Stride'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Miss Index'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ray Origin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ray Tmin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ray Direction'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ray Tmax'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpExecuteCallableKHR = &Opcode {
+		Opname:   "OpExecuteCallableKHR",
+		Class:    "Reserved",
+		Opcode:   4446,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Index'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Callable Data'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConvertUToAccelerationStructureKHR = &Opcode {
+		Opname:   "OpConvertUToAccelerationStructureKHR",
+		Class:    "Reserved",
+		Opcode:   4447,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Accel'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpIgnoreIntersectionKHR = &Opcode {
+		Opname:   "OpIgnoreIntersectionKHR",
+		Class:    "Reserved",
+		Opcode:   4448,
+		Operands: []Operand {
+		},
+	}
+	OpTerminateRayKHR = &Opcode {
+		Opname:   "OpTerminateRayKHR",
+		Class:    "Reserved",
+		Opcode:   4449,
+		Operands: []Operand {
+		},
+	}
+	OpSDot = &Opcode {
+		Opname:   "OpSDot",
+		Class:    "Arithmetic",
+		Opcode:   4450,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector 2'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindPackedVectorFormat,
+				Name:       "'Packed Vector Format'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpUDot = &Opcode {
+		Opname:   "OpUDot",
+		Class:    "Arithmetic",
+		Opcode:   4451,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector 2'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindPackedVectorFormat,
+				Name:       "'Packed Vector Format'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpSUDot = &Opcode {
+		Opname:   "OpSUDot",
+		Class:    "Arithmetic",
+		Opcode:   4452,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector 2'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindPackedVectorFormat,
+				Name:       "'Packed Vector Format'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpSDotAccSat = &Opcode {
+		Opname:   "OpSDotAccSat",
+		Class:    "Arithmetic",
+		Opcode:   4453,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector 2'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Accumulator'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindPackedVectorFormat,
+				Name:       "'Packed Vector Format'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpUDotAccSat = &Opcode {
+		Opname:   "OpUDotAccSat",
+		Class:    "Arithmetic",
+		Opcode:   4454,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector 2'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Accumulator'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindPackedVectorFormat,
+				Name:       "'Packed Vector Format'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpSUDotAccSat = &Opcode {
+		Opname:   "OpSUDotAccSat",
+		Class:    "Arithmetic",
+		Opcode:   4455,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector 1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vector 2'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Accumulator'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindPackedVectorFormat,
+				Name:       "'Packed Vector Format'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpTypeCooperativeMatrixKHR = &Opcode {
+		Opname:   "OpTypeCooperativeMatrixKHR",
+		Class:    "Type-Declaration",
+		Opcode:   4456,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Component Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Scope'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Rows'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Columns'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Use'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpCooperativeMatrixLoadKHR = &Opcode {
+		Opname:   "OpCooperativeMatrixLoadKHR",
+		Class:    "Memory",
+		Opcode:   4457,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'MemoryLayout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Stride'",
+				Quantifier: "?",
+			}, 
+			Operand {
+				Kind:       OperandKindMemoryAccess,
+				Name:       "'Memory Operand'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpCooperativeMatrixStoreKHR = &Opcode {
+		Opname:   "OpCooperativeMatrixStoreKHR",
+		Class:    "Memory",
+		Opcode:   4458,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Object'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'MemoryLayout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Stride'",
+				Quantifier: "?",
+			}, 
+			Operand {
+				Kind:       OperandKindMemoryAccess,
+				Name:       "'Memory Operand'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpCooperativeMatrixMulAddKHR = &Opcode {
+		Opname:   "OpCooperativeMatrixMulAddKHR",
+		Class:    "Arithmetic",
+		Opcode:   4459,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'B'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'C'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindCooperativeMatrixOperands,
+				Name:       "'Cooperative Matrix Operands'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpCooperativeMatrixLengthKHR = &Opcode {
+		Opname:   "OpCooperativeMatrixLengthKHR",
+		Class:    "Miscellaneous",
+		Opcode:   4460,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Type'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConstantCompositeReplicateEXT = &Opcode {
+		Opname:   "OpConstantCompositeReplicateEXT",
+		Class:    "Constant-Creation",
+		Opcode:   4461,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSpecConstantCompositeReplicateEXT = &Opcode {
+		Opname:   "OpSpecConstantCompositeReplicateEXT",
+		Class:    "Constant-Creation",
+		Opcode:   4462,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpCompositeConstructReplicateEXT = &Opcode {
+		Opname:   "OpCompositeConstructReplicateEXT",
+		Class:    "Composite",
+		Opcode:   4463,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeRayQueryKHR = &Opcode {
+		Opname:   "OpTypeRayQueryKHR",
+		Class:    "Type-Declaration",
+		Opcode:   4472,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryInitializeKHR = &Opcode {
+		Opname:   "OpRayQueryInitializeKHR",
+		Class:    "Reserved",
+		Opcode:   4473,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Accel'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayFlags'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'CullMask'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayOrigin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayTMin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayDirection'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayTMax'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryTerminateKHR = &Opcode {
+		Opname:   "OpRayQueryTerminateKHR",
+		Class:    "Reserved",
+		Opcode:   4474,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGenerateIntersectionKHR = &Opcode {
+		Opname:   "OpRayQueryGenerateIntersectionKHR",
+		Class:    "Reserved",
+		Opcode:   4475,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'HitT'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryConfirmIntersectionKHR = &Opcode {
+		Opname:   "OpRayQueryConfirmIntersectionKHR",
+		Class:    "Reserved",
+		Opcode:   4476,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryProceedKHR = &Opcode {
+		Opname:   "OpRayQueryProceedKHR",
+		Class:    "Reserved",
+		Opcode:   4477,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionTypeKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionTypeKHR",
+		Class:    "Reserved",
+		Opcode:   4479,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageSampleWeightedQCOM = &Opcode {
+		Opname:   "OpImageSampleWeightedQCOM",
+		Class:    "Image",
+		Opcode:   4480,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Texture'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinates'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Weights'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageBoxFilterQCOM = &Opcode {
+		Opname:   "OpImageBoxFilterQCOM",
+		Class:    "Image",
+		Opcode:   4481,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Texture'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Coordinates'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Box Size'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageBlockMatchSSDQCOM = &Opcode {
+		Opname:   "OpImageBlockMatchSSDQCOM",
+		Class:    "Image",
+		Opcode:   4482,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target Coordinates'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reference'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reference Coordinates'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Block Size'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageBlockMatchSADQCOM = &Opcode {
+		Opname:   "OpImageBlockMatchSADQCOM",
+		Class:    "Image",
+		Opcode:   4483,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target Coordinates'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reference'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reference Coordinates'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Block Size'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageBlockMatchWindowSSDQCOM = &Opcode {
+		Opname:   "OpImageBlockMatchWindowSSDQCOM",
+		Class:    "Image",
+		Opcode:   4500,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target Coordinates'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reference Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reference Coordinates'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Block Size'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageBlockMatchWindowSADQCOM = &Opcode {
+		Opname:   "OpImageBlockMatchWindowSADQCOM",
+		Class:    "Image",
+		Opcode:   4501,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target Coordinates'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reference Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reference Coordinates'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Block Size'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageBlockMatchGatherSSDQCOM = &Opcode {
+		Opname:   "OpImageBlockMatchGatherSSDQCOM",
+		Class:    "Image",
+		Opcode:   4502,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target Coordinates'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reference Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reference Coordinates'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Block Size'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpImageBlockMatchGatherSADQCOM = &Opcode {
+		Opname:   "OpImageBlockMatchGatherSADQCOM",
+		Class:    "Image",
+		Opcode:   4503,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target Coordinates'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reference Sampled Image'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Reference Coordinates'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Block Size'",
+				Quantifier: "",
+			}, 
+		},
+	}
 	OpGroupIAddNonUniformAMD = &Opcode {
 		Opname:   "OpGroupIAddNonUniformAMD",
 		Class:    "Group",
@@ -10491,7 +12117,1238 @@
 			}, 
 			Operand {
 				Kind:       OperandKindIdScope,
-				Name:       "'Execution'",
+				Name:       "'Scope'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAllocateNodePayloadsAMDX = &Opcode {
+		Opname:   "OpAllocateNodePayloadsAMDX",
+		Class:    "Reserved",
+		Opcode:   5074,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Visibility'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload Count'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Node Index'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpEnqueueNodePayloadsAMDX = &Opcode {
+		Opname:   "OpEnqueueNodePayloadsAMDX",
+		Class:    "Reserved",
+		Opcode:   5075,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload Array'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeNodePayloadArrayAMDX = &Opcode {
+		Opname:   "OpTypeNodePayloadArrayAMDX",
+		Class:    "Reserved",
+		Opcode:   5076,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload Type'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFinishWritingNodePayloadAMDX = &Opcode {
+		Opname:   "OpFinishWritingNodePayloadAMDX",
+		Class:    "Reserved",
+		Opcode:   5078,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpNodePayloadArrayLengthAMDX = &Opcode {
+		Opname:   "OpNodePayloadArrayLengthAMDX",
+		Class:    "Reserved",
+		Opcode:   5090,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload Array'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpIsNodePayloadValidAMDX = &Opcode {
+		Opname:   "OpIsNodePayloadValidAMDX",
+		Class:    "Reserved",
+		Opcode:   5101,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Node Index'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConstantStringAMDX = &Opcode {
+		Opname:   "OpConstantStringAMDX",
+		Class:    "Reserved",
+		Opcode:   5103,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralString,
+				Name:       "'Literal String'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSpecConstantStringAMDX = &Opcode {
+		Opname:   "OpSpecConstantStringAMDX",
+		Class:    "Reserved",
+		Opcode:   5104,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralString,
+				Name:       "'Literal String'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupNonUniformQuadAllKHR = &Opcode {
+		Opname:   "OpGroupNonUniformQuadAllKHR",
+		Class:    "Non-Uniform",
+		Opcode:   5110,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Predicate'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupNonUniformQuadAnyKHR = &Opcode {
+		Opname:   "OpGroupNonUniformQuadAnyKHR",
+		Class:    "Non-Uniform",
+		Opcode:   5111,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Predicate'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectRecordHitMotionNV = &Opcode {
+		Opname:   "OpHitObjectRecordHitMotionNV",
+		Class:    "Reserved",
+		Opcode:   5249,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Acceleration Structure'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'InstanceId'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'PrimitiveId'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'GeometryIndex'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Kind'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Record Offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Record Stride'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Origin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TMin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Direction'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TMax'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Current Time'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'HitObject Attributes'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectRecordHitWithIndexMotionNV = &Opcode {
+		Opname:   "OpHitObjectRecordHitWithIndexMotionNV",
+		Class:    "Reserved",
+		Opcode:   5250,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Acceleration Structure'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'InstanceId'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'PrimitiveId'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'GeometryIndex'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Kind'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Record Index'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Origin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TMin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Direction'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TMax'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Current Time'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'HitObject Attributes'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectRecordMissMotionNV = &Opcode {
+		Opname:   "OpHitObjectRecordMissMotionNV",
+		Class:    "Reserved",
+		Opcode:   5251,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Index'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Origin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TMin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Direction'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TMax'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Current Time'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectGetWorldToObjectNV = &Opcode {
+		Opname:   "OpHitObjectGetWorldToObjectNV",
+		Class:    "Reserved",
+		Opcode:   5252,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectGetObjectToWorldNV = &Opcode {
+		Opname:   "OpHitObjectGetObjectToWorldNV",
+		Class:    "Reserved",
+		Opcode:   5253,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectGetObjectRayDirectionNV = &Opcode {
+		Opname:   "OpHitObjectGetObjectRayDirectionNV",
+		Class:    "Reserved",
+		Opcode:   5254,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectGetObjectRayOriginNV = &Opcode {
+		Opname:   "OpHitObjectGetObjectRayOriginNV",
+		Class:    "Reserved",
+		Opcode:   5255,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectTraceRayMotionNV = &Opcode {
+		Opname:   "OpHitObjectTraceRayMotionNV",
+		Class:    "Reserved",
+		Opcode:   5256,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Acceleration Structure'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayFlags'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Cullmask'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Record Offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Record Stride'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Miss Index'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Origin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TMin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Direction'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TMax'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Time'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectGetShaderRecordBufferHandleNV = &Opcode {
+		Opname:   "OpHitObjectGetShaderRecordBufferHandleNV",
+		Class:    "Reserved",
+		Opcode:   5257,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectGetShaderBindingTableRecordIndexNV = &Opcode {
+		Opname:   "OpHitObjectGetShaderBindingTableRecordIndexNV",
+		Class:    "Reserved",
+		Opcode:   5258,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectRecordEmptyNV = &Opcode {
+		Opname:   "OpHitObjectRecordEmptyNV",
+		Class:    "Reserved",
+		Opcode:   5259,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectTraceRayNV = &Opcode {
+		Opname:   "OpHitObjectTraceRayNV",
+		Class:    "Reserved",
+		Opcode:   5260,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Acceleration Structure'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayFlags'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Cullmask'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Record Offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Record Stride'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Miss Index'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Origin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TMin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Direction'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TMax'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectRecordHitNV = &Opcode {
+		Opname:   "OpHitObjectRecordHitNV",
+		Class:    "Reserved",
+		Opcode:   5261,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Acceleration Structure'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'InstanceId'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'PrimitiveId'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'GeometryIndex'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Kind'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Record Offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Record Stride'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Origin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TMin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Direction'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TMax'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'HitObject Attributes'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectRecordHitWithIndexNV = &Opcode {
+		Opname:   "OpHitObjectRecordHitWithIndexNV",
+		Class:    "Reserved",
+		Opcode:   5262,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Acceleration Structure'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'InstanceId'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'PrimitiveId'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'GeometryIndex'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Kind'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Record Index'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Origin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TMin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Direction'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TMax'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'HitObject Attributes'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectRecordMissNV = &Opcode {
+		Opname:   "OpHitObjectRecordMissNV",
+		Class:    "Reserved",
+		Opcode:   5263,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'SBT Index'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Origin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TMin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Direction'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TMax'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectExecuteShaderNV = &Opcode {
+		Opname:   "OpHitObjectExecuteShaderNV",
+		Class:    "Reserved",
+		Opcode:   5264,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectGetCurrentTimeNV = &Opcode {
+		Opname:   "OpHitObjectGetCurrentTimeNV",
+		Class:    "Reserved",
+		Opcode:   5265,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectGetAttributesNV = &Opcode {
+		Opname:   "OpHitObjectGetAttributesNV",
+		Class:    "Reserved",
+		Opcode:   5266,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object Attribute'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectGetHitKindNV = &Opcode {
+		Opname:   "OpHitObjectGetHitKindNV",
+		Class:    "Reserved",
+		Opcode:   5267,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectGetPrimitiveIndexNV = &Opcode {
+		Opname:   "OpHitObjectGetPrimitiveIndexNV",
+		Class:    "Reserved",
+		Opcode:   5268,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectGetGeometryIndexNV = &Opcode {
+		Opname:   "OpHitObjectGetGeometryIndexNV",
+		Class:    "Reserved",
+		Opcode:   5269,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectGetInstanceIdNV = &Opcode {
+		Opname:   "OpHitObjectGetInstanceIdNV",
+		Class:    "Reserved",
+		Opcode:   5270,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectGetInstanceCustomIndexNV = &Opcode {
+		Opname:   "OpHitObjectGetInstanceCustomIndexNV",
+		Class:    "Reserved",
+		Opcode:   5271,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectGetWorldRayDirectionNV = &Opcode {
+		Opname:   "OpHitObjectGetWorldRayDirectionNV",
+		Class:    "Reserved",
+		Opcode:   5272,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectGetWorldRayOriginNV = &Opcode {
+		Opname:   "OpHitObjectGetWorldRayOriginNV",
+		Class:    "Reserved",
+		Opcode:   5273,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectGetRayTMaxNV = &Opcode {
+		Opname:   "OpHitObjectGetRayTMaxNV",
+		Class:    "Reserved",
+		Opcode:   5274,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectGetRayTMinNV = &Opcode {
+		Opname:   "OpHitObjectGetRayTMinNV",
+		Class:    "Reserved",
+		Opcode:   5275,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectIsEmptyNV = &Opcode {
+		Opname:   "OpHitObjectIsEmptyNV",
+		Class:    "Reserved",
+		Opcode:   5276,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectIsHitNV = &Opcode {
+		Opname:   "OpHitObjectIsHitNV",
+		Class:    "Reserved",
+		Opcode:   5277,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpHitObjectIsMissNV = &Opcode {
+		Opname:   "OpHitObjectIsMissNV",
+		Class:    "Reserved",
+		Opcode:   5278,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpReorderThreadWithHitObjectNV = &Opcode {
+		Opname:   "OpReorderThreadWithHitObjectNV",
+		Class:    "Reserved",
+		Opcode:   5279,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hit Object'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hint'",
+				Quantifier: "?",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Bits'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpReorderThreadWithHintNV = &Opcode {
+		Opname:   "OpReorderThreadWithHintNV",
+		Class:    "Reserved",
+		Opcode:   5280,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Hint'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Bits'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeHitObjectNV = &Opcode {
+		Opname:   "OpTypeHitObjectNV",
+		Class:    "Type-Declaration",
+		Opcode:   5281,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
 				Quantifier: "",
 			}, 
 		},
@@ -10538,6 +13395,72 @@
 			}, 
 		},
 	}
+	OpCooperativeMatrixConvertNV = &Opcode {
+		Opname:   "OpCooperativeMatrixConvertNV",
+		Class:    "Conversion",
+		Opcode:   5293,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Matrix'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpEmitMeshTasksEXT = &Opcode {
+		Opname:   "OpEmitMeshTasksEXT",
+		Class:    "Reserved",
+		Opcode:   5294,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Group Count X'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Group Count Y'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Group Count Z'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpSetMeshOutputsEXT = &Opcode {
+		Opname:   "OpSetMeshOutputsEXT",
+		Class:    "Reserved",
+		Opcode:   5295,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Vertex Count'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Primitive Count'",
+				Quantifier: "",
+			}, 
+		},
+	}
 	OpGroupNonUniformPartitionNV = &Opcode {
 		Opname:   "OpGroupNonUniformPartitionNV",
 		Class:    "Non-Uniform",
@@ -10577,10 +13500,10 @@
 			}, 
 		},
 	}
-	OpReportIntersectionNV = &Opcode {
-		Opname:   "OpReportIntersectionNV",
+	OpFetchMicroTriangleVertexPositionNV = &Opcode {
+		Opname:   "OpFetchMicroTriangleVertexPositionNV",
 		Class:    "Reserved",
-		Opcode:   5334,
+		Opcode:   5300,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10594,12 +13517,69 @@
 			}, 
 			Operand {
 				Kind:       OperandKindIdRef,
-				Name:       "'Hit'",
+				Name:       "'Accel'",
 				Quantifier: "",
 			}, 
 			Operand {
 				Kind:       OperandKindIdRef,
-				Name:       "'HitKind'",
+				Name:       "'Instance Id'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Geometry Index'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Primitive Index'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Barycentric'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFetchMicroTriangleVertexBarycentricNV = &Opcode {
+		Opname:   "OpFetchMicroTriangleVertexBarycentricNV",
+		Class:    "Reserved",
+		Opcode:   5301,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Accel'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Instance Id'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Geometry Index'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Primitive Index'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Barycentric'",
 				Quantifier: "",
 			}, 
 		},
@@ -10638,13 +13618,6 @@
 		Operands: []Operand {
 		},
 	}
-	OpIgnoreIntersectionKHR = &Opcode {
-		Opname:   "OpIgnoreIntersectionKHR",
-		Class:    "Reserved",
-		Opcode:   5335,
-		Operands: []Operand {
-		},
-	}
 	OpTerminateRayNV = &Opcode {
 		Opname:   "OpTerminateRayNV",
 		Class:    "Reserved",
@@ -10652,13 +13625,6 @@
 		Operands: []Operand {
 		},
 	}
-	OpTerminateRayKHR = &Opcode {
-		Opname:   "OpTerminateRayKHR",
-		Class:    "Reserved",
-		Opcode:   5336,
-		Operands: []Operand {
-		},
-	}
 	OpTraceNV = &Opcode {
 		Opname:   "OpTraceNV",
 		Class:    "Reserved",
@@ -10721,10 +13687,10 @@
 			}, 
 		},
 	}
-	OpTraceRayKHR = &Opcode {
-		Opname:   "OpTraceRayKHR",
+	OpTraceMotionNV = &Opcode {
+		Opname:   "OpTraceMotionNV",
 		Class:    "Reserved",
-		Opcode:   5337,
+		Opcode:   5338,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdRef,
@@ -10778,161 +13744,87 @@
 			}, 
 			Operand {
 				Kind:       OperandKindIdRef,
+				Name:       "'Time'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
 				Name:       "'PayloadId'",
 				Quantifier: "",
 			}, 
 		},
 	}
-	OpTypeAccelerationStructureNV = &Opcode {
-		Opname:   "OpTypeAccelerationStructureNV",
+	OpTraceRayMotionNV = &Opcode {
+		Opname:   "OpTraceRayMotionNV",
 		Class:    "Reserved",
-		Opcode:   5341,
+		Opcode:   5339,
 		Operands: []Operand {
 			Operand {
-				Kind:       OperandKindIdResult,
-				Name:       "",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpTypeAccelerationStructureKHR = &Opcode {
-		Opname:   "OpTypeAccelerationStructureKHR",
-		Class:    "Reserved",
-		Opcode:   5341,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdResult,
-				Name:       "",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpTypeRayQueryProvisionalKHR = &Opcode {
-		Opname:   "OpTypeRayQueryProvisionalKHR",
-		Class:    "Reserved",
-		Opcode:   4472,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdResult,
-				Name:       "",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryInitializeKHR = &Opcode {
-		Opname:   "OpRayQueryInitializeKHR",
-		Class:    "Reserved",
-		Opcode:   4473,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-			Operand {
 				Kind:       OperandKindIdRef,
 				Name:       "'Accel'",
 				Quantifier: "",
 			}, 
 			Operand {
 				Kind:       OperandKindIdRef,
-				Name:       "'RayFlags'",
+				Name:       "'Ray Flags'",
 				Quantifier: "",
 			}, 
 			Operand {
 				Kind:       OperandKindIdRef,
-				Name:       "'CullMask'",
+				Name:       "'Cull Mask'",
 				Quantifier: "",
 			}, 
 			Operand {
 				Kind:       OperandKindIdRef,
-				Name:       "'RayOrigin'",
+				Name:       "'SBT Offset'",
 				Quantifier: "",
 			}, 
 			Operand {
 				Kind:       OperandKindIdRef,
-				Name:       "'RayTMin'",
+				Name:       "'SBT Stride'",
 				Quantifier: "",
 			}, 
 			Operand {
 				Kind:       OperandKindIdRef,
-				Name:       "'RayDirection'",
+				Name:       "'Miss Index'",
 				Quantifier: "",
 			}, 
 			Operand {
 				Kind:       OperandKindIdRef,
-				Name:       "'RayTMax'",
+				Name:       "'Ray Origin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ray Tmin'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ray Direction'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ray Tmax'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Time'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Payload'",
 				Quantifier: "",
 			}, 
 		},
 	}
-	OpRayQueryTerminateKHR = &Opcode {
-		Opname:   "OpRayQueryTerminateKHR",
+	OpRayQueryGetIntersectionTriangleVertexPositionsKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionTriangleVertexPositionsKHR",
 		Class:    "Reserved",
-		Opcode:   4474,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryGenerateIntersectionKHR = &Opcode {
-		Opname:   "OpRayQueryGenerateIntersectionKHR",
-		Class:    "Reserved",
-		Opcode:   4475,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'HitT'",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryConfirmIntersectionKHR = &Opcode {
-		Opname:   "OpRayQueryConfirmIntersectionKHR",
-		Class:    "Reserved",
-		Opcode:   4476,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryProceedKHR = &Opcode {
-		Opname:   "OpRayQueryProceedKHR",
-		Class:    "Reserved",
-		Opcode:   4477,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdResultType,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdResult,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryGetIntersectionTypeKHR = &Opcode {
-		Opname:   "OpRayQueryGetIntersectionTypeKHR",
-		Class:    "Reserved",
-		Opcode:   4479,
+		Opcode:   5340,
 		Operands: []Operand {
 			Operand {
 				Kind:       OperandKindIdResultType,
@@ -10956,438 +13848,16 @@
 			}, 
 		},
 	}
-	OpRayQueryGetRayTMinKHR = &Opcode {
-		Opname:   "OpRayQueryGetRayTMinKHR",
-		Class:    "Reserved",
-		Opcode:   6016,
+	OpTypeAccelerationStructureKHR = &Opcode {
+		Opname:   "OpTypeAccelerationStructureKHR",
+		Class:    "Type-Declaration",
+		Opcode:   5341,
 		Operands: []Operand {
 			Operand {
-				Kind:       OperandKindIdResultType,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
 				Kind:       OperandKindIdResult,
 				Name:       "",
 				Quantifier: "",
 			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryGetRayFlagsKHR = &Opcode {
-		Opname:   "OpRayQueryGetRayFlagsKHR",
-		Class:    "Reserved",
-		Opcode:   6017,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdResultType,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdResult,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryGetIntersectionTKHR = &Opcode {
-		Opname:   "OpRayQueryGetIntersectionTKHR",
-		Class:    "Reserved",
-		Opcode:   6018,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdResultType,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdResult,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'Intersection'",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryGetIntersectionInstanceCustomIndexKHR = &Opcode {
-		Opname:   "OpRayQueryGetIntersectionInstanceCustomIndexKHR",
-		Class:    "Reserved",
-		Opcode:   6019,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdResultType,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdResult,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'Intersection'",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryGetIntersectionInstanceIdKHR = &Opcode {
-		Opname:   "OpRayQueryGetIntersectionInstanceIdKHR",
-		Class:    "Reserved",
-		Opcode:   6020,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdResultType,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdResult,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'Intersection'",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = &Opcode {
-		Opname:   "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR",
-		Class:    "Reserved",
-		Opcode:   6021,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdResultType,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdResult,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'Intersection'",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryGetIntersectionGeometryIndexKHR = &Opcode {
-		Opname:   "OpRayQueryGetIntersectionGeometryIndexKHR",
-		Class:    "Reserved",
-		Opcode:   6022,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdResultType,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdResult,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'Intersection'",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryGetIntersectionPrimitiveIndexKHR = &Opcode {
-		Opname:   "OpRayQueryGetIntersectionPrimitiveIndexKHR",
-		Class:    "Reserved",
-		Opcode:   6023,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdResultType,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdResult,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'Intersection'",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryGetIntersectionBarycentricsKHR = &Opcode {
-		Opname:   "OpRayQueryGetIntersectionBarycentricsKHR",
-		Class:    "Reserved",
-		Opcode:   6024,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdResultType,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdResult,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'Intersection'",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryGetIntersectionFrontFaceKHR = &Opcode {
-		Opname:   "OpRayQueryGetIntersectionFrontFaceKHR",
-		Class:    "Reserved",
-		Opcode:   6025,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdResultType,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdResult,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'Intersection'",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = &Opcode {
-		Opname:   "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR",
-		Class:    "Reserved",
-		Opcode:   6026,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdResultType,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdResult,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryGetIntersectionObjectRayDirectionKHR = &Opcode {
-		Opname:   "OpRayQueryGetIntersectionObjectRayDirectionKHR",
-		Class:    "Reserved",
-		Opcode:   6027,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdResultType,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdResult,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'Intersection'",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryGetIntersectionObjectRayOriginKHR = &Opcode {
-		Opname:   "OpRayQueryGetIntersectionObjectRayOriginKHR",
-		Class:    "Reserved",
-		Opcode:   6028,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdResultType,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdResult,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'Intersection'",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryGetWorldRayDirectionKHR = &Opcode {
-		Opname:   "OpRayQueryGetWorldRayDirectionKHR",
-		Class:    "Reserved",
-		Opcode:   6029,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdResultType,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdResult,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryGetWorldRayOriginKHR = &Opcode {
-		Opname:   "OpRayQueryGetWorldRayOriginKHR",
-		Class:    "Reserved",
-		Opcode:   6030,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdResultType,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdResult,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryGetIntersectionObjectToWorldKHR = &Opcode {
-		Opname:   "OpRayQueryGetIntersectionObjectToWorldKHR",
-		Class:    "Reserved",
-		Opcode:   6031,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdResultType,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdResult,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'Intersection'",
-				Quantifier: "",
-			}, 
-		},
-	}
-	OpRayQueryGetIntersectionWorldToObjectKHR = &Opcode {
-		Opname:   "OpRayQueryGetIntersectionWorldToObjectKHR",
-		Class:    "Reserved",
-		Opcode:   6032,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdResultType,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdResult,
-				Name:       "",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'RayQuery'",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'Intersection'",
-				Quantifier: "",
-			}, 
 		},
 	}
 	OpExecuteCallableNV = &Opcode {
@@ -11407,26 +13877,9 @@
 			}, 
 		},
 	}
-	OpExecuteCallableKHR = &Opcode {
-		Opname:   "OpExecuteCallableKHR",
-		Class:    "Reserved",
-		Opcode:   5344,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'SBT Index'",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'Callable DataId'",
-				Quantifier: "",
-			}, 
-		},
-	}
 	OpTypeCooperativeMatrixNV = &Opcode {
 		Opname:   "OpTypeCooperativeMatrixNV",
-		Class:    "Reserved",
+		Class:    "Type-Declaration",
 		Opcode:   5358,
 		Operands: []Operand {
 			Operand {
@@ -11593,9 +14046,392 @@
 		Operands: []Operand {
 		},
 	}
-	OpDemoteToHelperInvocationEXT = &Opcode {
-		Opname:   "OpDemoteToHelperInvocationEXT",
+	OpCooperativeMatrixReduceNV = &Opcode {
+		Opname:   "OpCooperativeMatrixReduceNV",
+		Class:    "Arithmetic",
+		Opcode:   5366,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Matrix'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindCooperativeMatrixReduce,
+				Name:       "'Reduce'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'CombineFunc'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpCooperativeMatrixLoadTensorNV = &Opcode {
+		Opname:   "OpCooperativeMatrixLoadTensorNV",
+		Class:    "Memory",
+		Opcode:   5367,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Object'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TensorLayout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindMemoryAccess,
+				Name:       "'Memory Operand'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindTensorAddressingOperands,
+				Name:       "'Tensor Addressing Operands'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpCooperativeMatrixStoreTensorNV = &Opcode {
+		Opname:   "OpCooperativeMatrixStoreTensorNV",
+		Class:    "Memory",
+		Opcode:   5368,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Object'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TensorLayout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindMemoryAccess,
+				Name:       "'Memory Operand'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindTensorAddressingOperands,
+				Name:       "'Tensor Addressing Operands'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpCooperativeMatrixPerElementOpNV = &Opcode {
+		Opname:   "OpCooperativeMatrixPerElementOpNV",
+		Class:    "Function",
+		Opcode:   5369,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Matrix'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Func'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operands'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpTypeTensorLayoutNV = &Opcode {
+		Opname:   "OpTypeTensorLayoutNV",
+		Class:    "Type-Declaration",
+		Opcode:   5370,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Dim'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClampMode'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeTensorViewNV = &Opcode {
+		Opname:   "OpTypeTensorViewNV",
+		Class:    "Type-Declaration",
+		Opcode:   5371,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Dim'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'HasDimensions'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'p'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpCreateTensorLayoutNV = &Opcode {
+		Opname:   "OpCreateTensorLayoutNV",
 		Class:    "Reserved",
+		Opcode:   5372,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTensorLayoutSetDimensionNV = &Opcode {
+		Opname:   "OpTensorLayoutSetDimensionNV",
+		Class:    "Reserved",
+		Opcode:   5373,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TensorLayout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Dim'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpTensorLayoutSetStrideNV = &Opcode {
+		Opname:   "OpTensorLayoutSetStrideNV",
+		Class:    "Reserved",
+		Opcode:   5374,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TensorLayout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Stride'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpTensorLayoutSliceNV = &Opcode {
+		Opname:   "OpTensorLayoutSliceNV",
+		Class:    "Reserved",
+		Opcode:   5375,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TensorLayout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operands'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpTensorLayoutSetClampValueNV = &Opcode {
+		Opname:   "OpTensorLayoutSetClampValueNV",
+		Class:    "Reserved",
+		Opcode:   5376,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TensorLayout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpCreateTensorViewNV = &Opcode {
+		Opname:   "OpCreateTensorViewNV",
+		Class:    "Reserved",
+		Opcode:   5377,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTensorViewSetDimensionNV = &Opcode {
+		Opname:   "OpTensorViewSetDimensionNV",
+		Class:    "Reserved",
+		Opcode:   5378,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TensorView'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Dim'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpTensorViewSetStrideNV = &Opcode {
+		Opname:   "OpTensorViewSetStrideNV",
+		Class:    "Reserved",
+		Opcode:   5379,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TensorView'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Stride'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpDemoteToHelperInvocation = &Opcode {
+		Opname:   "OpDemoteToHelperInvocation",
+		Class:    "Control-Flow",
 		Opcode:   5380,
 		Operands: []Operand {
 		},
@@ -11617,6 +14453,283 @@
 			}, 
 		},
 	}
+	OpTensorViewSetClipNV = &Opcode {
+		Opname:   "OpTensorViewSetClipNV",
+		Class:    "Reserved",
+		Opcode:   5382,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TensorView'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClipRowOffset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClipRowSpan'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClipColOffset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ClipColSpan'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTensorLayoutSetBlockSizeNV = &Opcode {
+		Opname:   "OpTensorLayoutSetBlockSizeNV",
+		Class:    "Reserved",
+		Opcode:   5384,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'TensorLayout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'BlockSize'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpCooperativeMatrixTransposeNV = &Opcode {
+		Opname:   "OpCooperativeMatrixTransposeNV",
+		Class:    "Conversion",
+		Opcode:   5390,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Matrix'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConvertUToImageNV = &Opcode {
+		Opname:   "OpConvertUToImageNV",
+		Class:    "Reserved",
+		Opcode:   5391,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConvertUToSamplerNV = &Opcode {
+		Opname:   "OpConvertUToSamplerNV",
+		Class:    "Reserved",
+		Opcode:   5392,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConvertImageToUNV = &Opcode {
+		Opname:   "OpConvertImageToUNV",
+		Class:    "Reserved",
+		Opcode:   5393,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConvertSamplerToUNV = &Opcode {
+		Opname:   "OpConvertSamplerToUNV",
+		Class:    "Reserved",
+		Opcode:   5394,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConvertUToSampledImageNV = &Opcode {
+		Opname:   "OpConvertUToSampledImageNV",
+		Class:    "Reserved",
+		Opcode:   5395,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConvertSampledImageToUNV = &Opcode {
+		Opname:   "OpConvertSampledImageToUNV",
+		Class:    "Reserved",
+		Opcode:   5396,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSamplerImageAddressingModeNV = &Opcode {
+		Opname:   "OpSamplerImageAddressingModeNV",
+		Class:    "Reserved",
+		Opcode:   5397,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Bit Width'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRawAccessChainNV = &Opcode {
+		Opname:   "OpRawAccessChainNV",
+		Class:    "Memory",
+		Opcode:   5398,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Base'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Byte stride'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Element index'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Byte offset'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindRawAccessChainOperands,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
 	OpSubgroupShuffleINTEL = &Opcode {
 		Opname:   "OpSubgroupShuffleINTEL",
 		Class:    "Group",
@@ -12260,6 +15373,249 @@
 			}, 
 		},
 	}
+	OpConstantFunctionPointerINTEL = &Opcode {
+		Opname:   "OpConstantFunctionPointerINTEL",
+		Class:    "@exclude",
+		Opcode:   5600,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Function'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFunctionPointerCallINTEL = &Opcode {
+		Opname:   "OpFunctionPointerCallINTEL",
+		Class:    "@exclude",
+		Opcode:   5601,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Operand 1'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpAsmTargetINTEL = &Opcode {
+		Opname:   "OpAsmTargetINTEL",
+		Class:    "@exclude",
+		Opcode:   5609,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralString,
+				Name:       "'Asm target'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAsmINTEL = &Opcode {
+		Opname:   "OpAsmINTEL",
+		Class:    "@exclude",
+		Opcode:   5610,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Asm type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralString,
+				Name:       "'Asm instructions'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralString,
+				Name:       "'Constraints'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAsmCallINTEL = &Opcode {
+		Opname:   "OpAsmCallINTEL",
+		Class:    "@exclude",
+		Opcode:   5611,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Asm'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Argument 0'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpAtomicFMinEXT = &Opcode {
+		Opname:   "OpAtomicFMinEXT",
+		Class:    "Atomic",
+		Opcode:   5614,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAtomicFMaxEXT = &Opcode {
+		Opname:   "OpAtomicFMaxEXT",
+		Class:    "Atomic",
+		Opcode:   5615,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAssumeTrueKHR = &Opcode {
+		Opname:   "OpAssumeTrueKHR",
+		Class:    "Miscellaneous",
+		Opcode:   5630,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Condition'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpExpectKHR = &Opcode {
+		Opname:   "OpExpectKHR",
+		Class:    "Miscellaneous",
+		Opcode:   5631,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ExpectedValue'",
+				Quantifier: "",
+			}, 
+		},
+	}
 	OpDecorateString = &Opcode {
 		Opname:   "OpDecorateString",
 		Class:    "Annotation",
@@ -12277,23 +15633,6 @@
 			}, 
 		},
 	}
-	OpDecorateStringGOOGLE = &Opcode {
-		Opname:   "OpDecorateStringGOOGLE",
-		Class:    "Annotation",
-		Opcode:   5632,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'Target'",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindDecoration,
-				Name:       "",
-				Quantifier: "",
-			}, 
-		},
-	}
 	OpMemberDecorateString = &Opcode {
 		Opname:   "OpMemberDecorateString",
 		Class:    "Annotation",
@@ -12316,28 +15655,6 @@
 			}, 
 		},
 	}
-	OpMemberDecorateStringGOOGLE = &Opcode {
-		Opname:   "OpMemberDecorateStringGOOGLE",
-		Class:    "Annotation",
-		Opcode:   5633,
-		Operands: []Operand {
-			Operand {
-				Kind:       OperandKindIdRef,
-				Name:       "'Struct Type'",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindLiteralInteger,
-				Name:       "'Member'",
-				Quantifier: "",
-			}, 
-			Operand {
-				Kind:       OperandKindDecoration,
-				Name:       "",
-				Quantifier: "",
-			}, 
-		},
-	}
 	OpVmeImageINTEL = &Opcode {
 		Opname:   "OpVmeImageINTEL",
 		Class:    "@exclude",
@@ -15439,6 +18756,3782 @@
 			}, 
 		},
 	}
+	OpVariableLengthArrayINTEL = &Opcode {
+		Opname:   "OpVariableLengthArrayINTEL",
+		Class:    "@exclude",
+		Opcode:   5818,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Lenght'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSaveMemoryINTEL = &Opcode {
+		Opname:   "OpSaveMemoryINTEL",
+		Class:    "@exclude",
+		Opcode:   5819,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRestoreMemoryINTEL = &Opcode {
+		Opname:   "OpRestoreMemoryINTEL",
+		Class:    "@exclude",
+		Opcode:   5820,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ptr'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatSinCosPiINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatSinCosPiINTEL",
+		Class:    "@exclude",
+		Opcode:   5840,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'FromSign'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatCastINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatCastINTEL",
+		Class:    "@exclude",
+		Opcode:   5841,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatCastFromIntINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatCastFromIntINTEL",
+		Class:    "@exclude",
+		Opcode:   5842,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'FromSign'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatCastToIntINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatCastToIntINTEL",
+		Class:    "@exclude",
+		Opcode:   5843,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatAddINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatAddINTEL",
+		Class:    "@exclude",
+		Opcode:   5846,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'B'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M2'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatSubINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatSubINTEL",
+		Class:    "@exclude",
+		Opcode:   5847,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'B'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M2'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatMulINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatMulINTEL",
+		Class:    "@exclude",
+		Opcode:   5848,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'B'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M2'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatDivINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatDivINTEL",
+		Class:    "@exclude",
+		Opcode:   5849,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'B'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M2'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatGTINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatGTINTEL",
+		Class:    "@exclude",
+		Opcode:   5850,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'B'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatGEINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatGEINTEL",
+		Class:    "@exclude",
+		Opcode:   5851,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'B'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatLTINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatLTINTEL",
+		Class:    "@exclude",
+		Opcode:   5852,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'B'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatLEINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatLEINTEL",
+		Class:    "@exclude",
+		Opcode:   5853,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'B'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatEQINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatEQINTEL",
+		Class:    "@exclude",
+		Opcode:   5854,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'B'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M2'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatRecipINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatRecipINTEL",
+		Class:    "@exclude",
+		Opcode:   5855,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatRSqrtINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatRSqrtINTEL",
+		Class:    "@exclude",
+		Opcode:   5856,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatCbrtINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatCbrtINTEL",
+		Class:    "@exclude",
+		Opcode:   5857,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatHypotINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatHypotINTEL",
+		Class:    "@exclude",
+		Opcode:   5858,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'B'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M2'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatSqrtINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatSqrtINTEL",
+		Class:    "@exclude",
+		Opcode:   5859,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatLogINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatLogINTEL",
+		Class:    "@exclude",
+		Opcode:   5860,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatLog2INTEL = &Opcode {
+		Opname:   "OpArbitraryFloatLog2INTEL",
+		Class:    "@exclude",
+		Opcode:   5861,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatLog10INTEL = &Opcode {
+		Opname:   "OpArbitraryFloatLog10INTEL",
+		Class:    "@exclude",
+		Opcode:   5862,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatLog1pINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatLog1pINTEL",
+		Class:    "@exclude",
+		Opcode:   5863,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatExpINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatExpINTEL",
+		Class:    "@exclude",
+		Opcode:   5864,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatExp2INTEL = &Opcode {
+		Opname:   "OpArbitraryFloatExp2INTEL",
+		Class:    "@exclude",
+		Opcode:   5865,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatExp10INTEL = &Opcode {
+		Opname:   "OpArbitraryFloatExp10INTEL",
+		Class:    "@exclude",
+		Opcode:   5866,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatExpm1INTEL = &Opcode {
+		Opname:   "OpArbitraryFloatExpm1INTEL",
+		Class:    "@exclude",
+		Opcode:   5867,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatSinINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatSinINTEL",
+		Class:    "@exclude",
+		Opcode:   5868,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatCosINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatCosINTEL",
+		Class:    "@exclude",
+		Opcode:   5869,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatSinCosINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatSinCosINTEL",
+		Class:    "@exclude",
+		Opcode:   5870,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatSinPiINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatSinPiINTEL",
+		Class:    "@exclude",
+		Opcode:   5871,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatCosPiINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatCosPiINTEL",
+		Class:    "@exclude",
+		Opcode:   5872,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatASinINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatASinINTEL",
+		Class:    "@exclude",
+		Opcode:   5873,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatASinPiINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatASinPiINTEL",
+		Class:    "@exclude",
+		Opcode:   5874,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatACosINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatACosINTEL",
+		Class:    "@exclude",
+		Opcode:   5875,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatACosPiINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatACosPiINTEL",
+		Class:    "@exclude",
+		Opcode:   5876,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatATanINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatATanINTEL",
+		Class:    "@exclude",
+		Opcode:   5877,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatATanPiINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatATanPiINTEL",
+		Class:    "@exclude",
+		Opcode:   5878,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatATan2INTEL = &Opcode {
+		Opname:   "OpArbitraryFloatATan2INTEL",
+		Class:    "@exclude",
+		Opcode:   5879,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'B'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M2'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatPowINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatPowINTEL",
+		Class:    "@exclude",
+		Opcode:   5880,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'B'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M2'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatPowRINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatPowRINTEL",
+		Class:    "@exclude",
+		Opcode:   5881,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'B'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M2'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArbitraryFloatPowNINTEL = &Opcode {
+		Opname:   "OpArbitraryFloatPowNINTEL",
+		Class:    "@exclude",
+		Opcode:   5882,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'A'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'M1'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'B'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Mout'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'EnableSubnormals'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingMode'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'RoundingAccuracy'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpLoopControlINTEL = &Opcode {
+		Opname:   "OpLoopControlINTEL",
+		Class:    "Reserved",
+		Opcode:   5887,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Loop Control Parameters'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpAliasDomainDeclINTEL = &Opcode {
+		Opname:   "OpAliasDomainDeclINTEL",
+		Class:    "@exclude",
+		Opcode:   5911,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Name'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpAliasScopeDeclINTEL = &Opcode {
+		Opname:   "OpAliasScopeDeclINTEL",
+		Class:    "@exclude",
+		Opcode:   5912,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Alias Domain'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Name'",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpAliasScopeListDeclINTEL = &Opcode {
+		Opname:   "OpAliasScopeListDeclINTEL",
+		Class:    "@exclude",
+		Opcode:   5913,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'AliasScope1, AliasScope2, ...'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpFixedSqrtINTEL = &Opcode {
+		Opname:   "OpFixedSqrtINTEL",
+		Class:    "@exclude",
+		Opcode:   5923,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'S'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'I'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'rI'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Q'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'O'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFixedRecipINTEL = &Opcode {
+		Opname:   "OpFixedRecipINTEL",
+		Class:    "@exclude",
+		Opcode:   5924,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'S'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'I'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'rI'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Q'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'O'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFixedRsqrtINTEL = &Opcode {
+		Opname:   "OpFixedRsqrtINTEL",
+		Class:    "@exclude",
+		Opcode:   5925,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'S'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'I'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'rI'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Q'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'O'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFixedSinINTEL = &Opcode {
+		Opname:   "OpFixedSinINTEL",
+		Class:    "@exclude",
+		Opcode:   5926,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'S'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'I'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'rI'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Q'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'O'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFixedCosINTEL = &Opcode {
+		Opname:   "OpFixedCosINTEL",
+		Class:    "@exclude",
+		Opcode:   5927,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'S'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'I'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'rI'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Q'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'O'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFixedSinCosINTEL = &Opcode {
+		Opname:   "OpFixedSinCosINTEL",
+		Class:    "@exclude",
+		Opcode:   5928,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'S'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'I'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'rI'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Q'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'O'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFixedSinPiINTEL = &Opcode {
+		Opname:   "OpFixedSinPiINTEL",
+		Class:    "@exclude",
+		Opcode:   5929,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'S'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'I'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'rI'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Q'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'O'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFixedCosPiINTEL = &Opcode {
+		Opname:   "OpFixedCosPiINTEL",
+		Class:    "@exclude",
+		Opcode:   5930,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'S'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'I'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'rI'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Q'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'O'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFixedSinCosPiINTEL = &Opcode {
+		Opname:   "OpFixedSinCosPiINTEL",
+		Class:    "@exclude",
+		Opcode:   5931,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'S'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'I'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'rI'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Q'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'O'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFixedLogINTEL = &Opcode {
+		Opname:   "OpFixedLogINTEL",
+		Class:    "@exclude",
+		Opcode:   5932,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'S'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'I'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'rI'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Q'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'O'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFixedExpINTEL = &Opcode {
+		Opname:   "OpFixedExpINTEL",
+		Class:    "@exclude",
+		Opcode:   5933,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input Type'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'S'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'I'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'rI'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Q'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'O'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpPtrCastToCrossWorkgroupINTEL = &Opcode {
+		Opname:   "OpPtrCastToCrossWorkgroupINTEL",
+		Class:    "@exclude",
+		Opcode:   5934,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpCrossWorkgroupCastToPtrINTEL = &Opcode {
+		Opname:   "OpCrossWorkgroupCastToPtrINTEL",
+		Class:    "@exclude",
+		Opcode:   5938,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpReadPipeBlockingINTEL = &Opcode {
+		Opname:   "OpReadPipeBlockingINTEL",
+		Class:    "Pipe",
+		Opcode:   5946,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Alignment'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpWritePipeBlockingINTEL = &Opcode {
+		Opname:   "OpWritePipeBlockingINTEL",
+		Class:    "Pipe",
+		Opcode:   5947,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Size'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Packet Alignment'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpFPGARegINTEL = &Opcode {
+		Opname:   "OpFPGARegINTEL",
+		Class:    "Reserved",
+		Opcode:   5949,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Result'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Input'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetRayTMinKHR = &Opcode {
+		Opname:   "OpRayQueryGetRayTMinKHR",
+		Class:    "Reserved",
+		Opcode:   6016,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetRayFlagsKHR = &Opcode {
+		Opname:   "OpRayQueryGetRayFlagsKHR",
+		Class:    "Reserved",
+		Opcode:   6017,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionTKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionTKHR",
+		Class:    "Reserved",
+		Opcode:   6018,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionInstanceCustomIndexKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionInstanceCustomIndexKHR",
+		Class:    "Reserved",
+		Opcode:   6019,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionInstanceIdKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionInstanceIdKHR",
+		Class:    "Reserved",
+		Opcode:   6020,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR",
+		Class:    "Reserved",
+		Opcode:   6021,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionGeometryIndexKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionGeometryIndexKHR",
+		Class:    "Reserved",
+		Opcode:   6022,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionPrimitiveIndexKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionPrimitiveIndexKHR",
+		Class:    "Reserved",
+		Opcode:   6023,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionBarycentricsKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionBarycentricsKHR",
+		Class:    "Reserved",
+		Opcode:   6024,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionFrontFaceKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionFrontFaceKHR",
+		Class:    "Reserved",
+		Opcode:   6025,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR",
+		Class:    "Reserved",
+		Opcode:   6026,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionObjectRayDirectionKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionObjectRayDirectionKHR",
+		Class:    "Reserved",
+		Opcode:   6027,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionObjectRayOriginKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionObjectRayOriginKHR",
+		Class:    "Reserved",
+		Opcode:   6028,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetWorldRayDirectionKHR = &Opcode {
+		Opname:   "OpRayQueryGetWorldRayDirectionKHR",
+		Class:    "Reserved",
+		Opcode:   6029,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetWorldRayOriginKHR = &Opcode {
+		Opname:   "OpRayQueryGetWorldRayOriginKHR",
+		Class:    "Reserved",
+		Opcode:   6030,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionObjectToWorldKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionObjectToWorldKHR",
+		Class:    "Reserved",
+		Opcode:   6031,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpRayQueryGetIntersectionWorldToObjectKHR = &Opcode {
+		Opname:   "OpRayQueryGetIntersectionWorldToObjectKHR",
+		Class:    "Reserved",
+		Opcode:   6032,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'RayQuery'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Intersection'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpAtomicFAddEXT = &Opcode {
+		Opname:   "OpAtomicFAddEXT",
+		Class:    "Atomic",
+		Opcode:   6035,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Pointer'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeBufferSurfaceINTEL = &Opcode {
+		Opname:   "OpTypeBufferSurfaceINTEL",
+		Class:    "Type-Declaration",
+		Opcode:   6086,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindAccessQualifier,
+				Name:       "'AccessQualifier'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpTypeStructContinuedINTEL = &Opcode {
+		Opname:   "OpTypeStructContinuedINTEL",
+		Class:    "Type-Declaration",
+		Opcode:   6090,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Member 0 type', + 'member 1 type', + ...",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpConstantCompositeContinuedINTEL = &Opcode {
+		Opname:   "OpConstantCompositeContinuedINTEL",
+		Class:    "Constant-Creation",
+		Opcode:   6091,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Constituents'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpSpecConstantCompositeContinuedINTEL = &Opcode {
+		Opname:   "OpSpecConstantCompositeContinuedINTEL",
+		Class:    "Constant-Creation",
+		Opcode:   6092,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Constituents'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpCompositeConstructContinuedINTEL = &Opcode {
+		Opname:   "OpCompositeConstructContinuedINTEL",
+		Class:    "Composite",
+		Opcode:   6096,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Constituents'",
+				Quantifier: "*",
+			}, 
+		},
+	}
+	OpConvertFToBF16INTEL = &Opcode {
+		Opname:   "OpConvertFToBF16INTEL",
+		Class:    "Conversion",
+		Opcode:   6116,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Float Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpConvertBF16ToFINTEL = &Opcode {
+		Opname:   "OpConvertBF16ToFINTEL",
+		Class:    "Conversion",
+		Opcode:   6117,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'BFloat16 Value'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpControlBarrierArriveINTEL = &Opcode {
+		Opname:   "OpControlBarrierArriveINTEL",
+		Class:    "Barrier",
+		Opcode:   6142,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpControlBarrierWaitINTEL = &Opcode {
+		Opname:   "OpControlBarrierWaitINTEL",
+		Class:    "Barrier",
+		Opcode:   6143,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Memory'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdMemorySemantics,
+				Name:       "'Semantics'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpArithmeticFenceEXT = &Opcode {
+		Opname:   "OpArithmeticFenceEXT",
+		Class:    "Miscellaneous",
+		Opcode:   6145,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Target '",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpSubgroupBlockPrefetchINTEL = &Opcode {
+		Opname:   "OpSubgroupBlockPrefetchINTEL",
+		Class:    "Group",
+		Opcode:   6221,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Ptr'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'NumBytes'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindMemoryAccess,
+				Name:       "",
+				Quantifier: "?",
+			}, 
+		},
+	}
+	OpGroupIMulKHR = &Opcode {
+		Opname:   "OpGroupIMulKHR",
+		Class:    "Group",
+		Opcode:   6401,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupFMulKHR = &Opcode {
+		Opname:   "OpGroupFMulKHR",
+		Class:    "Group",
+		Opcode:   6402,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupBitwiseAndKHR = &Opcode {
+		Opname:   "OpGroupBitwiseAndKHR",
+		Class:    "Group",
+		Opcode:   6403,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupBitwiseOrKHR = &Opcode {
+		Opname:   "OpGroupBitwiseOrKHR",
+		Class:    "Group",
+		Opcode:   6404,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupBitwiseXorKHR = &Opcode {
+		Opname:   "OpGroupBitwiseXorKHR",
+		Class:    "Group",
+		Opcode:   6405,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupLogicalAndKHR = &Opcode {
+		Opname:   "OpGroupLogicalAndKHR",
+		Class:    "Group",
+		Opcode:   6406,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupLogicalOrKHR = &Opcode {
+		Opname:   "OpGroupLogicalOrKHR",
+		Class:    "Group",
+		Opcode:   6407,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpGroupLogicalXorKHR = &Opcode {
+		Opname:   "OpGroupLogicalXorKHR",
+		Class:    "Group",
+		Opcode:   6408,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdScope,
+				Name:       "'Execution'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindGroupOperation,
+				Name:       "'Operation'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'X'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpMaskedGatherINTEL = &Opcode {
+		Opname:   "OpMaskedGatherINTEL",
+		Class:    "Memory",
+		Opcode:   6428,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdResultType,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdResult,
+				Name:       "",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'PtrVector'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Alignment'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Mask'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'FillEmpty'",
+				Quantifier: "",
+			}, 
+		},
+	}
+	OpMaskedScatterINTEL = &Opcode {
+		Opname:   "OpMaskedScatterINTEL",
+		Class:    "Memory",
+		Opcode:   6429,
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'InputVector'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'PtrVector'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Alignment'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Mask'",
+				Quantifier: "",
+			}, 
+		},
+	}
 
 	GLSLStd450_Round = &Opcode {
 		Opname:   "Round",
@@ -19600,6 +26693,51 @@
 			}, 
 		},
 	}
+	OpenCLDebugInfo100_DebugModuleINTEL = &Opcode {
+		Opname:   "DebugModuleINTEL",
+		Operands: []Operand {
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Name'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Source'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'Parent'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'Line'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'ConfigurationMacros'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'IncludePath'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindIdRef,
+				Name:       "'APINotesFile'",
+				Quantifier: "",
+			}, 
+			Operand {
+				Kind:       OperandKindLiteralInteger,
+				Name:       "'IsDeclaration'",
+				Quantifier: "",
+			}, 
+		},
+	}
 
 
 	OperandKindImageOperands = &OperandKind {
@@ -19618,56 +26756,56 @@
 				Value:        0x0001,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{{OperandKindIdRef, ""},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Lod",
 				Value:        0x0002,
 				Capabilities: []string{},
 				Parameters:   []Parameter{{OperandKindIdRef, ""},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Grad",
 				Value:        0x0004,
 				Capabilities: []string{},
 				Parameters:   []Parameter{{OperandKindIdRef, ""},{OperandKindIdRef, ""},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ConstOffset",
 				Value:        0x0008,
 				Capabilities: []string{},
 				Parameters:   []Parameter{{OperandKindIdRef, ""},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Offset",
 				Value:        0x0010,
 				Capabilities: []string{"ImageGatherExtended",},
 				Parameters:   []Parameter{{OperandKindIdRef, ""},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ConstOffsets",
 				Value:        0x0020,
 				Capabilities: []string{"ImageGatherExtended",},
 				Parameters:   []Parameter{{OperandKindIdRef, ""},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Sample",
 				Value:        0x0040,
 				Capabilities: []string{},
 				Parameters:   []Parameter{{OperandKindIdRef, ""},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "MinLod",
 				Value:        0x0080,
 				Capabilities: []string{"MinLod",},
 				Parameters:   []Parameter{{OperandKindIdRef, ""},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "MakeTexelAvailable",
@@ -19677,13 +26815,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "MakeTexelAvailableKHR",
-				Value:        0x0100,
-				Capabilities: []string{"VulkanMemoryModel",},
-				Parameters:   []Parameter{{OperandKindIdScope, ""},},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "MakeTexelVisible",
 				Value:        0x0200,
 				Capabilities: []string{"VulkanMemoryModel",},
@@ -19691,13 +26822,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "MakeTexelVisibleKHR",
-				Value:        0x0200,
-				Capabilities: []string{"VulkanMemoryModel",},
-				Parameters:   []Parameter{{OperandKindIdScope, ""},},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "NonPrivateTexel",
 				Value:        0x0400,
 				Capabilities: []string{"VulkanMemoryModel",},
@@ -19705,13 +26829,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "NonPrivateTexelKHR",
-				Value:        0x0400,
-				Capabilities: []string{"VulkanMemoryModel",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "VolatileTexel",
 				Value:        0x0800,
 				Capabilities: []string{"VulkanMemoryModel",},
@@ -19719,13 +26836,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "VolatileTexelKHR",
-				Value:        0x0800,
-				Capabilities: []string{"VulkanMemoryModel",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "SignExtend",
 				Value:        0x1000,
 				Capabilities: []string{},
@@ -19739,6 +26849,20 @@
 				Parameters:   []Parameter{},
 				Version:      "1.4",
 			},
+			Enumerant{
+				Enumerant:    "Nontemporal",
+				Value:        0x4000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.6",
+			},
+			Enumerant{
+				Enumerant:    "Offsets",
+				Value:        0x10000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{{OperandKindIdRef, ""},},
+				Version:      "1.0",
+			},
 		},
 		Bases:      []*OperandKind {},
 	}
@@ -19751,42 +26875,63 @@
 				Value:        0x0000,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "NotNaN",
 				Value:        0x0001,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "NotInf",
 				Value:        0x0002,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "NSZ",
 				Value:        0x0004,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "AllowRecip",
 				Value:        0x0008,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Fast",
 				Value:        0x0010,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "AllowContract",
+				Value:        0x10000,
+				Capabilities: []string{"FloatControls2","FPFastMathModeINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "AllowReassoc",
+				Value:        0x20000,
+				Capabilities: []string{"FloatControls2","FPFastMathModeINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "AllowTransform",
+				Value:        0x40000,
+				Capabilities: []string{"FloatControls2",},
+				Parameters:   []Parameter{},
+				Version:      "None",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -19800,21 +26945,21 @@
 				Value:        0x0000,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Flatten",
 				Value:        0x0001,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "DontFlatten",
 				Value:        0x0002,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -19828,21 +26973,21 @@
 				Value:        0x0000,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Unroll",
 				Value:        0x0001,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "DontUnroll",
 				Value:        0x0002,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "DependencyInfinite",
@@ -19893,6 +27038,76 @@
 				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},},
 				Version:      "1.4",
 			},
+			Enumerant{
+				Enumerant:    "InitiationIntervalINTEL",
+				Value:        0x10000,
+				Capabilities: []string{"FPGALoopControlsINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MaxConcurrencyINTEL",
+				Value:        0x20000,
+				Capabilities: []string{"FPGALoopControlsINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "DependencyArrayINTEL",
+				Value:        0x40000,
+				Capabilities: []string{"FPGALoopControlsINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PipelineEnableINTEL",
+				Value:        0x80000,
+				Capabilities: []string{"FPGALoopControlsINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "LoopCoalesceINTEL",
+				Value:        0x100000,
+				Capabilities: []string{"FPGALoopControlsINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MaxInterleavingINTEL",
+				Value:        0x200000,
+				Capabilities: []string{"FPGALoopControlsINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SpeculatedIterationsINTEL",
+				Value:        0x400000,
+				Capabilities: []string{"FPGALoopControlsINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "NoFusionINTEL",
+				Value:        0x800000,
+				Capabilities: []string{"FPGALoopControlsINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "LoopCountINTEL",
+				Value:        0x1000000,
+				Capabilities: []string{"FPGALoopControlsINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MaxReinvocationDelayINTEL",
+				Value:        0x2000000,
+				Capabilities: []string{"FPGALoopControlsINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},},
+				Version:      "None",
+			},
 		},
 		Bases:      []*OperandKind {},
 	}
@@ -19905,35 +27120,42 @@
 				Value:        0x0000,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Inline",
 				Value:        0x0001,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "DontInline",
 				Value:        0x0002,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Pure",
 				Value:        0x0004,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Const",
 				Value:        0x0008,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "OptNoneEXT",
+				Value:        0x10000,
+				Capabilities: []string{"OptNoneEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -19947,84 +27169,77 @@
 				Value:        0x0000,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
-			},
-			Enumerant{
-				Enumerant:    "None",
-				Value:        0x0000,
-				Capabilities: []string{},
-				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Acquire",
 				Value:        0x0002,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Release",
 				Value:        0x0004,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "AcquireRelease",
 				Value:        0x0008,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SequentiallyConsistent",
 				Value:        0x0010,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "UniformMemory",
 				Value:        0x0040,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SubgroupMemory",
 				Value:        0x0080,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "WorkgroupMemory",
 				Value:        0x0100,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "CrossWorkgroupMemory",
 				Value:        0x0200,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "AtomicCounterMemory",
 				Value:        0x0400,
 				Capabilities: []string{"AtomicStorage",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ImageMemory",
 				Value:        0x0800,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "OutputMemory",
@@ -20034,13 +27249,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "OutputMemoryKHR",
-				Value:        0x1000,
-				Capabilities: []string{"VulkanMemoryModel",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "MakeAvailable",
 				Value:        0x2000,
 				Capabilities: []string{"VulkanMemoryModel",},
@@ -20048,13 +27256,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "MakeAvailableKHR",
-				Value:        0x2000,
-				Capabilities: []string{"VulkanMemoryModel",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "MakeVisible",
 				Value:        0x4000,
 				Capabilities: []string{"VulkanMemoryModel",},
@@ -20062,13 +27263,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "MakeVisibleKHR",
-				Value:        0x4000,
-				Capabilities: []string{"VulkanMemoryModel",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "Volatile",
 				Value:        0x8000,
 				Capabilities: []string{"VulkanMemoryModel",},
@@ -20087,28 +27281,28 @@
 				Value:        0x0000,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Volatile",
 				Value:        0x0001,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Aligned",
 				Value:        0x0002,
 				Capabilities: []string{},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, ""},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Nontemporal",
 				Value:        0x0004,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "MakePointerAvailable",
@@ -20118,13 +27312,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "MakePointerAvailableKHR",
-				Value:        0x0008,
-				Capabilities: []string{"VulkanMemoryModel",},
-				Parameters:   []Parameter{{OperandKindIdScope, ""},},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "MakePointerVisible",
 				Value:        0x0010,
 				Capabilities: []string{"VulkanMemoryModel",},
@@ -20132,13 +27319,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "MakePointerVisibleKHR",
-				Value:        0x0010,
-				Capabilities: []string{"VulkanMemoryModel",},
-				Parameters:   []Parameter{{OperandKindIdScope, ""},},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "NonPrivatePointer",
 				Value:        0x0020,
 				Capabilities: []string{"VulkanMemoryModel",},
@@ -20146,11 +27326,18 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "NonPrivatePointerKHR",
-				Value:        0x0020,
-				Capabilities: []string{"VulkanMemoryModel",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
+				Enumerant:    "AliasScopeINTELMask",
+				Value:        0x10000,
+				Capabilities: []string{"MemoryAccessAliasingINTEL",},
+				Parameters:   []Parameter{{OperandKindIdRef, ""},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "NoAliasINTELMask",
+				Value:        0x20000,
+				Capabilities: []string{"MemoryAccessAliasingINTEL",},
+				Parameters:   []Parameter{{OperandKindIdRef, ""},},
+				Version:      "None",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -20164,14 +27351,14 @@
 				Value:        0x0000,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "CmdExecTime",
 				Value:        0x0001,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -20183,80 +27370,150 @@
 			Enumerant{
 				Enumerant:    "NoneKHR",
 				Value:        0x0000,
-				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "OpaqueKHR",
 				Value:        0x0001,
-				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "NoOpaqueKHR",
 				Value:        0x0002,
-				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "TerminateOnFirstHitKHR",
 				Value:        0x0004,
-				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "SkipClosestHitShaderKHR",
 				Value:        0x0008,
-				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "CullBackFacingTrianglesKHR",
 				Value:        0x0010,
-				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "CullFrontFacingTrianglesKHR",
 				Value:        0x0020,
-				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "CullOpaqueKHR",
 				Value:        0x0040,
-				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "CullNoOpaqueKHR",
 				Value:        0x0080,
-				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "SkipTrianglesKHR",
 				Value:        0x0100,
-				Capabilities: []string{"RayTraversalPrimitiveCullingProvisionalKHR",},
+				Capabilities: []string{"RayTraversalPrimitiveCullingKHR",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "SkipAABBsKHR",
 				Value:        0x0200,
-				Capabilities: []string{"RayTraversalPrimitiveCullingProvisionalKHR",},
+				Capabilities: []string{"RayTraversalPrimitiveCullingKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ForceOpacityMicromap2StateEXT",
+				Value:        0x0400,
+				Capabilities: []string{"RayTracingOpacityMicromapEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindFragmentShadingRate = &OperandKind {
+		Kind:       "FragmentShadingRate",
+		Category:   "BitEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "Vertical2Pixels",
+				Value:        0x0001,
+				Capabilities: []string{"FragmentShadingRateKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "Vertical4Pixels",
+				Value:        0x0002,
+				Capabilities: []string{"FragmentShadingRateKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "Horizontal2Pixels",
+				Value:        0x0004,
+				Capabilities: []string{"FragmentShadingRateKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "Horizontal4Pixels",
+				Value:        0x0008,
+				Capabilities: []string{"FragmentShadingRateKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindRawAccessChainOperands = &OperandKind {
+		Kind:       "RawAccessChainOperands",
+		Category:   "BitEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "None",
+				Value:        0x0000,
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
 				Version:      "",
 			},
+			Enumerant{
+				Enumerant:    "RobustnessPerComponentNV",
+				Value:        0x0001,
+				Capabilities: []string{"RawAccessChainsNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RobustnessPerElementNV",
+				Value:        0x0002,
+				Capabilities: []string{"RawAccessChainsNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
 		},
 		Bases:      []*OperandKind {},
 	}
@@ -20269,42 +27526,91 @@
 				Value:        0,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ESSL",
 				Value:        1,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "GLSL",
 				Value:        2,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "OpenCL_C",
 				Value:        3,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "OpenCL_CPP",
 				Value:        4,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "HLSL",
 				Value:        5,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "CPP_for_OpenCL",
+				Value:        6,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "SYCL",
+				Value:        7,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "HERO_C",
+				Value:        8,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "NZSL",
+				Value:        9,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "WGSL",
+				Value:        10,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "Slang",
+				Value:        11,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "Zig",
+				Value:        12,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.0",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -20318,49 +27624,49 @@
 				Value:        0,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "TessellationControl",
 				Value:        1,
 				Capabilities: []string{"Tessellation",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "TessellationEvaluation",
 				Value:        2,
 				Capabilities: []string{"Tessellation",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Geometry",
 				Value:        3,
 				Capabilities: []string{"Geometry",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Fragment",
 				Value:        4,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "GLCompute",
 				Value:        5,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Kernel",
 				Value:        6,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "TaskNV",
@@ -20377,86 +27683,58 @@
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "RayGenerationNV",
-				Value:        5313,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
 				Enumerant:    "RayGenerationKHR",
 				Value:        5313,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "IntersectionNV",
-				Value:        5314,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "IntersectionKHR",
 				Value:        5314,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "AnyHitNV",
-				Value:        5315,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "AnyHitKHR",
 				Value:        5315,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "ClosestHitNV",
-				Value:        5316,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "ClosestHitKHR",
 				Value:        5316,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "MissNV",
-				Value:        5317,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "MissKHR",
 				Value:        5317,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "CallableNV",
-				Value:        5318,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "CallableKHR",
 				Value:        5318,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "TaskEXT",
+				Value:        5364,
+				Capabilities: []string{"MeshShadingEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MeshEXT",
+				Value:        5365,
+				Capabilities: []string{"MeshShadingEXT",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
@@ -20472,21 +27750,21 @@
 				Value:        0,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Physical32",
 				Value:        1,
 				Capabilities: []string{"Addresses",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Physical64",
 				Value:        2,
 				Capabilities: []string{"Addresses",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "PhysicalStorageBuffer64",
@@ -20495,13 +27773,6 @@
 				Parameters:   []Parameter{},
 				Version:      "1.5",
 			},
-			Enumerant{
-				Enumerant:    "PhysicalStorageBuffer64EXT",
-				Value:        5348,
-				Capabilities: []string{"PhysicalStorageBufferAddresses",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
 		},
 		Bases:      []*OperandKind {},
 	}
@@ -20514,21 +27785,21 @@
 				Value:        0,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "GLSL450",
 				Value:        1,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "OpenCL",
 				Value:        2,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Vulkan",
@@ -20537,13 +27808,6 @@
 				Parameters:   []Parameter{},
 				Version:      "1.5",
 			},
-			Enumerant{
-				Enumerant:    "VulkanKHR",
-				Value:        3,
-				Capabilities: []string{"VulkanMemoryModel",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
 		},
 		Bases:      []*OperandKind {},
 	}
@@ -20556,217 +27820,217 @@
 				Value:        0,
 				Capabilities: []string{"Geometry",},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Number of <<Invocation,invocations>>'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SpacingEqual",
 				Value:        1,
 				Capabilities: []string{"Tessellation",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SpacingFractionalEven",
 				Value:        2,
 				Capabilities: []string{"Tessellation",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SpacingFractionalOdd",
 				Value:        3,
 				Capabilities: []string{"Tessellation",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "VertexOrderCw",
 				Value:        4,
 				Capabilities: []string{"Tessellation",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "VertexOrderCcw",
 				Value:        5,
 				Capabilities: []string{"Tessellation",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "PixelCenterInteger",
 				Value:        6,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "OriginUpperLeft",
 				Value:        7,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "OriginLowerLeft",
 				Value:        8,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "EarlyFragmentTests",
 				Value:        9,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "PointMode",
 				Value:        10,
 				Capabilities: []string{"Tessellation",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Xfb",
 				Value:        11,
 				Capabilities: []string{"TransformFeedback",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "DepthReplacing",
 				Value:        12,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "DepthGreater",
 				Value:        14,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "DepthLess",
 				Value:        15,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "DepthUnchanged",
 				Value:        16,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "LocalSize",
 				Value:        17,
 				Capabilities: []string{},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, "'x size'"},{OperandKindLiteralInteger, "'y size'"},{OperandKindLiteralInteger, "'z size'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "LocalSizeHint",
 				Value:        18,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, "'x size'"},{OperandKindLiteralInteger, "'y size'"},{OperandKindLiteralInteger, "'z size'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "InputPoints",
 				Value:        19,
 				Capabilities: []string{"Geometry",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "InputLines",
 				Value:        20,
 				Capabilities: []string{"Geometry",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "InputLinesAdjacency",
 				Value:        21,
 				Capabilities: []string{"Geometry",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Triangles",
 				Value:        22,
 				Capabilities: []string{"Geometry","Tessellation",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "InputTrianglesAdjacency",
 				Value:        23,
 				Capabilities: []string{"Geometry",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Quads",
 				Value:        24,
 				Capabilities: []string{"Tessellation",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Isolines",
 				Value:        25,
 				Capabilities: []string{"Tessellation",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "OutputVertices",
 				Value:        26,
-				Capabilities: []string{"Geometry","Tessellation","MeshShadingNV",},
+				Capabilities: []string{"Geometry","Tessellation","MeshShadingNV","MeshShadingEXT",},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Vertex count'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "OutputPoints",
 				Value:        27,
-				Capabilities: []string{"Geometry","MeshShadingNV",},
+				Capabilities: []string{"Geometry","MeshShadingNV","MeshShadingEXT",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "OutputLineStrip",
 				Value:        28,
 				Capabilities: []string{"Geometry",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "OutputTriangleStrip",
 				Value:        29,
 				Capabilities: []string{"Geometry",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "VecTypeHint",
 				Value:        30,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Vector type'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ContractionOff",
 				Value:        31,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Initializer",
@@ -20814,10 +28078,38 @@
 				Enumerant:    "LocalSizeHintId",
 				Value:        39,
 				Capabilities: []string{"Kernel",},
-				Parameters:   []Parameter{{OperandKindIdRef, "'Local Size Hint'"},},
+				Parameters:   []Parameter{{OperandKindIdRef, "'x size hint'"},{OperandKindIdRef, "'y size hint'"},{OperandKindIdRef, "'z size hint'"},},
 				Version:      "1.2",
 			},
 			Enumerant{
+				Enumerant:    "NonCoherentColorAttachmentReadEXT",
+				Value:        4169,
+				Capabilities: []string{"TileImageColorReadAccessEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "NonCoherentDepthAttachmentReadEXT",
+				Value:        4170,
+				Capabilities: []string{"TileImageDepthReadAccessEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "NonCoherentStencilAttachmentReadEXT",
+				Value:        4171,
+				Capabilities: []string{"TileImageStencilReadAccessEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupUniformControlFlowKHR",
+				Value:        4421,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
 				Enumerant:    "PostDepthCoverage",
 				Value:        4446,
 				Capabilities: []string{"SampleMaskPostDepthCoverage",},
@@ -20860,6 +28152,13 @@
 				Version:      "1.4",
 			},
 			Enumerant{
+				Enumerant:    "EarlyAndLateFragmentTestsAMD",
+				Value:        5017,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
 				Enumerant:    "StencilRefReplacingEXT",
 				Value:        5027,
 				Capabilities: []string{"StencilExportEXT",},
@@ -20867,37 +28166,142 @@
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "OutputLinesNV",
-				Value:        5269,
-				Capabilities: []string{"MeshShadingNV",},
+				Enumerant:    "CoalescingAMDX",
+				Value:        5069,
+				Capabilities: []string{"ShaderEnqueueAMDX",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "OutputPrimitivesNV",
+				Enumerant:    "IsApiEntryAMDX",
+				Value:        5070,
+				Capabilities: []string{"ShaderEnqueueAMDX",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Is Entry'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MaxNodeRecursionAMDX",
+				Value:        5071,
+				Capabilities: []string{"ShaderEnqueueAMDX",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Number of recursions'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "StaticNumWorkgroupsAMDX",
+				Value:        5072,
+				Capabilities: []string{"ShaderEnqueueAMDX",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'x size'"},{OperandKindIdRef, "'y size'"},{OperandKindIdRef, "'z size'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ShaderIndexAMDX",
+				Value:        5073,
+				Capabilities: []string{"ShaderEnqueueAMDX",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Shader Index'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MaxNumWorkgroupsAMDX",
+				Value:        5077,
+				Capabilities: []string{"ShaderEnqueueAMDX",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'x size'"},{OperandKindIdRef, "'y size'"},{OperandKindIdRef, "'z size'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "StencilRefUnchangedFrontAMD",
+				Value:        5079,
+				Capabilities: []string{"StencilExportEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "StencilRefGreaterFrontAMD",
+				Value:        5080,
+				Capabilities: []string{"StencilExportEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "StencilRefLessFrontAMD",
+				Value:        5081,
+				Capabilities: []string{"StencilExportEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "StencilRefUnchangedBackAMD",
+				Value:        5082,
+				Capabilities: []string{"StencilExportEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "StencilRefGreaterBackAMD",
+				Value:        5083,
+				Capabilities: []string{"StencilExportEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "StencilRefLessBackAMD",
+				Value:        5084,
+				Capabilities: []string{"StencilExportEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "QuadDerivativesKHR",
+				Value:        5088,
+				Capabilities: []string{"QuadControlKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RequireFullQuadsKHR",
+				Value:        5089,
+				Capabilities: []string{"QuadControlKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SharesInputWithAMDX",
+				Value:        5102,
+				Capabilities: []string{"ShaderEnqueueAMDX",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Node Name'"},{OperandKindIdRef, "'Shader Index'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "OutputLinesEXT",
+				Value:        5269,
+				Capabilities: []string{"MeshShadingNV","MeshShadingEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "OutputPrimitivesEXT",
 				Value:        5270,
-				Capabilities: []string{"MeshShadingNV",},
+				Capabilities: []string{"MeshShadingNV","MeshShadingEXT",},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Primitive count'"},},
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "DerivativeGroupQuadsNV",
+				Enumerant:    "DerivativeGroupQuadsKHR",
 				Value:        5289,
-				Capabilities: []string{"ComputeDerivativeGroupQuadsNV",},
+				Capabilities: []string{"ComputeDerivativeGroupQuadsNV","ComputeDerivativeGroupQuadsKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "DerivativeGroupLinearNV",
+				Enumerant:    "DerivativeGroupLinearKHR",
 				Value:        5290,
-				Capabilities: []string{"ComputeDerivativeGroupLinearNV",},
+				Capabilities: []string{"ComputeDerivativeGroupLinearNV","ComputeDerivativeGroupLinearKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "OutputTrianglesNV",
+				Enumerant:    "OutputTrianglesEXT",
 				Value:        5298,
-				Capabilities: []string{"MeshShadingNV",},
+				Capabilities: []string{"MeshShadingNV","MeshShadingEXT",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
@@ -20943,6 +28347,132 @@
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
+			Enumerant{
+				Enumerant:    "SharedLocalMemorySizeINTEL",
+				Value:        5618,
+				Capabilities: []string{"VectorComputeINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Size'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RoundingModeRTPINTEL",
+				Value:        5620,
+				Capabilities: []string{"RoundToInfinityINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Target Width'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RoundingModeRTNINTEL",
+				Value:        5621,
+				Capabilities: []string{"RoundToInfinityINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Target Width'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FloatingPointModeALTINTEL",
+				Value:        5622,
+				Capabilities: []string{"RoundToInfinityINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Target Width'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FloatingPointModeIEEEINTEL",
+				Value:        5623,
+				Capabilities: []string{"RoundToInfinityINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Target Width'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MaxWorkgroupSizeINTEL",
+				Value:        5893,
+				Capabilities: []string{"KernelAttributesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'max_x_size'"},{OperandKindLiteralInteger, "'max_y_size'"},{OperandKindLiteralInteger, "'max_z_size'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MaxWorkDimINTEL",
+				Value:        5894,
+				Capabilities: []string{"KernelAttributesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'max_dimensions'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "NoGlobalOffsetINTEL",
+				Value:        5895,
+				Capabilities: []string{"KernelAttributesINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "NumSIMDWorkitemsINTEL",
+				Value:        5896,
+				Capabilities: []string{"FPGAKernelAttributesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'vector_width'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SchedulerTargetFmaxMhzINTEL",
+				Value:        5903,
+				Capabilities: []string{"FPGAKernelAttributesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'target_fmax'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MaximallyReconvergesKHR",
+				Value:        6023,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FPFastMathDefault",
+				Value:        6028,
+				Capabilities: []string{"FloatControls2",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Target Type'"},{OperandKindIdRef, "'Fast-Math Mode'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "StreamingInterfaceINTEL",
+				Value:        6154,
+				Capabilities: []string{"FPGAKernelAttributesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'StallFreeReturn'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RegisterMapInterfaceINTEL",
+				Value:        6160,
+				Capabilities: []string{"FPGAKernelAttributesv2INTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'WaitForDoneWrite'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "NamedBarrierCountINTEL",
+				Value:        6417,
+				Capabilities: []string{"VectorComputeINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Barrier Count'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MaximumRegistersINTEL",
+				Value:        6461,
+				Capabilities: []string{"RegisterLimitsINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Number of Registers'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MaximumRegistersIdINTEL",
+				Value:        6462,
+				Capabilities: []string{"RegisterLimitsINTEL",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Number of Registers'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "NamedMaximumRegistersINTEL",
+				Value:        6463,
+				Capabilities: []string{"RegisterLimitsINTEL",},
+				Parameters:   []Parameter{{OperandKindNamedMaximumNumberOfRegisters, "'Named Maximum Number of Registers'"},},
+				Version:      "None",
+			},
 		},
 		Bases:      []*OperandKind {},
 	}
@@ -20955,84 +28485,84 @@
 				Value:        0,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Input",
 				Value:        1,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Uniform",
 				Value:        2,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Output",
 				Value:        3,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Workgroup",
 				Value:        4,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "CrossWorkgroup",
 				Value:        5,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Private",
 				Value:        6,
-				Capabilities: []string{"Shader",},
+				Capabilities: []string{"Shader","VectorComputeINTEL",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Function",
 				Value:        7,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Generic",
 				Value:        8,
 				Capabilities: []string{"GenericPointer",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "PushConstant",
 				Value:        9,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "AtomicCounter",
 				Value:        10,
 				Capabilities: []string{"AtomicStorage",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Image",
 				Value:        11,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "StorageBuffer",
@@ -21042,86 +28572,58 @@
 				Version:      "1.3",
 			},
 			Enumerant{
-				Enumerant:    "CallableDataNV",
-				Value:        5328,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Enumerant:    "TileImageEXT",
+				Value:        4172,
+				Capabilities: []string{"TileImageColorReadAccessEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "NodePayloadAMDX",
+				Value:        5068,
+				Capabilities: []string{"ShaderEnqueueAMDX",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "CallableDataKHR",
 				Value:        5328,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "IncomingCallableDataNV",
-				Value:        5329,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "IncomingCallableDataKHR",
 				Value:        5329,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "RayPayloadNV",
-				Value:        5338,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "RayPayloadKHR",
 				Value:        5338,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "HitAttributeNV",
-				Value:        5339,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "HitAttributeKHR",
 				Value:        5339,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "IncomingRayPayloadNV",
-				Value:        5342,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "IncomingRayPayloadKHR",
 				Value:        5342,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "ShaderRecordBufferNV",
-				Value:        5343,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "ShaderRecordBufferKHR",
 				Value:        5343,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
@@ -21133,11 +28635,39 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "PhysicalStorageBufferEXT",
-				Value:        5349,
-				Capabilities: []string{"PhysicalStorageBufferAddresses",},
+				Enumerant:    "HitObjectAttributeNV",
+				Value:        5385,
+				Capabilities: []string{"ShaderInvocationReorderNV",},
 				Parameters:   []Parameter{},
-				Version:      "1.5",
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "TaskPayloadWorkgroupEXT",
+				Value:        5402,
+				Capabilities: []string{"MeshShadingEXT",},
+				Parameters:   []Parameter{},
+				Version:      "1.4",
+			},
+			Enumerant{
+				Enumerant:    "CodeSectionINTEL",
+				Value:        5605,
+				Capabilities: []string{"FunctionPointersINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "DeviceOnlyINTEL",
+				Value:        5936,
+				Capabilities: []string{"USMStorageClassesINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "HostOnlyINTEL",
+				Value:        5937,
+				Capabilities: []string{"USMStorageClassesINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -21149,51 +28679,58 @@
 			Enumerant{
 				Enumerant:    "1D",
 				Value:        0,
-				Capabilities: []string{"Sampled1D","Image1D",},
+				Capabilities: []string{"Sampled1D",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "2D",
 				Value:        1,
-				Capabilities: []string{"Shader","Kernel","ImageMSArray",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "3D",
 				Value:        2,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Cube",
 				Value:        3,
-				Capabilities: []string{"Shader","ImageCubeArray",},
+				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rect",
 				Value:        4,
-				Capabilities: []string{"SampledRect","ImageRect",},
+				Capabilities: []string{"SampledRect",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Buffer",
 				Value:        5,
-				Capabilities: []string{"SampledBuffer","ImageBuffer",},
+				Capabilities: []string{"SampledBuffer",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SubpassData",
 				Value:        6,
 				Capabilities: []string{"InputAttachment",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "TileImageDataEXT",
+				Value:        4173,
+				Capabilities: []string{"TileImageColorReadAccessEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -21205,37 +28742,37 @@
 			Enumerant{
 				Enumerant:    "None",
 				Value:        0,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ClampToEdge",
 				Value:        1,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Clamp",
 				Value:        2,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Repeat",
 				Value:        3,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "RepeatMirrored",
 				Value:        4,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -21247,16 +28784,16 @@
 			Enumerant{
 				Enumerant:    "Nearest",
 				Value:        0,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Linear",
 				Value:        1,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -21270,280 +28807,294 @@
 				Value:        0,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rgba32f",
 				Value:        1,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rgba16f",
 				Value:        2,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "R32f",
 				Value:        3,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rgba8",
 				Value:        4,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rgba8Snorm",
 				Value:        5,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rg32f",
 				Value:        6,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rg16f",
 				Value:        7,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "R11fG11fB10f",
 				Value:        8,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "R16f",
 				Value:        9,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rgba16",
 				Value:        10,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rgb10A2",
 				Value:        11,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rg16",
 				Value:        12,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rg8",
 				Value:        13,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "R16",
 				Value:        14,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "R8",
 				Value:        15,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rgba16Snorm",
 				Value:        16,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rg16Snorm",
 				Value:        17,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rg8Snorm",
 				Value:        18,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "R16Snorm",
 				Value:        19,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "R8Snorm",
 				Value:        20,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rgba32i",
 				Value:        21,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rgba16i",
 				Value:        22,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rgba8i",
 				Value:        23,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "R32i",
 				Value:        24,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rg32i",
 				Value:        25,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rg16i",
 				Value:        26,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rg8i",
 				Value:        27,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "R16i",
 				Value:        28,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "R8i",
 				Value:        29,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rgba32ui",
 				Value:        30,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rgba16ui",
 				Value:        31,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rgba8ui",
 				Value:        32,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "R32ui",
 				Value:        33,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rgb10a2ui",
 				Value:        34,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rg32ui",
 				Value:        35,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rg16ui",
 				Value:        36,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rg8ui",
 				Value:        37,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "R16ui",
 				Value:        38,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "R8ui",
 				Value:        39,
 				Capabilities: []string{"StorageImageExtendedFormats",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "R64ui",
+				Value:        40,
+				Capabilities: []string{"Int64ImageEXT",},
+				Parameters:   []Parameter{},
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "R64i",
+				Value:        41,
+				Capabilities: []string{"Int64ImageEXT",},
+				Parameters:   []Parameter{},
+				Version:      "1.0",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -21555,142 +29106,142 @@
 			Enumerant{
 				Enumerant:    "R",
 				Value:        0,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "A",
 				Value:        1,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "RG",
 				Value:        2,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "RA",
 				Value:        3,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "RGB",
 				Value:        4,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "RGBA",
 				Value:        5,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "BGRA",
 				Value:        6,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ARGB",
 				Value:        7,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Intensity",
 				Value:        8,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Luminance",
 				Value:        9,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Rx",
 				Value:        10,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "RGx",
 				Value:        11,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "RGBx",
 				Value:        12,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Depth",
 				Value:        13,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "DepthStencil",
 				Value:        14,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "sRGB",
 				Value:        15,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "sRGBx",
 				Value:        16,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "sRGBA",
 				Value:        17,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "sBGRA",
 				Value:        18,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ABGR",
 				Value:        19,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -21702,121 +29253,142 @@
 			Enumerant{
 				Enumerant:    "SnormInt8",
 				Value:        0,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SnormInt16",
 				Value:        1,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "UnormInt8",
 				Value:        2,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "UnormInt16",
 				Value:        3,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "UnormShort565",
 				Value:        4,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "UnormShort555",
 				Value:        5,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "UnormInt101010",
 				Value:        6,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SignedInt8",
 				Value:        7,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SignedInt16",
 				Value:        8,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SignedInt32",
 				Value:        9,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "UnsignedInt8",
 				Value:        10,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "UnsignedInt16",
 				Value:        11,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "UnsignedInt32",
 				Value:        12,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "HalfFloat",
 				Value:        13,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Float",
 				Value:        14,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "UnormInt24",
 				Value:        15,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "UnormInt101010_2",
 				Value:        16,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "UnsignedIntRaw10EXT",
+				Value:        19,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "UnsignedIntRaw12EXT",
+				Value:        20,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "UnormInt2_101010EXT",
+				Value:        21,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.0",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -21830,28 +29402,168 @@
 				Value:        0,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "RTZ",
 				Value:        1,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "RTP",
 				Value:        2,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "RTN",
 				Value:        3,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindFPDenormMode = &OperandKind {
+		Kind:       "FPDenormMode",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "Preserve",
+				Value:        0,
+				Capabilities: []string{"FunctionFloatControlINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FlushToZero",
+				Value:        1,
+				Capabilities: []string{"FunctionFloatControlINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindQuantizationModes = &OperandKind {
+		Kind:       "QuantizationModes",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "TRN",
+				Value:        0,
+				Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "TRN_ZERO",
+				Value:        1,
+				Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RND",
+				Value:        2,
+				Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RND_ZERO",
+				Value:        3,
+				Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RND_INF",
+				Value:        4,
+				Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RND_MIN_INF",
+				Value:        5,
+				Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RND_CONV",
+				Value:        6,
+				Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RND_CONV_ODD",
+				Value:        7,
+				Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindFPOperationMode = &OperandKind {
+		Kind:       "FPOperationMode",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "IEEE",
+				Value:        0,
+				Capabilities: []string{"FunctionFloatControlINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ALT",
+				Value:        1,
+				Capabilities: []string{"FunctionFloatControlINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindOverflowModes = &OperandKind {
+		Kind:       "OverflowModes",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "WRAP",
+				Value:        0,
+				Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SAT",
+				Value:        1,
+				Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SAT_ZERO",
+				Value:        2,
+				Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SAT_SYM",
+				Value:        3,
+				Capabilities: []string{"ArbitraryPrecisionFixedPointINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -21865,14 +29577,21 @@
 				Value:        0,
 				Capabilities: []string{"Linkage",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Import",
 				Value:        1,
 				Capabilities: []string{"Linkage",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "LinkOnceODR",
+				Value:        2,
+				Capabilities: []string{"Linkage",},
+				Parameters:   []Parameter{},
+				Version:      "None",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -21886,21 +29605,56 @@
 				Value:        0,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "WriteOnly",
 				Value:        1,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ReadWrite",
 				Value:        2,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindHostAccessQualifier = &OperandKind {
+		Kind:       "HostAccessQualifier",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "NoneINTEL",
+				Value:        0,
+				Capabilities: []string{"GlobalVariableHostAccessINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ReadINTEL",
+				Value:        1,
+				Capabilities: []string{"GlobalVariableHostAccessINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "WriteINTEL",
+				Value:        2,
+				Capabilities: []string{"GlobalVariableHostAccessINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ReadWriteINTEL",
+				Value:        3,
+				Capabilities: []string{"GlobalVariableHostAccessINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -21914,56 +29668,63 @@
 				Value:        0,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Sext",
 				Value:        1,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ByVal",
 				Value:        2,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Sret",
 				Value:        3,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "NoAlias",
 				Value:        4,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "NoCapture",
 				Value:        5,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "NoWrite",
 				Value:        6,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "NoReadWrite",
 				Value:        7,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "RuntimeAlignedINTEL",
+				Value:        5940,
+				Capabilities: []string{"RuntimeAlignedAttributeINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "1.0",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -21977,187 +29738,187 @@
 				Value:        0,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SpecId",
 				Value:        1,
 				Capabilities: []string{"Shader","Kernel",},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Specialization Constant ID'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Block",
 				Value:        2,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "BufferBlock",
 				Value:        3,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "RowMajor",
 				Value:        4,
 				Capabilities: []string{"Matrix",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ColMajor",
 				Value:        5,
 				Capabilities: []string{"Matrix",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ArrayStride",
 				Value:        6,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Array Stride'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "MatrixStride",
 				Value:        7,
 				Capabilities: []string{"Matrix",},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Matrix Stride'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "GLSLShared",
 				Value:        8,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "GLSLPacked",
 				Value:        9,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "CPacked",
 				Value:        10,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "BuiltIn",
 				Value:        11,
 				Capabilities: []string{},
 				Parameters:   []Parameter{{OperandKindBuiltIn, ""},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "NoPerspective",
 				Value:        13,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Flat",
 				Value:        14,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Patch",
 				Value:        15,
 				Capabilities: []string{"Tessellation",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Centroid",
 				Value:        16,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Sample",
 				Value:        17,
 				Capabilities: []string{"SampleRateShading",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Invariant",
 				Value:        18,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Restrict",
 				Value:        19,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Aliased",
 				Value:        20,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Volatile",
 				Value:        21,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Constant",
 				Value:        22,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Coherent",
 				Value:        23,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "NonWritable",
 				Value:        24,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "NonReadable",
 				Value:        25,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Uniform",
 				Value:        26,
-				Capabilities: []string{"Shader",},
+				Capabilities: []string{"Shader","UniformDecoration",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "UniformId",
 				Value:        27,
-				Capabilities: []string{"Shader",},
+				Capabilities: []string{"Shader","UniformDecoration",},
 				Parameters:   []Parameter{{OperandKindIdScope, "'Execution'"},},
 				Version:      "1.4",
 			},
@@ -22166,119 +29927,119 @@
 				Value:        28,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Stream",
 				Value:        29,
 				Capabilities: []string{"GeometryStreams",},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Stream Number'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Location",
 				Value:        30,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Location'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Component",
 				Value:        31,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Component'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Index",
 				Value:        32,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Index'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Binding",
 				Value:        33,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Binding Point'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "DescriptorSet",
 				Value:        34,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Descriptor Set'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Offset",
 				Value:        35,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Byte Offset'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "XfbBuffer",
 				Value:        36,
 				Capabilities: []string{"TransformFeedback",},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, "'XFB Buffer Number'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "XfbStride",
 				Value:        37,
 				Capabilities: []string{"TransformFeedback",},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, "'XFB Stride'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "FuncParamAttr",
 				Value:        38,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{{OperandKindFunctionParameterAttribute, "'Function Parameter Attribute'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "FPRoundingMode",
 				Value:        39,
 				Capabilities: []string{},
 				Parameters:   []Parameter{{OperandKindFPRoundingMode, "'Floating-Point Rounding Mode'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "FPFastMathMode",
 				Value:        40,
-				Capabilities: []string{"Kernel",},
+				Capabilities: []string{"Kernel","FloatControls2",},
 				Parameters:   []Parameter{{OperandKindFPFastMathMode, "'Fast-Math Mode'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "LinkageAttributes",
 				Value:        41,
 				Capabilities: []string{"Linkage",},
 				Parameters:   []Parameter{{OperandKindLiteralString, "'Name'"},{OperandKindLinkageType, "'Linkage Type'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "NoContraction",
 				Value:        42,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "InputAttachmentIndex",
 				Value:        43,
 				Capabilities: []string{"InputAttachment",},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Attachment Index'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Alignment",
 				Value:        44,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Alignment'"},},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "MaxByteOffset",
@@ -22316,6 +30077,27 @@
 				Version:      "1.4",
 			},
 			Enumerant{
+				Enumerant:    "WeightTextureQCOM",
+				Value:        4487,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BlockMatchTextureQCOM",
+				Value:        4488,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BlockMatchSamplerQCOM",
+				Value:        4499,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
 				Enumerant:    "ExplicitInterpAMD",
 				Value:        4999,
 				Capabilities: []string{},
@@ -22323,6 +30105,76 @@
 				Version:      "None",
 			},
 			Enumerant{
+				Enumerant:    "NodeSharesPayloadLimitsWithAMDX",
+				Value:        5019,
+				Capabilities: []string{"ShaderEnqueueAMDX",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Payload Type'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "NodeMaxPayloadsAMDX",
+				Value:        5020,
+				Capabilities: []string{"ShaderEnqueueAMDX",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Max number of payloads'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "TrackFinishWritingAMDX",
+				Value:        5078,
+				Capabilities: []string{"ShaderEnqueueAMDX",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PayloadNodeNameAMDX",
+				Value:        5091,
+				Capabilities: []string{"ShaderEnqueueAMDX",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Node Name'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PayloadNodeBaseIndexAMDX",
+				Value:        5098,
+				Capabilities: []string{"ShaderEnqueueAMDX",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Base Index'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PayloadNodeSparseArrayAMDX",
+				Value:        5099,
+				Capabilities: []string{"ShaderEnqueueAMDX",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PayloadNodeArraySizeAMDX",
+				Value:        5100,
+				Capabilities: []string{"ShaderEnqueueAMDX",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Array Size'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PayloadDispatchIndirectAMDX",
+				Value:        5105,
+				Capabilities: []string{"ShaderEnqueueAMDX",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ArrayStrideIdEXT",
+				Value:        5124,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Array Stride'"},},
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "OffsetIdEXT",
+				Value:        5125,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Byte Offset'"},},
+				Version:      "1.0",
+			},
+			Enumerant{
 				Enumerant:    "OverrideCoverageNV",
 				Value:        5248,
 				Capabilities: []string{"SampleMaskOverrideCoverageNV",},
@@ -22351,9 +30203,9 @@
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "PerPrimitiveNV",
+				Enumerant:    "PerPrimitiveEXT",
 				Value:        5271,
-				Capabilities: []string{"MeshShadingNV",},
+				Capabilities: []string{"MeshShadingNV","MeshShadingEXT",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
@@ -22367,14 +30219,14 @@
 			Enumerant{
 				Enumerant:    "PerTaskNV",
 				Value:        5273,
-				Capabilities: []string{"MeshShadingNV",},
+				Capabilities: []string{"MeshShadingNV","MeshShadingEXT",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "PerVertexNV",
+				Enumerant:    "PerVertexKHR",
 				Value:        5285,
-				Capabilities: []string{"FragmentBarycentricNV",},
+				Capabilities: []string{"FragmentBarycentricKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
@@ -22386,13 +30238,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "NonUniformEXT",
-				Value:        5300,
-				Capabilities: []string{"ShaderNonUniform",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "RestrictPointer",
 				Value:        5355,
 				Capabilities: []string{"PhysicalStorageBufferAddresses",},
@@ -22400,13 +30245,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "RestrictPointerEXT",
-				Value:        5355,
-				Capabilities: []string{"PhysicalStorageBufferAddresses",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "AliasedPointer",
 				Value:        5356,
 				Capabilities: []string{"PhysicalStorageBufferAddresses",},
@@ -22414,11 +30252,102 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "AliasedPointerEXT",
-				Value:        5356,
-				Capabilities: []string{"PhysicalStorageBufferAddresses",},
+				Enumerant:    "HitObjectShaderRecordBufferNV",
+				Value:        5386,
+				Capabilities: []string{"ShaderInvocationReorderNV",},
 				Parameters:   []Parameter{},
-				Version:      "1.5",
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BindlessSamplerNV",
+				Value:        5398,
+				Capabilities: []string{"BindlessTextureNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BindlessImageNV",
+				Value:        5399,
+				Capabilities: []string{"BindlessTextureNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BoundSamplerNV",
+				Value:        5400,
+				Capabilities: []string{"BindlessTextureNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BoundImageNV",
+				Value:        5401,
+				Capabilities: []string{"BindlessTextureNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SIMTCallINTEL",
+				Value:        5599,
+				Capabilities: []string{"VectorComputeINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'N'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ReferencedIndirectlyINTEL",
+				Value:        5602,
+				Capabilities: []string{"IndirectReferencesINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ClobberINTEL",
+				Value:        5607,
+				Capabilities: []string{"AsmINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralString, "'Register'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SideEffectsINTEL",
+				Value:        5608,
+				Capabilities: []string{"AsmINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "VectorComputeVariableINTEL",
+				Value:        5624,
+				Capabilities: []string{"VectorComputeINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FuncParamIOKindINTEL",
+				Value:        5625,
+				Capabilities: []string{"VectorComputeINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Kind'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "VectorComputeFunctionINTEL",
+				Value:        5626,
+				Capabilities: []string{"VectorComputeINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "StackCallINTEL",
+				Value:        5627,
+				Capabilities: []string{"VectorComputeINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "GlobalVariableOffsetINTEL",
+				Value:        5628,
+				Capabilities: []string{"VectorComputeINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Offset'"},},
+				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "CounterBuffer",
@@ -22428,13 +30357,6 @@
 				Version:      "1.4",
 			},
 			Enumerant{
-				Enumerant:    "HlslCounterBufferGOOGLE",
-				Value:        5634,
-				Capabilities: []string{},
-				Parameters:   []Parameter{{OperandKindIdRef, "'Counter Buffer'"},},
-				Version:      "None",
-			},
-			Enumerant{
 				Enumerant:    "UserSemantic",
 				Value:        5635,
 				Capabilities: []string{},
@@ -22442,19 +30364,383 @@
 				Version:      "1.4",
 			},
 			Enumerant{
-				Enumerant:    "HlslSemanticGOOGLE",
-				Value:        5635,
-				Capabilities: []string{},
-				Parameters:   []Parameter{{OperandKindLiteralString, "'Semantic'"},},
-				Version:      "None",
-			},
-			Enumerant{
 				Enumerant:    "UserTypeGOOGLE",
 				Value:        5636,
 				Capabilities: []string{},
 				Parameters:   []Parameter{{OperandKindLiteralString, "'User Type'"},},
 				Version:      "None",
 			},
+			Enumerant{
+				Enumerant:    "FunctionRoundingModeINTEL",
+				Value:        5822,
+				Capabilities: []string{"FunctionFloatControlINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Target Width'"},{OperandKindFPRoundingMode, "'FP Rounding Mode'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FunctionDenormModeINTEL",
+				Value:        5823,
+				Capabilities: []string{"FunctionFloatControlINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Target Width'"},{OperandKindFPDenormMode, "'FP Denorm Mode'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RegisterINTEL",
+				Value:        5825,
+				Capabilities: []string{"FPGAMemoryAttributesINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MemoryINTEL",
+				Value:        5826,
+				Capabilities: []string{"FPGAMemoryAttributesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralString, "'Memory Type'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "NumbanksINTEL",
+				Value:        5827,
+				Capabilities: []string{"FPGAMemoryAttributesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Banks'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BankwidthINTEL",
+				Value:        5828,
+				Capabilities: []string{"FPGAMemoryAttributesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Bank Width'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MaxPrivateCopiesINTEL",
+				Value:        5829,
+				Capabilities: []string{"FPGAMemoryAttributesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Maximum Copies'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SinglepumpINTEL",
+				Value:        5830,
+				Capabilities: []string{"FPGAMemoryAttributesINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "DoublepumpINTEL",
+				Value:        5831,
+				Capabilities: []string{"FPGAMemoryAttributesINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MaxReplicatesINTEL",
+				Value:        5832,
+				Capabilities: []string{"FPGAMemoryAttributesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Maximum Replicates'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SimpleDualPortINTEL",
+				Value:        5833,
+				Capabilities: []string{"FPGAMemoryAttributesINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MergeINTEL",
+				Value:        5834,
+				Capabilities: []string{"FPGAMemoryAttributesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralString, "'Merge Key'"},{OperandKindLiteralString, "'Merge Type'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BankBitsINTEL",
+				Value:        5835,
+				Capabilities: []string{"FPGAMemoryAttributesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Bank Bits'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ForcePow2DepthINTEL",
+				Value:        5836,
+				Capabilities: []string{"FPGAMemoryAttributesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Force Key'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "StridesizeINTEL",
+				Value:        5883,
+				Capabilities: []string{"FPGAMemoryAttributesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Stride Size'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "WordsizeINTEL",
+				Value:        5884,
+				Capabilities: []string{"FPGAMemoryAttributesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Word Size'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "TrueDualPortINTEL",
+				Value:        5885,
+				Capabilities: []string{"FPGAMemoryAttributesINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BurstCoalesceINTEL",
+				Value:        5899,
+				Capabilities: []string{"FPGAMemoryAccessesINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "CacheSizeINTEL",
+				Value:        5900,
+				Capabilities: []string{"FPGAMemoryAccessesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Cache Size in bytes'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "DontStaticallyCoalesceINTEL",
+				Value:        5901,
+				Capabilities: []string{"FPGAMemoryAccessesINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PrefetchINTEL",
+				Value:        5902,
+				Capabilities: []string{"FPGAMemoryAccessesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Prefetcher Size in bytes'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "StallEnableINTEL",
+				Value:        5905,
+				Capabilities: []string{"FPGAClusterAttributesINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FuseLoopsInFunctionINTEL",
+				Value:        5907,
+				Capabilities: []string{"LoopFuseINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MathOpDSPModeINTEL",
+				Value:        5909,
+				Capabilities: []string{"FPGADSPControlINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Mode'"},{OperandKindLiteralInteger, "'Propagate'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "AliasScopeINTEL",
+				Value:        5914,
+				Capabilities: []string{"MemoryAccessAliasingINTEL",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Aliasing Scopes List'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "NoAliasINTEL",
+				Value:        5915,
+				Capabilities: []string{"MemoryAccessAliasingINTEL",},
+				Parameters:   []Parameter{{OperandKindIdRef, "'Aliasing Scopes List'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "InitiationIntervalINTEL",
+				Value:        5917,
+				Capabilities: []string{"FPGAInvocationPipeliningAttributesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Cycles'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MaxConcurrencyINTEL",
+				Value:        5918,
+				Capabilities: []string{"FPGAInvocationPipeliningAttributesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Invocations'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PipelineEnableINTEL",
+				Value:        5919,
+				Capabilities: []string{"FPGAInvocationPipeliningAttributesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Enable'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BufferLocationINTEL",
+				Value:        5921,
+				Capabilities: []string{"FPGABufferLocationINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Buffer Location ID'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "IOPipeStorageINTEL",
+				Value:        5944,
+				Capabilities: []string{"IOPipesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'IO Pipe ID'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FunctionFloatingPointModeINTEL",
+				Value:        6080,
+				Capabilities: []string{"FunctionFloatControlINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Target Width'"},{OperandKindFPOperationMode, "'FP Operation Mode'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SingleElementVectorINTEL",
+				Value:        6085,
+				Capabilities: []string{"VectorComputeINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "VectorComputeCallableFunctionINTEL",
+				Value:        6087,
+				Capabilities: []string{"VectorComputeINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MediaBlockIOINTEL",
+				Value:        6140,
+				Capabilities: []string{"VectorComputeINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "StallFreeINTEL",
+				Value:        6151,
+				Capabilities: []string{"FPGAClusterAttributesV2INTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FPMaxErrorDecorationINTEL",
+				Value:        6170,
+				Capabilities: []string{"FPMaxErrorINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralFloat, "'Max Error'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "LatencyControlLabelINTEL",
+				Value:        6172,
+				Capabilities: []string{"FPGALatencyControlINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Latency Label'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "LatencyControlConstraintINTEL",
+				Value:        6173,
+				Capabilities: []string{"FPGALatencyControlINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Relative To'"},{OperandKindLiteralInteger, "'Control Type'"},{OperandKindLiteralInteger, "'Relative Cycle'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ConduitKernelArgumentINTEL",
+				Value:        6175,
+				Capabilities: []string{"FPGAArgumentInterfacesINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RegisterMapKernelArgumentINTEL",
+				Value:        6176,
+				Capabilities: []string{"FPGAArgumentInterfacesINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MMHostInterfaceAddressWidthINTEL",
+				Value:        6177,
+				Capabilities: []string{"FPGAArgumentInterfacesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'AddressWidth'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MMHostInterfaceDataWidthINTEL",
+				Value:        6178,
+				Capabilities: []string{"FPGAArgumentInterfacesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'DataWidth'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MMHostInterfaceLatencyINTEL",
+				Value:        6179,
+				Capabilities: []string{"FPGAArgumentInterfacesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Latency'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MMHostInterfaceReadWriteModeINTEL",
+				Value:        6180,
+				Capabilities: []string{"FPGAArgumentInterfacesINTEL",},
+				Parameters:   []Parameter{{OperandKindAccessQualifier, "'ReadWriteMode'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MMHostInterfaceMaxBurstINTEL",
+				Value:        6181,
+				Capabilities: []string{"FPGAArgumentInterfacesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'MaxBurstCount'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MMHostInterfaceWaitRequestINTEL",
+				Value:        6182,
+				Capabilities: []string{"FPGAArgumentInterfacesINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Waitrequest'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "StableKernelArgumentINTEL",
+				Value:        6183,
+				Capabilities: []string{"FPGAArgumentInterfacesINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "HostAccessINTEL",
+				Value:        6188,
+				Capabilities: []string{"GlobalVariableHostAccessINTEL",},
+				Parameters:   []Parameter{{OperandKindHostAccessQualifier, "'Access'"},{OperandKindLiteralString, "'Name'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "InitModeINTEL",
+				Value:        6190,
+				Capabilities: []string{"GlobalVariableFPGADecorationsINTEL",},
+				Parameters:   []Parameter{{OperandKindInitializationModeQualifier, "'Trigger'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ImplementInRegisterMapINTEL",
+				Value:        6191,
+				Capabilities: []string{"GlobalVariableFPGADecorationsINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "Value"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "CacheControlLoadINTEL",
+				Value:        6442,
+				Capabilities: []string{"CacheControlsINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Cache Level'"},{OperandKindLoadCacheControl, "'Cache Control'"},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "CacheControlStoreINTEL",
+				Value:        6443,
+				Capabilities: []string{"CacheControlsINTEL",},
+				Parameters:   []Parameter{{OperandKindLiteralInteger, "'Cache Level'"},{OperandKindStoreCacheControl, "'Cache Control'"},},
+				Version:      "None",
+			},
 		},
 		Bases:      []*OperandKind {},
 	}
@@ -22467,287 +30753,322 @@
 				Value:        0,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "PointSize",
 				Value:        1,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ClipDistance",
 				Value:        3,
 				Capabilities: []string{"ClipDistance",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "CullDistance",
 				Value:        4,
 				Capabilities: []string{"CullDistance",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "VertexId",
 				Value:        5,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "InstanceId",
 				Value:        6,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "PrimitiveId",
 				Value:        7,
-				Capabilities: []string{"Geometry","Tessellation","RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"Geometry","Tessellation","RayTracingNV","RayTracingKHR","MeshShadingNV","MeshShadingEXT",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "InvocationId",
 				Value:        8,
 				Capabilities: []string{"Geometry","Tessellation",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Layer",
 				Value:        9,
-				Capabilities: []string{"Geometry","ShaderLayer","ShaderViewportIndexLayerEXT",},
+				Capabilities: []string{"Geometry","ShaderLayer","ShaderViewportIndexLayerEXT","MeshShadingNV","MeshShadingEXT",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ViewportIndex",
 				Value:        10,
-				Capabilities: []string{"MultiViewport","ShaderViewportIndex","ShaderViewportIndexLayerEXT",},
+				Capabilities: []string{"MultiViewport","ShaderViewportIndex","ShaderViewportIndexLayerEXT","MeshShadingNV","MeshShadingEXT",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "TessLevelOuter",
 				Value:        11,
 				Capabilities: []string{"Tessellation",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "TessLevelInner",
 				Value:        12,
 				Capabilities: []string{"Tessellation",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "TessCoord",
 				Value:        13,
 				Capabilities: []string{"Tessellation",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "PatchVertices",
 				Value:        14,
 				Capabilities: []string{"Tessellation",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "FragCoord",
 				Value:        15,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "PointCoord",
 				Value:        16,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "FrontFacing",
 				Value:        17,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SampleId",
 				Value:        18,
 				Capabilities: []string{"SampleRateShading",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SamplePosition",
 				Value:        19,
 				Capabilities: []string{"SampleRateShading",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SampleMask",
 				Value:        20,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "FragDepth",
 				Value:        22,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "HelperInvocation",
 				Value:        23,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "NumWorkgroups",
 				Value:        24,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "WorkgroupSize",
 				Value:        25,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "WorkgroupId",
 				Value:        26,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "LocalInvocationId",
 				Value:        27,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "GlobalInvocationId",
 				Value:        28,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "LocalInvocationIndex",
 				Value:        29,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "WorkDim",
 				Value:        30,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "GlobalSize",
 				Value:        31,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "EnqueuedWorkgroupSize",
 				Value:        32,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "GlobalOffset",
 				Value:        33,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "GlobalLinearId",
 				Value:        34,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SubgroupSize",
 				Value:        36,
 				Capabilities: []string{"Kernel","GroupNonUniform","SubgroupBallotKHR",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SubgroupMaxSize",
 				Value:        37,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "NumSubgroups",
 				Value:        38,
 				Capabilities: []string{"Kernel","GroupNonUniform",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "NumEnqueuedSubgroups",
 				Value:        39,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SubgroupId",
 				Value:        40,
 				Capabilities: []string{"Kernel","GroupNonUniform",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SubgroupLocalInvocationId",
 				Value:        41,
 				Capabilities: []string{"Kernel","GroupNonUniform","SubgroupBallotKHR",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "VertexIndex",
 				Value:        42,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "InstanceIndex",
 				Value:        43,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "CoreIDARM",
+				Value:        4160,
+				Capabilities: []string{"CoreBuiltinsARM",},
+				Parameters:   []Parameter{},
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "CoreCountARM",
+				Value:        4161,
+				Capabilities: []string{"CoreBuiltinsARM",},
+				Parameters:   []Parameter{},
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "CoreMaxIDARM",
+				Value:        4162,
+				Capabilities: []string{"CoreBuiltinsARM",},
+				Parameters:   []Parameter{},
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "WarpIDARM",
+				Value:        4163,
+				Capabilities: []string{"CoreBuiltinsARM",},
+				Parameters:   []Parameter{},
+				Version:      "1.0",
+			},
+			Enumerant{
+				Enumerant:    "WarpMaxIDARM",
+				Value:        4164,
+				Capabilities: []string{"CoreBuiltinsARM",},
+				Parameters:   []Parameter{},
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SubgroupEqMask",
@@ -22785,41 +31106,6 @@
 				Version:      "1.3",
 			},
 			Enumerant{
-				Enumerant:    "SubgroupEqMaskKHR",
-				Value:        4416,
-				Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",},
-				Parameters:   []Parameter{},
-				Version:      "1.3",
-			},
-			Enumerant{
-				Enumerant:    "SubgroupGeMaskKHR",
-				Value:        4417,
-				Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",},
-				Parameters:   []Parameter{},
-				Version:      "1.3",
-			},
-			Enumerant{
-				Enumerant:    "SubgroupGtMaskKHR",
-				Value:        4418,
-				Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",},
-				Parameters:   []Parameter{},
-				Version:      "1.3",
-			},
-			Enumerant{
-				Enumerant:    "SubgroupLeMaskKHR",
-				Value:        4419,
-				Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",},
-				Parameters:   []Parameter{},
-				Version:      "1.3",
-			},
-			Enumerant{
-				Enumerant:    "SubgroupLtMaskKHR",
-				Value:        4420,
-				Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",},
-				Parameters:   []Parameter{},
-				Version:      "1.3",
-			},
-			Enumerant{
 				Enumerant:    "BaseVertex",
 				Value:        4424,
 				Capabilities: []string{"DrawParameters",},
@@ -22836,11 +31122,18 @@
 			Enumerant{
 				Enumerant:    "DrawIndex",
 				Value:        4426,
-				Capabilities: []string{"DrawParameters","MeshShadingNV",},
+				Capabilities: []string{"DrawParameters","MeshShadingNV","MeshShadingEXT",},
 				Parameters:   []Parameter{},
 				Version:      "1.3",
 			},
 			Enumerant{
+				Enumerant:    "PrimitiveShadingRateKHR",
+				Value:        4432,
+				Capabilities: []string{"FragmentShadingRateKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
 				Enumerant:    "DeviceIndex",
 				Value:        4438,
 				Capabilities: []string{"DeviceGroup",},
@@ -22855,6 +31148,13 @@
 				Version:      "1.3",
 			},
 			Enumerant{
+				Enumerant:    "ShadingRateKHR",
+				Value:        4444,
+				Capabilities: []string{"FragmentShadingRateKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
 				Enumerant:    "BaryCoordNoPerspAMD",
 				Value:        4992,
 				Capabilities: []string{},
@@ -22911,6 +31211,20 @@
 				Version:      "None",
 			},
 			Enumerant{
+				Enumerant:    "RemainingRecursionLevelsAMDX",
+				Value:        5021,
+				Capabilities: []string{"ShaderEnqueueAMDX",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ShaderIndexAMDX",
+				Value:        5073,
+				Capabilities: []string{"ShaderEnqueueAMDX",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
 				Enumerant:    "ViewportMaskNV",
 				Value:        5253,
 				Capabilities: []string{"ShaderViewportMaskNV","MeshShadingNV",},
@@ -23009,247 +31323,191 @@
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "BaryCoordNV",
+				Enumerant:    "BaryCoordKHR",
 				Value:        5286,
-				Capabilities: []string{"FragmentBarycentricNV",},
+				Capabilities: []string{"FragmentBarycentricKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "BaryCoordNoPerspNV",
+				Enumerant:    "BaryCoordNoPerspKHR",
 				Value:        5287,
-				Capabilities: []string{"FragmentBarycentricNV",},
+				Capabilities: []string{"FragmentBarycentricKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "FragSizeEXT",
 				Value:        5292,
-				Capabilities: []string{"FragmentDensityEXT","ShadingRateNV",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "FragmentSizeNV",
-				Value:        5292,
-				Capabilities: []string{"ShadingRateNV","FragmentDensityEXT",},
+				Capabilities: []string{"FragmentDensityEXT",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "FragInvocationCountEXT",
 				Value:        5293,
-				Capabilities: []string{"FragmentDensityEXT","ShadingRateNV",},
+				Capabilities: []string{"FragmentDensityEXT",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "InvocationsPerPixelNV",
-				Value:        5293,
-				Capabilities: []string{"ShadingRateNV","FragmentDensityEXT",},
+				Enumerant:    "PrimitivePointIndicesEXT",
+				Value:        5294,
+				Capabilities: []string{"MeshShadingEXT",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "LaunchIdNV",
-				Value:        5319,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Enumerant:    "PrimitiveLineIndicesEXT",
+				Value:        5295,
+				Capabilities: []string{"MeshShadingEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "PrimitiveTriangleIndicesEXT",
+				Value:        5296,
+				Capabilities: []string{"MeshShadingEXT",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "CullPrimitiveEXT",
+				Value:        5299,
+				Capabilities: []string{"MeshShadingEXT",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "LaunchIdKHR",
 				Value:        5319,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "LaunchSizeNV",
-				Value:        5320,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "LaunchSizeKHR",
 				Value:        5320,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "WorldRayOriginNV",
-				Value:        5321,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "WorldRayOriginKHR",
 				Value:        5321,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "WorldRayDirectionNV",
-				Value:        5322,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "WorldRayDirectionKHR",
 				Value:        5322,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "ObjectRayOriginNV",
-				Value:        5323,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "ObjectRayOriginKHR",
 				Value:        5323,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "ObjectRayDirectionNV",
-				Value:        5324,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "ObjectRayDirectionKHR",
 				Value:        5324,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "RayTminNV",
-				Value:        5325,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "RayTminKHR",
 				Value:        5325,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "RayTmaxNV",
-				Value:        5326,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "RayTmaxKHR",
 				Value:        5326,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "InstanceCustomIndexNV",
-				Value:        5327,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "InstanceCustomIndexKHR",
 				Value:        5327,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "ObjectToWorldNV",
-				Value:        5330,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "ObjectToWorldKHR",
 				Value:        5330,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "WorldToObjectNV",
-				Value:        5331,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "WorldToObjectKHR",
 				Value:        5331,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "HitTNV",
 				Value:        5332,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "HitTKHR",
-				Value:        5332,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "HitKindNV",
-				Value:        5333,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "HitKindKHR",
 				Value:        5333,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "IncomingRayFlagsNV",
-				Value:        5351,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Enumerant:    "CurrentRayTimeNV",
+				Value:        5334,
+				Capabilities: []string{"RayTracingMotionBlurNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "HitTriangleVertexPositionsKHR",
+				Value:        5335,
+				Capabilities: []string{"RayTracingPositionFetchKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "HitMicroTriangleVertexPositionsNV",
+				Value:        5337,
+				Capabilities: []string{"RayTracingDisplacementMicromapNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "HitMicroTriangleVertexBarycentricsNV",
+				Value:        5344,
+				Capabilities: []string{"RayTracingDisplacementMicromapNV",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "IncomingRayFlagsKHR",
 				Value:        5351,
-				Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingNV","RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "RayGeometryIndexKHR",
 				Value:        5352,
-				Capabilities: []string{"RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingKHR",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
@@ -23281,6 +31539,27 @@
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
+			Enumerant{
+				Enumerant:    "HitKindFrontFacingMicroTriangleNV",
+				Value:        5405,
+				Capabilities: []string{"RayTracingDisplacementMicromapNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "HitKindBackFacingMicroTriangleNV",
+				Value:        5406,
+				Capabilities: []string{"RayTracingDisplacementMicromapNV",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "CullMaskKHR",
+				Value:        6021,
+				Capabilities: []string{"RayCullMaskKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
 		},
 		Bases:      []*OperandKind {},
 	}
@@ -23293,35 +31572,35 @@
 				Value:        0,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Device",
 				Value:        1,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Workgroup",
 				Value:        2,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Subgroup",
 				Value:        3,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Invocation",
 				Value:        4,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "QueueFamily",
@@ -23331,18 +31610,11 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "QueueFamilyKHR",
-				Value:        5,
-				Capabilities: []string{"VulkanMemoryModel",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "ShaderCallKHR",
 				Value:        6,
-				Capabilities: []string{"RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayTracingKHR",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "None",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -23356,21 +31628,21 @@
 				Value:        0,
 				Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "InclusiveScan",
 				Value:        1,
 				Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ExclusiveScan",
 				Value:        2,
 				Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ClusteredReduce",
@@ -23412,21 +31684,21 @@
 				Value:        0,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "WaitKernel",
 				Value:        1,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "WaitWorkGroup",
 				Value:        2,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -23440,392 +31712,392 @@
 				Value:        0,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Shader",
 				Value:        1,
 				Capabilities: []string{"Matrix",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Geometry",
 				Value:        2,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Tessellation",
 				Value:        3,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Addresses",
 				Value:        4,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Linkage",
 				Value:        5,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Kernel",
 				Value:        6,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Vector16",
 				Value:        7,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Float16Buffer",
 				Value:        8,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Float16",
 				Value:        9,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Float64",
 				Value:        10,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Int64",
 				Value:        11,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Int64Atomics",
 				Value:        12,
 				Capabilities: []string{"Int64",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ImageBasic",
 				Value:        13,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ImageReadWrite",
 				Value:        14,
 				Capabilities: []string{"ImageBasic",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ImageMipmap",
 				Value:        15,
 				Capabilities: []string{"ImageBasic",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Pipes",
 				Value:        17,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Groups",
 				Value:        18,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "DeviceEnqueue",
 				Value:        19,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "LiteralSampler",
 				Value:        20,
 				Capabilities: []string{"Kernel",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "AtomicStorage",
 				Value:        21,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Int16",
 				Value:        22,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "TessellationPointSize",
 				Value:        23,
 				Capabilities: []string{"Tessellation",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "GeometryPointSize",
 				Value:        24,
 				Capabilities: []string{"Geometry",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ImageGatherExtended",
 				Value:        25,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "StorageImageMultisample",
 				Value:        27,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "UniformBufferArrayDynamicIndexing",
 				Value:        28,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SampledImageArrayDynamicIndexing",
 				Value:        29,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "StorageBufferArrayDynamicIndexing",
 				Value:        30,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "StorageImageArrayDynamicIndexing",
 				Value:        31,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ClipDistance",
 				Value:        32,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "CullDistance",
 				Value:        33,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ImageCubeArray",
 				Value:        34,
 				Capabilities: []string{"SampledCubeArray",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SampleRateShading",
 				Value:        35,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ImageRect",
 				Value:        36,
 				Capabilities: []string{"SampledRect",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SampledRect",
 				Value:        37,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "GenericPointer",
 				Value:        38,
 				Capabilities: []string{"Addresses",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Int8",
 				Value:        39,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "InputAttachment",
 				Value:        40,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SparseResidency",
 				Value:        41,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "MinLod",
 				Value:        42,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Sampled1D",
 				Value:        43,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "Image1D",
 				Value:        44,
 				Capabilities: []string{"Sampled1D",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SampledCubeArray",
 				Value:        45,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SampledBuffer",
 				Value:        46,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ImageBuffer",
 				Value:        47,
 				Capabilities: []string{"SampledBuffer",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ImageMSArray",
 				Value:        48,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "StorageImageExtendedFormats",
 				Value:        49,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "ImageQuery",
 				Value:        50,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "DerivativeControl",
 				Value:        51,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "InterpolationFunction",
 				Value:        52,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "TransformFeedback",
 				Value:        53,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "GeometryStreams",
 				Value:        54,
 				Capabilities: []string{"Geometry",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "StorageImageReadWithoutFormat",
 				Value:        55,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "StorageImageWriteWithoutFormat",
 				Value:        56,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "MultiViewport",
 				Value:        57,
 				Capabilities: []string{"Geometry",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "1.0",
 			},
 			Enumerant{
 				Enumerant:    "SubgroupDispatch",
@@ -23919,6 +32191,55 @@
 				Version:      "1.5",
 			},
 			Enumerant{
+				Enumerant:    "UniformDecoration",
+				Value:        71,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.6",
+			},
+			Enumerant{
+				Enumerant:    "CoreBuiltinsARM",
+				Value:        4165,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "TileImageColorReadAccessEXT",
+				Value:        4166,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "TileImageDepthReadAccessEXT",
+				Value:        4167,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "TileImageStencilReadAccessEXT",
+				Value:        4168,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "CooperativeMatrixLayoutsARM",
+				Value:        4201,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FragmentShadingRateKHR",
+				Value:        4422,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
 				Enumerant:    "SubgroupBallotKHR",
 				Value:        4423,
 				Capabilities: []string{},
@@ -23933,6 +32254,27 @@
 				Version:      "1.3",
 			},
 			Enumerant{
+				Enumerant:    "WorkgroupMemoryExplicitLayoutKHR",
+				Value:        4428,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "WorkgroupMemoryExplicitLayout8BitAccessKHR",
+				Value:        4429,
+				Capabilities: []string{"WorkgroupMemoryExplicitLayoutKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "WorkgroupMemoryExplicitLayout16BitAccessKHR",
+				Value:        4430,
+				Capabilities: []string{"WorkgroupMemoryExplicitLayoutKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
 				Enumerant:    "SubgroupVoteKHR",
 				Value:        4431,
 				Capabilities: []string{},
@@ -23947,23 +32289,9 @@
 				Version:      "1.3",
 			},
 			Enumerant{
-				Enumerant:    "StorageUniformBufferBlock16",
-				Value:        4433,
-				Capabilities: []string{},
-				Parameters:   []Parameter{},
-				Version:      "1.3",
-			},
-			Enumerant{
 				Enumerant:    "UniformAndStorageBuffer16BitAccess",
 				Value:        4434,
-				Capabilities: []string{"StorageBuffer16BitAccess","StorageUniformBufferBlock16",},
-				Parameters:   []Parameter{},
-				Version:      "1.3",
-			},
-			Enumerant{
-				Enumerant:    "StorageUniform16",
-				Value:        4434,
-				Capabilities: []string{"StorageBuffer16BitAccess","StorageUniformBufferBlock16",},
+				Capabilities: []string{"StorageBuffer16BitAccess",},
 				Parameters:   []Parameter{},
 				Version:      "1.3",
 			},
@@ -24087,9 +32415,58 @@
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "RayTraversalPrimitiveCullingProvisionalKHR",
+				Enumerant:    "RayQueryKHR",
+				Value:        4472,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "UntypedPointersKHR",
+				Value:        4473,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RayTraversalPrimitiveCullingKHR",
 				Value:        4478,
-				Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",},
+				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RayTracingKHR",
+				Value:        4479,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "TextureSampleWeightedQCOM",
+				Value:        4484,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "TextureBoxFilterQCOM",
+				Value:        4485,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "TextureBlockMatchQCOM",
+				Value:        4486,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "TextureBlockMatch2QCOM",
+				Value:        4498,
+				Capabilities: []string{},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
@@ -24129,13 +32506,34 @@
 				Version:      "None",
 			},
 			Enumerant{
+				Enumerant:    "Int64ImageEXT",
+				Value:        5016,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
 				Enumerant:    "ShaderClockKHR",
 				Value:        5055,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ShaderEnqueueAMDX",
+				Value:        5067,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
+				Enumerant:    "QuadControlKHR",
+				Value:        5087,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
 				Enumerant:    "SampleMaskOverrideCoverageNV",
 				Value:        5249,
 				Capabilities: []string{"SampleRateShading",},
@@ -24157,16 +32555,9 @@
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "ShaderViewportIndexLayerNV",
-				Value:        5254,
-				Capabilities: []string{"MultiViewport",},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
 				Enumerant:    "ShaderViewportMaskNV",
 				Value:        5255,
-				Capabilities: []string{"ShaderViewportIndexLayerNV",},
+				Capabilities: []string{"ShaderViewportIndexLayerEXT",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
@@ -24206,28 +32597,28 @@
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "FragmentBarycentricNV",
+				Enumerant:    "MeshShadingEXT",
+				Value:        5283,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FragmentBarycentricKHR",
 				Value:        5284,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "ComputeDerivativeGroupQuadsNV",
+				Enumerant:    "ComputeDerivativeGroupQuadsKHR",
 				Value:        5288,
-				Capabilities: []string{},
-				Parameters:   []Parameter{},
-				Version:      "None",
-			},
-			Enumerant{
-				Enumerant:    "FragmentDensityEXT",
-				Value:        5291,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "ShadingRateNV",
+				Enumerant:    "FragmentDensityEXT",
 				Value:        5291,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
@@ -24248,13 +32639,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "ShaderNonUniformEXT",
-				Value:        5301,
-				Capabilities: []string{"Shader",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "RuntimeDescriptorArray",
 				Value:        5302,
 				Capabilities: []string{"Shader",},
@@ -24262,13 +32646,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "RuntimeDescriptorArrayEXT",
-				Value:        5302,
-				Capabilities: []string{"Shader",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "InputAttachmentArrayDynamicIndexing",
 				Value:        5303,
 				Capabilities: []string{"InputAttachment",},
@@ -24276,13 +32653,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "InputAttachmentArrayDynamicIndexingEXT",
-				Value:        5303,
-				Capabilities: []string{"InputAttachment",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "UniformTexelBufferArrayDynamicIndexing",
 				Value:        5304,
 				Capabilities: []string{"SampledBuffer",},
@@ -24290,13 +32660,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "UniformTexelBufferArrayDynamicIndexingEXT",
-				Value:        5304,
-				Capabilities: []string{"SampledBuffer",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "StorageTexelBufferArrayDynamicIndexing",
 				Value:        5305,
 				Capabilities: []string{"ImageBuffer",},
@@ -24304,13 +32667,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "StorageTexelBufferArrayDynamicIndexingEXT",
-				Value:        5305,
-				Capabilities: []string{"ImageBuffer",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "UniformBufferArrayNonUniformIndexing",
 				Value:        5306,
 				Capabilities: []string{"ShaderNonUniform",},
@@ -24318,13 +32674,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "UniformBufferArrayNonUniformIndexingEXT",
-				Value:        5306,
-				Capabilities: []string{"ShaderNonUniform",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "SampledImageArrayNonUniformIndexing",
 				Value:        5307,
 				Capabilities: []string{"ShaderNonUniform",},
@@ -24332,13 +32681,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "SampledImageArrayNonUniformIndexingEXT",
-				Value:        5307,
-				Capabilities: []string{"ShaderNonUniform",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "StorageBufferArrayNonUniformIndexing",
 				Value:        5308,
 				Capabilities: []string{"ShaderNonUniform",},
@@ -24346,13 +32688,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "StorageBufferArrayNonUniformIndexingEXT",
-				Value:        5308,
-				Capabilities: []string{"ShaderNonUniform",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "StorageImageArrayNonUniformIndexing",
 				Value:        5309,
 				Capabilities: []string{"ShaderNonUniform",},
@@ -24360,13 +32695,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "StorageImageArrayNonUniformIndexingEXT",
-				Value:        5309,
-				Capabilities: []string{"ShaderNonUniform",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "InputAttachmentArrayNonUniformIndexing",
 				Value:        5310,
 				Capabilities: []string{"InputAttachment","ShaderNonUniform",},
@@ -24374,13 +32702,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "InputAttachmentArrayNonUniformIndexingEXT",
-				Value:        5310,
-				Capabilities: []string{"InputAttachment","ShaderNonUniform",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "UniformTexelBufferArrayNonUniformIndexing",
 				Value:        5311,
 				Capabilities: []string{"SampledBuffer","ShaderNonUniform",},
@@ -24388,13 +32709,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "UniformTexelBufferArrayNonUniformIndexingEXT",
-				Value:        5311,
-				Capabilities: []string{"SampledBuffer","ShaderNonUniform",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "StorageTexelBufferArrayNonUniformIndexing",
 				Value:        5312,
 				Capabilities: []string{"ImageBuffer","ShaderNonUniform",},
@@ -24402,11 +32716,11 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "StorageTexelBufferArrayNonUniformIndexingEXT",
-				Value:        5312,
-				Capabilities: []string{"ImageBuffer","ShaderNonUniform",},
+				Enumerant:    "RayTracingPositionFetchKHR",
+				Value:        5336,
+				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "1.5",
+				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "RayTracingNV",
@@ -24416,14 +32730,14 @@
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "VulkanMemoryModel",
-				Value:        5345,
-				Capabilities: []string{},
+				Enumerant:    "RayTracingMotionBlurNV",
+				Value:        5341,
+				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
-				Version:      "1.5",
+				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "VulkanMemoryModelKHR",
+				Enumerant:    "VulkanMemoryModel",
 				Value:        5345,
 				Capabilities: []string{},
 				Parameters:   []Parameter{},
@@ -24437,13 +32751,6 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "VulkanMemoryModelDeviceScopeKHR",
-				Value:        5346,
-				Capabilities: []string{},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
 				Enumerant:    "PhysicalStorageBufferAddresses",
 				Value:        5347,
 				Capabilities: []string{"Shader",},
@@ -24451,16 +32758,9 @@
 				Version:      "1.5",
 			},
 			Enumerant{
-				Enumerant:    "PhysicalStorageBufferAddressesEXT",
-				Value:        5347,
-				Capabilities: []string{"Shader",},
-				Parameters:   []Parameter{},
-				Version:      "1.5",
-			},
-			Enumerant{
-				Enumerant:    "ComputeDerivativeGroupLinearNV",
+				Enumerant:    "ComputeDerivativeGroupLinearKHR",
 				Value:        5350,
-				Capabilities: []string{},
+				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
@@ -24507,10 +32807,108 @@
 				Version:      "None",
 			},
 			Enumerant{
-				Enumerant:    "DemoteToHelperInvocationEXT",
+				Enumerant:    "DemoteToHelperInvocation",
 				Value:        5379,
 				Capabilities: []string{"Shader",},
 				Parameters:   []Parameter{},
+				Version:      "1.6",
+			},
+			Enumerant{
+				Enumerant:    "DisplacementMicromapNV",
+				Value:        5380,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RayTracingOpacityMicromapEXT",
+				Value:        5381,
+				Capabilities: []string{"RayQueryKHR","RayTracingKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ShaderInvocationReorderNV",
+				Value:        5383,
+				Capabilities: []string{"RayTracingKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BindlessTextureNV",
+				Value:        5390,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RayQueryPositionFetchKHR",
+				Value:        5391,
+				Capabilities: []string{"Shader",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "AtomicFloat16VectorNV",
+				Value:        5404,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RayTracingDisplacementMicromapNV",
+				Value:        5409,
+				Capabilities: []string{"RayTracingKHR",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RawAccessChainsNV",
+				Value:        5414,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "CooperativeMatrixReductionsNV",
+				Value:        5430,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "CooperativeMatrixConversionsNV",
+				Value:        5431,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "CooperativeMatrixPerElementOperationsNV",
+				Value:        5432,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "CooperativeMatrixTensorAddressingNV",
+				Value:        5433,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "CooperativeMatrixBlockLoadsNV",
+				Value:        5434,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "TensorAddressingNV",
+				Value:        5439,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
 				Version:      "None",
 			},
 			Enumerant{
@@ -24542,6 +32940,20 @@
 				Version:      "None",
 			},
 			Enumerant{
+				Enumerant:    "RoundToInfinityINTEL",
+				Value:        5582,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FloatingPointModeINTEL",
+				Value:        5583,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
 				Enumerant:    "IntegerFunctions2INTEL",
 				Value:        5584,
 				Capabilities: []string{"Shader",},
@@ -24549,6 +32961,69 @@
 				Version:      "None",
 			},
 			Enumerant{
+				Enumerant:    "FunctionPointersINTEL",
+				Value:        5603,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "IndirectReferencesINTEL",
+				Value:        5604,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "AsmINTEL",
+				Value:        5606,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "AtomicFloat32MinMaxEXT",
+				Value:        5612,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "AtomicFloat64MinMaxEXT",
+				Value:        5613,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "AtomicFloat16MinMaxEXT",
+				Value:        5616,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "VectorComputeINTEL",
+				Value:        5617,
+				Capabilities: []string{"VectorAnyINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "VectorAnyINTEL",
+				Value:        5619,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ExpectAssumeKHR",
+				Value:        5629,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
 				Enumerant:    "SubgroupAvcMotionEstimationINTEL",
 				Value:        5696,
 				Capabilities: []string{},
@@ -24569,6 +33044,384 @@
 				Parameters:   []Parameter{},
 				Version:      "None",
 			},
+			Enumerant{
+				Enumerant:    "VariableLengthArrayINTEL",
+				Value:        5817,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FunctionFloatControlINTEL",
+				Value:        5821,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FPGAMemoryAttributesINTEL",
+				Value:        5824,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FPFastMathModeINTEL",
+				Value:        5837,
+				Capabilities: []string{"Kernel",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ArbitraryPrecisionIntegersINTEL",
+				Value:        5844,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ArbitraryPrecisionFloatingPointINTEL",
+				Value:        5845,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "UnstructuredLoopControlsINTEL",
+				Value:        5886,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FPGALoopControlsINTEL",
+				Value:        5888,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "KernelAttributesINTEL",
+				Value:        5892,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FPGAKernelAttributesINTEL",
+				Value:        5897,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FPGAMemoryAccessesINTEL",
+				Value:        5898,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FPGAClusterAttributesINTEL",
+				Value:        5904,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "LoopFuseINTEL",
+				Value:        5906,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FPGADSPControlINTEL",
+				Value:        5908,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MemoryAccessAliasingINTEL",
+				Value:        5910,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FPGAInvocationPipeliningAttributesINTEL",
+				Value:        5916,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FPGABufferLocationINTEL",
+				Value:        5920,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ArbitraryPrecisionFixedPointINTEL",
+				Value:        5922,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "USMStorageClassesINTEL",
+				Value:        5935,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RuntimeAlignedAttributeINTEL",
+				Value:        5939,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "IOPipesINTEL",
+				Value:        5943,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BlockingPipesINTEL",
+				Value:        5945,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FPGARegINTEL",
+				Value:        5948,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "DotProductInputAll",
+				Value:        6016,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.6",
+			},
+			Enumerant{
+				Enumerant:    "DotProductInput4x8Bit",
+				Value:        6017,
+				Capabilities: []string{"Int8",},
+				Parameters:   []Parameter{},
+				Version:      "1.6",
+			},
+			Enumerant{
+				Enumerant:    "DotProductInput4x8BitPacked",
+				Value:        6018,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.6",
+			},
+			Enumerant{
+				Enumerant:    "DotProduct",
+				Value:        6019,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.6",
+			},
+			Enumerant{
+				Enumerant:    "RayCullMaskKHR",
+				Value:        6020,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "CooperativeMatrixKHR",
+				Value:        6022,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ReplicatedCompositesEXT",
+				Value:        6024,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BitInstructions",
+				Value:        6025,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "GroupNonUniformRotateKHR",
+				Value:        6026,
+				Capabilities: []string{"GroupNonUniform",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FloatControls2",
+				Value:        6029,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "AtomicFloat32AddEXT",
+				Value:        6033,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "AtomicFloat64AddEXT",
+				Value:        6034,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "LongCompositesINTEL",
+				Value:        6089,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "OptNoneEXT",
+				Value:        6094,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "AtomicFloat16AddEXT",
+				Value:        6095,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "DebugInfoModuleINTEL",
+				Value:        6114,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "BFloat16ConversionINTEL",
+				Value:        6115,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SplitBarrierINTEL",
+				Value:        6141,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ArithmeticFenceEXT",
+				Value:        6144,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FPGAClusterAttributesV2INTEL",
+				Value:        6150,
+				Capabilities: []string{"FPGAClusterAttributesINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FPGAKernelAttributesv2INTEL",
+				Value:        6161,
+				Capabilities: []string{"FPGAKernelAttributesINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FPMaxErrorINTEL",
+				Value:        6169,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FPGALatencyControlINTEL",
+				Value:        6171,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "FPGAArgumentInterfacesINTEL",
+				Value:        6174,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "GlobalVariableHostAccessINTEL",
+				Value:        6187,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "GlobalVariableFPGADecorationsINTEL",
+				Value:        6189,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SubgroupBufferPrefetchINTEL",
+				Value:        6220,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "GroupUniformArithmeticKHR",
+				Value:        6400,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MaskedGatherScatterINTEL",
+				Value:        6427,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "CacheControlsINTEL",
+				Value:        6441,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RegisterLimitsINTEL",
+				Value:        6460,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
 		},
 		Bases:      []*OperandKind {},
 	}
@@ -24579,16 +33432,16 @@
 			Enumerant{
 				Enumerant:    "RayQueryCandidateIntersectionKHR",
 				Value:        0,
-				Capabilities: []string{"RayQueryProvisionalKHR",},
+				Capabilities: []string{"RayQueryKHR",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "RayQueryCommittedIntersectionKHR",
 				Value:        1,
-				Capabilities: []string{"RayQueryProvisionalKHR",},
+				Capabilities: []string{"RayQueryKHR",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "None",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -24600,23 +33453,23 @@
 			Enumerant{
 				Enumerant:    "RayQueryCommittedIntersectionNoneKHR",
 				Value:        0,
-				Capabilities: []string{"RayQueryProvisionalKHR",},
+				Capabilities: []string{"RayQueryKHR",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "RayQueryCommittedIntersectionTriangleKHR",
 				Value:        1,
-				Capabilities: []string{"RayQueryProvisionalKHR",},
+				Capabilities: []string{"RayQueryKHR",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "RayQueryCommittedIntersectionGeneratedKHR",
 				Value:        2,
-				Capabilities: []string{"RayQueryProvisionalKHR",},
+				Capabilities: []string{"RayQueryKHR",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "None",
 			},
 		},
 		Bases:      []*OperandKind {},
@@ -24628,20 +33481,363 @@
 			Enumerant{
 				Enumerant:    "RayQueryCandidateIntersectionTriangleKHR",
 				Value:        0,
-				Capabilities: []string{"RayQueryProvisionalKHR",},
+				Capabilities: []string{"RayQueryKHR",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "None",
 			},
 			Enumerant{
 				Enumerant:    "RayQueryCandidateIntersectionAABBKHR",
 				Value:        1,
-				Capabilities: []string{"RayQueryProvisionalKHR",},
+				Capabilities: []string{"RayQueryKHR",},
 				Parameters:   []Parameter{},
-				Version:      "",
+				Version:      "None",
 			},
 		},
 		Bases:      []*OperandKind {},
 	}
+	OperandKindPackedVectorFormat = &OperandKind {
+		Kind:       "PackedVectorFormat",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "PackedVectorFormat4x8Bit",
+				Value:        0,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "1.6",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindCooperativeMatrixOperands = &OperandKind {
+		Kind:       "CooperativeMatrixOperands",
+		Category:   "BitEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "NoneKHR",
+				Value:        0x0000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MatrixASignedComponentsKHR",
+				Value:        0x0001,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MatrixBSignedComponentsKHR",
+				Value:        0x0002,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MatrixCSignedComponentsKHR",
+				Value:        0x0004,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MatrixResultSignedComponentsKHR",
+				Value:        0x0008,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "SaturatingAccumulationKHR",
+				Value:        0x0010,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindCooperativeMatrixLayout = &OperandKind {
+		Kind:       "CooperativeMatrixLayout",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "RowMajorKHR",
+				Value:        0,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ColumnMajorKHR",
+				Value:        1,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RowBlockedInterleavedARM",
+				Value:        4202,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ColumnBlockedInterleavedARM",
+				Value:        4203,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindCooperativeMatrixUse = &OperandKind {
+		Kind:       "CooperativeMatrixUse",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "MatrixAKHR",
+				Value:        0,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MatrixBKHR",
+				Value:        1,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "MatrixAccumulatorKHR",
+				Value:        2,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindCooperativeMatrixReduce = &OperandKind {
+		Kind:       "CooperativeMatrixReduce",
+		Category:   "BitEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "Row",
+				Value:        0x0001,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "Column",
+				Value:        0x0002,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "2x2",
+				Value:        0x0004,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindTensorClampMode = &OperandKind {
+		Kind:       "TensorClampMode",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "Undefined",
+				Value:        0,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "Constant",
+				Value:        1,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ClampToEdge",
+				Value:        2,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "Repeat",
+				Value:        3,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "RepeatMirrored",
+				Value:        4,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindTensorAddressingOperands = &OperandKind {
+		Kind:       "TensorAddressingOperands",
+		Category:   "BitEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "None",
+				Value:        0x0000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "TensorView",
+				Value:        0x0001,
+				Capabilities: []string{"CooperativeMatrixTensorAddressingNV",},
+				Parameters:   []Parameter{{OperandKindIdRef, ""},},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "DecodeFunc",
+				Value:        0x0002,
+				Capabilities: []string{"CooperativeMatrixBlockLoadsNV",},
+				Parameters:   []Parameter{{OperandKindIdRef, ""},},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindInitializationModeQualifier = &OperandKind {
+		Kind:       "InitializationModeQualifier",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "InitOnDeviceReprogramINTEL",
+				Value:        0,
+				Capabilities: []string{"GlobalVariableFPGADecorationsINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "InitOnDeviceResetINTEL",
+				Value:        1,
+				Capabilities: []string{"GlobalVariableFPGADecorationsINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindLoadCacheControl = &OperandKind {
+		Kind:       "LoadCacheControl",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "UncachedINTEL",
+				Value:        0,
+				Capabilities: []string{"CacheControlsINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "CachedINTEL",
+				Value:        1,
+				Capabilities: []string{"CacheControlsINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "StreamingINTEL",
+				Value:        2,
+				Capabilities: []string{"CacheControlsINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "InvalidateAfterReadINTEL",
+				Value:        3,
+				Capabilities: []string{"CacheControlsINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "ConstCachedINTEL",
+				Value:        4,
+				Capabilities: []string{"CacheControlsINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindStoreCacheControl = &OperandKind {
+		Kind:       "StoreCacheControl",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "UncachedINTEL",
+				Value:        0,
+				Capabilities: []string{"CacheControlsINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "WriteThroughINTEL",
+				Value:        1,
+				Capabilities: []string{"CacheControlsINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "WriteBackINTEL",
+				Value:        2,
+				Capabilities: []string{"CacheControlsINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+			Enumerant{
+				Enumerant:    "StreamingINTEL",
+				Value:        3,
+				Capabilities: []string{"CacheControlsINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindNamedMaximumNumberOfRegisters = &OperandKind {
+		Kind:       "NamedMaximumNumberOfRegisters",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+			Enumerant{
+				Enumerant:    "AutoINTEL",
+				Value:        0,
+				Capabilities: []string{"RegisterLimitsINTEL",},
+				Parameters:   []Parameter{},
+				Version:      "None",
+			},
+		},
+		Bases:      []*OperandKind {},
+	}
+	OperandKindFPEncoding = &OperandKind {
+		Kind:       "FPEncoding",
+		Category:   "ValueEnum",
+		Enumerants: []Enumerant {
+		},
+		Bases:      []*OperandKind {},
+	}
 	OperandKindIdResultType = &OperandKind {
 		Kind:       "IdResultType",
 		Category:   "Id",
@@ -24691,6 +33887,13 @@
 		},
 		Bases:      []*OperandKind {},
 	}
+	OperandKindLiteralFloat = &OperandKind {
+		Kind:       "LiteralFloat",
+		Category:   "Literal",
+		Enumerants: []Enumerant {
+		},
+		Bases:      []*OperandKind {},
+	}
 	OperandKindLiteralContextDependentNumber = &OperandKind {
 		Kind:       "LiteralContextDependentNumber",
 		Category:   "Literal",
@@ -24738,6 +33941,13 @@
 		Category:   "BitEnum",
 		Enumerants: []Enumerant {
 			Enumerant{
+				Enumerant:    "None",
+				Value:        0x0000,
+				Capabilities: []string{},
+				Parameters:   []Parameter{},
+				Version:      "",
+			},
+			Enumerant{
 				Enumerant:    "FlagIsProtected",
 				Value:        0x01,
 				Capabilities: []string{},